commit e7179fad45888f3afa25093eadcd8677f949bccf Author: LAX1DUDE Date: Sun Dec 25 01:12:28 2022 -0800 Update #0 - First Release diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e3813a4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# + +*.bat text eol=crlf +*.sh text eol=lf +gradlew text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cb59d98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +buildtools_config.json +MinecraftSrc.zip +/MinecraftSrc/* +/pullrequest_merged_* +/patches.* +/mcp918/* +!/mcp918/readme.txt +!/mcp918/assetsIndexTransformer.json +/rundir/* +/##TEAVM.TMP##/* \ No newline at end of file diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..7f6ab47 --- /dev/null +++ b/CREDITS @@ -0,0 +1,553 @@ + + EaglercraftX Developers + ~~~~~~~~~~~~~~~~~~~~~~~ + + lax1dude: + + - Creator of Eaglercraft + - Wrote HW accelerated OpenGL 1.3 emulator + - Wrote all desktop emulation code + - Ported the Minecraft 1.8 client src to TeaVM + - Wrote EaglercraftXBungee + - Wrote the patch and build system + + ayunami2000: + + - Many bug fixes + - Added resource packs + - Added screen recording + - Created the replit + + + + Code used within EaglercraftX + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: TeaVM + Project Author: Alexey Andreev + Project URL: https://teavm.org/ + + Used For: Compiling Java to JS, JRE implementation + + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: LWJGL 3 + Project Author: Spasi + Project URL: https://www.lwjgl.org + + Used For: OpenGL, OpenAL, and GLFW bindings in desktop debug runtime + + * Copyright (c) 2012-present Lightweight Java Game Library + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name Lightweight Java Game Library nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: java-diff-utils + Project Author: Google, forked by wumpz + Project URL: https://java-diff-utils.github.io/java-diff-utils/ + + Used For: generating and applying patch files in build system + + * Copyright 2009-2017 java-diff-utils. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Google Guava + Project Author: Google + Project URL: https://github.com/google/guava + + Used For: It's a dependency for Minecraft 1.8 + + * 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. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: javax.annotation + Project Author: Oracle Inc. + Project URL: ?? + + Used For: Dependency for Google Guava + + * Copyright (c) 2005-2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://oss.oracle.com/licenses/CDDL+GPL-1.1 + * or LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Commons Lang + Project Author: Apache Software Foundation + Project URL: https://commons.apache.org/proper/commons-lang/ + + Used For: It's a dependency for Minecraft 1.8 + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Commons IO + Project Author: Apache Software Foundation + Project URL: https://commons.apache.org/proper/commons-io/ + + Used For: simplifying file IO in build system + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Commons CSV + Project Author: Apache Software Foundation + Project URL: https://commons.apache.org/proper/commons-csv/ + + Used For: loading mod coder pack config files in build system + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: JSON-java + Project Author: Sean Leary (stleary) + Project URL: https://github.com/stleary/JSON-java + + Used For: JSON serialization and parsing in client and build system + + * Public domain. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Eclipse IDE Java Formatter + Project Author: Eclipse Foundation + Project URL: https://www.eclipse.org/ + + Used For: Formatting source code in build system before making diffs + + * License is here: https://www.eclipse.org/legal/epl-2.0/ + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: ObjectWeb ASM + Project Author: OW2 + Project URL: https://asm.ow2.io/ + + Used For: parsing method signatures in build system + + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Bouncy Castle Crypto + Project Author: The Legion of the Bouncy Castle + Project URL: https://www.bouncycastle.org/java.html + + Used For: MD5, SHA-1, SHA-256 implementations + + * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Harmony JRE + Project Author: Apache Software Foundation + Project URL: https://harmony.apache.org/ + + Used For: TeaVM compatible String.format implementation + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Apache Commons Codec + Project Author: Apache Software Foundation + Project URL: https://commons.apache.org/proper/commons-codec/ + + Used For: Base64 encoder/decoder implementation + + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: JZlib + Project Author: ymnk, JCraft Inc. + Project URL: http://www.jcraft.com/jzlib/ + + Used For: Deflate and GZIP implementations in client + + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + * INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Java-WebSocket + Project Author: Nathan Rajlich (TooTallNate) + Project URL: http://tootallnate.github.io/Java-WebSocket + + Used For: WebSockets in desktop runtime + + * Copyright (c) 2010-2020 Nathan Rajlich + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: Netty + Project Author: Netty Project + Project URL: https://netty.io/ + + Used For: 'ByteBuf' classes implementations for Minecraft 1.8's networking engine + + * Copyright 2015 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: 3D Sound System + Project Author: Paul Lamb + Project URL: http://www.paulscode.com/forum/index.php?topic=4.0 + + Used For: Audio in desktop runtime + + * SoundSystem License: + * + * You are free to use this library for any purpose, commercial or + * otherwise. You may modify this library or source code, and distribute it any + * way you like, provided the following conditions are met: + * + * 1) You must abide by the conditions of the aforementioned LWJGL License. + * + * 2) You may not falsely claim to be the author of this library or any + * unmodified portion of it. + * + * 3) You may not copyright this library or a modified version of it and then + * sue me for copyright infringement. + * + * 4) If you modify the source code, you must clearly document the changes made + * before redistributing the modified source code, so other users know it is not + * the original code. + * + * 5) You are not required to give me credit for this library in any derived + * work, but if you do, you must also mention my website: + * http://www.paulscode.com + * + * 6) I the author will not be responsible for any damages (physical, financial, + * or otherwise) caused by the use if this library or any part of it. + * + * 7) I the author do not guarantee, warrant, or make any representations, + * either expressed or implied, regarding the use of this library or any part of + * it. + * + * Author: Paul Lamb + * http://www.paulscode.com + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: JOrbis + Project Author: ymnk, JCraft Inc. + Project URL: http://www.jcraft.com/jorbis/ + + Used For: Audio in desktop runtime + + * JOrbis + * Copyright (C) 2000 ymnk, JCraft,Inc. + * + * Written by: 2000 ymnk + * + * Many thanks to + * Monty and + * The XIPHOPHORUS Company http://www.xiph.org/ . + * JOrbis has been based on their awesome works, Vorbis codec. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Project Name: sqlite-jdbc + Project Author: Taro L. Saito (xerial) + Project URL: https://github.com/xerial/sqlite-jdbc + + Used For: Default skin cache and authentication JDBC driver in EaglerXBungee + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + Make sure you also update the copy of this file in "sources/resources/assets/eagler/CREDITS" + + The content of both files should match, but not include this notice in the resources one diff --git a/CompileLatestClient.bat b/CompileLatestClient.bat new file mode 100644 index 0000000..e1f1a07 --- /dev/null +++ b/CompileLatestClient.bat @@ -0,0 +1,6 @@ +@echo off +title CompileLatestClient +java -cp "buildtools/BuildTools.jar" net.lax1dude.eaglercraft.v1_8.buildtools.gui.CompileLatestClientGUI +del /S /Q "##TEAVM.TMP##\*" +rmdir /S /Q "##TEAVM.TMP##" +pause \ No newline at end of file diff --git a/CompileLatestClient.sh b/CompileLatestClient.sh new file mode 100644 index 0000000..eddf1a9 --- /dev/null +++ b/CompileLatestClient.sh @@ -0,0 +1,3 @@ +#!/bin/sh +java -cp "buildtools/BuildTools.jar" net.lax1dude.eaglercraft.v1_8.buildtools.gui.CompileLatestClientGUI +rm -rf "##TEAVM.TMP##" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b427344 --- /dev/null +++ b/LICENSE @@ -0,0 +1,64 @@ + +Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + +WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES NORMALLY +FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED TO SHARE, +DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE SOFTWARE IN THIS +REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + +NOT FOR COMMERCIAL OR MALICIOUS USE + +Prohibited sharing, distribution, and repurposing these files includes but is not +limited to: Publicly sharing or distributing any significant portion, modified or +unmodified, of any source code files produced by the buildtools commands without +minification or obfuscation, with the exception of patch files. Publicly +repurposing, without permission, the source code for any file which is a part of +the Eaglercraft runtime, as in any additional source files required for output of +buildtools commands to run on a specific platform. Publicly repurposing, without +permission, any resource file found in the project's default resource pack that +does not exist in vanilla Minecraft resource packs. And, additionally, publicly +repurposing, without permission, any data produced by the act of compiling or +converting the files described in these previous three cases, besides the compiled +or minified application (the 'game') itself. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +Prohibited commercial use includes selling access to any files or data (including +the game itself) contained or produced by this repository, requiring the direct +forced viewing of advertisement presented when a user attempts to load a compiled +version of this project (or follow a hyperlink to the project's source or files +derived from it, examples include using adfoc.us or adf.ly on your site), and +selling content in any form that can only be accessed through the use of this +project or files produced by or derived from it. Making a pay-to-win multiplayer +server, for example, would be prohibited under these terms. + +Malicious use includes creating and/or distributing modified versions of the +game's 1.8 client (often referred to as "hacked clients") which give you an unfair +advantage on multiplayer servers, creating and/or distributing modified clients +which allow you to exploit bugs in EaglercraftXBungee or Eagler-compatible server +software, creating and/or distributing modified clients which allow you to exploit +bugs in Minecraft servers or Minecraft-compatible server software, or creating +and/or distributing modified clients which allow you to exploit bugs in portions of +any other software used in companion with EaglerXBungee or on an Eagler-compatible +server (e.g. VIAaaS, NGINX, Caddy) + +Distribution of any file or a product of compilation or conversion covered by this +document must retain all existing copyright notices found within the file and +folders before any modifications were made. + +BY VIOLATING THESE TERMS YOU AGREE TO A BLOCK/BAN FROM ALL SOURCES OF DOCUMENTATION +AND SUPPORT FOR THIS PROJECT, PERMISSION TO USE THESE FILES WAS GIVEN TO YOU IN +GOOD FAITH THAT THE WORK WILL NOT BE ABUSED, AND VIOLATING THIS AGREEMENT IS A +DEMONSTRATION OF A LACK OF RESPECT AND GOOD INTENTIONS ON BEHALF OF YOUR EFFORTS TO +LEARN HOW TO USE THESE TOOLS CORRECTLY AND IS JUSTIFICATION FOR YOUR EXCLUSION FROM +ANY AND ALL SOURCES OF DOCUMENTATION OR SUPPORT THAT WILL ASSIST YOU TO CONTINUE TO +VIOLATE THESE TERMS diff --git a/README.md b/README.md new file mode 100644 index 0000000..05cbf61 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# EaglercraftX 1.8 + +### Play real Minecraft 1.8 in your browser, currently only supports multiplayer + +![EaglercraftX 1.8 Screenshot Main Menu](https://g.deev.is/eaglercraft/eaglerx-480p.png) + +## ATTENTION MOJANG/MICROSOFT EMPLOYEE ASSIGNED TO STALK ME: + +### THIS REPOSITORY DOES NOT CONTAIN YOUR INTELLECTUAL PROPERTY + +### FILING A FALSE DMCA IS ILLEGAL AND IMMORAL + +### This repository contains: + + - **Utilities to decompile Minecraft 1.8 and apply patch files to it** + - **Source code to provide the LWJGL keyboard, mouse, and OpenGL APIs in a browser** + - **Source code for an OpenGL 1.3 emulator built on top of WebGL 2.0** + - **Patch files to mod the Minecraft 1.8 source code to make it browser compatible** + - **Browser-modified portions of Minecraft 1.8's open-source dependencies** + - **Plugins for Minecraft servers to allow the eagler client to connect to them** + +### This repository does NOT contain: + + - **Any portion of the decompiled Minecraft 1.8 source code or resources** + - **Any portion of Mod Coder Pack and it's config files** + - **Data that can be used alone to reconstruct portions of the game's source code** + - **Code configured by default to allow users to play without owning a copy of Minecraft** + +## Getting Started: + +### To compile the latest version of the client, on Windows: + +1. Make sure you have at least Java 11 installed and added to your PATH +2. Download (clone) this repository to your computer +3. Double click `CompileLatestClient.bat`, a GUI resembling a classic windows installer should open +4. Follow the steps shown to you in the new window to finish compiling + +### To compile the latest version of the client, on Linux/macOS: + +1. Make sure you have at least Java 11 installed +2. Download (clone) this repository to your computer +3. Open a terminal in the folder the repository was cloned to +4. Type `chmod +x CompileLatestClient.sh` and hit enter +5. Type `./CompileLatestClient.sh` and hit enter, a GUI resembling a classic windows installer should open +6. Follow the steps shown to you in the new window to finish compiling + +## Making a Server: + +**EaglercraftX 1.8's server is a BungeeCord/Waterfall PLUGIN, not an entire "fork" of bungeecord like the 1.5 Eaglerbungee was, and I can't believe I have to clarify this too but the EaglerXBungee 1.8 plugin is not compatible with the old 1.5 bungee, you must migrate to the latest version of official BungeeCord/Waterfall to use it** + +Simply set up the latest version of BungeeCord or Waterfall and download [EaglerXBungee-Latest.jar](https://gitlab.com/lax1dude/eaglercraftx-1.8/-/raw/main/gateway/EaglercraftXBungee/EaglerXBungee-Latest.jar) and place it in the plugins directory. + +Then to actually log in to the server with Eaglercraft, first join your server using vanilla Minecraft Java Edition 1.8 and run the new `/eagler` command to set a password. Then leave the server and switch to your EaglercraftX client. + +Set your EaglercraftX username to the same username as the vanilla minecraft account you set the password with, then when you try to join your server it will present you with a login screen where you can enter the password you set. If the password is correct it will let you join the server. + +**NOTE: If you set `online_mode` to `false` on BungeeCord/Waterfall's config.yml, the password system will be disabled and you will be able to join with any username without setting a password like Eaglercraft 1.5. This should only ever be used for testing.** + +A config guide will be added here too eventually + +## Contributing: + +This part of the guide is incomplete + +## Developing a Client: + +There is currently no system in place to make forks of 1.8 and merge commits made to the patch files in this repository with the patch files or workspace of the fork, you're on your own if you try to keep a fork of this repo for reasons other than to contribute to it diff --git a/build_clean_tmp.bat b/build_clean_tmp.bat new file mode 100644 index 0000000..ab67fe8 --- /dev/null +++ b/build_clean_tmp.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: clean +java -jar buildtools/BuildTools.jar clean +pause \ No newline at end of file diff --git a/build_clean_tmp.sh b/build_clean_tmp.sh new file mode 100644 index 0000000..efc9579 --- /dev/null +++ b/build_clean_tmp.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar clean \ No newline at end of file diff --git a/build_help.bat b/build_help.bat new file mode 100644 index 0000000..7261a76 --- /dev/null +++ b/build_help.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: help +java -jar buildtools/BuildTools.jar help +pause \ No newline at end of file diff --git a/build_help.sh b/build_help.sh new file mode 100644 index 0000000..920dd43 --- /dev/null +++ b/build_help.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar help \ No newline at end of file diff --git a/build_init.bat b/build_init.bat new file mode 100644 index 0000000..e6a66f5 --- /dev/null +++ b/build_init.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: init +java -jar buildtools/BuildTools.jar init +pause \ No newline at end of file diff --git a/build_init.sh b/build_init.sh new file mode 100644 index 0000000..56581db --- /dev/null +++ b/build_init.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar init \ No newline at end of file diff --git a/build_make_pullrequest.bat b/build_make_pullrequest.bat new file mode 100644 index 0000000..b74baf4 --- /dev/null +++ b/build_make_pullrequest.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: pullrequest +java -jar buildtools/BuildTools.jar pullrequest +pause \ No newline at end of file diff --git a/build_make_pullrequest.sh b/build_make_pullrequest.sh new file mode 100644 index 0000000..7b8dd53 --- /dev/null +++ b/build_make_pullrequest.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar pullrequest \ No newline at end of file diff --git a/build_make_unpatched.bat b/build_make_unpatched.bat new file mode 100644 index 0000000..32855a5 --- /dev/null +++ b/build_make_unpatched.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: unpatched +java -jar buildtools/BuildTools.jar unpatched +pause \ No newline at end of file diff --git a/build_make_unpatched.sh b/build_make_unpatched.sh new file mode 100644 index 0000000..48d88e3 --- /dev/null +++ b/build_make_unpatched.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar unpatched \ No newline at end of file diff --git a/build_make_workspace.bat b/build_make_workspace.bat new file mode 100644 index 0000000..33a2d78 --- /dev/null +++ b/build_make_workspace.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: workspace +java -jar buildtools/BuildTools.jar workspace +pause \ No newline at end of file diff --git a/build_make_workspace.sh b/build_make_workspace.sh new file mode 100644 index 0000000..b5e156b --- /dev/null +++ b/build_make_workspace.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar workspace \ No newline at end of file diff --git a/build_merge_direct.bat b/build_merge_direct.bat new file mode 100644 index 0000000..aeb3318 --- /dev/null +++ b/build_merge_direct.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: merge_direct +java -jar buildtools/BuildTools.jar merge_direct +pause \ No newline at end of file diff --git a/build_merge_direct.sh b/build_merge_direct.sh new file mode 100644 index 0000000..49d09e0 --- /dev/null +++ b/build_merge_direct.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar merge_direct \ No newline at end of file diff --git a/build_merge_pullrequest.bat b/build_merge_pullrequest.bat new file mode 100644 index 0000000..f08d0e9 --- /dev/null +++ b/build_merge_pullrequest.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: merge +java -jar buildtools/BuildTools.jar merge +pause \ No newline at end of file diff --git a/build_merge_pullrequest.sh b/build_merge_pullrequest.sh new file mode 100644 index 0000000..01730f1 --- /dev/null +++ b/build_merge_pullrequest.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar merge \ No newline at end of file diff --git a/build_test_pullrequest.bat b/build_test_pullrequest.bat new file mode 100644 index 0000000..a6fcb37 --- /dev/null +++ b/build_test_pullrequest.bat @@ -0,0 +1,4 @@ +@echo off +title BuildTools: pullrequest_test +java -jar buildtools/BuildTools.jar pullrequest_test +pause \ No newline at end of file diff --git a/build_test_pullrequest.sh b/build_test_pullrequest.sh new file mode 100644 index 0000000..3fdfcf4 --- /dev/null +++ b/build_test_pullrequest.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -jar buildtools/BuildTools.jar pullrequest_test \ No newline at end of file diff --git a/buildtools/BuildTools.jar b/buildtools/BuildTools.jar new file mode 100644 index 0000000..20885f5 Binary files /dev/null and b/buildtools/BuildTools.jar differ diff --git a/buildtools/Java11Check.jar b/buildtools/Java11Check.jar new file mode 100644 index 0000000..c9194da Binary files /dev/null and b/buildtools/Java11Check.jar differ diff --git a/buildtools/MAINFEST-README-PLEASE.txt b/buildtools/MAINFEST-README-PLEASE.txt new file mode 100644 index 0000000..868b1e4 --- /dev/null +++ b/buildtools/MAINFEST-README-PLEASE.txt @@ -0,0 +1,4 @@ + +when you export the BuildTools source as a jar you must copy the "MANIFEST.MF" file in this directory into the "META-INF" directory inside the JAR!! + +reasons: limitations in eclipse/intellij, lazyness diff --git a/buildtools/MANIFEST.MF b/buildtools/MANIFEST.MF new file mode 100644 index 0000000..dd4cfea --- /dev/null +++ b/buildtools/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Main-Class: net.lax1dude.eaglercraft.v1_8.buildtools.EaglerBuildTools +Class-Path: Java11Check.jar deps/asm-signature.jar deps/commons-csv-1.9.0.jar deps/commons-io-2.11.0.jar deps/eclipse-formatter.jar deps/java-diff-utils-4.11.jar deps/json-20220320.jar + diff --git a/buildtools/TeaVMBridge.jar b/buildtools/TeaVMBridge.jar new file mode 100644 index 0000000..ed38b21 Binary files /dev/null and b/buildtools/TeaVMBridge.jar differ diff --git a/buildtools/deps/asm-signature.jar b/buildtools/deps/asm-signature.jar new file mode 100644 index 0000000..b5c7c74 Binary files /dev/null and b/buildtools/deps/asm-signature.jar differ diff --git a/buildtools/deps/commons-csv-1.9.0-javadoc.jar b/buildtools/deps/commons-csv-1.9.0-javadoc.jar new file mode 100644 index 0000000..0d03f8b Binary files /dev/null and b/buildtools/deps/commons-csv-1.9.0-javadoc.jar differ diff --git a/buildtools/deps/commons-csv-1.9.0.jar b/buildtools/deps/commons-csv-1.9.0.jar new file mode 100644 index 0000000..0e3f678 Binary files /dev/null and b/buildtools/deps/commons-csv-1.9.0.jar differ diff --git a/buildtools/deps/commons-io-2.11.0-javadoc.jar b/buildtools/deps/commons-io-2.11.0-javadoc.jar new file mode 100644 index 0000000..7ca3a96 Binary files /dev/null and b/buildtools/deps/commons-io-2.11.0-javadoc.jar differ diff --git a/buildtools/deps/commons-io-2.11.0.jar b/buildtools/deps/commons-io-2.11.0.jar new file mode 100644 index 0000000..be507d9 Binary files /dev/null and b/buildtools/deps/commons-io-2.11.0.jar differ diff --git a/buildtools/deps/eclipse-formatter.jar b/buildtools/deps/eclipse-formatter.jar new file mode 100644 index 0000000..f7ebb6d Binary files /dev/null and b/buildtools/deps/eclipse-formatter.jar differ diff --git a/buildtools/deps/java-diff-utils-4.11-javadoc.jar b/buildtools/deps/java-diff-utils-4.11-javadoc.jar new file mode 100644 index 0000000..bed4587 Binary files /dev/null and b/buildtools/deps/java-diff-utils-4.11-javadoc.jar differ diff --git a/buildtools/deps/java-diff-utils-4.11.jar b/buildtools/deps/java-diff-utils-4.11.jar new file mode 100644 index 0000000..25ff1b9 Binary files /dev/null and b/buildtools/deps/java-diff-utils-4.11.jar differ diff --git a/buildtools/deps/json-20220320-javadoc.jar b/buildtools/deps/json-20220320-javadoc.jar new file mode 100644 index 0000000..03d1342 Binary files /dev/null and b/buildtools/deps/json-20220320-javadoc.jar differ diff --git a/buildtools/deps/json-20220320.jar b/buildtools/deps/json-20220320.jar new file mode 100644 index 0000000..6121978 Binary files /dev/null and b/buildtools/deps/json-20220320.jar differ diff --git a/buildtools/production-favicon.png b/buildtools/production-favicon.png new file mode 100644 index 0000000..9c9fc89 Binary files /dev/null and b/buildtools/production-favicon.png differ diff --git a/buildtools/production-index-ext.html b/buildtools/production-index-ext.html new file mode 100644 index 0000000..faa27f5 --- /dev/null +++ b/buildtools/production-index-ext.html @@ -0,0 +1,48 @@ + + + + + + + + EaglercraftX 1.8 + + + + + + + + + + + + + \ No newline at end of file diff --git a/buildtools/production-index.html b/buildtools/production-index.html new file mode 100644 index 0000000..331f51f --- /dev/null +++ b/buildtools/production-index.html @@ -0,0 +1,52 @@ + + + + + + + + EaglercraftX 1.8 + + + + + + + + + + + + + \ No newline at end of file diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildTools.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildTools.java new file mode 100644 index 0000000..234ef1e --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildTools.java @@ -0,0 +1,167 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; + +import net.lax1dude.eaglercraft.v1_8.buildtools.task.diff.MergePullRequest; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.diff.PullRequestTask; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.CreateUnpatched; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.InitTask; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.SetupWorkspace; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.TaskClean; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EaglerBuildTools { + + public static File repositoryRoot = new File("."); + + public static void main(String[] args) { + System.out.println("Eaglercraft 1.8 Build Tools"); + System.out.println("Copyright (c) 2022 LAX1DUDE"); + System.out.println(); + + if(!System.getProperty("eaglercraft.isJava11", "false").equalsIgnoreCase("true")) { + try { + if (!(boolean) Class + .forName("net.lax1dude.eaglercraft.v1_8.buildtools.Java11Check", true, + new URLClassLoader(new URL[] { (new File("buildtools/Java11Check.jar")).toURI().toURL() })) + .getMethod("classLoadCheck").invoke(null)) { + throw new RuntimeException("wtf?"); + } + }catch(Throwable t) { + System.err.println("ERROR: A minimum of Java 11 is required to run this tool!"); + System.err.println(); + System.err.println("You are using Java " + System.getProperty("java.version")); + System.err.println(); + return; + } + } + + if(args.length == 0 || (args.length == 1 && args[0].equalsIgnoreCase("help"))) { + System.out.println("Options:"); + System.out.println(" help - displays this message"); + System.out.println(" init - decompiles 1.8.8 and applies the main repo's patch files"); + System.out.println(" workspace - creates a dev workspace with a gradle project to compile the source"); + System.out.println(" pullrequest - scans changes in the dev workspace and creates patch files for pull requests"); + System.out.println(" pullrequest_test - makes new workspace and re-applies the patches in 'pullrequest'"); + System.out.println(" unpatched - creates a zip file with the vanilla minecraft source without patches"); + System.out.println(" merge - merges the patch files in the pullrequest folder with the repo's main patch files"); + System.out.println(" merge_direct - merges changes in the dev workspace with the repo's main patch files"); + System.out.println(" clean - delete init and pullrequest directories, keeps dev workspace"); + System.out.println(); + }else if(args.length == 1 && args[0].equalsIgnoreCase("init")) { + LicensePrompt.display(); + System.out.println("Running task '" + args[0] + "':"); + System.out.println(); + if(InitTask.initTask()) { + System.out.println(); + System.out.println("Task Complete."); + System.out.println(); + }else { + System.err.println(); + System.err.println("Task Failed!"); + System.err.println(); + } + }else if(args.length == 1 && args[0].equalsIgnoreCase("workspace")) { + System.out.println("Running task '" + args[0] + "':"); + System.out.println(); + if(SetupWorkspace.setupWorkspace()) { + System.out.println(); + System.out.println("Task Complete."); + System.out.println(); + }else { + System.err.println(); + System.err.println("Task Failed!"); + System.err.println(); + } + }else if(args.length == 1 && args[0].equalsIgnoreCase("pullrequest")) { + System.out.println("Running task '" + args[0] + "':"); + System.out.println(); + if(PullRequestTask.pullRequest()) { + System.out.println(); + System.out.println("Task Complete."); + System.out.println(); + }else { + System.err.println(); + System.err.println("Task Failed!"); + System.err.println(); + } + }else if(args.length == 1 && args[0].equalsIgnoreCase("pullrequest_test")) { + System.out.println("Running task '" + args[0] + "':"); + System.out.println(); + if(SetupWorkspace.pullRequestTest()) { + System.out.println(); + System.out.println("Task Complete."); + System.out.println(); + }else { + System.err.println(); + System.err.println("Task Failed!"); + System.err.println(); + } + }else if(args.length == 1 && args[0].equalsIgnoreCase("unpatched")) { + System.out.println("Running task '" + args[0] + "':"); + System.out.println(); + if(CreateUnpatched.createUnpatched()) { + System.out.println(); + System.out.println("Task Complete."); + System.out.println(); + }else { + System.err.println(); + System.err.println("Task Failed!"); + System.err.println(); + } + }else if(args.length == 1 && args[0].equalsIgnoreCase("merge")) { + System.out.println("Running task '" + args[0] + "':"); + System.out.println(); + if(MergePullRequest.mergeTask()) { + System.out.println(); + System.out.println("Task Complete."); + System.out.println(); + }else { + System.err.println(); + System.err.println("Task Failed!"); + System.err.println(); + } + }else if(args.length == 1 && args[0].equalsIgnoreCase("merge_direct")) { + System.out.println("Running task '" + args[0] + "':"); + System.out.println(); + if(MergePullRequest.mergeDirect()) { + System.out.println(); + System.out.println("Task Complete."); + System.out.println(); + }else { + System.err.println(); + System.err.println("Task Failed!"); + System.err.println(); + } + }else if(args.length == 1 && args[0].equalsIgnoreCase("clean")) { + System.out.println("Running task '" + args[0] + "':"); + System.out.println(); + if(TaskClean.taskClean()) { + System.out.println(); + System.out.println("Task Complete."); + System.out.println(); + }else { + System.err.println(); + System.err.println("Task Failed!"); + System.err.println(); + } + }else { + System.err.println("Invalid arguments!"); + } + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildToolsConfig.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildToolsConfig.java new file mode 100644 index 0000000..f70957a --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/EaglerBuildToolsConfig.java @@ -0,0 +1,183 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +import org.json.JSONObject; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EaglerBuildToolsConfig { + + public static File temporary_directory = new File(System.getProperty("user.home"), ".eaglercraft_1.8_buildtools"); + private static boolean temporary_directory_isInit = false; + private static boolean temporary_directory_mentioned = false; + + public static File workspace_directory = new File("../eaglercraft_1.8_workspace"); + private static boolean workspace_directory_isInit = false; + private static boolean workspace_directory_mentioned = false; + + private static boolean config_file_loaded = false; + + public static final File configFile = new File("./buildtools_config.json"); + + public static void load() { + if(configFile.exists()) { + try(FileInputStream is = new FileInputStream(configFile)) { + byte[] r = new byte[(int)configFile.length()]; + is.read(r); + is.close(); + String jsonTxt = new String(r, StandardCharsets.UTF_8); + JSONObject obj = new JSONObject(jsonTxt); + String path = obj.optString("temporary_directory", null); + if(path != null) { + temporary_directory = new File(path); + temporary_directory_isInit = true; + } + path = obj.optString("workspace_directory", null); + if(path != null) { + workspace_directory = new File(path); + workspace_directory_isInit = true; + } + }catch(Throwable ex) { + System.err.println("Failed to read config!"); + ex.printStackTrace(); + } + } + } + + public static void save() { + JSONObject obj = new JSONObject(); + if(temporary_directory_isInit) obj.put("temporary_directory", temporary_directory.getAbsolutePath()); + if(workspace_directory_isInit) obj.put("workspace_directory", workspace_directory.getAbsoluteFile()); + try(FileOutputStream os = new FileOutputStream(configFile)) { + os.write(obj.toString(4).getBytes(StandardCharsets.UTF_8)); + os.close(); + }catch(IOException e) { + System.err.println("Failed to write config!"); + e.printStackTrace(); + } + } + + private static void mentionConfigPath() { + System.out.println("Edit '" + configFile.getName() + "' to change"); + } + + public static File getTemporaryDirectory() { + if(!config_file_loaded) { + load(); + config_file_loaded = true; + } + if(!temporary_directory_isInit) { + File f = temporary_directory; + System.out.println(); + System.out.println("Using temporary directory: " + f.getAbsolutePath()); + temporary_directory_mentioned = true; + f = askIfChangeIsWanted(f); + temporary_directory = f; + temporary_directory_isInit = true; + while(!temporary_directory.isDirectory() && !temporary_directory.mkdirs()) { + System.err.println("Failed to create: " + f.getAbsolutePath()); + temporary_directory = askIfChangeIsWanted(f); + } + save(); + System.out.println(); + return temporary_directory; + }else { + if(!temporary_directory_mentioned) { + System.out.println("Using temporary directory: " + temporary_directory.getAbsolutePath()); + temporary_directory_mentioned = true; + while(!temporary_directory.isDirectory() && !temporary_directory.mkdirs()) { + System.err.println("Failed to create: " + temporary_directory.getAbsolutePath()); + temporary_directory = askIfChangeIsWanted(temporary_directory); + } + mentionConfigPath(); + } + return temporary_directory; + } + } + + public static File getWorkspaceDirectory() { + if(!config_file_loaded) { + load(); + config_file_loaded = true; + } + if(!workspace_directory_isInit) { + File f = workspace_directory; + System.out.println(); + System.out.println("Using workspace directory: " + f.getAbsolutePath()); + workspace_directory_mentioned = true; + f = askIfChangeIsWanted(f); + workspace_directory = f; + workspace_directory_isInit = true; + while(!workspace_directory.isDirectory() && !workspace_directory.mkdirs()) { + System.err.println("Failed to create: " + f.getAbsolutePath()); + workspace_directory = askIfChangeIsWanted(f); + } + save(); + System.out.println(); + return workspace_directory; + }else { + if(!workspace_directory_mentioned) { + System.out.println("Using workspace directory: " + workspace_directory.getAbsolutePath()); + workspace_directory_mentioned = true; + while(!workspace_directory.isDirectory() && !workspace_directory.mkdirs()) { + System.err.println("Failed to create: " + workspace_directory.getAbsolutePath()); + workspace_directory = askIfChangeIsWanted(workspace_directory); + } + mentionConfigPath(); + } + return workspace_directory; + } + } + + public static File askIfChangeIsWanted(File in) { + System.out.println("Would you like to change this directory?"); + System.out.println("Enter 'Y' for yes or 'N' for no: "); + String l = "N"; + + try { + l = (new BufferedReader(new InputStreamReader(System.in))).readLine(); + } catch (IOException e) { + } + + if(l != null && ((l = l.trim()).equalsIgnoreCase("y") || l.equalsIgnoreCase("yes"))) { + System.out.println(); + System.out.println("Type a new filename or hit 'Enter' to browse: "); + try { + l = (new BufferedReader(new InputStreamReader(System.in))).readLine(); + } catch (IOException e) { + } + if(l != null && (l = l.trim()).length() > 0) { + in = new File(l); + }else { + File f = FileChooserTool.load(true); + if(f == null) { + System.out.println("You hit cancel on the file chooser, the directory '" + in.getAbsolutePath() + "' will be used."); + in = askIfChangeIsWanted(in); + }else { + in = f; + } + } + } + + return in; + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/FileChooserTool.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/FileChooserTool.java new file mode 100644 index 0000000..54e056b --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/FileChooserTool.java @@ -0,0 +1,46 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools; + +import java.io.File; + +import javax.swing.JFileChooser; +import javax.swing.JFrame; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class FileChooserTool { + + public static final JFileChooser fc = new JFileChooser(); + + public static File load(boolean directory) { + fc.setFileSelectionMode(directory ? JFileChooser.DIRECTORIES_ONLY : JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + fc.setFileHidingEnabled(false); + fc.setDialogTitle("Eaglercraft Buildtools"); + JFrame parent = new JFrame(); + parent.setBounds(0, 0, 50, 50); + parent.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + parent.setAlwaysOnTop(true); + parent.setTitle("File Chooser"); + parent.setLocationRelativeTo(null); + parent.setVisible(true); + if(fc.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + parent.dispose(); + return fc.getSelectedFile(); + }else { + parent.dispose(); + return null; + } + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/LicensePrompt.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/LicensePrompt.java new file mode 100644 index 0000000..2238ee0 --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/LicensePrompt.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class LicensePrompt { + + public static void main(String[] args) { + System.out.println(); + display(); + } + + public static void display() { + System.out.println("WARNING: You must agree to the LICENSE before running this command"); + System.out.println(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(LicensePrompt.class.getResourceAsStream("/lang/LICENSE_console_wrapped.txt"), StandardCharsets.UTF_8))) { + String line; + while((line = reader.readLine()) != null) { + if(line.equals("")) { + pressEnter(); + }else { + System.out.println(line); + } + } + }catch(IOException ex) { + System.err.println(); + System.err.println("ERROR: could not display license text"); + System.err.println("Please read the \"LICENSE\" file before using this software"); + System.err.println(); + pressEnter(); + } + } + + private static void pressEnter() { + System.out.println(); + System.out.println("(press ENTER to continue)"); + while(true) { + try { + if(System.in.read() == '\n') { + break; + } + }catch(IOException ex) { + throw new RuntimeException("Unexpected IOException reading STDIN", ex); + } + } + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/decompiler/LocalVariableGenerator.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/decompiler/LocalVariableGenerator.java new file mode 100644 index 0000000..2684839 --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/decompiler/LocalVariableGenerator.java @@ -0,0 +1,447 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.decompiler; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.signature.SignatureReader; +import org.objectweb.asm.signature.SignatureVisitor; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class LocalVariableGenerator extends SignatureVisitor { + + public static final Map primitiveNames = new HashMap(); + public static final Set illegalVariableNames = new HashSet(); + + static { + + primitiveNames.put('Z', "Flag"); + primitiveNames.put('C', "Char"); + primitiveNames.put('B', "Byte"); + primitiveNames.put('S', "Short"); + primitiveNames.put('I', "Int"); + primitiveNames.put('F', "Float"); + primitiveNames.put('J', "Long"); + primitiveNames.put('D', "Double"); + primitiveNames.put('V', "Void"); + + illegalVariableNames.addAll(Arrays.asList( + "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", + "continue", "const", "default", "do", "double", "else", "enum", "exports", "extends", + "final", "finally", "float", "for", "goto", "if", "implements", "import", + "instanceof", "int", "interface", "long", "native", "new", "package", "private", + "protected", "public", "return", "short", "static", "strictfp", "super", "switch", + "synchronized", "this", "throw", "throws", "transient", "try", "var", "void", + "volatile", "while", "string" + )); + + } + + private String baseClass = null; + private boolean isArray = false; + private String typeParam1 = null; + private boolean typeParam1IsArray = false; + private String typeParam2 = null; + private boolean typeParam2IsArray = false; + + public static final SignatureVisitor nopVisitor = new SignatureVisitor(Opcodes.ASM5) {}; + + LocalVariableGenerator() { + super(Opcodes.ASM5); + } + + public static String createName(String sig) { + SignatureReader rd = new SignatureReader(sig); + LocalVariableGenerator gen = new LocalVariableGenerator(); + rd.acceptType(gen); + return gen.getResult(); + } + + private String removePath(String in) { + int idx = in.lastIndexOf('/'); + int idx2 = in.lastIndexOf('$'); + if(idx2 > idx && idx2 != in.length() - 1) { + idx = idx2; + } + if(idx != -1) { + in = in.substring(idx + 1); + } + if(in.length() == 0 || Character.isDigit(in.charAt(0))) { + in = "obj" + in; + } + return in; + } + + String getResult() { + String rt; + if(baseClass == null) { + rt = "Object"; + }else { + rt = removePath(baseClass); + } + if(typeParam1 == null && typeParam2 == null) { + if(isArray) { + rt = "ArrayOf" + rt; + } + }else { + if(isArray) { + rt = rt + "Array"; + } + } + if(typeParam1 != null && typeParam2 == null) { + if(typeParam1IsArray) { + typeParam1 = typeParam1 + "Array"; + } + rt = rt + "Of" + removePath(typeParam1); + }else if(typeParam1 != null && typeParam2 != null) { + if(typeParam1IsArray) { + typeParam1 = typeParam1 + "Array"; + } + if(typeParam2IsArray) { + typeParam2 = typeParam2 + "Array"; + } + rt = rt + "Of" + removePath(typeParam1) + "And" + removePath(typeParam2); + } + return rt; + } + + @Override + public SignatureVisitor visitArrayType() { + if(baseClass == null) { + isArray = true; + return new ArrayTypeVisitor(); + }else { + return nopVisitor; + } + } + + private class ArrayTypeVisitor extends SignatureVisitor { + + protected ArrayTypeVisitor() { + super(Opcodes.ASM5); + } + + @Override + public void visitBaseType(char descriptor) { + if(baseClass == null) { + baseClass = primitiveNames.get(descriptor); + } + } + + @Override + public void visitClassType(String name) { + if(baseClass == null) { + baseClass = name; + } + } + + @Override + public SignatureVisitor visitArrayType() { + if(baseClass == null) { + baseClass = "array"; + } + return nopVisitor; + } + + @Override public SignatureVisitor visitClassBound() { return nopVisitor; } + @Override public SignatureVisitor visitExceptionType() { return nopVisitor; } + @Override public SignatureVisitor visitInterface() { return nopVisitor; } + @Override public SignatureVisitor visitInterfaceBound() { return nopVisitor; } + @Override public SignatureVisitor visitParameterType() { return nopVisitor; } + @Override public SignatureVisitor visitTypeArgument(char wildcard) { return nopVisitor; } + @Override public SignatureVisitor visitReturnType() { return nopVisitor; } + + } + + @Override + public void visitBaseType(char descriptor) { + if(baseClass == null) { + baseClass = primitiveNames.get(descriptor); + } + } + + @Override + public SignatureVisitor visitClassBound() { + //System.out.println("class: " + this); + return nopVisitor; + } + + @Override + public void visitClassType(String name) { + //System.out.println("classType: " + name); + if(baseClass == null) { + baseClass = name; + } + } + + @Override + public void visitEnd() { + + } + + @Override + public SignatureVisitor visitExceptionType() { + return nopVisitor; + } + + @Override + public void visitFormalTypeParameter(String name) { + //System.out.println("formalTypeParam: " + name); + } + + @Override + public void visitInnerClassType(String name) { + //System.out.println("innerClassType: " + name); + + } + + @Override + public SignatureVisitor visitInterface() { + return nopVisitor; + } + + @Override + public SignatureVisitor visitInterfaceBound() { + return nopVisitor; + } + + @Override + public SignatureVisitor visitParameterType() { + return nopVisitor; + } + + private class TypeParamVisitor extends SignatureVisitor { + + private boolean hasVisited = false; + private final int firstOrSecond; + + protected TypeParamVisitor(int firstOrSecond) { + super(Opcodes.ASM5); + this.firstOrSecond = firstOrSecond; + } + + @Override + public void visitBaseType(char descriptor) { + if(!hasVisited) { + if(firstOrSecond == 1) { + if(typeParam1 == null) { + typeParam1 = primitiveNames.get(descriptor); + } + }else if(firstOrSecond == 2) { + if(typeParam2 == null) { + typeParam2 = primitiveNames.get(descriptor); + } + } + hasVisited = true; + } + } + + @Override + public void visitClassType(String name) { + if(!hasVisited) { + if(firstOrSecond == 1) { + if(typeParam1 == null) { + typeParam1 = name; + } + }else if(firstOrSecond == 2) { + if(typeParam2 == null) { + typeParam2 = name; + } + } + hasVisited = true; + } + } + + @Override + public SignatureVisitor visitArrayType() { + if(!hasVisited) { + if(firstOrSecond == 1) { + if(typeParam1 == null) { + typeParam1IsArray = true; + return new TypeParamArrayVisitor(); + } + }else if(firstOrSecond == 2) { + if(typeParam2 == null) { + typeParam2IsArray = true; + return new TypeParamArrayVisitor(); + } + } + } + return nopVisitor; + } + + private class TypeParamArrayVisitor extends SignatureVisitor { + + protected TypeParamArrayVisitor() { + super(Opcodes.ASM5); + } + + @Override + public void visitBaseType(char descriptor) { + if(!hasVisited) { + if(firstOrSecond == 1) { + if(typeParam1 == null) { + typeParam1 = primitiveNames.get(descriptor); + } + }else if(firstOrSecond == 2) { + if(typeParam2 == null) { + typeParam2 = primitiveNames.get(descriptor); + } + } + hasVisited = true; + } + } + + @Override + public void visitClassType(String name) { + if(!hasVisited) { + if(firstOrSecond == 1) { + if(typeParam1 == null) { + typeParam1 = name; + } + }else if(firstOrSecond == 2) { + if(typeParam2 == null) { + typeParam2 = name; + } + } + hasVisited = true; + } + } + + @Override + public SignatureVisitor visitArrayType() { + if(!hasVisited) { + if(firstOrSecond == 1) { + if(typeParam1 == null) { + typeParam1 = "array"; + } + }else if(firstOrSecond == 2) { + if(typeParam2 == null) { + typeParam2 = "array"; + } + } + hasVisited = true; + } + return nopVisitor; + } + + @Override public SignatureVisitor visitClassBound() { return nopVisitor; } + @Override public SignatureVisitor visitExceptionType() { return nopVisitor; } + @Override public SignatureVisitor visitInterface() { return nopVisitor; } + @Override public SignatureVisitor visitInterfaceBound() { return nopVisitor; } + @Override public SignatureVisitor visitParameterType() { return nopVisitor; } + @Override public SignatureVisitor visitTypeArgument(char wildcard) { return nopVisitor; } + @Override public SignatureVisitor visitReturnType() { return nopVisitor; } + + } + + } + + @Override + public SignatureVisitor visitReturnType() { + return nopVisitor; + } + + @Override + public SignatureVisitor visitSuperclass() { + return nopVisitor; + } + + @Override + public void visitTypeArgument() { + + } + + @Override + public SignatureVisitor visitTypeArgument(char wildcard) { + if(typeParam1 == null) { + return new TypeParamVisitor(1); + }else if(typeParam2 == null) { + return new TypeParamVisitor(2); + }else { + return nopVisitor; + } + } + + @Override + public void visitTypeVariable(String name) { + + } + + public static String nextLocalVariableNameFromString(Map tmpLocalsMap, String signature, String pfx) { + String str = signature.length() == 1 ? primitiveNames.get(signature.charAt(0)) : null; + if(str == null) { + str = LocalVariableGenerator.createName(signature); + } + String ls = str.toLowerCase(); + Integer i = tmpLocalsMap.get(ls); + if(i == null) { + tmpLocalsMap.put(ls, 1); + if(Character.isDigit(str.charAt(str.length() - 1))) { + str = str + "_1"; + }else { + str = illegalVariableNames.contains(str.toLowerCase()) ? str + "1" : str; + } + }else { + int ii = i.intValue() + 1; + tmpLocalsMap.put(ls, ii); + if(Character.isDigit(str.charAt(str.length() - 1)) || str.contains("And") || str.length() > 16) { + str = str + "_" + ii; + }else { + str = str + ii; + } + } + return pfx == null ? camelCase(str) : pfx + str; + } + + public static String nextLocalVariableName(Map tmpLocalsMap, LocalVariableGenerator signature, String pfx) { + String str = signature.getResult(); + String ls = str.toLowerCase(); + Integer i = tmpLocalsMap.get(ls); + if(i == null) { + tmpLocalsMap.put(ls, 1); + if(Character.isDigit(str.charAt(str.length() - 1))) { + str = str + "_1"; + }else { + str = illegalVariableNames.contains(str.toLowerCase()) ? str + "1" : str; + } + }else { + int ii = i.intValue() + 1; + tmpLocalsMap.put(ls, ii); + if(Character.isDigit(str.charAt(str.length() - 1)) || str.contains("And") || str.length() > 16) { + str = str + "_" + ii; + }else { + str = str + ii; + } + } + return pfx == null ? camelCase(str) : pfx + str; + } + + public static String camelCase(String in) { + if(in == null || in.length() <= 0) { + return "name"; + }else { + if(in.length() > 1 && Character.isUpperCase(in.charAt(0)) && Character.isUpperCase(in.charAt(1))) { + return "var" + in; + }else { + return in.substring(0, 1).toLowerCase() + in.substring(1); + } + } + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/decompiler/ParameterSplitter.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/decompiler/ParameterSplitter.java new file mode 100644 index 0000000..77fd2be --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/decompiler/ParameterSplitter.java @@ -0,0 +1,88 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.decompiler; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.signature.SignatureReader; +import org.objectweb.asm.signature.SignatureVisitor; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class ParameterSplitter extends SignatureVisitor { + + protected ParameterSplitter() { + super(Opcodes.ASM5); + } + + protected static final ArrayList ret = new ArrayList(); + protected static final HashMap usedLocals = new HashMap(); + + public static int getParameterArray(String sig, String[] input) { + SignatureReader rd = new SignatureReader(sig); + ParameterSplitter pms = new ParameterSplitter(); + ret.clear(); + usedLocals.clear(); + rd.accept(pms); + int l = ret.size(); + if(l > input.length) { + l = input.length; + } + int c = 0; + for(int i = 0; i < l; ++i) { + if(input[i] == null) { + input[i] = LocalVariableGenerator.nextLocalVariableName(usedLocals, ret.get(i), "par"); + ++c; + } + } + return c; + } + + public static String[] getParameterSigArray(String sig, String pfx) { + SignatureReader rd = new SignatureReader(sig); + ParameterSplitter pms = new ParameterSplitter(); + ret.clear(); + usedLocals.clear(); + rd.accept(pms); + String[] r = new String[ret.size()]; + for(int i = 0; i < r.length; ++i) { + r[i] = LocalVariableGenerator.nextLocalVariableName(usedLocals, ret.get(i), pfx); + } + return r; + } + + @Override + public SignatureVisitor visitParameterType() { + LocalVariableGenerator lv = new LocalVariableGenerator(); + ret.add(lv); + return lv; + } + + @Override public SignatureVisitor visitClassBound() { return LocalVariableGenerator.nopVisitor; } + @Override public SignatureVisitor visitExceptionType() { return LocalVariableGenerator.nopVisitor; } + @Override public SignatureVisitor visitInterface() { return LocalVariableGenerator.nopVisitor; } + @Override public SignatureVisitor visitInterfaceBound() { return LocalVariableGenerator.nopVisitor; } + @Override public SignatureVisitor visitTypeArgument(char wildcard) { return LocalVariableGenerator.nopVisitor; } + @Override public SignatureVisitor visitReturnType() { return LocalVariableGenerator.nopVisitor; } + @Override public SignatureVisitor visitArrayType() { return LocalVariableGenerator.nopVisitor; } + @Override public void visitBaseType(char descriptor) { } + @Override public void visitClassType(String name) { } + @Override public void visitEnd() { } + @Override public void visitFormalTypeParameter(String name) { } + @Override public void visitInnerClassType(String name) { } + @Override public SignatureVisitor visitSuperclass() { return LocalVariableGenerator.nopVisitor; } + @Override public void visitTypeArgument() { } + @Override public void visitTypeVariable(String name) { } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientFrame.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientFrame.java new file mode 100644 index 0000000..0502d4d --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientFrame.java @@ -0,0 +1,1355 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.gui; + +import javax.swing.JFrame; +import java.awt.Color; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import javax.swing.JButton; +import java.awt.FlowLayout; +import javax.swing.JLabel; +import javax.swing.JOptionPane; + +import java.awt.Font; +import java.awt.Toolkit; + +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.border.EmptyBorder; +import javax.swing.JSeparator; +import java.awt.Component; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.EventQueue; + +import javax.swing.SwingConstants; +import javax.swing.JTextPane; +import java.awt.Insets; +import java.awt.Point; + +import javax.swing.JTextField; +import javax.swing.JRadioButton; +import javax.swing.JScrollBar; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.awt.event.ActionEvent; +import javax.swing.event.HyperlinkListener; + +import net.lax1dude.eaglercraft.v1_8.buildtools.gui.TeaVMBinaries.MissingJARsException; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.FFMPEG; + +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.ChangeEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JPopupMenu; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JMenuItem; +import javax.swing.BoxLayout; +import java.awt.ComponentOrientation; +import javax.swing.JTextArea; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class CompileLatestClientFrame { + + public JFrame frmCompileLatestClient; + public JTextField textField_ModCoderPack; + public JTextField textField_JarFilePath; + public JTextField textField_AssetsIndexJSON; + public JTextField textField_MavenRepoCustomURL; + public JTextField textField_MavenRepoLocal; + public JTextField textField_OutputDirectory; + public JTextField textField_RepositoryPath; + public JRadioButton rdbtnMavenRepoLocal; + public JRadioButton rdbtnMavenRepoCustom; + public JRadioButton rdbtnMavenRepoSonatype; + public JRadioButton rdbtnMavenRepoBintray; + public JRadioButton rdbtnMavenRepoCentral; + public JTextPane txtpnLogOutput; + public JLabel lblProgressState; + public JButton btnBack; + public JButton btnNext; + public JPanel pagesRoot; + public JScrollPane scrollPane; + public JTextPane txtpnfuckOffreview; + public JCheckBox chckbxOutputOfflineDownload; + public JCheckBox chckbxKeepTemporaryFiles; + public JTextField textField_pathToFFmpeg; + public JButton btnBrowsePathToFFmpeg; + public JCheckBox chckbxUsePathFFmpeg; + public JCheckBox chckbxAgreeLicense; + public JTextArea txtrLicenseText; + public JScrollPane scrollPane_LicenseText; + + /** + * Create the application. + */ + public CompileLatestClientFrame() { + initialize(); + } + + public int page = 0; + public boolean compiling; + public boolean finished; + + public void setPage(int i) { + if(i < 0) i = 0; + if(i > 10) i = 10; + if(page != i) { + if(page < i) { + String pp; + switch(page) { + case 1: + if(!chckbxAgreeLicense.isSelected()) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "You must agree to the license to continue!", "Permission Denied", JOptionPane.ERROR_MESSAGE); + return; + } + break; + case 2: + pp = textField_RepositoryPath.getText().trim(); + if(pp.length() == 0) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Please select a folder!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else if(!(new File(pp)).isDirectory()){ + JOptionPane.showMessageDialog(frmCompileLatestClient, "The path \"" + pp + "\" is not a folder!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + } + break; + case 3: + pp = textField_ModCoderPack.getText().trim(); + if(pp.length() == 0) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Please select a file!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else if(!(new File(pp)).isFile()){ + JOptionPane.showMessageDialog(frmCompileLatestClient, "The path \"" + pp + "\" is not a file!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + } + break; + case 4: + pp = textField_JarFilePath.getText().trim(); + if(pp.length() == 0) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Please select a file!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else if(!(new File(pp)).isFile()){ + JOptionPane.showMessageDialog(frmCompileLatestClient, "The path \"" + pp + "\" is not a file!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + } + break; + case 5: + pp = textField_AssetsIndexJSON.getText().trim(); + if(pp.length() == 0) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Please select a file!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else if(!(new File(pp)).isFile()){ + JOptionPane.showMessageDialog(frmCompileLatestClient, "The path \"" + pp + "\" is not a file!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + } + break; + case 6: + if(rdbtnMavenRepoLocal.isSelected()) { + pp = textField_MavenRepoLocal.getText().trim(); + if(pp.length() == 0) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Please select a folder!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else { + File f = new File(pp); + if(!f.isDirectory()){ + JOptionPane.showMessageDialog(frmCompileLatestClient, "The path \"" + pp + "\" is not a folder!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else { + try { + TeaVMBinaries.loadFromDirectory(f); + }catch(MissingJARsException ex) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "The following JARs are missing:\n - " + String.join("\n - ", ex.jars), "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + } + } + } + }else if(rdbtnMavenRepoCustom.isSelected()) { + pp = textField_MavenRepoCustomURL.getText().trim(); + if(pp.length() == 0) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Please enter a URL!", "Invalid URL", JOptionPane.ERROR_MESSAGE); + return; + }else { + try { + URL u = new URL(pp); + }catch(MalformedURLException ex) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "MalformedURLException: " + ex.getMessage(), "Invalid URL", JOptionPane.ERROR_MESSAGE); + return; + } + } + } + break; + case 7: + if(!chckbxUsePathFFmpeg.isSelected()) { + pp = textField_pathToFFmpeg.getText().trim(); + if(pp.length() == 0) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Please select a file!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else if(!(new File(pp)).isFile()){ + JOptionPane.showMessageDialog(frmCompileLatestClient, "The path \"" + pp + "\" is not a file!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + } + }else if(!isFFmpegOnPath()) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "FFmpeg is not on your system's PATH!", "Error", JOptionPane.ERROR_MESSAGE); + chckbxUsePathFFmpeg.setSelected(false); + chckbxUsePathFFmpeg.setEnabled(false); + textField_pathToFFmpeg.setEnabled(true); + btnBrowsePathToFFmpeg.setEnabled(true); + return; + } + break; + case 8: + pp = textField_OutputDirectory.getText().trim(); + if(pp.length() == 0) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Please select a folder!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else { + File f = new File(pp); + if(!f.isDirectory()){ + JOptionPane.showMessageDialog(frmCompileLatestClient, "The path \"" + pp + "\" is not a folder!", "Invalid Path", JOptionPane.ERROR_MESSAGE); + return; + }else if(f.list().length > 0) { + if(JOptionPane.showConfirmDialog(frmCompileLatestClient, "The directory \"" + pp + "\" is not empty, would you like to continue?\nThe existing files will be deleted.", "Confirm", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { + return; + } + } + } + break; + default: + break; + } + } + CardLayout pagesLayout = (CardLayout)pagesRoot.getLayout(); + switch(i) { + case 0: + default: + pagesLayout.show(pagesRoot, "pageHome"); + break; + case 1: + pagesLayout.show(pagesRoot, "pageLicense"); + break; + case 2: + pagesLayout.show(pagesRoot, "pageBrowseRepositoryPath"); + break; + case 3: + pagesLayout.show(pagesRoot, "pageModCoderPack"); + break; + case 4: + pagesLayout.show(pagesRoot, "pageBrowseJarFile"); + break; + case 5: + pagesLayout.show(pagesRoot, "pageBrowseAssetsIndexJSON"); + break; + case 6: + pagesLayout.show(pagesRoot, "pageMavenRepo"); + break; + case 7: + pagesLayout.show(pagesRoot, "pageFFmpeg"); + if(isFFmpegOnPath()) { + chckbxUsePathFFmpeg.setEnabled(true); + if(!hasAskedFFmpegOnPath) { + hasAskedFFmpegOnPath = true; + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + if(JOptionPane.showConfirmDialog(frmCompileLatestClient, "FFmpeg was found on your system's PATH!\nwould you like to select it automatically?", "Confirmation", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + chckbxUsePathFFmpeg.setSelected(true); + textField_pathToFFmpeg.setEnabled(false); + btnBrowsePathToFFmpeg.setEnabled(false); + setPage(page + 1); + } + } + }); + } + }else { + chckbxUsePathFFmpeg.setEnabled(false); + } + break; + case 8: + pagesLayout.show(pagesRoot, "pageOutputDirectory"); + break; + case 9: + txtpnfuckOffreview.setText("\r\nfuck off\r\n\r\n

Are these correct?

\r\n

" + + " • Repository Path: " + htmlentities((new File(textField_RepositoryPath.getText())).getAbsolutePath()) + "

\r\n

" + + " • Mod Coder Pack: " + htmlentities((new File(textField_ModCoderPack.getText())).getAbsolutePath()) + "

\r\n

" + + " • 1.8.8.jar path: " + htmlentities((new File(textField_JarFilePath.getText())).getAbsolutePath()) + "

\r\n

" + + " • 1.8.json path: " + htmlentities((new File(textField_AssetsIndexJSON.getText())).getAbsolutePath()) + "

\r\n

" + + " • TeaVM Maven: " + htmlentities(rdbtnMavenRepoLocal.isSelected() ? (new File(textField_MavenRepoLocal.getText())).getAbsolutePath() : getRepositoryURL()) + "

\r\n

" + + " • FFmpeg: " + (isFFmpegOnPath() ? "(Already Installed)" : htmlentities((new File(textField_pathToFFmpeg.getText())).getAbsolutePath())) + "

\r\n

" + + " • Output Directory: " + htmlentities((new File(textField_OutputDirectory.getText())).getAbsolutePath()) + "

\r\n

" + + " • Make Offline Download: " + (chckbxOutputOfflineDownload.isSelected() ? "Yes" : "No") + "

\r\n

" + + " • Keep Temporary Files: " + (chckbxKeepTemporaryFiles.isSelected() ? "Yes" : "No") + "

\r\n" + + "

 Press the \"Compile >>\" button to confirm

\r\n\r\n"); + pagesLayout.show(pagesRoot, "pageConfirmSettings"); + break; + case 10: + pagesLayout.show(pagesRoot, "pageLogOutput"); + lblProgressState.setText("Compiling, Please Wait..."); + compiling = true; + Point p = frmCompileLatestClient.getLocation(); + frmCompileLatestClient.setLocation(p.x - 150, p.y - 150); + Dimension d = frmCompileLatestClient.getSize(); + frmCompileLatestClient.setSize(d.width + 300, d.height + 300); + frmCompileLatestClient.setResizable(true); + (new Thread(new Runnable() { + public void run() { + CompileLatestClientGUI.runCompiler(); + } + }, "Compiler Thread")).start(); + break; + } + page = i; + btnBack.setEnabled(i > 0 && i != 10); + btnNext.setEnabled(i != 10 && !(i == 1 && !chckbxAgreeLicense.isSelected())); + btnNext.setText(i != 10 ? (i != 9 ? "Next >>" : "Compile >>") : "Finish"); + } + } + + public static File getMinecraftDir() { + String os = System.getProperty("os.name").toLowerCase(); + if(os.contains("win")) { + String home = System.getenv("APPDATA"); + if(home == null) { + home = System.getProperty("user.home", "."); + } + return new File(home, ".minecraft"); + }else if(os.contains("osx") || os.contains("macos")) { + return new File(System.getProperty("user.home", "."), "Library/Application Support/.minecraft"); + }else { + return new File(System.getProperty("user.home", "."), ".minecraft"); + } + } + + /** + * Initialize the contents of the frame. + */ + private void initialize() { + frmCompileLatestClient = new JFrame(); + frmCompileLatestClient.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + if(compiling && !finished) { + if(JOptionPane.showConfirmDialog(frmCompileLatestClient, "Compilier is still running, do you really want to exit?", "Confirmation", JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION) { + System.exit(0); + } + }else { + System.exit(0); + } + } + }); + frmCompileLatestClient.setIconImage(Toolkit.getDefaultToolkit().getImage(CompileLatestClientFrame.class.getResource("/icon/icon32.png"))); + frmCompileLatestClient.setTitle("Compile Latest Client"); + frmCompileLatestClient.setResizable(false); + frmCompileLatestClient.getContentPane().setBackground(Color.WHITE); + frmCompileLatestClient.getContentPane().setLayout(new BorderLayout(0, 0)); + + JPanel panel = new JPanel(); + panel.setBackground(Color.WHITE); + frmCompileLatestClient.getContentPane().add(panel, BorderLayout.SOUTH); + panel.setLayout(new BorderLayout(0, 0)); + + JPanel panel_35 = new JPanel(); + panel_35.setBackground(Color.WHITE); + FlowLayout flowLayout = (FlowLayout) panel_35.getLayout(); + flowLayout.setVgap(10); + flowLayout.setHgap(10); + flowLayout.setAlignment(FlowLayout.LEFT); + panel.add(panel_35, BorderLayout.EAST); + + btnBack = new JButton("<< Back"); + btnBack.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setPage(page - 1); + } + }); + panel_35.add(btnBack); + btnBack.setEnabled(false); + + btnNext = new JButton("Next >>"); + btnNext.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if(finished) { + System.exit(0); + }else { + setPage(page + 1); + } + } + }); + panel_35.add(btnNext); + + lblProgressState = new JLabel(""); + lblProgressState.setFont(new Font("Dialog", Font.BOLD, 16)); + lblProgressState.setBorder(new EmptyBorder(0, 10, 0, 10)); + panel.add(lblProgressState, BorderLayout.CENTER); + + JPanel panel_1 = new JPanel(); + panel_1.setBackground(Color.WHITE); + frmCompileLatestClient.getContentPane().add(panel_1, BorderLayout.NORTH); + panel_1.setLayout(new BorderLayout(0, 0)); + + JPanel panel_2 = new JPanel(); + panel_2.setBackground(Color.WHITE); + panel_1.add(panel_2, BorderLayout.CENTER); + panel_2.setLayout(new BorderLayout(0, 0)); + + JLabel lblNewLabel_1 = new JLabel("EaglercraftX 1.8 Client Compiler"); + lblNewLabel_1.setVerticalAlignment(SwingConstants.BOTTOM); + lblNewLabel_1.setPreferredSize(new Dimension(152, 24)); + lblNewLabel_1.setFont(new Font("Dialog", Font.BOLD, 14)); + panel_2.add(lblNewLabel_1, BorderLayout.NORTH); + + JLabel lblNewLabel_2 = new JLabel("Copyright (c) 2022 lax1dude"); + lblNewLabel_2.setVerticalAlignment(SwingConstants.TOP); + lblNewLabel_2.setPreferredSize(new Dimension(27, 24)); + lblNewLabel_2.setFont(new Font("Dialog", Font.PLAIN, 14)); + panel_2.add(lblNewLabel_2, BorderLayout.SOUTH); + + JLabel lblNewLabel = new JLabel(""); + lblNewLabel.setBorder(new EmptyBorder(8, 8, 8, 16)); + lblNewLabel.setIcon(new ImageIcon(CompileLatestClientFrame.class.getResource("/icon/eagler.png"))); + panel_1.add(lblNewLabel, BorderLayout.WEST); + + JPanel panel_23 = new JPanel(); + panel_23.setPreferredSize(new Dimension(10, 1)); + panel_23.setBackground(Color.DARK_GRAY); + panel_1.add(panel_23, BorderLayout.NORTH); + panel_23.setLayout(null); + + JPanel panel_28 = new JPanel(); + panel_28.setBackground(Color.DARK_GRAY); + panel_28.setPreferredSize(new Dimension(10, 1)); + panel_1.add(panel_28, BorderLayout.SOUTH); + panel_28.setLayout(null); + + JPanel panel_3 = new JPanel(); + panel_3.setBackground(Color.WHITE); + frmCompileLatestClient.getContentPane().add(panel_3, BorderLayout.CENTER); + panel_3.setLayout(new BorderLayout(0, 0)); + + pagesRoot = new JPanel(); + pagesRoot.setBackground(Color.WHITE); + panel_3.add(pagesRoot, BorderLayout.CENTER); + pagesRoot.setLayout(new CardLayout(0, 0)); + + JPanel pageHome = new JPanel(); + pageHome.setBackground(Color.WHITE); + pagesRoot.add(pageHome, "pageHome"); + pageHome.setLayout(new BorderLayout(0, 0)); + + JTextPane txtpnfuckOffwelcome = new JTextPane(); + txtpnfuckOffwelcome.setEditable(false); + txtpnfuckOffwelcome.setMargin(new Insets(10, 10, 10, 10)); + txtpnfuckOffwelcome.setContentType("text/html"); + txtpnfuckOffwelcome.setText("\r\nfuck off\r\n\r\n

Welcome to the EaglercraftX 1.8 Client Compiler

\r\n

This tool will allow you to automatically compile the latest version of the EaglercraftX 1.8 client using the files in this repository.

\r\n

You are required to download several required files manually in order to better respect the Microsoft/Mojang TOS. The links to these files will be provided.

\r\n

To view or modify portions of the EaglercraftX 1.8 source code directly, please use the other batch files to generate a gradle project instead of compiling the javascript files directly

\r\n

If you are from Microsoft/Mojang or the developer of MCP trying to get dirt on me, please just let me live my life, the repository does not contain your intellectual property. Using this code to play your game for free is not the default behavior of the gateway plugin or this compiler utility and is not encouraged by the documentation

\r\n\r\n"); + pageHome.add(txtpnfuckOffwelcome, BorderLayout.CENTER); + + JPanel pageLicense = new JPanel(); + pageLicense.setBackground(Color.WHITE); + pagesRoot.add(pageLicense, "pageLicense"); + pageLicense.setLayout(new BorderLayout(0, 0)); + + scrollPane_LicenseText = new JScrollPane(); + scrollPane_LicenseText.setBorder(null); + scrollPane_LicenseText.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane_LicenseText.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + pageLicense.add(scrollPane_LicenseText, BorderLayout.CENTER); + + txtrLicenseText = new JTextArea(); + txtrLicenseText.setMargin(new Insets(10, 10, 10, 10)); + txtrLicenseText.setAutoscrolls(false); + txtrLicenseText.setText(loadLicense()); + txtrLicenseText.setFont(new Font("Dialog", Font.PLAIN, 14)); + txtrLicenseText.setLineWrap(true); + txtrLicenseText.setEditable(false); + txtrLicenseText.setWrapStyleWord(true); + scrollPane_LicenseText.setViewportView(txtrLicenseText); + + JPanel panel_36 = new JPanel(); + pageLicense.add(panel_36, BorderLayout.SOUTH); + panel_36.setLayout(new BorderLayout(0, 0)); + + chckbxAgreeLicense = new JCheckBox("I agree to these terms and conditions"); + chckbxAgreeLicense.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + btnNext.setEnabled(chckbxAgreeLicense.isSelected()); + } + }); + panel_36.add(chckbxAgreeLicense); + chckbxAgreeLicense.setMargin(new Insets(7, 7, 7, 7)); + chckbxAgreeLicense.setFont(new Font("Dialog", Font.PLAIN, 14)); + chckbxAgreeLicense.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); + chckbxAgreeLicense.setBackground(Color.WHITE); + + JPanel panel_37 = new JPanel(); + panel_37.setBackground(Color.DARK_GRAY); + panel_37.setPreferredSize(new Dimension(10, 1)); + panel_36.add(panel_37, BorderLayout.NORTH); + + JPanel pageBrowseRepositoryPath = new JPanel(); + pageBrowseRepositoryPath.setBackground(Color.WHITE); + pagesRoot.add(pageBrowseRepositoryPath, "pageBrowseRepositoryPath"); + pageBrowseRepositoryPath.setLayout(new BorderLayout(0, 0)); + + JTextPane txtpnfuckOffeaglercraftx_1 = new JTextPane(); + txtpnfuckOffeaglercraftx_1.setEditable(false); + txtpnfuckOffeaglercraftx_1.setContentType("text/html"); + txtpnfuckOffeaglercraftx_1.setText("\r\nfuck off\r\n\r\n

EaglercraftX 1.8 Source Code

\r\n

It is assumed that you are running this tool in the root directory in your copy of the EaglercraftX 1.8 source code repository

\r\n

If this is not the case, please use the file chooser below to select where the repository is located

\r\n\r\n"); + txtpnfuckOffeaglercraftx_1.setMargin(new Insets(10, 10, 10, 10)); + pageBrowseRepositoryPath.add(txtpnfuckOffeaglercraftx_1, BorderLayout.CENTER); + + JPanel panel_24 = new JPanel(); + panel_24.setBorder(new EmptyBorder(0, 20, 40, 20)); + panel_24.setBackground(Color.WHITE); + pageBrowseRepositoryPath.add(panel_24, BorderLayout.SOUTH); + panel_24.setLayout(new BorderLayout(0, 0)); + + JPanel panel_25 = new JPanel(); + panel_25.setPreferredSize(new Dimension(10, 40)); + panel_25.setBackground(Color.WHITE); + panel_24.add(panel_25, BorderLayout.CENTER); + panel_25.setLayout(new BorderLayout(0, 0)); + + JLabel lblNewLabel_7 = new JLabel("EaglercraftX 1.8 Repository Path:"); + lblNewLabel_7.setFont(new Font("Dialog", Font.BOLD, 12)); + lblNewLabel_7.setPreferredSize(new Dimension(162, 20)); + panel_25.add(lblNewLabel_7, BorderLayout.NORTH); + + JPanel panel_26 = new JPanel(); + panel_26.setPreferredSize(new Dimension(10, 20)); + panel_26.setBackground(Color.WHITE); + panel_25.add(panel_26, BorderLayout.SOUTH); + panel_26.setLayout(new BorderLayout(0, 0)); + + JButton btnBrowseRepositoryPath = new JButton("Browse..."); + btnBrowseRepositoryPath.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String current = textField_RepositoryPath.getText().trim(); + if(current.length() == 0) { + current = (new File("")).getAbsolutePath(); + } + JFileChooser fileChooser = new JFileChooser(new File(current)); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileHidingEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if(fileChooser.showOpenDialog(frmCompileLatestClient) == JFileChooser.APPROVE_OPTION) { + textField_RepositoryPath.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + } + }); + panel_26.add(btnBrowseRepositoryPath, BorderLayout.EAST); + + JPanel panel_27 = new JPanel(); + panel_27.setBackground(Color.WHITE); + panel_27.setBorder(new EmptyBorder(0, 0, 0, 10)); + panel_26.add(panel_27, BorderLayout.CENTER); + panel_27.setLayout(new BorderLayout(0, 0)); + + textField_RepositoryPath = new JTextField(); + panel_27.add(textField_RepositoryPath, BorderLayout.CENTER); + textField_RepositoryPath.setText((new File("")).getAbsolutePath()); + textField_RepositoryPath.setColumns(10); + + JPanel pageModCoderPack = new JPanel(); + pageModCoderPack.setBackground(Color.WHITE); + pagesRoot.add(pageModCoderPack, "pageModCoderPack"); + pageModCoderPack.setLayout(new BorderLayout(0, 0)); + + JTextPane txtpnfuckOffwelcome_1 = new JTextPane(); + txtpnfuckOffwelcome_1.setMargin(new Insets(10, 10, 10, 10)); + txtpnfuckOffwelcome_1.setEditable(false); + txtpnfuckOffwelcome_1.setContentType("text/html"); + txtpnfuckOffwelcome_1.setText("\r\nfuck off\r\n\r\n

Mod Coder Pack

\r\n

A copy of Mod Coder Pack v9.18 is required to compile the EaglercraftX 1.8 client

\r\n

According to the Mod Coder Pack LICENSE.txt, \"You are NOT allowed to: release modified or unmodified versions of MCP anywhere\" so a copy of the files are not included in this repository, you're gonna have to download them separately

\r\n

The official download link is at: http://www.modcoderpack.com/

\r\n

Visit the link and download \"mcp918.zip\" and select it below

\r\n\r\n"); + txtpnfuckOffwelcome_1.addHyperlinkListener(new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + try { + Desktop.getDesktop().browse(e.getURL().toURI()); + } catch (Throwable t) { + } + } + } + }); + pageModCoderPack.add(txtpnfuckOffwelcome_1, BorderLayout.CENTER); + + JPanel panel_7 = new JPanel(); + panel_7.setBorder(new EmptyBorder(0, 20, 40, 20)); + panel_7.setAlignmentY(Component.TOP_ALIGNMENT); + panel_7.setBackground(Color.WHITE); + pageModCoderPack.add(panel_7, BorderLayout.SOUTH); + panel_7.setLayout(new BorderLayout(0, 0)); + + JPanel panel_14 = new JPanel(); + panel_14.setBackground(Color.WHITE); + panel_14.setPreferredSize(new Dimension(10, 40)); + panel_7.add(panel_14, BorderLayout.SOUTH); + panel_14.setLayout(new BorderLayout(0, 0)); + + JLabel lblNewLabel_3 = new JLabel("path to mcp918.zip:"); + lblNewLabel_3.setVerticalAlignment(SwingConstants.TOP); + lblNewLabel_3.setFont(new Font("Dialog", Font.BOLD, 12)); + lblNewLabel_3.setPreferredSize(new Dimension(46, 20)); + panel_14.add(lblNewLabel_3, BorderLayout.NORTH); + + JPanel panel_8 = new JPanel(); + panel_8.setPreferredSize(new Dimension(10, 20)); + panel_8.setBackground(Color.WHITE); + panel_14.add(panel_8, BorderLayout.SOUTH); + panel_8.setLayout(new BorderLayout(0, 0)); + + JPanel panel_9 = new JPanel(); + panel_9.setBorder(new EmptyBorder(0, 0, 0, 10)); + panel_9.setBackground(Color.WHITE); + panel_8.add(panel_9, BorderLayout.CENTER); + panel_9.setLayout(new BorderLayout(0, 0)); + + textField_ModCoderPack = new JTextField(); + panel_9.add(textField_ModCoderPack, BorderLayout.CENTER); + textField_ModCoderPack.setColumns(10); + + JButton btnBrowseModCoderPack = new JButton("Browse..."); + btnBrowseModCoderPack.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String current = textField_ModCoderPack.getText().trim(); + if(current.length() == 0) { + File f = new File(System.getProperty("user.home", "."), "Downloads"); + if(!f.exists()) { + f = new File(System.getProperty("user.home", ".")); + } + current = f.getAbsolutePath(); + } + JFileChooser fileChooser = new JFileChooser(new File(current)); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileHidingEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + if(fileChooser.showOpenDialog(frmCompileLatestClient) == JFileChooser.APPROVE_OPTION) { + textField_ModCoderPack.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + } + }); + panel_8.add(btnBrowseModCoderPack, BorderLayout.EAST); + + JPanel pageBrowseJarFile = new JPanel(); + pageBrowseJarFile.setBackground(Color.WHITE); + pagesRoot.add(pageBrowseJarFile, "pageBrowseJarFile"); + pageBrowseJarFile.setLayout(new BorderLayout(0, 0)); + + JTextPane txtpnfuckOffwelcome_1_1 = new JTextPane(); + txtpnfuckOffwelcome_1_1.setContentType("text/html"); + txtpnfuckOffwelcome_1_1.setText("\r\nfuck off\r\n\r\n

Minecraft 1.8 JAR File

\r\n

Obviously the JAR file containing the original Minecraft 1.8 bytecode is required to compile EaglercraftX 1.8, but it must again be downloaded separately from this repository because Microsoft/Mojang does not allow it to be redistributed without permission

\r\n

To download it, BUY MINECRAFT, install the Minecraft launcher, make a new launcher profile for MINECRAFT 1.8.8, and launch it at least once.

\r\n

Use the file chooser below to navigate to your \".minecraft\" folder, open the folder named \"versions\", then open the folder within it called \"1.8.8\", and then within that folder select the JAR file called \"1.8.8.jar\"

\r\n

You can also download \"Client Jar\" from this link: https://mcversions.net/download/1.8.8/

\r\n\r\n"); + txtpnfuckOffwelcome_1_1.setMargin(new Insets(10, 10, 10, 10)); + txtpnfuckOffwelcome_1_1.setEditable(false); + txtpnfuckOffwelcome_1_1.addHyperlinkListener(new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + try { + Desktop.getDesktop().browse(e.getURL().toURI()); + } catch (Throwable t) { + } + } + } + }); + pageBrowseJarFile.add(txtpnfuckOffwelcome_1_1, BorderLayout.CENTER); + + JPanel panel_11 = new JPanel(); + panel_11.setBorder(new EmptyBorder(0, 20, 10, 20)); + panel_11.setBackground(Color.WHITE); + pageBrowseJarFile.add(panel_11, BorderLayout.SOUTH); + panel_11.setLayout(new BorderLayout(0, 0)); + + JPanel panel_12 = new JPanel(); + panel_12.setPreferredSize(new Dimension(10, 40)); + panel_12.setBackground(Color.WHITE); + panel_11.add(panel_12, BorderLayout.CENTER); + panel_12.setLayout(new BorderLayout(0, 0)); + + JLabel lblNewLabel_4 = new JLabel("path to 1.8.8.jar:"); + lblNewLabel_4.setFont(new Font("Dialog", Font.BOLD, 12)); + lblNewLabel_4.setVerticalAlignment(SwingConstants.TOP); + lblNewLabel_4.setPreferredSize(new Dimension(46, 20)); + panel_12.add(lblNewLabel_4, BorderLayout.NORTH); + + JPanel panel_13 = new JPanel(); + panel_13.setBackground(Color.WHITE); + panel_13.setPreferredSize(new Dimension(10, 20)); + panel_12.add(panel_13, BorderLayout.SOUTH); + panel_13.setLayout(new BorderLayout(0, 0)); + + JButton btnBrowseJarFilePath = new JButton("Browse..."); + btnBrowseJarFilePath.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String current = textField_JarFilePath.getText().trim(); + if(current.length() == 0) { + File mc = getMinecraftDir(); + File f = new File(mc, "versions/1.8.8"); + if(!f.exists()) { + f = mc; + } + if(!mc.exists()) { + f = new File(""); + } + current = f.getAbsolutePath(); + } + JFileChooser fileChooser = new JFileChooser(new File(current)); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileHidingEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + if(fileChooser.showOpenDialog(frmCompileLatestClient) == JFileChooser.APPROVE_OPTION) { + textField_JarFilePath.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + } + }); + panel_13.add(btnBrowseJarFilePath, BorderLayout.EAST); + + JPanel panel_15 = new JPanel(); + panel_15.setBorder(new EmptyBorder(0, 0, 0, 10)); + panel_15.setBackground(Color.WHITE); + panel_13.add(panel_15, BorderLayout.CENTER); + panel_15.setLayout(new BorderLayout(0, 0)); + + textField_JarFilePath = new JTextField(); + textField_JarFilePath.setText(""); + panel_15.add(textField_JarFilePath, BorderLayout.CENTER); + textField_JarFilePath.setColumns(10); + frmCompileLatestClient.setBounds(100, 100, 640, 480); + frmCompileLatestClient.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + JPanel pageBrowseAssetsIndexJSON = new JPanel(); + pageBrowseAssetsIndexJSON.setBackground(Color.WHITE); + pagesRoot.add(pageBrowseAssetsIndexJSON, "pageBrowseAssetsIndexJSON"); + pageBrowseAssetsIndexJSON.setLayout(new BorderLayout(0, 0)); + + JTextPane txtpnfuckOffminecraft = new JTextPane(); + txtpnfuckOffminecraft.setEditable(false); + txtpnfuckOffminecraft.setMargin(new Insets(10, 10, 10, 10)); + txtpnfuckOffminecraft.setContentType("text/html"); + txtpnfuckOffminecraft.setText("\r\nfuck off\r\n\r\n

Minecraft 1.8 Assets Index JSON

\r\n

Some of Minecraft 1.8's assets are not included in the 1.8.8.jar file, they are downloaded separately, but are identified by their SHA-1 checksums. An additional JSON file must also be downloaded in order to map the checksums to their internal filenames

\r\n

Complete the previous step, then use the file chooser below to navigate to your \".minecraft\" folder, open the folder named \"assets\", then open the folder within it called \"indexes\", and then within that folder select the JSON file called \"1.8.json\"

\r\n

You can also download the JSON file from Mojang here:
https://launchermeta.mojang.com/v1/packages/f6ad102bcaa53b1a58358f16e376d548d44933ec/1.8.json

\r\n\r\n"); + txtpnfuckOffminecraft.addHyperlinkListener(new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + try { + Desktop.getDesktop().browse(e.getURL().toURI()); + } catch (Throwable t) { + } + } + } + }); + pageBrowseAssetsIndexJSON.add(txtpnfuckOffminecraft, BorderLayout.CENTER); + + JPanel panel_17 = new JPanel(); + panel_17.setBorder(new EmptyBorder(0, 20, 30, 20)); + panel_17.setBackground(Color.WHITE); + pageBrowseAssetsIndexJSON.add(panel_17, BorderLayout.SOUTH); + panel_17.setLayout(new BorderLayout(0, 0)); + + JPanel panel_18 = new JPanel(); + panel_18.setPreferredSize(new Dimension(10, 40)); + panel_18.setBackground(Color.WHITE); + panel_17.add(panel_18, BorderLayout.CENTER); + panel_18.setLayout(new BorderLayout(0, 0)); + + JLabel lblNewLabel_5 = new JLabel("path to 1.8.json:"); + lblNewLabel_5.setFont(new Font("Dialog", Font.BOLD, 12)); + lblNewLabel_5.setPreferredSize(new Dimension(46, 20)); + panel_18.add(lblNewLabel_5, BorderLayout.NORTH); + + JPanel panel_19 = new JPanel(); + panel_19.setBackground(Color.WHITE); + panel_19.setPreferredSize(new Dimension(10, 20)); + panel_18.add(panel_19, BorderLayout.SOUTH); + panel_19.setLayout(new BorderLayout(0, 0)); + + JButton btnBrowseAssetsIndexJSON = new JButton("Browse..."); + btnBrowseAssetsIndexJSON.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String current = textField_AssetsIndexJSON.getText().trim(); + if(current.length() == 0) { + File mc = getMinecraftDir(); + File f = new File(mc, "assets/indexes"); + if(!f.exists()) { + f = mc; + } + if(!mc.exists()) { + f = new File(""); + } + current = f.getAbsolutePath(); + } + JFileChooser fileChooser = new JFileChooser(new File(current)); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileHidingEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + if(fileChooser.showOpenDialog(frmCompileLatestClient) == JFileChooser.APPROVE_OPTION) { + textField_AssetsIndexJSON.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + } + }); + panel_19.add(btnBrowseAssetsIndexJSON, BorderLayout.EAST); + + JPanel panel_20 = new JPanel(); + panel_20.setBackground(Color.WHITE); + panel_20.setBorder(new EmptyBorder(0, 0, 0, 10)); + panel_19.add(panel_20, BorderLayout.CENTER); + panel_20.setLayout(new BorderLayout(0, 0)); + + textField_AssetsIndexJSON = new JTextField(); + panel_20.add(textField_AssetsIndexJSON, BorderLayout.CENTER); + textField_AssetsIndexJSON.setColumns(10); + + JPanel pageMavenRepo = new JPanel(); + pageMavenRepo.setBackground(Color.WHITE); + pagesRoot.add(pageMavenRepo, "pageMavenRepo"); + pageMavenRepo.setLayout(new BorderLayout(0, 0)); + + JTextPane txtpnfuckOffeaglercraftx = new JTextPane(); + txtpnfuckOffeaglercraftx.setContentType("text/html"); + txtpnfuckOffeaglercraftx.setText("\r\nfuck off\r\n\r\n

TeaVM Java to JavaScript Compiler

\r\n

EaglercraftX 1.8 uses TeaVM 0.6.1 to compile java to javascript. It's not included in the eagler repository to save space, so it must be downloaded from a public maven repository via HTTP, or loaded from a temporary local directory

\r\n\r\n"); + txtpnfuckOffeaglercraftx.setEditable(false); + txtpnfuckOffeaglercraftx.setMargin(new Insets(10, 10, 10, 10)); + pageMavenRepo.add(txtpnfuckOffeaglercraftx, BorderLayout.NORTH); + + JPanel panel_22 = new JPanel(); + panel_22.setBackground(Color.WHITE); + pageMavenRepo.add(panel_22, BorderLayout.CENTER); + panel_22.setLayout(null); + + JLabel lblNewLabel_6 = new JLabel("Download via HTTP:"); + lblNewLabel_6.setFont(new Font("Dialog", Font.BOLD, 12)); + lblNewLabel_6.setBounds(12, 0, 329, 20); + panel_22.add(lblNewLabel_6); + + ButtonGroup repoButtonGroup = new ButtonGroup(); + + rdbtnMavenRepoCentral = new JRadioButton("https://repo1.maven.org/maven2/"); + rdbtnMavenRepoCentral.setFont(new Font("Dialog", Font.PLAIN, 12)); + rdbtnMavenRepoCentral.setSelected(true); + rdbtnMavenRepoCentral.setBackground(Color.WHITE); + rdbtnMavenRepoCentral.setBounds(28, 27, 474, 23); + repoButtonGroup.add(rdbtnMavenRepoCentral); + panel_22.add(rdbtnMavenRepoCentral); + + rdbtnMavenRepoBintray = new JRadioButton("(DEPRECATED) https://jcenter.bintray.com/"); + rdbtnMavenRepoBintray.setFont(new Font("Dialog", Font.PLAIN, 12)); + rdbtnMavenRepoBintray.setBackground(Color.WHITE); + rdbtnMavenRepoBintray.setBounds(28, 52, 456, 23); + repoButtonGroup.add(rdbtnMavenRepoBintray); + panel_22.add(rdbtnMavenRepoBintray); + + rdbtnMavenRepoSonatype = new JRadioButton("https://oss.sonatype.org/content/repositories/releases/"); + rdbtnMavenRepoSonatype.setFont(new Font("Dialog", Font.PLAIN, 12)); + rdbtnMavenRepoSonatype.setBackground(Color.WHITE); + rdbtnMavenRepoSonatype.setBounds(28, 78, 456, 23); + repoButtonGroup.add(rdbtnMavenRepoSonatype); + panel_22.add(rdbtnMavenRepoSonatype); + + rdbtnMavenRepoCustom = new JRadioButton(""); + rdbtnMavenRepoCustom.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + textField_MavenRepoCustomURL.setEnabled(rdbtnMavenRepoCustom.isSelected()); + } + }); + rdbtnMavenRepoCustom.setFont(new Font("Dialog", Font.PLAIN, 12)); + rdbtnMavenRepoCustom.setBackground(Color.WHITE); + rdbtnMavenRepoCustom.setBounds(28, 104, 21, 23); + repoButtonGroup.add(rdbtnMavenRepoCustom); + panel_22.add(rdbtnMavenRepoCustom); + + textField_MavenRepoCustomURL = new JTextField(); + textField_MavenRepoCustomURL.setEnabled(false); + textField_MavenRepoCustomURL.setBounds(51, 106, 195, 20); + panel_22.add(textField_MavenRepoCustomURL); + textField_MavenRepoCustomURL.setColumns(10); + + JLabel lblNewLabel_6_1 = new JLabel("Local directory:"); + lblNewLabel_6_1.setFont(new Font("Dialog", Font.BOLD, 12)); + lblNewLabel_6_1.setBounds(12, 134, 329, 20); + panel_22.add(lblNewLabel_6_1); + + rdbtnMavenRepoLocal = new JRadioButton(""); + rdbtnMavenRepoLocal.setFont(new Font("Dialog", Font.PLAIN, 12)); + rdbtnMavenRepoLocal.setBackground(Color.WHITE); + rdbtnMavenRepoLocal.setBounds(28, 162, 21, 23); + repoButtonGroup.add(rdbtnMavenRepoLocal); + panel_22.add(rdbtnMavenRepoLocal); + + textField_MavenRepoLocal = new JTextField(); + textField_MavenRepoLocal.setEnabled(false); + textField_MavenRepoLocal.setColumns(10); + textField_MavenRepoLocal.setBounds(51, 163, 195, 20); + panel_22.add(textField_MavenRepoLocal); + + final JButton btnBrowseMavenRepoLocal = new JButton("Browse..."); + btnBrowseMavenRepoLocal.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String current = textField_MavenRepoLocal.getText().trim(); + if(current.length() == 0) { + current = (new File("")).getAbsolutePath(); + } + JFileChooser fileChooser = new JFileChooser(new File(current)); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileHidingEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if(fileChooser.showOpenDialog(frmCompileLatestClient) == JFileChooser.APPROVE_OPTION) { + textField_MavenRepoLocal.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + } + }); + btnBrowseMavenRepoLocal.setEnabled(false); + btnBrowseMavenRepoLocal.setBounds(256, 162, 89, 23); + panel_22.add(btnBrowseMavenRepoLocal); + + rdbtnMavenRepoLocal.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + textField_MavenRepoLocal.setEnabled(rdbtnMavenRepoLocal.isSelected()); + btnBrowseMavenRepoLocal.setEnabled(rdbtnMavenRepoLocal.isSelected()); + } + }); + + JPanel pageFFmpeg = new JPanel(); + pageFFmpeg.setBackground(Color.WHITE); + pagesRoot.add(pageFFmpeg, "pageFFmpeg"); + pageFFmpeg.setLayout(new BorderLayout(0, 0)); + + JTextPane txtpnfuckOffteavm_1 = new JTextPane(); + txtpnfuckOffteavm_1.addHyperlinkListener(new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + URL url = e.getURL(); + if(url.getHost().equals("check_ffmpeg")) { + if(isFFmpegOnPath()) { + JOptionPane.showMessageDialog(frmCompileLatestClient, "FFmpeg was found on your system's path!\nIt will be selected automatically", "Success", JOptionPane.INFORMATION_MESSAGE); + chckbxUsePathFFmpeg.setEnabled(true); + chckbxUsePathFFmpeg.setSelected(true); + textField_pathToFFmpeg.setEnabled(false); + btnBrowsePathToFFmpeg.setEnabled(false); + hasAskedFFmpegOnPath = true; + setPage(page + 1); + }else { + JOptionPane.showMessageDialog(frmCompileLatestClient, "FFmpeg was not found on your system's path!", "Error", JOptionPane.ERROR_MESSAGE); + } + }else { + try { + Desktop.getDesktop().browse(e.getURL().toURI()); + } catch (Throwable t) { + } + } + } + } + }); + txtpnfuckOffteavm_1.setEditable(false); + txtpnfuckOffteavm_1.setMargin(new Insets(10, 10, 10, 10)); + txtpnfuckOffteavm_1.setContentType("text/html"); + txtpnfuckOffteavm_1.setText("\r\nfuck off\r\n\r\n

FFmpeg Audio Encoder

\r\n

A tool called FFmpeg is required to compress minecraft's audio so it's smaller, the tool is an excessively large single standalone executable file so it is not included in this repository and must be downloaded externally

\r\n

Download the FFmpeg executable here: https://ffmpeg.org/download.html

\r\n

Select where you downloaded the file below

\r\n

If you are on linux and have an FFmpeg package available, install it and click here to detect it on your path automatically

\r\n\r\n"); + pageFFmpeg.add(txtpnfuckOffteavm_1, BorderLayout.CENTER); + + JPanel panel_4 = new JPanel(); + panel_4.setBorder(new EmptyBorder(0, 20, 40, 20)); + panel_4.setBackground(Color.WHITE); + pageFFmpeg.add(panel_4, BorderLayout.SOUTH); + panel_4.setLayout(new BorderLayout(0, 0)); + + JPanel panel_5 = new JPanel(); + panel_5.setPreferredSize(new Dimension(10, 40)); + panel_5.setBackground(Color.WHITE); + panel_4.add(panel_5, BorderLayout.CENTER); + panel_5.setLayout(new BorderLayout(0, 0)); + + JLabel lblNewLabel_9 = new JLabel(FFMPEG.windows ? "path to ffmpeg.exe:" : "path to ffmpeg:"); + lblNewLabel_9.setPreferredSize(new Dimension(76, 20)); + lblNewLabel_9.setFont(new Font("Dialog", Font.BOLD, 12)); + panel_5.add(lblNewLabel_9, BorderLayout.NORTH); + + JPanel panel_6 = new JPanel(); + panel_6.setBackground(Color.WHITE); + panel_6.setPreferredSize(new Dimension(10, 20)); + panel_5.add(panel_6, BorderLayout.SOUTH); + panel_6.setLayout(new BorderLayout(0, 0)); + + JPanel panel_10 = new JPanel(); + panel_10.setBorder(new EmptyBorder(0, 0, 0, 10)); + panel_10.setBackground(Color.WHITE); + panel_6.add(panel_10, BorderLayout.CENTER); + panel_10.setLayout(new BorderLayout(0, 0)); + + textField_pathToFFmpeg = new JTextField(); + textField_pathToFFmpeg.setText(""); + panel_10.add(textField_pathToFFmpeg, BorderLayout.CENTER); + textField_pathToFFmpeg.setColumns(10); + + JPanel panel_34 = new JPanel(); + panel_6.add(panel_34, BorderLayout.EAST); + panel_34.setLayout(new BorderLayout(0, 0)); + + btnBrowsePathToFFmpeg = new JButton("Browse..."); + panel_34.add(btnBrowsePathToFFmpeg); + + chckbxUsePathFFmpeg = new JCheckBox("Use FFmpeg on PATH"); + chckbxUsePathFFmpeg.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + if(chckbxUsePathFFmpeg.isSelected()) { + textField_pathToFFmpeg.setEnabled(false); + btnBrowsePathToFFmpeg.setEnabled(false); + }else { + textField_pathToFFmpeg.setEnabled(true); + btnBrowsePathToFFmpeg.setEnabled(true); + } + } + }); + chckbxUsePathFFmpeg.setFont(new Font("Dialog", Font.BOLD, 12)); + chckbxUsePathFFmpeg.setBackground(Color.WHITE); + chckbxUsePathFFmpeg.setMargin(new Insets(2, 10, 2, 2)); + panel_34.add(chckbxUsePathFFmpeg, BorderLayout.EAST); + btnBrowsePathToFFmpeg.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String current = textField_pathToFFmpeg.getText().trim(); + if(current.length() == 0) { + File f = new File(System.getProperty("user.home", "."), "Downloads"); + if(!f.exists()) { + f = new File(System.getProperty("user.home", ".")); + } + current = f.getAbsolutePath(); + } + JFileChooser fileChooser = new JFileChooser(new File(current)); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileHidingEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + if(fileChooser.showOpenDialog(frmCompileLatestClient) == JFileChooser.APPROVE_OPTION) { + textField_pathToFFmpeg.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + } + }); + + JPanel pageOutputDirectory = new JPanel(); + pageOutputDirectory.setBackground(Color.WHITE); + pagesRoot.add(pageOutputDirectory, "pageOutputDirectory"); + pageOutputDirectory.setLayout(new BorderLayout(0, 0)); + + JTextPane txtpnfuckOffteavm = new JTextPane(); + txtpnfuckOffteavm.setEditable(false); + txtpnfuckOffteavm.setContentType("text/html"); + txtpnfuckOffteavm.setText("\r\nfuck off\r\n\r\n

Output Directory

\r\n

Select the destination directory where you would like this tool to save the compiled files to once this tool is finished

\r\n

The the tool will generate an \"index.html\" file, a \"classes.js\" file, a \"classes.js.map\" file, an \"assets.epk\" file, a \"lang\" directory to hold additional .lang files, and optionally an offline download version of the client that does not require an HTTP server

\r\n\r\n"); + txtpnfuckOffteavm.setMargin(new Insets(10, 10, 10, 10)); + pageOutputDirectory.add(txtpnfuckOffteavm, BorderLayout.CENTER); + + JPanel panel_30 = new JPanel(); + panel_30.setBorder(new EmptyBorder(0, 20, 40, 20)); + panel_30.setBackground(Color.WHITE); + pageOutputDirectory.add(panel_30, BorderLayout.SOUTH); + panel_30.setLayout(new BoxLayout(panel_30, BoxLayout.Y_AXIS)); + + JPanel panel_31 = new JPanel(); + panel_31.setPreferredSize(new Dimension(10, 40)); + panel_31.setBackground(Color.WHITE); + panel_30.add(panel_31); + panel_31.setLayout(new BorderLayout(0, 0)); + + JLabel lblNewLabel_8 = new JLabel("compiler output directory:"); + lblNewLabel_8.setPreferredSize(new Dimension(124, 20)); + lblNewLabel_8.setFont(new Font("Dialog", Font.BOLD, 12)); + panel_31.add(lblNewLabel_8, BorderLayout.NORTH); + + JPanel panel_32 = new JPanel(); + panel_32.setBackground(Color.WHITE); + panel_32.setPreferredSize(new Dimension(10, 20)); + panel_31.add(panel_32, BorderLayout.SOUTH); + panel_32.setLayout(new BorderLayout(0, 0)); + + JButton btnBrowseOutputDirectory = new JButton("Browse..."); + btnBrowseOutputDirectory.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String current = textField_OutputDirectory.getText().trim(); + if(current.length() == 0) { + current = (new File("")).getAbsolutePath(); + } + JFileChooser fileChooser = new JFileChooser(new File(current)); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileHidingEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if(fileChooser.showOpenDialog(frmCompileLatestClient) == JFileChooser.APPROVE_OPTION) { + textField_OutputDirectory.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + } + }); + panel_32.add(btnBrowseOutputDirectory, BorderLayout.EAST); + + JPanel panel_33 = new JPanel(); + panel_33.setBorder(new EmptyBorder(0, 0, 0, 10)); + panel_33.setBackground(Color.WHITE); + panel_32.add(panel_33, BorderLayout.CENTER); + panel_33.setLayout(new BorderLayout(0, 0)); + + textField_OutputDirectory = new JTextField(); + panel_33.add(textField_OutputDirectory, BorderLayout.CENTER); + textField_OutputDirectory.setColumns(10); + + JPanel panel_16 = new JPanel(); + panel_16.setBackground(Color.WHITE); + panel_30.add(panel_16); + panel_16.setLayout(new BorderLayout(0, 0)); + + chckbxOutputOfflineDownload = new JCheckBox("Generate Offline Download"); + chckbxOutputOfflineDownload.setSelected(true); + panel_16.add(chckbxOutputOfflineDownload); + chckbxOutputOfflineDownload.setBackground(Color.WHITE); + chckbxOutputOfflineDownload.setPreferredSize(new Dimension(97, 30)); + + JPanel panel_21 = new JPanel(); + panel_21.setBackground(Color.WHITE); + panel_30.add(panel_21); + panel_21.setLayout(new BorderLayout(0, 0)); + + chckbxKeepTemporaryFiles = new JCheckBox("Keep Temporary Files"); + chckbxKeepTemporaryFiles.setBackground(Color.WHITE); + chckbxKeepTemporaryFiles.setPreferredSize(new Dimension(129, 30)); + panel_21.add(chckbxKeepTemporaryFiles, BorderLayout.NORTH); + + JPanel pageConfirmSettings = new JPanel(); + pageConfirmSettings.setBackground(Color.WHITE); + pagesRoot.add(pageConfirmSettings, "pageConfirmSettings"); + pageConfirmSettings.setLayout(new BorderLayout(0, 0)); + + txtpnfuckOffreview = new JTextPane(); + txtpnfuckOffreview.setEditable(false); + txtpnfuckOffreview.setContentType("text/html"); + txtpnfuckOffreview.setText("\r\nfuck off\r\n\r\n

Are these correct?

\r\n

 • Repository Path: path here

\r\n

 • Mod Coder Pack: path here

\r\n

 • 1.8.8.jar path: path here

\r\n

 • 1.8.json path: path here

\r\n

 • TeaVM Maven: path here

\r\n

 • FFmpeg: path here

\r\n

 • Output Directory: path here

\r\n

 • Make Offline Download: yes/no

\r\n

 • Keep Temporary Files: yes/no

\r\n

 Press the \"Compile >>\" button to confirm

\r\n\r\n"); + txtpnfuckOffreview.setMargin(new Insets(10, 10, 10, 10)); + pageConfirmSettings.add(txtpnfuckOffreview, BorderLayout.CENTER); + + JPanel pageLogOutput = new JPanel(); + pageLogOutput.setBackground(Color.WHITE); + pagesRoot.add(pageLogOutput, "pageLogOutput"); + pageLogOutput.setLayout(new BorderLayout(0, 0)); + + scrollPane = new JScrollPane(); + scrollPane.setBorder(null); + scrollPane.setBackground(Color.WHITE); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + pageLogOutput.add(scrollPane, BorderLayout.CENTER); + + txtpnLogOutput = new JTextPane(); + txtpnLogOutput.setAutoscrolls(false); + txtpnLogOutput.setMargin(new Insets(10, 10, 10, 10)); + txtpnLogOutput.setContentType("text/html"); + txtpnLogOutput.setText("shit"); + txtpnLogOutput.setEditable(false); + scrollPane.setViewportView(txtpnLogOutput); + + JPopupMenu popupMenu = new JPopupMenu(); + addPopup(txtpnLogOutput, popupMenu); + + JMenuItem mntmNewMenuItem = new JMenuItem("Select All"); + mntmNewMenuItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + txtpnLogOutput.selectAll(); + } + }); + popupMenu.add(mntmNewMenuItem); + + JSeparator separator_3 = new JSeparator(); + popupMenu.add(separator_3); + + JMenuItem mntmNewMenuItem_1 = new JMenuItem("Copy"); + mntmNewMenuItem_1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if(txtpnLogOutput.getSelectionStart() == txtpnLogOutput.getSelectionEnd()) { + txtpnLogOutput.selectAll(); + } + txtpnLogOutput.copy(); + } + }); + mntmNewMenuItem_1.setFont(mntmNewMenuItem_1.getFont().deriveFont(Font.BOLD)); + popupMenu.add(mntmNewMenuItem_1); + + JPanel panel_29 = new JPanel(); + panel_29.setBackground(Color.DARK_GRAY); + panel_29.setPreferredSize(new Dimension(10, 1)); + panel_3.add(panel_29, BorderLayout.SOUTH); + panel_29.setLayout(null); + } + + private final StringBuilder logAccum = new StringBuilder(); + private volatile boolean logDirty = false; + private volatile boolean isError = false; + + public void logInfo(String line) { + line = htmlentities2(line); + synchronized(logAccum) { + if(isError) { + isError = false; + logAccum.append("
");
+			}
+			logAccum.append(line);
+			logDirty = true;
+		}
+	}
+
+	public void logError(String line) {
+		line = htmlentities2(line);
+		synchronized(logAccum) {
+			if(!isError) {
+				isError = true;
+				logAccum.append("
");
+			}
+			logAccum.append(line);
+			logDirty = true;
+		}
+	}
+
+	public void launchLogUpdateThread() {
+		Thread lazyLogUpdateThread = new Thread(new Runnable() {
+			@Override
+			public void run() {
+				while(true) {
+					try {
+						Thread.sleep(150l);
+						synchronized(logAccum) {
+							if(logDirty) {
+								EventQueue.invokeAndWait(new Runnable() {
+									public void run() {
+										txtpnLogOutput.setText("shit

" + logAccum + "

"); + } + }); + EventQueue.invokeAndWait(new Runnable() { + public void run() { + JScrollBar bar = scrollPane.getVerticalScrollBar(); + bar.setValue(bar.getMaximum()); + scrollPane.getHorizontalScrollBar().setValue(0); + } + }); + logDirty = false; + } + } + }catch(Throwable t) { + } + } + } + }, "LazyLogUpdateThread"); + lazyLogUpdateThread.setDaemon(true); + lazyLogUpdateThread.start(); + } + + public void finishCompiling(final boolean failed, final String reason) { + try { + EventQueue.invokeAndWait(new Runnable() { + public void run() { + if(!finished) { + lblProgressState.setText(failed ? "Compilation Failed!" : "Compilation Succeeded!"); + lblProgressState.setForeground(failed ? new Color(0x88, 0, 0) : new Color(0, 0x88, 0)); + btnNext.setEnabled(true); + finished = true; + if(failed) { + JOptionPane.showMessageDialog(frmCompileLatestClient, wordWrap("Failed to Compile Client!\nReason: " + reason), "Error", JOptionPane.ERROR_MESSAGE); + }else { + JOptionPane.showMessageDialog(frmCompileLatestClient, "Finished Compiling", "Success", JOptionPane.INFORMATION_MESSAGE); + } + } + } + }); + } catch (InvocationTargetException | InterruptedException e) { + e.printStackTrace(); + } + } + + private static void addPopup(Component component, final JPopupMenu popup) { + component.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showMenu(e); + } + } + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showMenu(e); + } + } + private void showMenu(MouseEvent e) { + popup.show(e.getComponent(), e.getX(), e.getY()); + } + }); + } + + public String getRepositoryURL() { + if(rdbtnMavenRepoCustom.isSelected()) { + return textField_MavenRepoCustomURL.getText(); + }else if(rdbtnMavenRepoSonatype.isSelected()) { + return "https://oss.sonatype.org/content/repositories/releases/"; + }else if(rdbtnMavenRepoBintray.isSelected()) { + return "https://jcenter.bintray.com/"; + }else if(rdbtnMavenRepoCentral.isSelected()) { + return "https://repo1.maven.org/maven2/"; + }else { + return null; + } + } + + private boolean knowFoundFFmpeg = false; + private boolean foundFFmpeg = false; + private boolean hasAskedFFmpegOnPath = false; + + private boolean isFFmpegOnPath() { + if(!knowFoundFFmpeg) { + if(foundFFmpeg = FFMPEG.checkFFMPEGOnPath()) { + knowFoundFFmpeg = true; + } + } + return foundFFmpeg; + } + + private static String htmlentities(String strIn) { + return strIn.replace("<", "<").replace(">", ">"); + } + + private static String htmlentities2(String strIn) { + return strIn.replace("
", "[/pre]"); + } + + private static String wordWrap(String str) { + StringBuilder ret = new StringBuilder(); + while(str.length() > 100) { + ret.append(str.substring(0, 100)).append('\n'); + str = str.substring(100); + } + ret.append(str); + return ret.toString(); + } + + private static String loadLicense() { + try(BufferedReader reader = new BufferedReader(new InputStreamReader(CompileLatestClientFrame.class.getResourceAsStream("/lang/LICENSE.txt"), StandardCharsets.UTF_8))) { + StringBuilder ret = new StringBuilder(); + char[] copyBuffer = new char[4096]; + int i; + while((i = reader.read(copyBuffer)) != -1) { + ret.append(copyBuffer, 0, i); + } + return ret.toString(); + } catch (IOException e) { + return "Could not load LICENSE text!\n\nPlease read the file named \"LICENSE\" in the root directory of the repository before continuing"; + } + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientGUI.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientGUI.java new file mode 100644 index 0000000..ddfbd4b --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/CompileLatestClientGUI.java @@ -0,0 +1,416 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.gui; + +import java.awt.Desktop; +import java.awt.EventQueue; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +import org.apache.commons.io.FileUtils; + +import net.lax1dude.eaglercraft.v1_8.buildtools.EaglerBuildTools; +import net.lax1dude.eaglercraft.v1_8.buildtools.gui.TeaVMBinaries.MissingJARsException; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.DecompileMinecraft; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.FFMPEG; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.InitMCP; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.teavm.TeaVMBridge; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.teavm.TeaVMBridge.TeaVMClassLoadException; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.teavm.TeaVMBridge.TeaVMRuntimeException; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class CompileLatestClientGUI { + + public static CompileLatestClientFrame frame = null; + + public static void main(String[] args) { + System.out.println(); + System.out.println("Launching client compiler wizard..."); + System.out.println("Copyright (c) 2022 lax1dude"); + System.out.println(); + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException + | UnsupportedLookAndFeelException e) { + System.err.println("Could not set system look and feel: " + e.toString()); + } + if(!System.getProperty("eaglercraft.isJava11", "false").equalsIgnoreCase("true")) { + try { + if (!(boolean) Class + .forName("net.lax1dude.eaglercraft.v1_8.buildtools.Java11Check", true, + new URLClassLoader(new URL[] { (new File("buildtools/Java11Check.jar")).toURI().toURL() })) + .getMethod("classLoadCheck").invoke(null)) { + throw new RuntimeException("wtf?"); + } + }catch(Throwable t) { + JOptionPane.showMessageDialog(null, "Error: Java 11 is required to run this program", "Unsupported JRE", JOptionPane.ERROR_MESSAGE); + System.exit(-1); + return; + } + } + frame = new CompileLatestClientFrame(); + frame.frmCompileLatestClient.setLocationRelativeTo(null); + frame.frmCompileLatestClient.setVisible(true); + System.out.println("you eagler"); + System.out.println(); + frame.launchLogUpdateThread(); + System.setOut(new PrintStream(new ConsoleRedirector(false))); + System.setErr(new PrintStream(new ConsoleRedirector(true))); + if(JavaC.jdkHome == null) { + if(JOptionPane.showConfirmDialog(frame.frmCompileLatestClient, "Error: A JDK is required to run this program!\nYou are currently running on a JRE\nDo you have a JDK installed that you would like to use instead?", "Unsupported JRE", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + JOptionPane.showMessageDialog(frame.frmCompileLatestClient, "You need at least JDK 8 to compile EaglercraftX 1.8!\nSelect the path to the installation you want to use", "Unsupported JRE", JOptionPane.INFORMATION_MESSAGE); + JFileChooser fileChooser = new JFileChooser((new File(System.getProperty("java.home"))).getParentFile()); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileHidingEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + while(true) { + if(fileChooser.showOpenDialog(frame.frmCompileLatestClient) == JFileChooser.APPROVE_OPTION) { + File f = fileChooser.getSelectedFile(); + if(JavaC.windows ? (new File(f, "bin/javac.exe")).exists() : (new File(f, "bin/javac")).canExecute()) { + break; + }else { + if(JOptionPane.showConfirmDialog(frame.frmCompileLatestClient, "Could not find a java compiler in this directory!\nWould you like to try again?", "Unsupported JRE", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + continue; + } + } + } + JOptionPane.showMessageDialog(frame.frmCompileLatestClient, "Please install JDK 8 or newer to continue", "Unsupported JRE", JOptionPane.ERROR_MESSAGE); + System.exit(-1); + return; + } + JavaC.jdkHome = fileChooser.getSelectedFile(); + JOptionPane.showMessageDialog(frame.frmCompileLatestClient, "The JDK \"" + JavaC.jdkHome.getAbsolutePath() + "\" will be used to compile EaglercraftX", "Unsupported JRE", JOptionPane.INFORMATION_MESSAGE); + }else { + JOptionPane.showMessageDialog(frame.frmCompileLatestClient, "Please install a JDK and re-launch this program", "Unsupported JRE", JOptionPane.ERROR_MESSAGE); + System.exit(-1); + } + } + EventQueue.invokeLater(new Runnable() { + public void run() { + frame.scrollPane_LicenseText.getVerticalScrollBar().setValue(0); + } + }); + } + }); + } + + public static class CompileFailureException extends RuntimeException { + public CompileFailureException(String msg) { + super(msg); + } + public CompileFailureException(String msg, Throwable cause) { + super(msg, cause); + } + } + + public static void runCompiler() { + try { + runCompiler0(); + }catch(CompileFailureException t) { + System.out.println(); + System.err.println("Error: " + t.getMessage()); + t.printStackTrace(); + frame.finishCompiling(true, t.getMessage()); + return; + }catch(Throwable t) { + System.out.println(); + System.err.println("Error: unhandled exception caught while compiling!"); + t.printStackTrace(); + frame.finishCompiling(true, t.toString()); + return; + } + if(!frame.finished) { + System.out.println(); + System.err.println("Error: compilation finished with unknown status!"); + frame.finishCompiling(true, "Compilation finished with unknown status"); + } + } + + private static void runCompiler0() throws Throwable { + File repositoryFolder = new File(frame.textField_RepositoryPath.getText().trim()); + EaglerBuildTools.repositoryRoot = repositoryFolder; + File modCoderPack = new File(frame.textField_ModCoderPack.getText().trim()); + File minecraftJar = new File(frame.textField_JarFilePath.getText().trim()); + File assetsIndex = new File(frame.textField_AssetsIndexJSON.getText().trim()); + File outputDirectory = new File(frame.textField_OutputDirectory.getText().trim()); + File temporaryDirectory = new File(outputDirectory, "build"); + + File[] existingOutput = outputDirectory.listFiles(); + if(existingOutput.length > 0) { + System.out.println("Deleting existing files from the output directory..."); + try { + for(int i = 0; i < existingOutput.length; ++i) { + File f = existingOutput[i]; + if(f.isDirectory()) { + FileUtils.deleteDirectory(f); + }else { + if(!f.delete()) { + throw new IOException("Could not delete: " + f.getAbsolutePath()); + } + } + } + }catch(IOException t) { + throw new CompileFailureException("Could not delete old output directory: " + t.getMessage()); + } + } + + File mcpDataTMP = new File(temporaryDirectory, "ModCoderPack"); + File minecraftSrcTmp = new File(temporaryDirectory, "MinecraftSrc"); + + String ffmpeg = frame.chckbxUsePathFFmpeg.isSelected() ? "" : frame.textField_pathToFFmpeg.getText().trim(); + if(ffmpeg.length() == 0) { + FFMPEG.foundFFMPEG = "ffmpeg"; + }else { + FFMPEG.foundFFMPEG = ffmpeg; + } + + String mavenRepositoryURL = frame.getRepositoryURL(); + File mavenRepositoryFolder = null; + if(mavenRepositoryURL == null) { + mavenRepositoryFolder = new File(frame.textField_MavenRepoLocal.getText().trim()); + } + + boolean generateOfflineDownload = frame.chckbxOutputOfflineDownload.isSelected(); + boolean keepTemporaryFiles = frame.chckbxKeepTemporaryFiles.isSelected(); + + if(!mcpDataTMP.isDirectory() && !mcpDataTMP.mkdirs()) { + throw new CompileFailureException("Error: failed to create \"" + mcpDataTMP.getAbsolutePath() + "\"!"); + } + + if(!InitMCP.initTask(modCoderPack, mcpDataTMP)) { + throw new CompileFailureException("Error: could not initialize MCP from \"" + modCoderPack.getAbsolutePath() + "\"!"); + } + + if(!minecraftSrcTmp.isDirectory() && !minecraftSrcTmp.mkdirs()) { + throw new CompileFailureException("Error: failed to create \"" + minecraftSrcTmp.getAbsolutePath() + "\"!"); + } + + if(!DecompileMinecraft.decompileMinecraft(mcpDataTMP, minecraftJar, minecraftSrcTmp, assetsIndex, false)) { + throw new CompileFailureException("Error: could not decompile and patch 1.8.8.jar from \"" + minecraftJar.getAbsolutePath() + "\"!"); + } + + try { + FileUtils.copyFile(new File(repositoryFolder, "patches/minecraft/output_license.txt"), new File(temporaryDirectory, "MinecraftSrc/LICENSE")); + }catch(IOException ex) { + System.err.println("Error: failed to write LICENSE in temporary directory!"); + ex.printStackTrace(); + } + + System.out.println(); + + if(frame.rdbtnMavenRepoLocal.isSelected()) { + System.out.println("TeaVM JARs will be loaded from: " + frame.textField_MavenRepoLocal.getText()); + }else { + String url = frame.getRepositoryURL(); + System.out.println("TeaVM JARs will be downloaded from repository: " + url); + System.out.println(); + try { + TeaVMBinaries.downloadFromMaven(url, new File("##TEAVM.TMP##")); + }catch(MissingJARsException ex) { + throw new CompileFailureException(ex.getMessage()); + } + } + + System.out.println(); + + int compileResultCode; + File compiledResultClasses = new File(temporaryDirectory, "classes"); + + try { + try { + compileResultCode = JavaC.runJavaC(new File(minecraftSrcTmp, "minecraft_src_javadoc.jar"), + compiledResultClasses, temporaryDirectory, TeaVMBinaries.getTeaVMRuntimeClasspath(), + new File(repositoryFolder, "sources/main/java"), new File(repositoryFolder, "sources/teavm/java")); + }catch(IOException ex) { + throw new CompileFailureException("failed to run javac compiler! " + ex.toString(), ex); + } + + System.out.println(); + + if(compileResultCode == 0) { + System.out.println("Java compiler completed successfully"); + }else { + throw new CompileFailureException("failed to run javac compiler! exit code " + compileResultCode + ", check log"); + } + }finally { + File extractedSrcTmp = new File(temporaryDirectory, "MinecraftSrc/src_javadoc_tmp"); + if(extractedSrcTmp.exists()) { + System.out.println(); + System.out.println("Deleting temporary directory: " + extractedSrcTmp.getAbsolutePath()); + try { + FileUtils.deleteDirectory(extractedSrcTmp); + }catch(IOException ex) { + System.err.println("Failed to delete temporary directory!"); + ex.printStackTrace(); + } + } + } + + System.out.println(); + System.out.println("Preparing arguments for TeaVM..."); + + if(!TeaVMBinaries.tryLoadTeaVMBridge()) { + System.err.println("Failed to locate TeaVMBridge.jar, you can specify it's path manually by adding the JVM argument \"-Deaglercraft.TeaVMBridge=\""); + throw new CompileFailureException("Failed to locate TeaVMBridge.jar!"); + } + + Map teavmArgs = new HashMap(); + + List teavmClassPath = new ArrayList(); + teavmClassPath.add(compiledResultClasses.getAbsolutePath()); + teavmClassPath.addAll(Arrays.asList(TeaVMBinaries.getTeaVMRuntimeClasspath())); + teavmArgs.put("classPathEntries", teavmClassPath); + + teavmArgs.put("entryPointName", "main"); + teavmArgs.put("mainClass", "net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass"); + teavmArgs.put("minifying", true); + teavmArgs.put("optimizationLevel", "ADVANCED"); + teavmArgs.put("targetDirectory", outputDirectory.getAbsolutePath()); + teavmArgs.put("generateSourceMaps", true); + teavmArgs.put("targetFileName", "classes.js"); + + System.out.println(); + + boolean teavmStatus; + try { + teavmStatus = TeaVMBridge.compileTeaVM(teavmArgs); + }catch(TeaVMClassLoadException ex) { + throw new CompileFailureException("Failed to link TeaVM jar files! Did you select the wrong jar?", ex); + }catch(TeaVMRuntimeException ex) { + throw new CompileFailureException("Failed to run TeaVM! Check log", ex); + } + + if(!teavmStatus) { + frame.finishCompiling(true, "TeaVM reported problems, check the log"); + return; + } + + File epkCompiler = new File(repositoryFolder, "sources/setup/workspace_template/desktopRuntime/CompileEPK.jar"); + + if(!epkCompiler.exists()) { + throw new CompileFailureException("EPKCompiler JAR file is missing: " + epkCompiler.getAbsolutePath()); + } + + System.out.println(); + System.out.println("Writing default index.html..."); + + FileUtils.copyFile(new File(repositoryFolder, "buildtools/production-index.html"), new File(outputDirectory, "index.html")); + FileUtils.copyFile(new File(repositoryFolder, "buildtools/production-favicon.png"), new File(outputDirectory, "favicon.png")); + FileUtils.copyFile(new File(repositoryFolder, "sources/setup/workspace_template/javascript/fix-webm-duration.js"), new File(outputDirectory, "fix-webm-duration.js")); + + System.out.println(); + System.out.println("Running EPKCompiler on assets..."); + + EPKCompiler.compilerMain(epkCompiler, new String[] { + ((new File(minecraftSrcTmp, "minecraft_res_patch.jar")).getAbsolutePath() + System.getProperty("path.separator") + + (new File(repositoryFolder, "sources/resources")).getAbsolutePath()), (new File(outputDirectory, "assets.epk")).getAbsolutePath() }); + + System.out.println(); + System.out.println("Running EPKCompiler on languages.zip..."); + + EPKCompiler.compilerMain(epkCompiler, new String[] { + (new File(minecraftSrcTmp, "minecraft_languages.zip")).getAbsolutePath(), + (new File(temporaryDirectory, "languages.epk")).getAbsolutePath() }); + + System.out.println(); + System.out.println("Creating languages directory..."); + File langDirectory = new File(outputDirectory, "lang"); + + byte[] copyBuffer = new byte[16384]; + int i; + try(ZipInputStream zis = new ZipInputStream(new FileInputStream(new File(minecraftSrcTmp, "minecraft_languages.zip")))) { + ZipEntry etr; + while((etr = zis.getNextEntry()) != null) { + if(!etr.isDirectory()) { + File phile = new File(langDirectory, etr.getName()); + File parent = phile.getParentFile(); + if(!parent.exists() && !parent.mkdirs()) { + throw new IOException("Could not create directory: " + parent.getAbsolutePath()); + } + try(FileOutputStream os = new FileOutputStream(phile)) { + while((i = zis.read(copyBuffer)) != -1) { + os.write(copyBuffer, 0, i); + } + } + } + } + } + + System.out.println(); + + if(generateOfflineDownload) { + System.out.println("Running offline download generator..."); + System.out.println(); + File offlineDownloadGenerator = new File(repositoryFolder, "sources/setup/workspace_template/desktopRuntime/MakeOfflineDownload.jar"); + MakeOfflineDownload.compilerMain(offlineDownloadGenerator, new String[] { + (new File(repositoryFolder, "sources/setup/workspace_template/javascript/OfflineDownloadTemplate.txt")).getAbsolutePath(), + (new File(outputDirectory, "classes.js")).getAbsolutePath() + System.getProperty("path.separator") + + (new File(outputDirectory, "fix-webm-duration.js")).getAbsolutePath(), + (new File(outputDirectory, "assets.epk")).getAbsolutePath(), + (new File(outputDirectory, "EaglercraftX_1.8_Offline_en_US.html")).getAbsolutePath(), + (new File(outputDirectory, "EaglercraftX_1.8_Offline_International.html")).getAbsolutePath(), + (new File(outputDirectory, "build/languages.epk")).getAbsolutePath() + }); + } + + System.out.println("Releasing external ClassLoader(s)..."); + System.out.println(); + + TeaVMBridge.free(); + EPKCompiler.free(); + + if(generateOfflineDownload) { + MakeOfflineDownload.free(); + } + + if(!keepTemporaryFiles) { + System.out.println("Cleaning up temporary files..."); + try { + FileUtils.deleteDirectory(temporaryDirectory); + }catch(IOException ex) { + System.err.println("Failed to delete temporary directory: " + temporaryDirectory.getAbsolutePath()); + ex.printStackTrace(); + } + } + + System.out.println(); + System.out.println("Client build successful! Check the output directory for your files"); + + try { + Desktop.getDesktop().open(outputDirectory); + }catch(Throwable t) { + } + + frame.finishCompiling(false, ""); + } +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/ConsoleRedirector.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/ConsoleRedirector.java new file mode 100644 index 0000000..65f57cc --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/ConsoleRedirector.java @@ -0,0 +1,58 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.gui; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class ConsoleRedirector extends OutputStream { + + private final OutputStream stdout; + private final boolean err; + + public ConsoleRedirector(boolean err) { + stdout = err ? System.err : System.out; + this.err = err; + } + + @Override + public void write(byte[] b, int o, int l) throws IOException { + stdout.write(b, o, l); + String append = new String(b, o, l, StandardCharsets.US_ASCII); + if(err) { + CompileLatestClientGUI.frame.logError(append); + }else { + CompileLatestClientGUI.frame.logInfo(append); + } + } + + @Override + public void write(int b) throws IOException { + stdout.write(b); + write0(b); + } + + private void write0(int b) throws IOException { + char c = (char)b; + if(c != '\r') { + if(err && c != '\n') { + CompileLatestClientGUI.frame.logError(new String(new char[] { c })); + }else { + CompileLatestClientGUI.frame.logInfo(new String(new char[] { c })); + } + } + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/EPKCompiler.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/EPKCompiler.java new file mode 100644 index 0000000..a11c8ea --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/EPKCompiler.java @@ -0,0 +1,67 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.gui; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EPKCompiler { + + private static File currentJarFile = null; + private static URLClassLoader classLoader = null; + private static Method mainMethod = null; + + public static void compilerMain(File jarFile, String[] args) throws InvocationTargetException { + if(currentJarFile != null && !currentJarFile.equals(jarFile)) { + throw new IllegalArgumentException("Cannot load two different EPKCompiler versions into the same runtime"); + } + if(mainMethod == null) { + currentJarFile = jarFile; + try { + if(classLoader == null) { + classLoader = new URLClassLoader(new URL[] { jarFile.toURI().toURL() }, ClassLoader.getSystemClassLoader()); + } + Class epkCompilerMain = classLoader.loadClass("CompilePackage"); + mainMethod = epkCompilerMain.getDeclaredMethod("main", String[].class); + } catch (MalformedURLException | SecurityException e) { + throw new IllegalArgumentException("Illegal EPKCompiler JAR path!", e); + } catch (ClassNotFoundException | NoSuchMethodException e) { + throw new IllegalArgumentException("EPKCompiler JAR does not contain main class: 'CompilePackage'", e); + } + } + try { + mainMethod.invoke(null, new Object[] { args }); + } catch (IllegalAccessException | IllegalArgumentException e) { + throw new IllegalArgumentException("EPKCompiler JAR does not contain valid 'main' method", e); + } + } + + public static void free() { + if(classLoader != null) { + try { + classLoader.close(); + classLoader = null; + } catch (IOException e) { + System.err.println("Memory leak, failed to release EPKCompiler ClassLoader!"); + e.printStackTrace(); + } + } + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/JavaC.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/JavaC.java new file mode 100644 index 0000000..7b6acd4 --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/JavaC.java @@ -0,0 +1,223 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.gui; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class JavaC { + + public static final boolean windows; + + public static File jdkHome; + + public static final List compilerFlags = Arrays.asList( + "-Xlint:-unchecked", "-Xlint:-options", "-Xlint:-deprecation", + "-source", "1.8", "-target", "1.8" + ); + + private static int debugSourceFileCount = 0; + + public static int runJavaC(File mcSourceJar, File outputDirectory, File tmpDirectory, String[] teavmClasspath, + File... eaglerSourceDirs) throws IOException { + + if(!outputDirectory.exists() && !outputDirectory.mkdirs()) { + throw new IOException("Could not create output directory: " + outputDirectory.getAbsolutePath()); + } + + if(!tmpDirectory.exists() && !tmpDirectory.mkdirs()) { + throw new IOException("Could not create temporary directory: " + outputDirectory.getAbsolutePath()); + } + + File minecraftSrcTmp = new File(tmpDirectory, "MinecraftSrc/src_javadoc_tmp"); + + if(!minecraftSrcTmp.exists() && !minecraftSrcTmp.mkdirs()) { + throw new IOException("Could not create temporary directory: " + minecraftSrcTmp.getAbsolutePath()); + } + + debugSourceFileCount = 0; + + File argFile = new File(tmpDirectory, "sourceFiles.txt"); + try(PrintWriter writer = new PrintWriter(new FileWriter(argFile))) { + + System.out.println("Extracting decompiled source..."); + + byte[] copyBuffer = new byte[16384]; + int copyBufferLen; + try(ZipInputStream zis = new ZipInputStream(new FileInputStream(mcSourceJar))) { + ZipEntry etr; + while((etr = zis.getNextEntry()) != null && !etr.isDirectory()) { + String n = etr.getName(); + if(n.endsWith(".java")) { + File writeTo = new File(minecraftSrcTmp, n); + File parent = writeTo.getParentFile(); + if(!parent.exists() && !parent.mkdirs()) { + throw new IOException("Could not create temporary directory: " + parent.getAbsolutePath()); + } + try(OutputStream os = new FileOutputStream(writeTo)) { + while((copyBufferLen = zis.read(copyBuffer)) != -1) { + os.write(copyBuffer, 0, copyBufferLen); + } + } + writer.println("\"" + writeTo.getAbsolutePath().replace('\\', '/') + "\""); + ++debugSourceFileCount; + } + } + } + + System.out.println("Scanning source folder paths..."); + + for(int i = 0; i < eaglerSourceDirs.length; ++i) { + discoverSourceFiles(eaglerSourceDirs[i], writer); + } + + } + + List commandBuilder = new ArrayList(); + + if(windows) { + commandBuilder.add((new File(jdkHome, "bin/javac.exe")).getAbsolutePath()); + }else { + commandBuilder.add((new File(jdkHome, "bin/javac")).getAbsolutePath()); + } + + commandBuilder.addAll(compilerFlags); + + String pathSeparator = System.getProperty("path.separator"); + + commandBuilder.add("-classpath"); + commandBuilder.add(String.join(pathSeparator, teavmClasspath)); + + commandBuilder.add("-sourcepath"); + + StringBuilder sourcePathBuilder = new StringBuilder(); + sourcePathBuilder.append(mcSourceJar.getAbsolutePath()); + + for(int i = 0; i < eaglerSourceDirs.length; ++i) { + sourcePathBuilder.append(pathSeparator).append(eaglerSourceDirs[i].getAbsolutePath()); + } + + commandBuilder.add(sourcePathBuilder.toString()); + + commandBuilder.add("-d"); + commandBuilder.add(outputDirectory.getAbsolutePath()); + + commandBuilder.add("@" + argFile.getAbsolutePath()); + + System.out.println(); + for(int i = 0, l = commandBuilder.size(); i < l; ++i) { + String e = commandBuilder.get(i); + if(e.indexOf(' ') != -1) { + System.out.print("\"" + e + "\""); + }else { + System.out.print(e); + } + System.out.print(' '); + } + System.out.println(); + System.out.println(); + System.out.println("Compiling " + debugSourceFileCount + " source files..."); + + ProcessBuilder procBuilder = new ProcessBuilder(commandBuilder); + procBuilder.directory(tmpDirectory); + Process javacProcess = procBuilder.start(); + + InputStream stdout = javacProcess.getInputStream(); + InputStream stderr = javacProcess.getErrorStream(); + byte[] readBuffer = new byte[128]; + int j; + boolean tick; + + do { + tick = false; + + j = stdout.available(); + if(j > 0) { + if(j > 128) { + j = 128; + } + stdout.read(readBuffer, 0, j); + System.out.write(readBuffer, 0, j); + tick = true; + } + + j = stderr.available(); + if(j > 0) { + if(j > 128) { + j = 128; + } + stderr.read(readBuffer, 0, j); + System.err.write(readBuffer, 0, j); + tick = true; + } + + if(!tick) { + try { + Thread.sleep(10l); + } catch (InterruptedException e) { + } + } + + }while(javacProcess.isAlive()); + + while(true) { + try { + return javacProcess.waitFor(); + } catch (InterruptedException e) { + } + } + } + + private static void discoverSourceFiles(File folder, PrintWriter printWriter) throws IOException { + File[] files = folder.listFiles(); + for(int i = 0; i < files.length; ++i) { + File f = files[i]; + String name = f.getAbsolutePath(); + if(f.isDirectory()) { + discoverSourceFiles(f, printWriter); + }else { + if(name.endsWith(".java")) { + printWriter.println("\"" + name.replace('\\', '/') + "\""); + ++debugSourceFileCount; + } + } + } + } + + static { + windows = System.getProperty("os.name").toLowerCase().contains("windows"); + String javac = windows ? "javac.exe" : "javac"; + File jdkHomeProp = new File(System.getProperty("java.home")); + if((new File(jdkHomeProp, "bin/" + javac)).isFile()) { + jdkHome = jdkHomeProp; + }else if((new File(jdkHomeProp, "../bin/" + javac)).isFile()) { + jdkHome = jdkHomeProp.getParentFile(); + }else { + jdkHome = null; + } + } + + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/MakeOfflineDownload.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/MakeOfflineDownload.java new file mode 100644 index 0000000..94d437b --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/MakeOfflineDownload.java @@ -0,0 +1,67 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.gui; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class MakeOfflineDownload { + + private static File currentJarFile = null; + private static URLClassLoader classLoader = null; + private static Method mainMethod = null; + + public static void compilerMain(File jarFile, String[] args) throws InvocationTargetException { + if(currentJarFile != null && !currentJarFile.equals(jarFile)) { + throw new IllegalArgumentException("Cannot load two different MakeOfflineDownload versions into the same runtime"); + } + if(mainMethod == null) { + currentJarFile = jarFile; + try { + if(classLoader == null) { + classLoader = new URLClassLoader(new URL[] { jarFile.toURI().toURL() }, ClassLoader.getSystemClassLoader()); + } + Class epkCompilerMain = classLoader.loadClass("net.lax1dude.eaglercraft.v1_8.buildtools.workspace.MakeOfflineDownload"); + mainMethod = epkCompilerMain.getDeclaredMethod("main", String[].class); + } catch (MalformedURLException | SecurityException e) { + throw new IllegalArgumentException("Illegal MakeOfflineDownload JAR path!", e); + } catch (ClassNotFoundException | NoSuchMethodException e) { + throw new IllegalArgumentException("MakeOfflineDownload JAR does not contain main class: 'net.lax1dude.eaglercraft.v1_8.buildtools.workspace.MakeOfflineDownload'", e); + } + } + try { + mainMethod.invoke(null, new Object[] { args }); + } catch (IllegalAccessException | IllegalArgumentException e) { + throw new IllegalArgumentException("MakeOfflineDownload JAR does not contain valid 'main' method", e); + } + } + + public static void free() { + if(classLoader != null) { + try { + classLoader.close(); + classLoader = null; + } catch (IOException e) { + System.err.println("Memory leak, failed to release MakeOfflineDownload ClassLoader!"); + e.printStackTrace(); + } + } + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/TeaVMBinaries.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/TeaVMBinaries.java new file mode 100644 index 0000000..f357fb5 --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/TeaVMBinaries.java @@ -0,0 +1,422 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.gui; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.io.FileUtils; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class TeaVMBinaries { + + public static final String teavmCoreJar = "teavm-core-0.6.1.jar"; + public static final String teavmCoreMaven = "org/teavm/teavm-core/0.6.1/teavm-core-0.6.1.jar"; + public static File teavmCore = null; + + public static final String teavmCliJar = "teavm-cli-0.6.1.jar"; + public static final String teavmCliMaven = "org/teavm/teavm-cli/0.6.1/teavm-cli-0.6.1.jar"; + public static File teavmCli = null; + + public static final String teavmToolingJar = "teavm-tooling-0.6.1.jar"; + public static final String teavmToolingMaven = "org/teavm/teavm-tooling/0.6.1/teavm-tooling-0.6.1.jar"; + public static File teavmTooling = null; + + public static final String teavmPlatformJar = "teavm-platform-0.6.1.jar"; + public static final String teavmPlatformMaven = "org/teavm/teavm-platform/0.6.1/teavm-platform-0.6.1.jar"; + public static File teavmPlatform = null; + + public static final String teavmClasslibJar = "teavm-classlib-0.6.1.jar"; + public static final String teavmClasslibMaven = "org/teavm/teavm-classlib/0.6.1/teavm-classlib-0.6.1.jar"; + public static File teavmClasslib = null; + + public static final String teavmInteropJar = "teavm-interop-0.6.1.jar"; + public static final String teavmInteropMaven = "org/teavm/teavm-interop/0.6.1/teavm-interop-0.6.1.jar"; + public static File teavmInterop = null; + + public static final String teavmJSOJar = "teavm-jso-0.6.1.jar"; + public static final String teavmJSOMaven = "org/teavm/teavm-jso/0.6.1/teavm-jso-0.6.1.jar"; + public static File teavmJSO = null; + + public static final String teavmJSOApisJar = "teavm-jso-apis-0.6.1.jar"; + public static final String teavmJSOApisMaven = "org/teavm/teavm-jso-apis/0.6.1/teavm-jso-apis-0.6.1.jar"; + public static File teavmJSOApis = null; + + public static final String teavmJSOImplJar = "teavm-jso-impl-0.6.1.jar"; + public static final String teavmJSOImplMaven = "org/teavm/teavm-jso-impl/0.6.1/teavm-jso-impl-0.6.1.jar"; + public static File teavmJSOImpl = null; + + public static final String teavmMetaprogrammingAPIJar = "teavm-metaprogramming-api-0.6.1.jar"; + public static final String teavmMetaprogrammingAPIMaven = "org/teavm/teavm-metaprogramming-api/0.6.1/teavm-metaprogramming-api-0.6.1.jar"; + public static File teavmMetaprogrammingAPI = null; + + public static final String teavmMetaprogrammingImplJar = "teavm-metaprogramming-impl-0.6.1.jar"; + public static final String teavmMetaprogrammingImplMaven = "org/teavm/teavm-metaprogramming-impl/0.6.1/teavm-metaprogramming-impl-0.6.1.jar"; + public static File teavmMetaprogrammingImpl = null; + + public static final String teavmJodaTimeJar = "joda-time-2.7.jar"; + public static final String teavmJodaTimeMaven = "joda-time/joda-time/2.7/joda-time-2.7.jar"; + public static File teavmJodaTime = null; + + public static final String teavmJZLIBJar = "jzlib-1.1.3.jar"; + public static final String teavmJZLIBMaven = "com/jcraft/jzlib/1.1.3/jzlib-1.1.3.jar"; + public static File teavmJZLIB = null; + + public static File teavmBridge = null; + + public static class MissingJARsException extends RuntimeException { + + public final List jars; + + public MissingJARsException(String msg, List jars) { + super(msg); + this.jars = jars; + } + + public MissingJARsException(List jars) { + this("The following JAR files were not found: " + String.join(", ", jars), jars); + } + + } + + public static void downloadFromMaven(String url, File outputDir) throws MissingJARsException { + teavmCore = teavmPlatform = teavmClasslib = teavmInterop = teavmJSO = + teavmJSOApis = teavmJSOImpl = teavmMetaprogrammingAPI = teavmMetaprogrammingImpl = + teavmJodaTime = teavmJZLIB = teavmTooling = teavmCli = null; + + if(url.lastIndexOf('/') != url.length() - 1) { + url += "/"; + } + + String urlConc = url + teavmCoreMaven; + try { + File f = new File(outputDir, teavmCoreJar); + copyURLToFileCheck404(urlConc, f); + teavmCore = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmCliMaven; + try { + File f = new File(outputDir, teavmCliJar); + copyURLToFileCheck404(urlConc, f); + teavmCli = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmToolingMaven; + try { + File f = new File(outputDir, teavmToolingJar); + copyURLToFileCheck404(urlConc, f); + teavmTooling = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmPlatformMaven; + try { + File f = new File(outputDir, teavmPlatformJar); + copyURLToFileCheck404(urlConc, f); + teavmPlatform = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmClasslibMaven; + try { + File f = new File(outputDir, teavmClasslibJar); + copyURLToFileCheck404(urlConc, f); + teavmClasslib = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmInteropMaven; + try { + File f = new File(outputDir, teavmInteropJar); + copyURLToFileCheck404(urlConc, f); + teavmInterop = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmJSOMaven; + try { + File f = new File(outputDir, teavmJSOJar); + copyURLToFileCheck404(urlConc, f); + teavmJSO = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmJSOApisMaven; + try { + File f = new File(outputDir, teavmJSOApisJar); + copyURLToFileCheck404(urlConc, f); + teavmJSOApis = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmJSOImplMaven; + try { + File f = new File(outputDir, teavmJSOImplJar); + copyURLToFileCheck404(urlConc, f); + teavmJSOImpl = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmMetaprogrammingAPIMaven; + try { + File f = new File(outputDir, teavmMetaprogrammingAPIJar); + copyURLToFileCheck404(urlConc, f); + teavmMetaprogrammingAPI = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmMetaprogrammingImplMaven; + try { + File f = new File(outputDir, teavmMetaprogrammingImplJar); + copyURLToFileCheck404(urlConc, f); + teavmMetaprogrammingImpl = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmJodaTimeMaven; + try { + File f = new File(outputDir, teavmJodaTimeJar); + copyURLToFileCheck404(urlConc, f); + teavmJodaTime = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + urlConc = url + teavmJZLIBMaven; + try { + File f = new File(outputDir, teavmJZLIBJar); + copyURLToFileCheck404(urlConc, f); + teavmJZLIB = f; + }catch(IOException ex) { + System.err.println("Could not download JAR: " + urlConc); + ex.printStackTrace(); + throw new MissingJARsException("The following JAR file could not be downloaded: " + urlConc, Arrays.asList(urlConc)); + } + + } + + public static void loadFromDirectory(File directory) throws MissingJARsException { + teavmCore = teavmPlatform = teavmClasslib = teavmInterop = teavmJSO = + teavmJSOApis = teavmJSOImpl = teavmMetaprogrammingAPI = teavmMetaprogrammingImpl = + teavmJodaTime = teavmJZLIB = teavmTooling = teavmCli = null; + discoverJars(directory); + List missingJars = new ArrayList(); + if(teavmCore == null) { + missingJars.add(teavmCoreJar); + } + if(teavmCli == null) { + missingJars.add(teavmCliJar); + } + if(teavmTooling == null) { + missingJars.add(teavmToolingJar); + } + if(teavmPlatform == null) { + missingJars.add(teavmPlatformJar); + } + if(teavmClasslib == null) { + missingJars.add(teavmClasslibJar); + } + if(teavmInterop == null) { + missingJars.add(teavmInteropJar); + } + if(teavmJSO == null) { + missingJars.add(teavmJSOJar); + } + if(teavmJSOApis == null) { + missingJars.add(teavmJSOApisJar); + } + if(teavmJSOImpl == null) { + missingJars.add(teavmJSOImplJar); + } + if(teavmMetaprogrammingAPI == null) { + missingJars.add(teavmMetaprogrammingAPIJar); + } + if(teavmMetaprogrammingImpl == null) { + missingJars.add(teavmMetaprogrammingImplJar); + } + if(teavmJodaTime == null) { + missingJars.add(teavmJodaTimeJar); + } + if(teavmJZLIB == null) { + missingJars.add(teavmJZLIBJar); + } + if(missingJars.size() > 0) { + throw new MissingJARsException(missingJars); + } + } + + private static void discoverJars(File dir) { + File[] files = dir.listFiles(); + for(int i = 0; i < files.length; ++i) { + File f = files[i]; + if(f.isDirectory()) { + discoverJars(f); + }else { + String n = f.getName(); + switch(n) { + case teavmCoreJar: + teavmCore = f; + break; + case teavmCliJar: + teavmCli = f; + break; + case teavmToolingJar: + teavmTooling = f; + break; + case teavmPlatformJar: + teavmPlatform = f; + break; + case teavmClasslibJar: + teavmClasslib = f; + break; + case teavmInteropJar: + teavmInterop = f; + break; + case teavmJSOJar: + teavmJSO = f; + break; + case teavmJSOApisJar: + teavmJSOApis = f; + break; + case teavmJSOImplJar: + teavmJSOImpl = f; + break; + case teavmMetaprogrammingAPIJar: + teavmMetaprogrammingAPI = f; + break; + case teavmMetaprogrammingImplJar: + teavmMetaprogrammingImpl = f; + break; + case teavmJodaTimeJar: + teavmJodaTime = f; + break; + case teavmJZLIBJar: + teavmJZLIB = f; + break; + default: + break; + } + } + } + } + + private static void copyURLToFileCheck404(String urlIn, File fileOut) throws IOException { + System.out.println("downloading: " + urlIn); + URL url; + try { + url = new URL(urlIn); + }catch(MalformedURLException ex) { + throw new IOException("Invalid URL: " + urlIn, ex); + } + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + int respCode = connection.getResponseCode(); + if(respCode != 200) { + connection.disconnect(); + throw new IOException("Recieved response code: " + respCode); + } + try (InputStream stream = connection.getInputStream()) { + FileUtils.copyInputStreamToFile(stream, fileOut); + }finally { + connection.disconnect(); // is this required? + } + } + + public static boolean tryLoadTeaVMBridge() { + String override = System.getProperty("eaglercraft.TeaVMBridge"); + File teavmBridgeCheck; + if(override != null) { + teavmBridgeCheck = new File(override); + }else { + try { + teavmBridgeCheck = new File(new File(URLDecoder.decode( + TeaVMBinaries.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath(), + "UTF-8")).getParent(), "TeaVMBridge.jar"); + } catch (URISyntaxException | UnsupportedEncodingException e) { + System.err.println("Failed to locate TeaVMBridge.jar relative to BuildTools jar!"); + e.printStackTrace(); + return false; + } + } + if(teavmBridgeCheck.exists()) { + teavmBridge = teavmBridgeCheck; + return true; + }else { + System.err.println("File does not exist: " + teavmBridgeCheck.getAbsolutePath()); + return false; + } + } + + public static File[] getTeaVMCompilerClasspath() { + return new File[] { teavmCore, teavmCli, teavmTooling, teavmInterop, teavmMetaprogrammingAPI, teavmBridge }; + } + + public static String[] getTeaVMRuntimeClasspath() { + return new String[] { + teavmJodaTime.getAbsolutePath(), teavmJZLIB.getAbsolutePath(), teavmClasslib.getAbsolutePath(), + teavmInterop.getAbsolutePath(), teavmJSO.getAbsolutePath(), teavmJSOApis.getAbsolutePath(), + teavmJSOImpl.getAbsolutePath(), teavmMetaprogrammingAPI.getAbsolutePath(), + teavmMetaprogrammingImpl.getAbsolutePath(), teavmPlatform.getAbsolutePath() + }; + } + +} diff --git a/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/headless/CompileLatestClientHeadless.java b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/headless/CompileLatestClientHeadless.java new file mode 100644 index 0000000..37bb96f --- /dev/null +++ b/buildtools/src/main/java/net/lax1dude/eaglercraft/v1_8/buildtools/gui/headless/CompileLatestClientHeadless.java @@ -0,0 +1,612 @@ +package net.lax1dude.eaglercraft.v1_8.buildtools.gui.headless; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.commons.io.FileUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.buildtools.EaglerBuildTools; +import net.lax1dude.eaglercraft.v1_8.buildtools.LicensePrompt; +import net.lax1dude.eaglercraft.v1_8.buildtools.gui.EPKCompiler; +import net.lax1dude.eaglercraft.v1_8.buildtools.gui.JavaC; +import net.lax1dude.eaglercraft.v1_8.buildtools.gui.MakeOfflineDownload; +import net.lax1dude.eaglercraft.v1_8.buildtools.gui.TeaVMBinaries; +import net.lax1dude.eaglercraft.v1_8.buildtools.gui.CompileLatestClientGUI.CompileFailureException; +import net.lax1dude.eaglercraft.v1_8.buildtools.gui.TeaVMBinaries.MissingJARsException; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.DecompileMinecraft; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.FFMPEG; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.init.InitMCP; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.teavm.TeaVMBridge; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.teavm.TeaVMBridge.TeaVMClassLoadException; +import net.lax1dude.eaglercraft.v1_8.buildtools.task.teavm.TeaVMBridge.TeaVMRuntimeException; +import net.lax1dude.eaglercraft.v1_8.buildtools.util.FileReaderUTF; +import net.lax1dude.eaglercraft.v1_8.buildtools.util.FileWriterUTF; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class CompileLatestClientHeadless { + + public static void main(String[] args) throws Throwable { + + System.out.println(); + System.out.println("Launching client compiler..."); + System.out.println("Copyright (c) 2022 lax1dude"); + System.out.println(); + + boolean yes = false; + String configPath = null; + + if(args.length == 1) { + configPath = args[0]; + }else if(args.length == 2 && (yes = args[0].equalsIgnoreCase("-y"))) { + configPath = args[1]; + }else { + System.err.println("Usage: java -jar BuildTools.jar [-y] "); + System.err.println(); + System.exit(-1); + return; + } + + System.out.println("Loading config file: " + configPath); + System.out.println(); + + File configFile = new File(configPath); + String configSrc; + try { + configSrc = FileUtils.readFileToString(configFile, StandardCharsets.UTF_8); + }catch(FileNotFoundException ex) { + ex.printStackTrace(); + System.err.println(); + System.err.println("ERROR: File '" + configFile.getAbsolutePath() + "' does not exist!"); + System.err.println(); + System.exit(-1); + return; + } + + JSONObject configJSON; + try { + configJSON = new JSONObject(configSrc); + }catch(JSONException ex) { + System.err.println("ERROR: Could not parse '" + configFile.getName() + "' as JSON!"); + System.err.println(); + System.err.println(ex.toString()); + System.err.println(); + System.exit(-1); + return; + } + + File repositoryFolder; + File modCoderPack; + File minecraftJar; + File assetsIndex; + File outputDirectory; + File temporaryDirectory; + String ffmpeg = "ffmpeg"; + String mavenURL = null; + File mavenLocal = null; + File productionIndex = null; + File productionFavicon = null; + List addScripts = null; + List removeScripts = null; + List injectInOfflineScripts = null; + boolean generateOffline; + File offlineTemplate = null; + boolean keepTemporaryFiles; + boolean writeSourceMap = false; + boolean minifying = true; + try { + repositoryFolder = new File(configJSON.optString("repositoryFolder", ".")); + modCoderPack = new File(configJSON.getString("modCoderPack")); + minecraftJar = new File(configJSON.getString("minecraftJar")); + assetsIndex = new File(configJSON.getString("assetsIndex")); + outputDirectory = new File(configJSON.getString("outputDirectory")); + String tmpDir = configJSON.optString("temporaryDirectory"); + temporaryDirectory = tmpDir == null ? new File(outputDirectory, "build") : new File(tmpDir); + ffmpeg = configJSON.optString("ffmpeg", ffmpeg); + if(ffmpeg.length() == 0) { + ffmpeg = "ffmpeg"; + } + String prodIndex = configJSON.optString("productionIndex"); + if(prodIndex != null) { + productionIndex = new File(prodIndex); + String prodFavicon = configJSON.optString("productionFavicon"); + if(prodFavicon != null) { + productionFavicon = new File(prodFavicon); + } + JSONArray scripts = configJSON.optJSONArray("addScripts"); + if(scripts != null) { + int l = scripts.length(); + if(l > 0) { + addScripts = new ArrayList(l); + for(int i = 0; i < l; ++i) { + addScripts.add(scripts.getString(i)); + } + } + } + scripts = configJSON.optJSONArray("removeScripts"); + if(scripts != null) { + int l = scripts.length(); + if(l > 0) { + removeScripts = new ArrayList(l); + for(int i = 0; i < l; ++i) { + removeScripts.add(scripts.getString(i)); + } + } + } + scripts = configJSON.optJSONArray("injectInOffline"); + if(scripts != null) { + int l = scripts.length(); + if(l > 0) { + injectInOfflineScripts = new ArrayList(l); + for(int i = 0; i < l; ++i) { + injectInOfflineScripts.add(scripts.getString(i)); + } + } + } + } + mavenURL = configJSON.optString("mavenURL"); + mavenLocal = new File(configJSON.getString("mavenLocal")); + generateOffline = configJSON.optBoolean("generateOfflineDownload", false); + if(generateOffline) { + offlineTemplate = new File(configJSON.getString("offlineDownloadTemplate")); + } + keepTemporaryFiles = configJSON.optBoolean("keepTemporaryFiles", false); + writeSourceMap = configJSON.optBoolean("writeSourceMap", false); + minifying = configJSON.optBoolean("minifying", true); + }catch(JSONException ex) { + System.err.println("CONFIG ERROR: " + ex.toString()); + System.err.println(); + System.exit(-1); + return; + } + + System.out.println("Loaded config successfully:"); + System.out.println(); + System.out.println(" - Repository Folder: " + repositoryFolder.getAbsolutePath().replace('\\', '/')); + System.out.println(" - Mod Coder Pack: " + modCoderPack.getAbsolutePath().replace('\\', '/')); + System.out.println(" - Minecraft 1.8.8: " + minecraftJar.getAbsolutePath().replace('\\', '/')); + System.out.println(" - Assets Index 1.8: " + assetsIndex.getAbsolutePath().replace('\\', '/')); + System.out.println(" - Temporary Directory: " + temporaryDirectory.getAbsolutePath().replace('\\', '/')); + System.out.println(" - Output Directory: " + outputDirectory.getAbsolutePath().replace('\\', '/')); + System.out.println(" - FFmpeg Executable: " + ffmpeg.replace('\\', '/')); + System.out.println(" - Maven Repo URL: " + mavenURL); + System.out.println(" - Maven Local Dir: " + mavenLocal.getAbsolutePath().replace('\\', '/')); + System.out.println(" - Production Index: " + (productionIndex == null ? "null" : productionIndex.getAbsolutePath().replace('\\', '/'))); + System.out.println(" - Production Favicon: " + (productionFavicon == null ? "null" : productionFavicon.getAbsolutePath().replace('\\', '/'))); + System.out.println(" - Generate Offline: " + generateOffline); + System.out.println(" - Offline Template: " + (offlineTemplate == null ? "null" : offlineTemplate.getAbsolutePath().replace('\\', '/'))); + System.out.println(" - Inject in Offline: " + (injectInOfflineScripts == null ? "[ ]" : "[ " + String.join(", ", injectInOfflineScripts).replace('\\', '/') + " ]")); + System.out.println(" - Minifying: " + minifying); + System.out.println(" - Write Source Map: " + writeSourceMap); + System.out.println(" - Keep Temp Files: " + keepTemporaryFiles); + System.out.println(" - Add Scripts: " + (addScripts == null ? "[ ]" : "[ " + String.join(", ", addScripts).replace('\\', '/') + " ]")); + System.out.println(" - Remove Scripts: " + (removeScripts == null ? "[ ]" : "[ " + String.join(", ", removeScripts).replace('\\', '/') + " ]")); + System.out.println(); + + if(!yes) { + System.out.println(); + LicensePrompt.display(); + System.out.println(); + } + + EaglerBuildTools.repositoryRoot = repositoryFolder; + + try { + if(!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) { + throw new CompileFailureException("Could not create output directory!"); + } + + File[] existingOutput = outputDirectory.listFiles(); + if(existingOutput.length > 0) { + + if(!yes) { + System.out.print("Output directory has existing files, would you like to delete them? [y/n] "); + String str = (new BufferedReader(new InputStreamReader(System.in))).readLine(); + System.out.println(); + if(!str.equalsIgnoreCase("y") && !str.equalsIgnoreCase("yes")) { + System.out.println("Build cancelled."); + System.out.println(); + System.exit(-1); + return; + } + } + + System.out.println("Deleting existing files from the output directory..."); + + try { + for(int i = 0; i < existingOutput.length; ++i) { + File f = existingOutput[i]; + if(f.isDirectory()) { + FileUtils.deleteDirectory(f); + }else { + if(!f.delete()) { + throw new IOException("Could not delete: " + f.getAbsolutePath()); + } + } + } + }catch(IOException t) { + throw new CompileFailureException("Could not delete old output directory: " + t.getMessage()); + } + } + + File mcpDataTMP = new File(temporaryDirectory, "ModCoderPack"); + File minecraftSrcTmp = new File(temporaryDirectory, "MinecraftSrc"); + + if(ffmpeg.length() == 0) { + FFMPEG.foundFFMPEG = "ffmpeg"; + }else { + FFMPEG.foundFFMPEG = ffmpeg; + } + + if(!mcpDataTMP.isDirectory() && !mcpDataTMP.mkdirs()) { + throw new CompileFailureException("Error: failed to create \"" + mcpDataTMP.getAbsolutePath() + "\"!"); + } + + if(!InitMCP.initTask(modCoderPack, mcpDataTMP)) { + throw new CompileFailureException("Error: could not initialize MCP from \"" + modCoderPack.getAbsolutePath() + "\"!"); + } + + if(!minecraftSrcTmp.isDirectory() && !minecraftSrcTmp.mkdirs()) { + throw new CompileFailureException("Error: failed to create \"" + minecraftSrcTmp.getAbsolutePath() + "\"!"); + } + + if(!DecompileMinecraft.decompileMinecraft(mcpDataTMP, minecraftJar, minecraftSrcTmp, assetsIndex, false)) { + throw new CompileFailureException("Error: could not decompile and patch 1.8.8.jar from \"" + minecraftJar.getAbsolutePath() + "\"!"); + } + + try { + FileUtils.copyFile(new File(repositoryFolder, "patches/minecraft/output_license.txt"), new File(temporaryDirectory, "MinecraftSrc/LICENSE")); + }catch(IOException ex) { + System.err.println("Error: failed to write LICENSE in temporary directory!"); + ex.printStackTrace(); + } + + System.out.println(); + + if(mavenURL == null) { + System.out.println("TeaVM JARs will be loaded from: " + mavenLocal.getAbsolutePath()); + System.out.println(); + try { + TeaVMBinaries.loadFromDirectory(mavenLocal); + }catch(MissingJARsException ex) { + throw new CompileFailureException(ex.getMessage()); + } + }else { + System.out.println("TeaVM JARs will be downloaded from repository: " + mavenURL); + System.out.println(); + try { + TeaVMBinaries.downloadFromMaven(mavenURL, mavenLocal); + }catch(MissingJARsException ex) { + throw new CompileFailureException(ex.getMessage()); + } + System.out.println(); + System.out.println("Notice: make sure to delete \"" + mavenLocal.getAbsolutePath() + "\" when the compiler is finished, it will not be deleted automatically"); + System.out.println(); + } + + int compileResultCode; + File compiledResultClasses = new File(temporaryDirectory, "classes"); + + try { + try { + compileResultCode = JavaC.runJavaC(new File(minecraftSrcTmp, "minecraft_src_javadoc.jar"), + compiledResultClasses, temporaryDirectory, TeaVMBinaries.getTeaVMRuntimeClasspath(), + new File(repositoryFolder, "sources/main/java"), new File(repositoryFolder, "sources/teavm/java")); + }catch(IOException ex) { + throw new CompileFailureException("failed to run javac compiler! " + ex.toString(), ex); + } + + System.out.println(); + + if(compileResultCode == 0) { + System.out.println("Java compiler completed successfully"); + }else { + throw new CompileFailureException("failed to run javac compiler! exit code " + compileResultCode + ", check log"); + } + }finally { + File extractedSrcTmp = new File(temporaryDirectory, "MinecraftSrc/src_javadoc_tmp"); + if(extractedSrcTmp.exists()) { + System.out.println(); + System.out.println("Deleting temporary directory: " + extractedSrcTmp.getAbsolutePath()); + try { + FileUtils.deleteDirectory(extractedSrcTmp); + }catch(IOException ex) { + System.err.println("Failed to delete temporary directory!"); + ex.printStackTrace(); + } + } + } + + System.out.println(); + System.out.println("Preparing arguments for TeaVM..."); + + if(!TeaVMBinaries.tryLoadTeaVMBridge()) { + System.err.println("Failed to locate TeaVMBridge.jar, you can specify it's path manually by adding the JVM argument \"-Deaglercraft.TeaVMBridge=\""); + throw new CompileFailureException("Failed to locate TeaVMBridge.jar!"); + } + + Map teavmArgs = new HashMap(); + + List teavmClassPath = new ArrayList(); + teavmClassPath.add(compiledResultClasses.getAbsolutePath()); + teavmClassPath.addAll(Arrays.asList(TeaVMBinaries.getTeaVMRuntimeClasspath())); + teavmArgs.put("classPathEntries", teavmClassPath); + + teavmArgs.put("entryPointName", "main"); + teavmArgs.put("mainClass", "net.lax1dude.eaglercraft.v1_8.internal.teavm.MainClass"); + teavmArgs.put("minifying", minifying); + teavmArgs.put("optimizationLevel", "ADVANCED"); + teavmArgs.put("targetDirectory", outputDirectory.getAbsolutePath()); + teavmArgs.put("generateSourceMaps", writeSourceMap); + teavmArgs.put("targetFileName", "classes.js"); + + System.out.println(); + + boolean teavmStatus; + try { + teavmStatus = TeaVMBridge.compileTeaVM(teavmArgs); + }catch(TeaVMClassLoadException ex) { + throw new CompileFailureException("Failed to link TeaVM jar files! Did you select the wrong jar?", ex); + }catch(TeaVMRuntimeException ex) { + throw new CompileFailureException("Failed to run TeaVM! Check log", ex); + } + + if(!teavmStatus) { + System.out.println("TeaVM reported problems, check the log"); + System.out.println(); + System.exit(-1); + return; + } + + File epkCompiler = new File(repositoryFolder, "sources/setup/workspace_template/desktopRuntime/CompileEPK.jar"); + + if(!epkCompiler.exists()) { + throw new CompileFailureException("EPKCompiler JAR file is missing: " + epkCompiler.getAbsolutePath()); + } + + System.out.println(); + System.out.println("Writing index.html..."); + System.out.println(); + + String faviconExt = null; + if(productionFavicon != null) { + faviconExt = productionFavicon.getName(); + int i = faviconExt.lastIndexOf('.'); + if(i != -1) { + faviconExt = faviconExt.substring(i + 1); + } + } + + try(BufferedReader indexReader = new BufferedReader(new FileReaderUTF(productionIndex)); + PrintWriter indexWriter = new PrintWriter(new FileWriterUTF(new File(outputDirectory, "index.html")))) { + String line; + while((line = indexReader.readLine()) != null) { + String trim = line.trim(); + if(trim.startsWith(" href to \"favicon." + faviconExt + "\", MIME type \"" + contentType + "\" in index.html"); + }else { + System.out.println("Removed favicon from index.html, no favicon configured"); + } + continue; + } + } + if(trim.startsWith(" href to \"favicon." + faviconExt + "\""); + }else { + System.out.println("Removed og:image tag in index.html, no favicon configured"); + } + continue; + } + } + if(trim.startsWith(" tag with src \"" + addSrc + "\" to index.html"); + } + } + if(removeScripts != null && removeScripts.contains(srcSubStr)) { + System.out.println("Removed " + + "

404 Not Found


" + + "The requested resource " + + " could not be found on this server!

" + htmlEntities(plugin.getDescription().getName()) + "/" + + htmlEntities(plugin.getDescription().getVersion()) + "

").getBytes(StandardCharsets.UTF_8); + HttpContentType htmlContentType = new HttpContentType(new HashSet(Arrays.asList("html")), "text/html", "utf-8", 120000l); + long millis = System.currentTimeMillis(); + return new HttpMemoryCache(null, "~404", Unpooled.wrappedBuffer(src), htmlContentType, millis, millis, millis); + } + + private static HttpMemoryCache regenerateDefaultUpgrade404() { + EaglerXBungee plugin = EaglerXBungee.getEagler(); + String name = htmlEntities(plugin.getConfig().getServerName()); + byte[] src = ("" + name + + "

" + + "404 'Websocket Upgrade Failure' (rip)

The URL you have requested is the physical WebSocket address of '" + name + "'

To correctly join this server, load the latest EaglercraftX 1.8 client, click the 'Direct Connect' button
on the 'Multiplayer' screen, " + + "and enter this URL as the server address

").getBytes(StandardCharsets.UTF_8); + HttpContentType htmlContentType = new HttpContentType(new HashSet(Arrays.asList("html")), "text/html", "utf-8", 14400000l); + long millis = System.currentTimeMillis(); + return new HttpMemoryCache(null, "~404", Unpooled.wrappedBuffer(src), htmlContentType, millis, millis, millis); + } + + public static String htmlEntities(String input) { + return input.replace("<", "<").replace(">", ">"); + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/AsyncSkinProvider.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/AsyncSkinProvider.java new file mode 100644 index 0000000..f38e199 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/AsyncSkinProvider.java @@ -0,0 +1,453 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.logging.Level; + +import javax.imageio.ImageIO; + +import org.apache.commons.codec.binary.Base64; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.BinaryHttpClient.Response; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.ICacheProvider.CacheLoadedProfile; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.ICacheProvider.CacheLoadedSkin; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class AsyncSkinProvider { + + private static class SkinConsumerImpl implements Consumer { + + protected final Consumer responseConsumer; + + protected SkinConsumerImpl(Consumer consumer) { + this.responseConsumer = consumer; + } + + protected void doAccept(byte[] v) { + try { + responseConsumer.accept(v); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown caching new skin!", t); + } + } + + @Override + public void accept(Response response) { + if(response == null || response.exception != null || response.code != 200 || response.data == null) { + doAccept(null); + }else { + BufferedImage image; + try { + image = ImageIO.read(new ByteArrayInputStream(response.data)); + }catch(IOException ex) { + doAccept(null); + return; + } + try { + int srcWidth = image.getWidth(); + int srcHeight = image.getHeight(); + if(srcWidth < 64 || srcWidth > 512 || srcHeight < 32 || srcHeight > 512) { + doAccept(null); + return; + } + if(srcWidth != 64 || srcHeight != 64) { + if(srcWidth % 64 == 0) { + if(srcWidth == srcHeight * 2) { + BufferedImage scaled = new BufferedImage(64, 32, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = scaled.createGraphics(); + graphics.drawImage(image, 0, 0, 64, 32, 0, 0, srcWidth, srcHeight, null); + graphics.dispose(); + image = scaled; + srcWidth = 64; + srcHeight = 32; + }else if(srcWidth == srcHeight) { + BufferedImage scaled = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = scaled.createGraphics(); + graphics.drawImage(image, 0, 0, 64, 64, 0, 0, srcWidth, srcHeight, null); + graphics.dispose(); + image = scaled; + srcWidth = 64; + srcHeight = 64; + }else { + doAccept(null); + return; + } + }else { + doAccept(null); + return; + } + } + if(srcWidth == 64 && srcHeight == 64) { + int[] tmp = new int[4096]; + byte[] loadedPixels = new byte[16384]; + image.getRGB(0, 0, 64, 64, tmp, 0, 64); + SkinRescaler.convertToBytes(tmp, loadedPixels); + SkinPackets.setAlphaForChest(loadedPixels, (byte)255); + doAccept(loadedPixels); + return; + }else if(srcWidth == 64 && srcHeight == 32) { + int[] tmp1 = new int[2048]; + byte[] loadedPixels = new byte[16384]; + image.getRGB(0, 0, 64, 32, tmp1, 0, 64); + SkinRescaler.convert64x32To64x64(tmp1, loadedPixels); + SkinPackets.setAlphaForChest(loadedPixels, (byte)255); + doAccept(loadedPixels); + return; + }else { + doAccept(null); + return; + } + }catch(Throwable t) { + + } + } + } + + } + + private static class SkinCachingConsumer implements Consumer { + + protected final UUID skinUUID; + protected final String skinTexture; + protected final ICacheProvider cacheProvider; + protected final Consumer responseConsumer; + + protected SkinCachingConsumer(UUID skinUUID, String skinTexture, ICacheProvider cacheProvider, + Consumer responseConsumer) { + this.skinUUID = skinUUID; + this.skinTexture = skinTexture; + this.cacheProvider = cacheProvider; + this.responseConsumer = responseConsumer; + } + + @Override + public void accept(byte[] skin) { + if(skin != null) { + try { + cacheProvider.cacheSkinByUUID(skinUUID, skinTexture, skin); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown writing new skin to database!", t); + } + responseConsumer.accept(skin); + }else { + responseConsumer.accept(null); + } + } + + } + + public static class CacheFetchedProfile { + + public final UUID uuid; + public final String username; + public final String texture; + public final UUID textureUUID; + public final String model; + + protected CacheFetchedProfile(UUID uuid, String username, String texture, String model) { + this.uuid = uuid; + this.username = username; + this.texture = texture; + this.textureUUID = SkinPackets.createEaglerURLSkinUUID(texture); + this.model = model; + } + + protected CacheFetchedProfile(CacheLoadedProfile profile) { + this.uuid = profile.uuid; + this.username = profile.username; + this.texture = profile.texture; + this.textureUUID = SkinPackets.createEaglerURLSkinUUID(profile.texture); + this.model = profile.model; + } + + } + + private static class ProfileConsumerImpl implements Consumer { + + protected final UUID uuid; + protected final Consumer responseConsumer; + + protected ProfileConsumerImpl(UUID uuid, Consumer responseConsumer) { + this.uuid = uuid; + this.responseConsumer = responseConsumer; + } + + protected void doAccept(CacheFetchedProfile v) { + try { + responseConsumer.accept(v); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown caching new profile!", t); + } + } + + @Override + public void accept(Response response) { + if(response == null || response.exception != null || response.code != 200 || response.data == null) { + doAccept(null); + }else { + try { + JsonObject json = (new JsonParser()).parse(new String(response.data, StandardCharsets.UTF_8)).getAsJsonObject(); + String username = json.get("name").getAsString().toLowerCase(); + String texture = null; + String model = null; + JsonElement propsElement = json.get("properties"); + if(propsElement != null) { + try { + JsonArray properties = propsElement.getAsJsonArray(); + if(properties.size() > 0) { + for(int i = 0, l = properties.size(); i < l; ++i) { + JsonElement prop = properties.get(i); + if(prop.isJsonObject()) { + JsonObject propObj = prop.getAsJsonObject(); + if(propObj.get("name").getAsString().equals("textures")) { + String value = new String(Base64.decodeBase64(propObj.get("value").getAsString()), StandardCharsets.UTF_8); + JsonObject texturesJson = (new JsonParser()).parse(value).getAsJsonObject(); + if(texturesJson != null && texturesJson.has("textures")) { + texturesJson = texturesJson.getAsJsonObject("textures"); + JsonElement skin = texturesJson.get("SKIN"); + if(skin != null) { + model = "default"; + JsonObject skinObj = skin.getAsJsonObject(); + JsonElement urlElement = skinObj.get("url"); + if(urlElement != null && !urlElement.isJsonNull()) { + texture = urlElement.getAsString(); + } + JsonElement metaElement = skinObj.get("metadata"); + if(metaElement != null) { + JsonObject metaObj = metaElement.getAsJsonObject(); + JsonElement modelElement = metaObj.get("model"); + if(modelElement != null) { + model = modelElement.getAsString(); + } + } + } + } + break; + } + } + } + } + }catch(Throwable t2) { + } + } + if(texture == null && model == null) { + model = SkinService.isAlex(uuid) ? "slim" : "default"; + } + doAccept(new CacheFetchedProfile(uuid, username, texture, model)); + }catch(Throwable ex) { + doAccept(null); + } + } + } + + } + + private static class ProfileCachingConsumer implements Consumer { + + protected final UUID uuid; + protected final ICacheProvider cacheProvider; + protected final Consumer responseConsumer; + + protected ProfileCachingConsumer(UUID uuid, ICacheProvider cacheProvider, Consumer responseConsumer) { + this.uuid = uuid; + this.cacheProvider = cacheProvider; + this.responseConsumer = responseConsumer; + } + + @Override + public void accept(CacheFetchedProfile profile) { + if(profile != null) { + try { + cacheProvider.cacheProfileByUUID(uuid, profile.username, profile.texture, profile.model); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown writing new profile to database!", t); + } + responseConsumer.accept(profile); + }else { + responseConsumer.accept(null); + } + } + + } + + private static class UsernameToUUIDConsumerImpl implements Consumer { + + protected final String username; + protected final ICacheProvider cacheProvider; + protected final Consumer responseConsumer; + + protected UsernameToUUIDConsumerImpl(String username, ICacheProvider cacheProvider, Consumer responseConsumer) { + this.username = username; + this.cacheProvider = cacheProvider; + this.responseConsumer = responseConsumer; + } + + protected void doAccept(CacheFetchedProfile v) { + try { + responseConsumer.accept(v); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown caching new profile!", t); + } + } + + @Override + public void accept(Response response) { + if(response == null || response.exception != null || response.code != 200 || response.data == null) { + doAccept(null); + }else { + try { + JsonObject json = (new JsonParser()).parse(new String(response.data, StandardCharsets.UTF_8)).getAsJsonObject(); + String loadUsername = json.get("name").getAsString().toLowerCase(); + if(!username.equals(loadUsername)) { + doAccept(null); + } + UUID mojangUUID = SkinService.parseMojangUUID(json.get("id").getAsString()); + lookupProfileByUUID(mojangUUID, cacheProvider, responseConsumer, false); + }catch(Throwable t) { + doAccept(null); + } + } + } + + } + + private static final SimpleRateLimiter rateLimitDownload = new SimpleRateLimiter(); + private static final SimpleRateLimiter rateLimitLookup = new SimpleRateLimiter(); + + public static void downloadSkin(String skinTexture, ICacheProvider cacheProvider, Consumer responseConsumer) { + downloadSkin(SkinPackets.createEaglerURLSkinUUID(skinTexture), skinTexture, cacheProvider, responseConsumer); + } + + public static void downloadSkin(UUID skinUUID, String skinTexture, ICacheProvider cacheProvider, Consumer responseConsumer) { + CacheLoadedSkin loadedSkin = cacheProvider.loadSkinByUUID(skinUUID); + if(loadedSkin == null) { + URI uri; + try { + uri = URI.create(skinTexture); + }catch(IllegalArgumentException ex) { + try { + responseConsumer.accept(null); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown handling invalid skin!", t); + } + throw new CancelException(); + } + int globalRatelimit = EaglerXBungee.getEagler().getConfig().getSkinRateLimitGlobal(); + boolean isRateLimit; + synchronized(rateLimitDownload) { + isRateLimit = !rateLimitDownload.rateLimit(globalRatelimit); + } + if(!isRateLimit) { + BinaryHttpClient.asyncRequest("GET", uri, new SkinConsumerImpl( + new SkinCachingConsumer(skinUUID, skinTexture, cacheProvider, responseConsumer))); + }else { + EaglerXBungee.logger().warning("skin system reached the global texture download ratelimit of " + globalRatelimit + " while downloading up \"" + skinTexture + "\""); + throw new CancelException(); + } + }else { + try { + responseConsumer.accept(loadedSkin.texture); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown processing cached skin!", t); + } + throw new CancelException(); + } + } + + public static void lookupProfileByUUID(UUID playerUUID, ICacheProvider cacheProvider, Consumer responseConsumer) { + lookupProfileByUUID(playerUUID, cacheProvider, responseConsumer, true); + } + + private static void lookupProfileByUUID(UUID playerUUID, ICacheProvider cacheProvider, Consumer responseConsumer, boolean rateLimit) { + CacheLoadedProfile profile = cacheProvider.loadProfileByUUID(playerUUID); + if(profile == null) { + URI requestURI = URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + SkinService.getMojangUUID(playerUUID)); + int globalRatelimit = EaglerXBungee.getEagler().getConfig().getUuidRateLimitGlobal(); + boolean isRateLimit; + if(rateLimit) { + synchronized(rateLimitLookup) { + isRateLimit = !rateLimitLookup.rateLimit(globalRatelimit); + } + }else { + isRateLimit = false; + } + if(!isRateLimit) { + BinaryHttpClient.asyncRequest("GET", requestURI, new ProfileConsumerImpl(playerUUID, + new ProfileCachingConsumer(playerUUID, cacheProvider, responseConsumer))); + }else { + EaglerXBungee.logger().warning("skin system reached the global UUID lookup ratelimit of " + globalRatelimit + " while looking up \"" + playerUUID + "\""); + throw new CancelException(); + } + }else { + try { + responseConsumer.accept(new CacheFetchedProfile(profile)); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown processing cached profile!", t); + } + throw new CancelException(); + } + } + + public static void lookupProfileByUsername(String playerUsername, ICacheProvider cacheProvider, Consumer responseConsumer) { + String playerUsernameLower = playerUsername.toLowerCase(); + CacheLoadedProfile profile = cacheProvider.loadProfileByUsername(playerUsernameLower); + if(profile == null) { + if(!playerUsernameLower.equals(playerUsernameLower.replaceAll("[^a-z0-9_]", "_").trim())) { + try { + responseConsumer.accept(null); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown processing invalid profile!", t); + } + throw new CancelException(); + } + URI requestURI = URI.create("https://api.mojang.com/users/profiles/minecraft/" + playerUsername); + int globalRatelimit = EaglerXBungee.getEagler().getConfig().getUuidRateLimitGlobal(); + boolean isRateLimit; + synchronized(rateLimitLookup) { + isRateLimit = !rateLimitLookup.rateLimit(globalRatelimit); + } + if(!isRateLimit) { + BinaryHttpClient.asyncRequest("GET", requestURI, new UsernameToUUIDConsumerImpl(playerUsername, cacheProvider, responseConsumer)); + }else { + EaglerXBungee.logger().warning("skin system reached the global UUID lookup ratelimit of " + globalRatelimit + " while looking up \"" + playerUsername + "\""); + throw new CancelException(); + } + }else { + try { + responseConsumer.accept(new CacheFetchedProfile(profile)); + }catch(Throwable t) { + EaglerXBungee.logger().log(Level.SEVERE, "Exception thrown processing cached profile!", t); + } + throw new CancelException(); + } + } + + public static class CancelException extends RuntimeException { + + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/BinaryHttpClient.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/BinaryHttpClient.java new file mode 100644 index 0000000..780c9b7 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/BinaryHttpClient.java @@ -0,0 +1,258 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import javax.net.ssl.SSLEngine; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.DefaultHttpRequest; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslHandler; +import io.netty.handler.timeout.ReadTimeoutHandler; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee; +import net.md_5.bungee.netty.PipelineUtils; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class BinaryHttpClient { + + public static class Response { + + public final int code; + public final byte[] data; + public final Throwable exception; + + public Response(int code, byte[] data) { + this.code = code; + this.data = data; + this.exception = null; + } + + public Response(Throwable exception) { + this.code = -1; + this.data = null; + this.exception = exception; + } + + } + + private static class NettyHttpChannelFutureListener implements ChannelFutureListener { + + protected final String method; + protected final URI requestURI; + protected final Consumer responseCallback; + + protected NettyHttpChannelFutureListener(String method, URI requestURI, Consumer responseCallback) { + this.method = method; + this.requestURI = requestURI; + this.responseCallback = responseCallback; + } + + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + String path = requestURI.getRawPath() + + ((requestURI.getRawQuery() == null) ? "" : ("?" + requestURI.getRawQuery())); + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, + HttpMethod.valueOf(method), path); + request.headers().set(HttpHeaderNames.HOST, (Object) requestURI.getHost()); + request.headers().set(HttpHeaderNames.USER_AGENT, "Mozilla/5.0 EaglerXBungee/" + EaglerXBungee.getEagler().getDescription().getVersion()); + future.channel().writeAndFlush(request); + } else { + addressCache.invalidate(requestURI.getHost()); + responseCallback.accept(new Response(new IOException("Connection failed"))); + } + } + + } + + private static class NettyHttpChannelInitializer extends ChannelInitializer { + + protected final Consumer responseCallback; + protected final boolean ssl; + protected final String host; + protected final int port; + + protected NettyHttpChannelInitializer(Consumer responseCallback, boolean ssl, String host, int port) { + this.responseCallback = responseCallback; + this.ssl = ssl; + this.host = host; + this.port = port; + } + + @Override + protected void initChannel(Channel ch) throws Exception { + ch.pipeline().addLast("timeout", new ReadTimeoutHandler(5L, TimeUnit.SECONDS)); + if (this.ssl) { + SSLEngine engine = SslContextBuilder.forClient().build().newEngine(ch.alloc(), host, port); + ch.pipeline().addLast("ssl", new SslHandler(engine)); + } + + ch.pipeline().addLast("http", new HttpClientCodec()); + ch.pipeline().addLast("handler", new NettyHttpResponseHandler(responseCallback)); + } + + } + + private static class NettyHttpResponseHandler extends SimpleChannelInboundHandler { + + protected final Consumer responseCallback; + protected int responseCode = -1; + protected ByteBuf buffer = null; + + protected NettyHttpResponseHandler(Consumer responseCallback) { + this.responseCallback = responseCallback; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { + if (msg instanceof HttpResponse) { + HttpResponse response = (HttpResponse) msg; + responseCode = response.status().code(); + if (responseCode == HttpResponseStatus.NO_CONTENT.code()) { + this.done(ctx); + return; + } + } + if (msg instanceof HttpContent) { + HttpContent content = (HttpContent) msg; + if(buffer == null) { + buffer = ctx.alloc().buffer(); + } + this.buffer.writeBytes(content.content()); + if (msg instanceof LastHttpContent) { + this.done(ctx); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + responseCallback.accept(new Response(cause)); + } + + private void done(ChannelHandlerContext ctx) { + try { + byte[] array; + if(buffer != null) { + array = new byte[buffer.readableBytes()]; + buffer.readBytes(array); + buffer.release(); + }else { + array = new byte[0]; + } + responseCallback.accept(new Response(responseCode, array)); + }finally { + ctx.channel().pipeline().remove(this); + ctx.channel().close(); + } + } + + } + + private static final Cache addressCache = CacheBuilder.newBuilder().expireAfterWrite(15L, TimeUnit.MINUTES).build(); + private static EventLoopGroup eventLoop = null; + + public static void asyncRequest(String method, URI uri, Consumer responseCallback) { + EventLoopGroup eventLoop = getEventLoopGroup(); + + int port = uri.getPort(); + boolean ssl = false; + String scheme = uri.getScheme(); + switch(scheme) { + case "http": + if(port == -1) { + port = 80; + } + break; + case "https": + if(port == -1) { + port = 443; + } + ssl = true; + break; + default: + responseCallback.accept(new Response(new UnsupportedOperationException("Unsupported scheme: " + scheme))); + return; + } + + String host = uri.getHost(); + InetAddress inetHost = addressCache.getIfPresent(host); + if (inetHost == null) { + try { + inetHost = InetAddress.getByName(host); + } catch (UnknownHostException ex) { + responseCallback.accept(new Response(ex)); + return; + } + addressCache.put(host, inetHost); + } + + (new Bootstrap()).channel(PipelineUtils.getChannel(null)).group(eventLoop) + .handler(new NettyHttpChannelInitializer(responseCallback, ssl, host, port)) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000).option(ChannelOption.TCP_NODELAY, true) + .remoteAddress(inetHost, port).connect() + .addListener(new NettyHttpChannelFutureListener(method, uri, responseCallback)); + } + + private static EventLoopGroup getEventLoopGroup() { + if(eventLoop == null) { + eventLoop = PipelineUtils.newEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Skin Download Thread #%1$d").build()); + } + return eventLoop; + } + + public static void killEventLoop() { + if(eventLoop != null) { + EaglerXBungee.logger().info("Stopping skin cache HTTP client..."); + eventLoop.shutdownGracefully(); + try { + eventLoop.awaitTermination(30l, TimeUnit.SECONDS); + } catch (InterruptedException var13) { + ; + } + eventLoop = null; + } + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/ICacheProvider.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/ICacheProvider.java new file mode 100644 index 0000000..4f53094 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/ICacheProvider.java @@ -0,0 +1,88 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +import java.util.UUID; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public interface ICacheProvider { + + public static class CacheException extends RuntimeException { + + public CacheException() { + super(); + } + + public CacheException(String message, Throwable cause) { + super(message, cause); + } + + public CacheException(String message) { + super(message); + } + + public CacheException(Throwable cause) { + super(cause); + } + + } + + public static class CacheLoadedSkin { + + public final UUID uuid; + public final String url; + public final byte[] texture; + + public CacheLoadedSkin(UUID uuid, String url, byte[] texture) { + this.uuid = uuid; + this.url = url; + this.texture = texture; + } + + } + + public static class CacheLoadedProfile { + + public final UUID uuid; + public final String username; + public final String texture; + public final String model; + + public CacheLoadedProfile(UUID uuid, String username, String texture, String model) { + this.uuid = uuid; + this.username = username; + this.texture = texture; + this.model = model; + } + + public UUID getSkinUUID() { + return SkinPackets.createEaglerURLSkinUUID(texture); + } + + } + + CacheLoadedSkin loadSkinByUUID(UUID uuid) throws CacheException; + + void cacheSkinByUUID(UUID uuid, String url, byte[] textureBlob) throws CacheException; + + CacheLoadedProfile loadProfileByUUID(UUID uuid) throws CacheException; + + CacheLoadedProfile loadProfileByUsername(String username) throws CacheException; + + void cacheProfileByUUID(UUID uuid, String username, String texture, String model) throws CacheException; + + void flush() throws CacheException; + + void destroy(); + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/ISkinService.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/ISkinService.java new file mode 100644 index 0000000..5850e4f --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/ISkinService.java @@ -0,0 +1,44 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +import java.io.IOException; +import java.util.UUID; + +import net.md_5.bungee.UserConnection; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public interface ISkinService { + + void init(String uri, String driverClass, String driverPath, int keepObjectsDays, int keepProfilesDays, + int maxObjects, int maxProfiles); + + void processGetOtherSkin(final UUID searchUUID, final UserConnection sender); + + void processGetOtherSkin(UUID searchUUID, String skinURL, UserConnection sender); + + void registerEaglercraftPlayer(UUID clientUUID, byte[] generatedPacket, int modelId) throws IOException; + + void unregisterPlayer(UUID clientUUID); + + default void registerTextureToPlayerAssociation(String textureURL, UUID playerUUID) { + registerTextureToPlayerAssociation(SkinPackets.createEaglerURLSkinUUID(textureURL), playerUUID); + } + + void registerTextureToPlayerAssociation(UUID textureUUID, UUID playerUUID); + + void flush(); + + void shutdown(); + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/JDBCCacheProvider.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/JDBCCacheProvider.java new file mode 100644 index 0000000..e7f5dd3 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/JDBCCacheProvider.java @@ -0,0 +1,404 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; +import java.util.UUID; +import java.util.logging.Level; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.InflaterInputStream; + +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.sqlite.EaglerDrivers; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class JDBCCacheProvider implements ICacheProvider { + + public static JDBCCacheProvider initialize(String uri, String driverClass, String driverPath, int keepObjectsDays, + int keepProfilesDays, int maxObjects, int maxProfiles) throws CacheException { + Connection conn; + try { + conn = EaglerDrivers.connectToDatabase(uri, driverClass, driverPath, new Properties()); + if(conn == null) { + throw new IllegalStateException("Connection is null"); + } + }catch(Throwable t) { + throw new CacheException("Could not initialize '" + uri + "'!", t); + } + EaglerXBungee.logger().info("Connected to database: " + uri); + try { + try(Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE IF NOT EXISTS " + + "\"eaglercraft_skins_objects\" (" + + "\"TextureUUID\" TEXT(32) NOT NULL," + + "\"TextureURL\" VARCHAR(256) NOT NULL," + + "\"TextureTime\" DATETIME NOT NULL," + + "\"TextureData\" BLOB," + + "\"TextureLength\" INT(24) NOT NULL," + + "PRIMARY KEY(\"TextureUUID\"))"); + stmt.execute("CREATE TABLE IF NOT EXISTS " + + "\"eaglercraft_skins_profiles\" (" + + "\"ProfileUUID\" TEXT(32) NOT NULL," + + "\"ProfileName\" TEXT(16) NOT NULL," + + "\"ProfileTime\" DATETIME NOT NULL," + + "\"ProfileTexture\" VARCHAR(256)," + + "\"ProfileModel\" VARCHAR(16) NOT NULL," + + "PRIMARY KEY(\"ProfileUUID\"))"); + stmt.execute("CREATE INDEX IF NOT EXISTS \"profile_name_index\" " + + "ON \"eaglercraft_skins_profiles\" (\"ProfileName\")"); + } + JDBCCacheProvider cacheProvider = new JDBCCacheProvider(conn, uri, keepObjectsDays, keepProfilesDays, maxObjects, maxProfiles); + cacheProvider.flush(); + return cacheProvider; + }catch(CacheException ex) { + try { + conn.close(); + }catch(SQLException exx) { + } + throw ex; + }catch(Throwable t) { + try { + conn.close(); + }catch(SQLException exx) { + } + throw new CacheException("Could not initialize '" + uri + "'!", t); + } + } + + protected final Connection connection; + protected final String uri; + + protected final PreparedStatement discardExpiredObjects; + protected final PreparedStatement discardExpiredProfiles; + protected final PreparedStatement getTotalObjects; + protected final PreparedStatement getTotalProfiles; + protected final PreparedStatement deleteSomeOldestObjects; + protected final PreparedStatement deleteSomeOldestProfiles; + protected final PreparedStatement querySkinByUUID; + protected final PreparedStatement queryProfileByUUID; + protected final PreparedStatement queryProfileByUsername; + protected final PreparedStatement cacheNewSkin; + protected final PreparedStatement cacheNewProfile; + protected final PreparedStatement cacheHasSkin; + protected final PreparedStatement cacheHasProfile; + protected final PreparedStatement cacheUpdateSkin; + protected final PreparedStatement cacheUpdateProfile; + + protected long lastFlush; + + protected int keepObjectsDays; + protected int keepProfilesDays; + protected int maxObjects; + protected int maxProfiles; + + protected JDBCCacheProvider(Connection conn, String uri, int keepObjectsDays, int keepProfilesDays, int maxObjects, + int maxProfiles) throws SQLException { + this.connection = conn; + this.uri = uri; + this.lastFlush = 0l; + this.keepObjectsDays = keepObjectsDays; + this.keepProfilesDays = keepProfilesDays; + this.maxObjects = maxObjects; + this.maxProfiles = maxProfiles; + + this.discardExpiredObjects = connection.prepareStatement("DELETE FROM eaglercraft_skins_objects WHERE textureTime < ?"); + this.discardExpiredProfiles = connection.prepareStatement("DELETE FROM eaglercraft_skins_profiles WHERE profileTime < ?"); + this.getTotalObjects = connection.prepareStatement("SELECT COUNT(*) AS total_objects FROM eaglercraft_skins_objects"); + this.getTotalProfiles = connection.prepareStatement("SELECT COUNT(*) AS total_profiles FROM eaglercraft_skins_profiles"); + this.deleteSomeOldestObjects = connection.prepareStatement("DELETE FROM eaglercraft_skins_objects WHERE TextureUUID IN (SELECT TextureUUID FROM eaglercraft_skins_objects ORDER BY TextureTime ASC LIMIT ?)"); + this.deleteSomeOldestProfiles = connection.prepareStatement("DELETE FROM eaglercraft_skins_profiles WHERE ProfileUUID IN (SELECT ProfileUUID FROM eaglercraft_skins_profiles ORDER BY ProfileTime ASC LIMIT ?)"); + this.querySkinByUUID = connection.prepareStatement("SELECT TextureURL,TextureData,TextureLength FROM eaglercraft_skins_objects WHERE TextureUUID = ? LIMIT 1"); + this.queryProfileByUUID = connection.prepareStatement("SELECT ProfileName,ProfileTexture,ProfileModel FROM eaglercraft_skins_profiles WHERE ProfileUUID = ? LIMIT 1"); + this.queryProfileByUsername = connection.prepareStatement("SELECT ProfileUUID,ProfileTexture,ProfileModel FROM eaglercraft_skins_profiles WHERE ProfileName = ? LIMIT 1"); + this.cacheNewSkin = connection.prepareStatement("INSERT INTO eaglercraft_skins_objects (TextureUUID, TextureURL, TextureTime, TextureData, TextureLength) VALUES(?, ?, ?, ?, ?)"); + this.cacheNewProfile = connection.prepareStatement("INSERT INTO eaglercraft_skins_profiles (ProfileUUID, ProfileName, ProfileTime, ProfileTexture, ProfileModel) VALUES(?, ?, ?, ?, ?)"); + this.cacheHasSkin = connection.prepareStatement("SELECT COUNT(TextureUUID) AS has_object FROM eaglercraft_skins_objects WHERE TextureUUID = ? LIMIT 1"); + this.cacheHasProfile = connection.prepareStatement("SELECT COUNT(ProfileUUID) AS has_profile FROM eaglercraft_skins_profiles WHERE ProfileUUID = ? LIMIT 1"); + this.cacheUpdateSkin = connection.prepareStatement("UPDATE eaglercraft_skins_objects SET TextureURL = ?, TextureTime = ?, TextureData = ?, TextureLength = ? WHERE TextureUUID = ?"); + this.cacheUpdateProfile = connection.prepareStatement("UPDATE eaglercraft_skins_profiles SET ProfileName = ?, ProfileTime = ?, ProfileTexture = ?, ProfileModel = ? WHERE ProfileUUID = ?"); + } + + public CacheLoadedSkin loadSkinByUUID(UUID uuid) throws CacheException { + String uuidString = SkinService.getMojangUUID(uuid); + String queriedUrls; + byte[] queriedTexture; + int queriedLength; + try { + synchronized(querySkinByUUID) { + querySkinByUUID.setString(1, uuidString); + try(ResultSet resultSet = querySkinByUUID.executeQuery()) { + if(resultSet.next()) { + queriedUrls = resultSet.getString(1); + queriedTexture = resultSet.getBytes(2); + queriedLength = resultSet.getInt(3); + }else { + return null; + } + } + } + }catch(SQLException ex) { + throw new CacheException("SQL query failure while loading cached skin", ex); + } + if(queriedLength == 0) { + return new CacheLoadedSkin(uuid, queriedUrls, new byte[0]); + }else { + byte[] decompressed = new byte[queriedLength]; + try { + GZIPInputStream is = new GZIPInputStream(new ByteArrayInputStream(queriedTexture)); + int i = 0, j = 0; + while(j < queriedLength && (i = is.read(decompressed, j, queriedLength - j)) != -1) { + j += i; + } + }catch(IOException ex) { + throw new CacheException("SQL query failure while loading cached skin"); + } + return new CacheLoadedSkin(uuid, queriedUrls, decompressed); + } + } + + public void cacheSkinByUUID(UUID uuid, String url, byte[] textureBlob) throws CacheException { + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + try { + GZIPOutputStream deflateOut = new GZIPOutputStream(bao); + deflateOut.write(textureBlob); + deflateOut.close(); + }catch(IOException ex) { + throw new CacheException("Skin compression error", ex); + } + int len; + byte[] textureBlobCompressed; + if(textureBlob == null || textureBlob.length == 0) { + len = 0; + textureBlobCompressed = null; + }else { + len = textureBlob.length; + textureBlobCompressed = bao.toByteArray(); + } + try { + String uuidString = SkinService.getMojangUUID(uuid); + synchronized(cacheNewSkin) { + boolean has; + cacheHasSkin.setString(1, uuidString); + try(ResultSet resultSet = cacheHasSkin.executeQuery()) { + if(resultSet.next()) { + has = resultSet.getInt(1) > 0; + }else { + has = false; // ?? + } + } + if(has) { + cacheUpdateSkin.setString(1, url); + cacheUpdateSkin.setDate(2, new Date(System.currentTimeMillis())); + cacheUpdateSkin.setBytes(3, textureBlobCompressed); + cacheUpdateSkin.setInt(4, len); + cacheUpdateSkin.setString(5, uuidString); + cacheUpdateSkin.executeUpdate(); + }else { + cacheNewSkin.setString(1, uuidString); + cacheNewSkin.setString(2, url); + cacheNewSkin.setDate(3, new Date(System.currentTimeMillis())); + cacheNewSkin.setBytes(4, textureBlobCompressed); + cacheNewSkin.setInt(5, len); + cacheNewSkin.executeUpdate(); + } + } + }catch(SQLException ex) { + throw new CacheException("SQL query failure while caching new skin", ex); + } + } + + public CacheLoadedProfile loadProfileByUUID(UUID uuid) throws CacheException { + try { + String uuidString = SkinService.getMojangUUID(uuid); + synchronized(queryProfileByUUID) { + queryProfileByUUID.setString(1, uuidString); + try(ResultSet resultSet = queryProfileByUUID.executeQuery()) { + if(resultSet.next()) { + String profileName = resultSet.getString(1); + String profileTexture = resultSet.getString(2); + String profileModel = resultSet.getString(3); + return new CacheLoadedProfile(uuid, profileName, profileTexture, profileModel); + }else { + return null; + } + } + } + }catch(SQLException ex) { + throw new CacheException("SQL query failure while loading profile by uuid", ex); + } + } + + public CacheLoadedProfile loadProfileByUsername(String username) throws CacheException { + try { + synchronized(queryProfileByUsername) { + queryProfileByUsername.setString(1, username); + try(ResultSet resultSet = queryProfileByUsername.executeQuery()) { + if(resultSet.next()) { + UUID profileUUID = SkinService.parseMojangUUID(resultSet.getString(1)); + String profileTexture = resultSet.getString(2); + String profileModel = resultSet.getString(3); + return new CacheLoadedProfile(profileUUID, username, profileTexture, profileModel); + }else { + return null; + } + } + } + }catch(SQLException ex) { + throw new CacheException("SQL query failure while loading profile by username", ex); + } + } + + public void cacheProfileByUUID(UUID uuid, String username, String texture, String model) throws CacheException { + try { + String uuidString = SkinService.getMojangUUID(uuid); + synchronized(cacheNewProfile) { + boolean has; + cacheHasProfile.setString(1, uuidString); + try(ResultSet resultSet = cacheHasProfile.executeQuery()) { + if(resultSet.next()) { + has = resultSet.getInt(1) > 0; + }else { + has = false; // ?? + } + } + if(has) { + cacheUpdateProfile.setString(1, username); + cacheUpdateProfile.setDate(2, new Date(System.currentTimeMillis())); + cacheUpdateProfile.setString(3, texture); + cacheUpdateProfile.setString(4, model); + cacheUpdateProfile.setString(5, uuidString); + cacheUpdateProfile.executeUpdate(); + }else { + cacheNewProfile.setString(1, uuidString); + cacheNewProfile.setString(2, username); + cacheNewProfile.setDate(3, new Date(System.currentTimeMillis())); + cacheNewProfile.setString(4, texture); + cacheNewProfile.setString(5, model); + cacheNewProfile.executeUpdate(); + } + } + }catch(SQLException ex) { + throw new CacheException("SQL query failure while caching new profile", ex); + } + } + + @Override + public void flush() { + long millis = System.currentTimeMillis(); + if(millis - lastFlush > 1200000l) { // 30 minutes + lastFlush = millis; + try { + Date expiryObjects = new Date(millis - keepObjectsDays * 86400000l); + Date expiryProfiles = new Date(millis - keepProfilesDays * 86400000l); + + synchronized(discardExpiredObjects) { + discardExpiredObjects.setDate(1, expiryObjects); + discardExpiredObjects.execute(); + } + synchronized(discardExpiredProfiles) { + discardExpiredProfiles.setDate(1, expiryProfiles); + discardExpiredProfiles.execute(); + } + + int totalObjects, totalProfiles; + + synchronized(getTotalObjects) { + try(ResultSet resultSet = getTotalObjects.executeQuery()) { + if(resultSet.next()) { + totalObjects = resultSet.getInt(1); + }else { + throw new SQLException("Empty ResultSet recieved when checking \"eaglercraft_skins_objects\" row count"); + } + } + } + + synchronized(getTotalProfiles) { + try(ResultSet resultSet = getTotalProfiles.executeQuery()) { + if(resultSet.next()) { + totalProfiles = resultSet.getInt(1); + }else { + throw new SQLException("Empty ResultSet recieved when checking \"eaglercraft_skins_profiles\" row count"); + } + } + } + + if(totalObjects > maxObjects) { + int deleteCount = totalObjects - maxObjects + (maxObjects >> 3); + EaglerXBungee.logger().warning("Skin object cache has passed " + maxObjects + " skins in size (" + + totalObjects + "), deleting " + deleteCount + " skins from the cache to free space"); + synchronized(deleteSomeOldestObjects) { + deleteSomeOldestObjects.setInt(1, deleteCount); + deleteSomeOldestObjects.executeUpdate(); + } + } + + if(totalProfiles > maxProfiles) { + int deleteCount = totalProfiles - maxProfiles + (maxProfiles >> 3); + EaglerXBungee.logger().warning("Skin profile cache has passed " + maxProfiles + " profiles in size (" + + totalProfiles + "), deleting " + deleteCount + " profiles from the cache to free space"); + synchronized(deleteSomeOldestProfiles) { + deleteSomeOldestProfiles.setInt(1, deleteCount); + deleteSomeOldestProfiles.executeUpdate(); + } + } + + }catch(SQLException ex) { + throw new CacheException("SQL query failure while flushing cache!", ex); + } + } + } + + private void destroyStatement(Statement stmt) { + try { + stmt.close(); + } catch (SQLException e) { + } + } + + @Override + public void destroy() { + destroyStatement(discardExpiredObjects); + destroyStatement(discardExpiredProfiles); + destroyStatement(getTotalObjects); + destroyStatement(getTotalProfiles); + destroyStatement(deleteSomeOldestObjects); + destroyStatement(deleteSomeOldestProfiles); + destroyStatement(querySkinByUUID); + destroyStatement(queryProfileByUUID); + destroyStatement(queryProfileByUsername); + destroyStatement(cacheNewSkin); + destroyStatement(cacheNewProfile); + destroyStatement(cacheHasSkin); + destroyStatement(cacheHasProfile); + destroyStatement(cacheUpdateSkin); + destroyStatement(cacheUpdateProfile); + try { + connection.close(); + EaglerXBungee.logger().info("Successfully disconnected from database '" + uri + "'"); + } catch (SQLException e) { + EaglerXBungee.logger().log(Level.WARNING, "Exception disconnecting from database '" + uri + "'!", e); + } + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SimpleRateLimiter.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SimpleRateLimiter.java new file mode 100644 index 0000000..aef2cb5 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SimpleRateLimiter.java @@ -0,0 +1,42 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class SimpleRateLimiter { + + private long timer; + private int count; + + public SimpleRateLimiter() { + timer = System.currentTimeMillis(); + count = 0; + } + + public boolean rateLimit(int maxPerMinute) { + int t = 60000 / maxPerMinute; + long millis = System.currentTimeMillis(); + int decr = (int)(millis - timer) / t; + if(decr > 0) { + timer += decr * t; + count -= decr; + } + if(count >= maxPerMinute) { + return false; + }else { + ++count; + return true; + } + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinPackets.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinPackets.java new file mode 100644 index 0000000..a7b64ce --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinPackets.java @@ -0,0 +1,255 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.UUID; + +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee; +import net.md_5.bungee.UserConnection; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class SkinPackets { + + public static final int PACKET_MY_SKIN_PRESET = 0x01; + public static final int PACKET_MY_SKIN_CUSTOM = 0x02; + public static final int PACKET_GET_OTHER_SKIN = 0x03; + public static final int PACKET_OTHER_SKIN_PRESET = 0x04; + public static final int PACKET_OTHER_SKIN_CUSTOM = 0x05; + public static final int PACKET_GET_SKIN_BY_URL = 0x06; + + public static void processPacket(byte[] data, UserConnection sender, ISkinService skinService) throws IOException { + if(data.length == 0) { + throw new IOException("Zero-length packet recieved"); + } + int packetId = (int)data[0] & 0xFF; + try { + switch(packetId) { + case PACKET_GET_OTHER_SKIN: + processGetOtherSkin(data, sender, skinService); + break; + case PACKET_GET_SKIN_BY_URL: + processGetOtherSkinByURL(data, sender, skinService); + break; + default: + throw new IOException("Unknown packet type " + packetId); + } + }catch(IOException ex) { + throw ex; + }catch(Throwable t) { + throw new IOException("Unhandled exception handling packet type " + packetId, t); + } + } + + private static void processGetOtherSkin(byte[] data, UserConnection sender, ISkinService skinService) throws IOException { + if(data.length != 17) { + throw new IOException("Invalid length " + data.length + " for skin request packet"); + } + UUID searchUUID = bytesToUUID(data, 1); + skinService.processGetOtherSkin(searchUUID, sender); + } + + private static void processGetOtherSkinByURL(byte[] data, UserConnection sender, ISkinService skinService) throws IOException { + if(data.length < 20) { + throw new IOException("Invalid length " + data.length + " for skin request packet"); + } + UUID searchUUID = bytesToUUID(data, 1); + int urlLength = (data[17] << 8) | data[18]; + if(data.length < 19 + urlLength) { + throw new IOException("Invalid length " + data.length + " for skin request packet with " + urlLength + " length URL"); + } + String urlStr = bytesToAscii(data, 19, urlLength); + urlStr = SkinService.sanitizeTextureURL(urlStr); + if(urlStr == null) { + throw new IOException("Invalid URL for skin request packet"); + } + URL url; + try { + url = new URL(urlStr); + }catch(MalformedURLException t) { + throw new IOException("Invalid URL for skin request packet", t); + } + String host = url.getHost(); + if(EaglerXBungee.getEagler().getConfig().isValidSkinHost(host)) { + UUID validUUID = createEaglerURLSkinUUID(urlStr); + if(!searchUUID.equals(validUUID)) { + throw new IOException("Invalid generated UUID from skin URL"); + } + skinService.processGetOtherSkin(searchUUID, urlStr, sender); + }else { + throw new IOException("Invalid host in skin packet: " + host); + } + } + + public static void registerEaglerPlayer(UUID clientUUID, byte[] bs, ISkinService skinService) throws IOException { + if(bs.length == 0) { + throw new IOException("Zero-length packet recieved"); + } + byte[] generatedPacket; + int skinModel = -1; + int packetType = (int)bs[0] & 0xFF; + switch(packetType) { + case PACKET_MY_SKIN_PRESET: + if(bs.length != 5) { + throw new IOException("Invalid length " + bs.length + " for preset skin packet"); + } + generatedPacket = SkinPackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF)); + break; + case PACKET_MY_SKIN_CUSTOM: + byte[] pixels = new byte[16384]; + if(bs.length != 2 + pixels.length) { + throw new IOException("Invalid length " + bs.length + " for custom skin packet"); + } + setAlphaForChest(pixels, (byte)255); + System.arraycopy(bs, 2, pixels, 0, pixels.length); + generatedPacket = SkinPackets.makeCustomResponse(clientUUID, (skinModel = (int)bs[1] & 0xFF), pixels); + break; + default: + throw new IOException("Unknown skin packet type: " + packetType); + } + skinService.registerEaglercraftPlayer(clientUUID, generatedPacket, skinModel); + } + + public static void registerEaglerPlayerFallback(UUID clientUUID, ISkinService skinService) throws IOException { + int skinModel = (clientUUID.hashCode() & 1) != 0 ? 1 : 0; + byte[] generatedPacket = SkinPackets.makePresetResponse(clientUUID, skinModel); + skinService.registerEaglercraftPlayer(clientUUID, generatedPacket, skinModel); + } + + public static void setAlphaForChest(byte[] skin64x64, byte alpha) { + if(skin64x64.length != 16384) { + throw new IllegalArgumentException("Skin is not 64x64!"); + } + for(int y = 20; y < 32; ++y) { + for(int x = 16; x < 40; ++x) { + skin64x64[(y << 8) | (x << 2)] = alpha; + } + } + } + + public static byte[] makePresetResponse(UUID uuid) { + return makePresetResponse(uuid, (uuid.hashCode() & 1) != 0 ? 1 : 0); + } + + public static byte[] makePresetResponse(UUID uuid, int presetId) { + byte[] ret = new byte[1 + 16 + 4]; + ret[0] = (byte)PACKET_OTHER_SKIN_PRESET; + UUIDToBytes(uuid, ret, 1); + ret[17] = (byte)(presetId >> 24); + ret[18] = (byte)(presetId >> 16); + ret[19] = (byte)(presetId >> 8); + ret[20] = (byte)(presetId & 0xFF); + return ret; + } + + public static byte[] makeCustomResponse(UUID uuid, int model, byte[] pixels) { + byte[] ret = new byte[1 + 16 + 1 + pixels.length]; + ret[0] = (byte)PACKET_OTHER_SKIN_CUSTOM; + UUIDToBytes(uuid, ret, 1); + ret[17] = (byte)model; + System.arraycopy(pixels, 0, ret, 18, pixels.length); + return ret; + } + + public static UUID bytesToUUID(byte[] bytes, int off) { + long msb = (((long) bytes[off] & 0xFFl) << 56l) | (((long) bytes[off + 1] & 0xFFl) << 48l) + | (((long) bytes[off + 2] & 0xFFl) << 40l) | (((long) bytes[off + 3] & 0xFFl) << 32l) + | (((long) bytes[off + 4] & 0xFFl) << 24l) | (((long) bytes[off + 5] & 0xFFl) << 16l) + | (((long) bytes[off + 6] & 0xFFl) << 8l) | ((long) bytes[off + 7] & 0xFFl); + long lsb = (((long) bytes[off + 8] & 0xFFl) << 56l) | (((long) bytes[off + 9] & 0xFFl) << 48l) + | (((long) bytes[off + 10] & 0xFFl) << 40l) | (((long) bytes[off + 11] & 0xFFl) << 32l) + | (((long) bytes[off + 12] & 0xFFl) << 24l) | (((long) bytes[off + 13] & 0xFFl) << 16l) + | (((long) bytes[off + 14] & 0xFFl) << 8l) | ((long) bytes[off + 15] & 0xFFl); + return new UUID(msb, lsb); + } + + private static final String hex = "0123456789abcdef"; + + public static String bytesToString(byte[] bytes, int off, int len) { + char[] ret = new char[len << 1]; + for(int i = 0; i < len; ++i) { + ret[i * 2] = hex.charAt((bytes[off + i] >> 4) & 0xF); + ret[i * 2 + 1] = hex.charAt(bytes[off + i] & 0xF); + } + return new String(ret); + } + + public static String bytesToAscii(byte[] bytes, int off, int len) { + char[] ret = new char[len]; + for(int i = 0; i < len; ++i) { + ret[i] = (char)((int)bytes[off + i] & 0xFF); + } + return new String(ret); + } + + public static String bytesToAscii(byte[] bytes) { + return bytesToAscii(bytes, 0, bytes.length); + } + + public static void UUIDToBytes(UUID uuid, byte[] bytes, int off) { + long msb = uuid.getMostSignificantBits(); + long lsb = uuid.getLeastSignificantBits(); + bytes[off] = (byte)(msb >> 56l); + bytes[off + 1] = (byte)(msb >> 48l); + bytes[off + 2] = (byte)(msb >> 40l); + bytes[off + 3] = (byte)(msb >> 32l); + bytes[off + 4] = (byte)(msb >> 24l); + bytes[off + 5] = (byte)(msb >> 16l); + bytes[off + 6] = (byte)(msb >> 8l); + bytes[off + 7] = (byte)(msb & 0xFFl); + bytes[off + 8] = (byte)(lsb >> 56l); + bytes[off + 9] = (byte)(lsb >> 48l); + bytes[off + 10] = (byte)(lsb >> 40l); + bytes[off + 11] = (byte)(lsb >> 32l); + bytes[off + 12] = (byte)(lsb >> 24l); + bytes[off + 13] = (byte)(lsb >> 16l); + bytes[off + 14] = (byte)(lsb >> 8l); + bytes[off + 15] = (byte)(lsb & 0xFFl); + } + + public static byte[] asciiString(String string) { + byte[] str = new byte[string.length()]; + for(int i = 0; i < str.length; ++i) { + str[i] = (byte)string.charAt(i); + } + return str; + } + + public static UUID createEaglerURLSkinUUID(String skinUrl) { + return UUID.nameUUIDFromBytes(asciiString("EaglercraftSkinURL:" + skinUrl)); + } + + public static int getModelId(String modelName) { + return "slim".equalsIgnoreCase(modelName) ? 1 : 0; + } + + public static byte[] rewriteUUID(UUID newUUID, byte[] pkt) { + byte[] ret = new byte[pkt.length]; + System.arraycopy(pkt, 0, ret, 0, pkt.length); + UUIDToBytes(newUUID, ret, 1); + return ret; + } + + public static byte[] rewriteUUIDModel(UUID newUUID, byte[] pkt, int model) { + byte[] ret = new byte[pkt.length]; + System.arraycopy(pkt, 0, ret, 0, pkt.length); + UUIDToBytes(newUUID, ret, 1); + if(ret[0] == (byte)PACKET_OTHER_SKIN_CUSTOM) { + ret[17] = (byte)model; + } + return ret; + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinRescaler.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinRescaler.java new file mode 100644 index 0000000..cf55d88 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinRescaler.java @@ -0,0 +1,74 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class SkinRescaler { + + public static void convertToBytes(int[] imageIn, byte[] imageOut) { + for(int i = 0, j, k; i < imageIn.length; ++i) { + j = i << 2; + k = imageIn[i]; + imageOut[j] = (byte)(k >> 24); + imageOut[j + 1] = (byte)(k & 0xFF); + imageOut[j + 2] = (byte)(k >> 8); + imageOut[j + 3] = (byte)(k >> 16); + } + } + + public static void convert64x32To64x64(int[] imageIn, byte[] imageOut) { + copyRawPixels(imageIn, imageOut, 0, 0, 0, 0, 64, 32, 64, 64, false); + copyRawPixels(imageIn, imageOut, 24, 48, 20, 52, 4, 16, 8, 20, 64, 64); + copyRawPixels(imageIn, imageOut, 28, 48, 24, 52, 8, 16, 12, 20, 64, 64); + copyRawPixels(imageIn, imageOut, 20, 52, 16, 64, 8, 20, 12, 32, 64, 64); + copyRawPixels(imageIn, imageOut, 24, 52, 20, 64, 4, 20, 8, 32, 64, 64); + copyRawPixels(imageIn, imageOut, 28, 52, 24, 64, 0, 20, 4, 32, 64, 64); + copyRawPixels(imageIn, imageOut, 32, 52, 28, 64, 12, 20, 16, 32, 64, 64); + copyRawPixels(imageIn, imageOut, 40, 48, 36, 52, 44, 16, 48, 20, 64, 64); + copyRawPixels(imageIn, imageOut, 44, 48, 40, 52, 48, 16, 52, 20, 64, 64); + copyRawPixels(imageIn, imageOut, 36, 52, 32, 64, 48, 20, 52, 32, 64, 64); + copyRawPixels(imageIn, imageOut, 40, 52, 36, 64, 44, 20, 48, 32, 64, 64); + copyRawPixels(imageIn, imageOut, 44, 52, 40, 64, 40, 20, 44, 32, 64, 64); + copyRawPixels(imageIn, imageOut, 48, 52, 44, 64, 52, 20, 56, 32, 64, 64); + } + + private static void copyRawPixels(int[] imageIn, byte[] imageOut, int dx1, int dy1, int dx2, int dy2, int sx1, + int sy1, int sx2, int sy2, int imgSrcWidth, int imgDstWidth) { + if(dx1 > dx2) { + copyRawPixels(imageIn, imageOut, sx1, sy1, dx2, dy1, sx2 - sx1, sy2 - sy1, imgSrcWidth, imgDstWidth, true); + } else { + copyRawPixels(imageIn, imageOut, sx1, sy1, dx1, dy1, sx2 - sx1, sy2 - sy1, imgSrcWidth, imgDstWidth, false); + } + } + + private static void copyRawPixels(int[] imageIn, byte[] imageOut, int srcX, int srcY, int dstX, int dstY, int width, + int height, int imgSrcWidth, int imgDstWidth, boolean flip) { + int i, j; + for(int y = 0; y < height; ++y) { + for(int x = 0; x < width; ++x) { + i = imageIn[(srcY + y) * imgSrcWidth + srcX + x]; + if(flip) { + j = (dstY + y) * imgDstWidth + dstX + width - x - 1; + }else { + j = (dstY + y) * imgDstWidth + dstX + x; + } + j = j << 2; + imageOut[j] = (byte)(i >> 24); + imageOut[j + 1] = (byte)(i & 0xFF); + imageOut[j + 2] = (byte)(i >> 8); + imageOut[j + 3] = (byte)(i >> 16); + } + } + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinService.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinService.java new file mode 100644 index 0000000..0975faa --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinService.java @@ -0,0 +1,1010 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +import java.io.IOException; +import java.net.URI; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; + +import org.apache.commons.codec.binary.Base64; + +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.hash.TObjectIntHashMap; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerBungeeConfig; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.AsyncSkinProvider.CacheFetchedProfile; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.AsyncSkinProvider.CancelException; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.connection.LoginResult; +import net.md_5.bungee.protocol.Property; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class SkinService implements ISkinService { + + public static final int masterRateLimitPerPlayer = 250; + + public static final int PACKET_MY_SKIN_PRESET = 0x01; + public static final int PACKET_MY_SKIN_CUSTOM = 0x02; + public static final int PACKET_GET_OTHER_SKIN = 0x03; + public static final int PACKET_OTHER_SKIN_PRESET = 0x04; + public static final int PACKET_OTHER_SKIN_CUSTOM = 0x05; + + public static final String CHANNEL = "EAG|Skins-1.8"; + + private final Map onlinePlayersCache = new HashMap(); + private final Multimap onlinePlayersFromTexturesMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final Map onlinePlayersToTexturesMap = new HashMap(); + private final Map foreignSkinCache = new HashMap(); + + private final Map pendingTextures = new HashMap(); + private final Map pendingUUIDs = new HashMap(); + private final Map pendingNameLookups = new HashMap(); + + private final TObjectIntMap antagonists = new TObjectIntHashMap(); + private long antagonistCooldown = System.currentTimeMillis(); + + private final Consumer> antagonistLogger = new Consumer>() { + + @Override + public void accept(Set t) { + if(t.size() == 1) { + int limit = EaglerXBungee.getEagler().getConfig().getAntagonistsRateLimit() << 1; + UUID offender = t.iterator().next(); + synchronized(antagonists) { + int v = antagonists.get(offender); + if(v == antagonists.getNoEntryValue()) { + antagonists.put(offender, 1); + }else { + if(v <= limit) { + antagonists.put(offender, v + 1); + } + } + } + } + } + + }; + + private ICacheProvider cacheProvider = null; + + protected static class CachedForeignSkin { + + protected final UUID uuid; + protected final byte[] data; + protected final int modelKnown; + protected long lastHit; + + protected CachedForeignSkin(UUID uuid, byte[] data, int modelKnown) { + this.uuid = uuid; + this.data = data; + this.modelKnown = modelKnown; + this.lastHit = System.currentTimeMillis(); + } + + } + + protected static class CachedPlayerSkin { + + protected final byte[] data; + protected final UUID textureUUID; + protected final int modelId; + + protected CachedPlayerSkin(byte[] data, UUID textureUUID, int modelId) { + this.data = data; + this.textureUUID = textureUUID; + this.modelId = modelId; + } + + } + + protected class PendingTextureDownload implements Consumer { + + protected final UUID textureUUID; + protected final String textureURL; + + protected final Set antagonists; + protected final List> callbacks; + protected final Consumer> antagonistsCallback; + + protected final long initializedTime; + protected boolean finalized; + + protected PendingTextureDownload(UUID textureUUID, String textureURL, UUID caller, Consumer callback, + Consumer> antagonistsCallback) { + this.textureUUID = textureUUID; + this.textureURL = textureURL; + this.antagonists = new LinkedHashSet(); + this.antagonists.add(caller); + this.callbacks = new LinkedList(); + this.callbacks.add(callback); + this.antagonistsCallback = antagonistsCallback; + this.initializedTime = System.currentTimeMillis(); + this.finalized = false; + } + + @Override + public void accept(byte[] t) { + for(int i = 0, l = callbacks.size(); i < l; ++i) { + try { + callbacks.get(i).accept(t); + }catch(Throwable t2) { + } + } + if(t != null) { + synchronized(pendingTextures) { + finalized = true; + pendingTextures.remove(textureUUID); + } + } + } + + } + + protected class PendingProfileUUIDLookup implements Consumer { + + protected final UUID profileUUID; + + protected final Set antagonists; + protected final List> callbacks; + protected final Consumer> antagonistsCallback; + + protected final long initializedTime; + protected boolean finalized; + + protected PendingProfileUUIDLookup(UUID profileUUID, UUID caller, Consumer callback, + Consumer> antagonistsCallback) { + this.profileUUID = profileUUID; + this.antagonists = new LinkedHashSet(); + this.antagonists.add(caller); + this.callbacks = new LinkedList(); + this.callbacks.add(callback); + this.antagonistsCallback = antagonistsCallback; + this.initializedTime = System.currentTimeMillis(); + this.finalized = false; + } + + @Override + public void accept(CacheFetchedProfile t) { + for(int i = 0, l = callbacks.size(); i < l; ++i) { + try { + callbacks.get(i).accept(t); + }catch(Throwable t2) { + } + } + if(t != null) { + synchronized(pendingUUIDs) { + finalized = true; + pendingUUIDs.remove(profileUUID); + } + } + } + + } + + protected class PendingProfileNameLookup implements Consumer { + + protected final String profileName; + + protected final Set antagonists; + protected final List> callbacks; + protected final Consumer> antagonistsCallback; + + protected final long initializedTime; + protected boolean finalized; + + protected PendingProfileNameLookup(String profileName, UUID caller, Consumer callback, + Consumer> antagonistsCallback) { + this.profileName = profileName; + this.antagonists = new LinkedHashSet(); + this.antagonists.add(caller); + this.callbacks = new LinkedList(); + this.callbacks.add(callback); + this.antagonistsCallback = antagonistsCallback; + this.initializedTime = System.currentTimeMillis(); + this.finalized = false; + } + + @Override + public void accept(CacheFetchedProfile t) { + for(int i = 0, l = callbacks.size(); i < l; ++i) { + try { + callbacks.get(i).accept(t); + }catch(Throwable t2) { + } + } + if(t != null) { + synchronized(pendingNameLookups) { + finalized = true; + pendingNameLookups.remove(profileName); + } + } + } + + } + + public void init(String uri, String driverClass, String driverPath, int keepObjectsDays, int keepProfilesDays, + int maxObjects, int maxProfiles) { + antagonistCooldown = System.currentTimeMillis(); + if(cacheProvider == null) { + cacheProvider = JDBCCacheProvider.initialize(uri, driverClass, driverPath, keepObjectsDays, + keepProfilesDays, maxObjects, maxProfiles); + } + resetMaps(); + } + + public void processGetOtherSkin(final UUID searchUUID, final UserConnection sender) { + EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)sender.getPendingConnection(); + if(!eaglerHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) { + return; + } + + CachedPlayerSkin maybeCachedPacket; + synchronized(onlinePlayersCache) { + maybeCachedPacket = onlinePlayersCache.get(searchUUID); + } + + if(maybeCachedPacket != null) { + sender.sendData(SkinService.CHANNEL, maybeCachedPacket.data); + }else { + ProxiedPlayer player = BungeeCord.getInstance().getPlayer(searchUUID); + UUID playerTexture; + synchronized(onlinePlayersToTexturesMap) { + playerTexture = onlinePlayersToTexturesMap.get(searchUUID); + } + if(playerTexture != null) { + Collection possiblePlayers; + synchronized(onlinePlayersFromTexturesMap) { + possiblePlayers = onlinePlayersFromTexturesMap.get(playerTexture); + } + boolean playersExist = possiblePlayers.size() > 0; + if(playersExist) { + for(UUID uuid : possiblePlayers) { + synchronized(onlinePlayersCache) { + maybeCachedPacket = onlinePlayersCache.get(uuid); + } + if(maybeCachedPacket != null) { + byte[] rewritten = SkinPackets.rewriteUUID(searchUUID, maybeCachedPacket.data); + if(player != null) { + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewritten, + maybeCachedPacket.textureUUID, maybeCachedPacket.modelId)); + } + } + sender.sendData(SkinService.CHANNEL, rewritten); + return; + } + } + } + CachedForeignSkin foreignSkin; + synchronized(foreignSkinCache) { + foreignSkin = foreignSkinCache.get(playerTexture); + } + if(foreignSkin != null && foreignSkin.modelKnown != -1) { + if(player != null) { + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(searchUUID, + new CachedPlayerSkin(SkinPackets.rewriteUUID(searchUUID, foreignSkin.data), + playerTexture, foreignSkin.modelKnown)); + } + synchronized(foreignSkinCache) { + foreignSkinCache.remove(playerTexture); + } + }else { + foreignSkin.lastHit = System.currentTimeMillis(); + } + sender.sendData(SkinService.CHANNEL, foreignSkin.data); + return; + } + } + if(player != null && (player instanceof UserConnection)) { + if(player instanceof UserConnection) { + LoginResult loginProfile = ((UserConnection)player).getPendingConnection().getLoginProfile(); + if(loginProfile != null) { + Property[] props = loginProfile.getProperties(); + if(props.length > 0) { + for(int i = 0; i < props.length; ++i) { + Property pp = props[i]; + if(pp.getName().equals("textures")) { + try { + String jsonStr = SkinPackets.bytesToAscii(Base64.decodeBase64(pp.getValue())); + JsonObject json = (new JsonParser()).parse(jsonStr).getAsJsonObject(); + JsonObject skinObj = json.getAsJsonObject("SKIN"); + if(skinObj != null) { + JsonElement url = json.get("url"); + if(url != null) { + String urlStr = url.getAsString(); + urlStr = SkinService.sanitizeTextureURL(urlStr); + if(urlStr == null) { + break; + } + int model = 0; + JsonElement el = skinObj.get("metadata"); + if(el != null && el.isJsonObject()) { + el = el.getAsJsonObject().get("model"); + if(el != null) { + model = SkinPackets.getModelId(el.getAsString()); + } + } + UUID skinUUID = SkinPackets.createEaglerURLSkinUUID(urlStr); + + CachedForeignSkin foreignSkin; + synchronized(foreignSkinCache) { + foreignSkin = foreignSkinCache.remove(skinUUID); + } + if(foreignSkin != null) { + registerTextureToPlayerAssociation(skinUUID, searchUUID); + byte[] rewrite = SkinPackets.rewriteUUIDModel(searchUUID, foreignSkin.data, model); + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(searchUUID, new CachedPlayerSkin( + rewrite, skinUUID, model)); + } + sender.sendData(SkinService.CHANNEL, rewrite); + return; + } + + // download player skin, put in onlinePlayersCache, no limit + + if(!isLimitedAsAntagonist(player.getUniqueId())) { + processResolveURLTextureForOnline(sender, searchUUID, skinUUID, urlStr, model); + } + + return; + } + } + }catch(Throwable t) { + } + } + } + } + } + } + if(!isLimitedAsAntagonist(player.getUniqueId())) { + if(player.getPendingConnection().isOnlineMode()) { + processResolveProfileTextureByUUIDForOnline(sender, searchUUID); + }else { + processResolveProfileTextureByNameForOnline(sender, player.getPendingConnection().getName(), searchUUID); + } + } + }else { + CachedForeignSkin foreignSkin; + synchronized(foreignSkinCache) { + foreignSkin = foreignSkinCache.get(searchUUID); + } + if(foreignSkin != null) { + foreignSkin.lastHit = System.currentTimeMillis(); + sender.sendData(SkinService.CHANNEL, foreignSkin.data); + }else { + if (eaglerHandler.skinUUIDLookupRateLimiter + .rateLimit(EaglerXBungee.getEagler().getConfig().getUuidRateLimitPlayer()) + && !isLimitedAsAntagonist(sender.getUniqueId())) { + processResolveProfileTextureByUUIDForeign(sender, searchUUID); + } + } + } + } + } + + public void processGetOtherSkin(UUID searchUUID, String skinURL, UserConnection sender) { + EaglerBungeeConfig config = EaglerXBungee.getEagler().getConfig(); + EaglerInitialHandler eaglerHandler = (EaglerInitialHandler)sender.getPendingConnection(); + if(!eaglerHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) { + return; + } + CachedForeignSkin foreignSkin; + synchronized(foreignSkinCache) { + foreignSkin = foreignSkinCache.get(searchUUID); + } + if(foreignSkin != null) { + foreignSkin.lastHit = System.currentTimeMillis(); + sender.sendData(SkinService.CHANNEL, foreignSkin.data); + }else { + Collection possiblePlayers; + synchronized(onlinePlayersFromTexturesMap) { + possiblePlayers = onlinePlayersFromTexturesMap.get(searchUUID); + } + boolean playersExist = possiblePlayers.size() > 0; + if(playersExist) { + for(UUID uuid : possiblePlayers) { + CachedPlayerSkin maybeCachedPacket; + synchronized(onlinePlayersCache) { + maybeCachedPacket = onlinePlayersCache.get(uuid); + } + if(maybeCachedPacket != null) { + byte[] rewritten = SkinPackets.rewriteUUID(searchUUID, maybeCachedPacket.data); + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(searchUUID, maybeCachedPacket); + } + sender.sendData(SkinService.CHANNEL, rewritten); + return; + } + } + } + if(eaglerHandler.skinTextureDownloadRateLimiter.rateLimit(config.getSkinRateLimitPlayer()) && !isLimitedAsAntagonist(sender.getUniqueId())) { + processResolveURLTextureForForeign(sender, searchUUID, searchUUID, skinURL, -1); + } + } + } + + private void processResolveURLTextureForOnline(final UserConnection initiator, final UUID onlineCacheUUID, + final UUID skinUUID, final String urlStr, final int modelId) { + synchronized(pendingTextures) { + PendingTextureDownload alreadyPending = pendingTextures.get(skinUUID); + if(alreadyPending != null) { + if(alreadyPending.antagonists.add(initiator.getUniqueId())) { + alreadyPending.callbacks.add(new Consumer() { + + @Override + public void accept(byte[] t) { + CachedPlayerSkin skin; + synchronized(onlinePlayersCache) { + skin = onlinePlayersCache.get(onlineCacheUUID); + } + if(skin != null) { + initiator.sendData(SkinService.CHANNEL, skin.data); + } + } + + }); + } + }else { + PendingTextureDownload newTask = new PendingTextureDownload( + skinUUID, urlStr, initiator.getUniqueId(), new Consumer() { + + @Override + public void accept(byte[] t) { + CachedPlayerSkin skin; + if(t != null) { + registerTextureToPlayerAssociation(skinUUID, onlineCacheUUID); + skin = new CachedPlayerSkin(SkinPackets.makeCustomResponse(onlineCacheUUID, modelId, t), skinUUID, modelId); + }else { + skin = new CachedPlayerSkin(SkinPackets.makePresetResponse(onlineCacheUUID), null, -1); + } + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(onlineCacheUUID, skin); + } + initiator.sendData(SkinService.CHANNEL, skin.data); + } + + }, antagonistLogger); + try { + AsyncSkinProvider.downloadSkin(skinUUID, urlStr, cacheProvider, newTask); + }catch(CancelException ex) { + return; + } + pendingTextures.put(skinUUID, newTask); + } + } + } + + private void processResolveURLTextureForForeign(final UserConnection initiator, final UUID foreignCacheUUID, + final UUID skinUUID, final String urlStr, final int modelId) { + synchronized(pendingTextures) { + PendingTextureDownload alreadyPending = pendingTextures.get(skinUUID); + if(alreadyPending != null) { + if(alreadyPending.antagonists.add(initiator.getUniqueId())) { + alreadyPending.callbacks.add(new Consumer() { + + @Override + public void accept(byte[] t) { + CachedForeignSkin skin; + synchronized(foreignSkinCache) { + skin = foreignSkinCache.get(foreignCacheUUID); + } + if(skin != null) { + initiator.sendData(SkinService.CHANNEL, skin.data); + } + } + + }); + } + }else { + PendingTextureDownload newTask = new PendingTextureDownload(skinUUID, urlStr, initiator.getUniqueId(), new Consumer() { + + @Override + public void accept(byte[] t) { + CachedForeignSkin skin; + if(t != null) { + skin = new CachedForeignSkin(foreignCacheUUID, SkinPackets.makeCustomResponse(foreignCacheUUID, modelId, t), modelId); + }else { + skin = new CachedForeignSkin(foreignCacheUUID, SkinPackets.makePresetResponse(foreignCacheUUID), -1); + } + synchronized(foreignSkinCache) { + foreignSkinCache.put(foreignCacheUUID, skin); + } + initiator.sendData(SkinService.CHANNEL, skin.data); + } + + }, antagonistLogger); + try { + AsyncSkinProvider.downloadSkin(skinUUID, urlStr, cacheProvider, newTask); + }catch(CancelException ex) { + return; + } + pendingTextures.put(skinUUID, newTask); + } + } + } + + private void processResolveProfileTextureByUUIDForOnline(final UserConnection initiator, final UUID playerUUID) { + synchronized(pendingUUIDs) { + PendingProfileUUIDLookup alreadyPending = pendingUUIDs.get(playerUUID); + if(alreadyPending != null) { + if(alreadyPending.antagonists.add(initiator.getUniqueId())) { + alreadyPending.callbacks.add(new Consumer() { + + @Override + public void accept(CacheFetchedProfile t) { + if(t == null || t.texture == null) { + CachedPlayerSkin skin; + synchronized(onlinePlayersCache) { + skin = onlinePlayersCache.get(playerUUID); + } + if(skin != null) { + initiator.sendData(SkinService.CHANNEL, skin.data); + } + }else { + processResolveURLTextureForOnline(initiator, playerUUID, t.textureUUID, t.texture, + SkinPackets.getModelId(t.model)); + } + } + + }); + } + }else { + PendingProfileUUIDLookup newTask = new PendingProfileUUIDLookup( + playerUUID, initiator.getUniqueId(), new Consumer() { + + @Override + public void accept(CacheFetchedProfile t) { + if(t == null || t.texture == null) { + CachedPlayerSkin skin; + if(t == null) { + skin = new CachedPlayerSkin(SkinPackets.makePresetResponse(playerUUID), null, -1); + }else { + skin = new CachedPlayerSkin(SkinPackets.makePresetResponse(playerUUID, + SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1); + } + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(playerUUID, skin); + } + initiator.sendData(SkinService.CHANNEL, skin.data); + }else { + processResolveURLTextureForOnline(initiator, playerUUID, t.textureUUID, t.texture, + SkinPackets.getModelId(t.model)); + } + } + + }, antagonistLogger); + try { + AsyncSkinProvider.lookupProfileByUUID(playerUUID, cacheProvider, newTask); + }catch(CancelException ex) { + return; + } + pendingUUIDs.put(playerUUID, newTask); + } + } + } + + private void processResolveProfileTextureByNameForOnline(final UserConnection initiator, final String playerName, final UUID mapUUID) { + synchronized(pendingNameLookups) { + PendingProfileNameLookup alreadyPending = pendingNameLookups.get(playerName); + if(alreadyPending != null) { + if(alreadyPending.antagonists.add(initiator.getUniqueId())) { + alreadyPending.callbacks.add(new Consumer() { + + @Override + public void accept(CacheFetchedProfile t) { + if(t == null || t.texture == null) { + CachedPlayerSkin skin; + synchronized(onlinePlayersCache) { + skin = onlinePlayersCache.get(t.uuid); + } + if(skin != null) { + initiator.sendData(SkinService.CHANNEL, skin.data); + } + }else { + processResolveURLTextureForOnline(initiator, mapUUID, t.textureUUID, t.texture, + SkinPackets.getModelId(t.model)); + } + } + + }); + } + }else { + PendingProfileNameLookup newTask = new PendingProfileNameLookup( + playerName, initiator.getUniqueId(), new Consumer() { + + @Override + public void accept(CacheFetchedProfile t) { + if(t == null || t.texture == null) { + CachedPlayerSkin skin; + if(t == null) { + skin = new CachedPlayerSkin(SkinPackets.makePresetResponse(mapUUID), null, -1); + }else { + skin = new CachedPlayerSkin(SkinPackets.makePresetResponse(mapUUID, + SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1); + } + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(mapUUID, skin); + } + initiator.sendData(SkinService.CHANNEL, skin.data); + }else { + processResolveURLTextureForOnline(initiator, mapUUID, t.textureUUID, t.texture, + SkinPackets.getModelId(t.model)); + } + } + + }, antagonistLogger); + try { + AsyncSkinProvider.lookupProfileByUsername(playerName, cacheProvider, newTask); + }catch(CancelException ex) { + return; + } + pendingNameLookups.put(playerName, newTask); + } + } + } + + private void processResolveProfileTextureByUUIDForeign(final UserConnection initiator, final UUID playerUUID) { + synchronized(pendingUUIDs) { + PendingProfileUUIDLookup alreadyPending = pendingUUIDs.get(playerUUID); + if(alreadyPending != null) { + if(alreadyPending.antagonists.add(initiator.getUniqueId())) { + alreadyPending.callbacks.add(new Consumer() { + + @Override + public void accept(CacheFetchedProfile t) { + if(t == null || t.texture == null) { + CachedForeignSkin skin; + synchronized(foreignSkinCache) { + skin = foreignSkinCache.get(playerUUID); + } + if(skin != null) { + initiator.sendData(SkinService.CHANNEL, skin.data); + } + }else { + processResolveURLTextureForForeign(initiator, playerUUID, t.textureUUID, t.texture, + SkinPackets.getModelId(t.model)); + } + } + + }); + } + }else { + PendingProfileUUIDLookup newTask = new PendingProfileUUIDLookup( + playerUUID, initiator.getUniqueId(), new Consumer() { + + @Override + public void accept(CacheFetchedProfile t) { + if(t == null || t.texture == null) { + CachedForeignSkin skin; + if(t == null) { + skin = new CachedForeignSkin(playerUUID, SkinPackets.makePresetResponse(playerUUID), -1); + }else { + skin = new CachedForeignSkin(playerUUID, SkinPackets.makePresetResponse( + playerUUID, SkinPackets.getModelId(t.model) == 1 ? 1 : 0), -1); + } + synchronized(foreignSkinCache) { + foreignSkinCache.put(playerUUID, skin); + } + initiator.sendData(SkinService.CHANNEL, skin.data); + }else { + processResolveURLTextureForForeign(initiator, playerUUID, t.textureUUID, t.texture, + SkinPackets.getModelId(t.model)); + } + } + + }, antagonistLogger); + try { + AsyncSkinProvider.lookupProfileByUUID(playerUUID, cacheProvider, newTask); + }catch(CancelException ex) { + return; + } + pendingUUIDs.put(playerUUID, newTask); + } + } + } + + public void registerEaglercraftPlayer(UUID clientUUID, byte[] generatedPacket, int modelId) throws IOException { + synchronized(foreignSkinCache) { + foreignSkinCache.remove(clientUUID); + } + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(clientUUID, new CachedPlayerSkin(generatedPacket, null, modelId)); + } + } + + public void unregisterPlayer(UUID clientUUID) { + CachedPlayerSkin data; + synchronized(onlinePlayersCache) { + data = onlinePlayersCache.remove(clientUUID); + } + if(data != null) { + synchronized(foreignSkinCache) { + foreignSkinCache.put(clientUUID, new CachedForeignSkin(clientUUID, data.data, data.modelId)); + } + if(data.textureUUID != null) { + synchronized(foreignSkinCache) { + foreignSkinCache.put(data.textureUUID, new CachedForeignSkin(data.textureUUID, data.data, data.modelId)); + } + } + deletePlayerTextureAssociation(clientUUID, data.textureUUID); + }else { + deletePlayerTextureAssociation(clientUUID, null); + } + } + + private void deletePlayerTextureAssociation(UUID clientUUID, UUID textureUUID) { + if(textureUUID != null) { + synchronized(onlinePlayersToTexturesMap) { + onlinePlayersToTexturesMap.remove(clientUUID); + } + synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersFromTexturesMap.remove(textureUUID, clientUUID); + } + }else { + UUID removedUUID; + synchronized(onlinePlayersToTexturesMap) { + removedUUID = onlinePlayersToTexturesMap.remove(clientUUID); + } + if(removedUUID != null) { + synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersFromTexturesMap.remove(removedUUID, clientUUID); + } + } + } + } + + public void registerTextureToPlayerAssociation(UUID textureUUID, UUID playerUUID) { + synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersFromTexturesMap.put(textureUUID, playerUUID); + } + synchronized(onlinePlayersToTexturesMap) { + onlinePlayersToTexturesMap.put(playerUUID, textureUUID); + } + CachedForeignSkin foreign; + synchronized(foreignSkinCache) { + foreign = foreignSkinCache.remove(textureUUID); + } + if(foreign != null) { + synchronized(onlinePlayersCache) { + onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(foreign.data, textureUUID, foreign.modelKnown)); + } + } + } + + public void flush() { + long millis = System.currentTimeMillis(); + + synchronized(foreignSkinCache) { + Iterator itr = foreignSkinCache.values().iterator(); + while(itr.hasNext()) { + if(millis - itr.next().lastHit > 900000l) { // 15 minutes + itr.remove(); + } + } + } + + synchronized(pendingTextures) { + Iterator itr = pendingTextures.values().iterator(); + while(itr.hasNext()) { + PendingTextureDownload etr = itr.next(); + if(millis - etr.initializedTime > (etr.finalized ? 5000l : 10000l)) { + itr.remove(); + try { + etr.antagonistsCallback.accept(etr.antagonists); + }catch(Throwable t) { + } + } + } + } + + synchronized(pendingUUIDs) { + Iterator itr = pendingUUIDs.values().iterator(); + while(itr.hasNext()) { + PendingProfileUUIDLookup etr = itr.next(); + if(millis - etr.initializedTime > (etr.finalized ? 5000l : 10000l)) { + itr.remove(); + try { + etr.antagonistsCallback.accept(etr.antagonists); + }catch(Throwable t) { + } + } + } + } + + synchronized(pendingNameLookups) { + Iterator itr = pendingNameLookups.values().iterator(); + while(itr.hasNext()) { + PendingProfileNameLookup etr = itr.next(); + if(millis - etr.initializedTime > (etr.finalized ? 5000l : 10000l)) { + itr.remove(); + try { + etr.antagonistsCallback.accept(etr.antagonists); + }catch(Throwable t) { + } + } + } + } + + int cooldownPeriod = 60000 / EaglerXBungee.getEagler().getConfig().getAntagonistsRateLimit(); + int elapsedCooldown = (int)(millis - antagonistCooldown); + elapsedCooldown /= cooldownPeriod; + if(cooldownPeriod > 0) { + antagonistCooldown += elapsedCooldown * cooldownPeriod; + synchronized(antagonists) { + Iterator itr = antagonists.keySet().iterator(); + while(itr.hasNext()) { + UUID key = itr.next(); + int i = antagonists.get(key) - elapsedCooldown; + if(i <= 0) { + itr.remove(); + }else { + antagonists.put(key, i); + } + } + } + } + + cacheProvider.flush(); + } + + public void shutdown() { + resetMaps(); + if(cacheProvider != null) { + cacheProvider.destroy(); + } + cacheProvider = null; + } + + private boolean isLimitedAsAntagonist(UUID uuid) { + int limit = EaglerXBungee.getEagler().getConfig().getAntagonistsRateLimit(); + limit += limit >> 1; + synchronized(antagonists) { + int i = antagonists.get(uuid); + return i != antagonists.getNoEntryValue() && i > limit; + } + } + + private void resetMaps() { + synchronized(onlinePlayersCache) { + onlinePlayersCache.clear(); + } + synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersFromTexturesMap.clear(); + } + synchronized(onlinePlayersToTexturesMap) { + onlinePlayersToTexturesMap.clear(); + } + synchronized(foreignSkinCache) { + foreignSkinCache.clear(); + } + synchronized(pendingTextures) { + pendingTextures.clear(); + } + synchronized(pendingUUIDs) { + pendingUUIDs.clear(); + } + synchronized(pendingNameLookups) { + pendingNameLookups.clear(); + } + synchronized(antagonists) { + antagonists.clear(); + } + } + + public static String sanitizeTextureURL(String url) { + try { + URI uri = URI.create(url); + StringBuilder builder = new StringBuilder(); + String scheme = uri.getScheme(); + if(scheme == null) { + return null; + } + String host = uri.getHost(); + if(host == null) { + return null; + } + scheme = scheme.toLowerCase(); + builder.append(scheme).append("://"); + builder.append(host); + int port = uri.getPort(); + if(port != -1) { + switch(scheme) { + case "http": + if(port == 80) { + port = -1; + } + break; + case "https": + if(port == 443) { + port = -1; + } + break; + default: + return null; + } + if(port != -1) { + builder.append(":").append(port); + } + } + String path = uri.getRawPath(); + if(path != null) { + if(path.contains("//")) { + path = String.join("/", path.split("[\\/]+")); + } + int len = path.length(); + if(len > 1 && path.charAt(len - 1) == '/') { + path = path.substring(0, len - 1); + } + builder.append(path); + } + return builder.toString(); + }catch(Throwable t) { + return null; + } + } + + private static final String hexString = "0123456789abcdef"; + + private static final char[] HEX = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + public static String getMojangUUID(UUID uuid) { + char[] ret = new char[32]; + long msb = uuid.getMostSignificantBits(); + long lsb = uuid.getLeastSignificantBits(); + for(int i = 0, j; i < 16; ++i) { + j = (15 - i) << 2; + ret[i] = HEX[(int)((msb >> j) & 15l)]; + ret[i + 16] = HEX[(int)((lsb >> j) & 15l)]; + } + return new String(ret); + } + + public static UUID parseMojangUUID(String uuid) { + long msb = 0l; + long lsb = 0l; + for(int i = 0, j; i < 16; ++i) { + j = (15 - i) << 2; + msb |= ((long)hexString.indexOf(uuid.charAt(i)) << j); + lsb |= ((long)hexString.indexOf(uuid.charAt(i + 16)) << j); + } + return new UUID(msb, lsb); + } + + public static boolean isAlex(UUID skinUUID) { + return (skinUUID.hashCode() & 1) != 0; + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinServiceOffline.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinServiceOffline.java new file mode 100644 index 0000000..b30e3ca --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/skins/SkinServiceOffline.java @@ -0,0 +1,118 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; + +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; + +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler; +import net.md_5.bungee.UserConnection; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class SkinServiceOffline implements ISkinService { + + public static final int masterRateLimitPerPlayer = 250; + + private static class CachedSkin { + + protected final UUID uuid; + protected final byte[] packet; + + protected CachedSkin(UUID uuid, byte[] packet) { + this.uuid = uuid; + this.packet = packet; + } + + } + + private final Map skinCache = new HashMap(); + + private final Multimap onlinePlayersFromTexturesMap = MultimapBuilder.hashKeys().hashSetValues().build(); + + public void init(String uri, String driverClass, String driverPath, int keepObjectsDays, int keepProfilesDays, + int maxObjects, int maxProfiles) { + synchronized(skinCache) { + skinCache.clear(); + } + } + + public void processGetOtherSkin(UUID searchUUID, UserConnection sender) { + if(((EaglerInitialHandler)sender.getPendingConnection()).skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) { + CachedSkin cached; + synchronized(skinCache) { + cached = skinCache.get(searchUUID); + } + if(cached != null) { + sender.sendData(SkinService.CHANNEL, cached.packet); + }else { + sender.sendData(SkinService.CHANNEL, SkinPackets.makePresetResponse(searchUUID)); + } + } + } + + public void processGetOtherSkin(UUID searchUUID, String skinURL, UserConnection sender) { + Collection uuids; + synchronized(onlinePlayersFromTexturesMap) { + uuids = onlinePlayersFromTexturesMap.get(searchUUID); + } + if(uuids.size() > 0) { + CachedSkin cached; + synchronized(skinCache) { + Iterator uuidItr = uuids.iterator(); + while(uuidItr.hasNext()) { + cached = skinCache.get(uuidItr.next()); + if(cached != null) { + sender.sendData(SkinService.CHANNEL, SkinPackets.rewriteUUID(searchUUID, cached.packet)); + } + } + } + } + sender.sendData(SkinService.CHANNEL, SkinPackets.makePresetResponse(searchUUID)); + } + + public void registerEaglercraftPlayer(UUID clientUUID, byte[] generatedPacket, int modelId) throws IOException { + synchronized(skinCache) { + skinCache.put(clientUUID, new CachedSkin(clientUUID, generatedPacket)); + } + } + + public void unregisterPlayer(UUID clientUUID) { + synchronized(skinCache) { + skinCache.remove(clientUUID); + } + } + + public void registerTextureToPlayerAssociation(UUID textureUUID, UUID playerUUID) { + synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersFromTexturesMap.put(textureUUID, playerUUID); + } + } + + public void flush() { + // no + } + + public void shutdown() { + synchronized(skinCache) { + skinCache.clear(); + } + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/sqlite/EaglerDrivers.java b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/sqlite/EaglerDrivers.java new file mode 100644 index 0000000..0f96a2f --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/sqlite/EaglerDrivers.java @@ -0,0 +1,116 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.sqlite; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.codehaus.plexus.util.FileUtils; + +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EaglerDrivers { + + private static Driver initializeDriver(String address, String driverClass) { + URLClassLoader classLoader = driversJARs.get(address); + if(classLoader == null) { + File driver; + if(address.equalsIgnoreCase("internal")) { + driver = new File(EaglerXBungee.getEagler().getDataFolder(), "drivers/sqlite-jdbc.jar"); + driver.getParentFile().mkdirs(); + if(!driver.exists()) { + try { + FileUtils.copyURLToFile(EaglerDrivers.class.getResource("sqlite-jdbc.jar"), driver); + } catch (IOException ex) { + EaglerXBungee.logger().severe("Could not extract sqlite-jdbc.jar!"); + throw new ExceptionInInitializerError(ex); + } + } + }else { + driver = new File(address); + } + URL driverURL; + try { + driverURL = driver.toURI().toURL(); + }catch(MalformedURLException ex) { + EaglerXBungee.logger().severe("Invalid JDBC driver path: " + address); + throw new ExceptionInInitializerError(ex); + } + classLoader = new URLClassLoader(new URL[] { driverURL }, ClassLoader.getSystemClassLoader()); + driversJARs.put(address, classLoader); + } + + Class loadedDriver; + try { + loadedDriver = classLoader.loadClass(driverClass); + }catch(ClassNotFoundException ex) { + try { + classLoader.close(); + } catch (IOException e) { + } + EaglerXBungee.logger().severe("Could not find JDBC driver class: " + driverClass); + throw new ExceptionInInitializerError(ex); + } + Driver sqlDriver = null; + try { + sqlDriver = (Driver) loadedDriver.newInstance(); + }catch(Throwable ex) { + try { + classLoader.close(); + } catch (IOException e) { + } + EaglerXBungee.logger().severe("Could not initialize JDBC driver class: " + driverClass); + throw new ExceptionInInitializerError(ex); + } + + return sqlDriver; + } + + private static final Map driversJARs = new HashMap(); + private static final Map driversDrivers = new HashMap(); + + public static Connection connectToDatabase(String address, String driverClass, String driverPath, Properties props) + throws SQLException { + if(driverClass.equalsIgnoreCase("internal")) { + driverClass = "org.sqlite.JDBC"; + } + if(driverPath == null) { + try { + Class.forName(driverClass); + } catch (ClassNotFoundException e) { + throw new SQLException("Driver class not found in JRE: " + driverClass, e); + } + return DriverManager.getConnection(address, props); + }else { + String driverMapPath = "" + driverPath + "?" + driverClass; + Driver dv = driversDrivers.get(driverMapPath); + if(dv == null) { + dv = initializeDriver(driverPath, driverClass); + driversDrivers.put(driverMapPath, dv); + } + return dv.connect(address, props); + } + } + +} diff --git a/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_authservice.yml b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_authservice.yml new file mode 100644 index 0000000..cdaa330 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_authservice.yml @@ -0,0 +1,17 @@ +enable_authentication_system: true +use_onboard_eaglerx_system: true +auth_db_uri: 'jdbc:sqlite:eaglercraft_auths.db' +sql_driver_class: 'internal' +sql_driver_path: 'internal' +password_prompt_screen_text: 'Enter your password to join:' +wrong_password_screen_text: 'Password Incorrect!' +not_registered_screen_text: 'You are not registered on this server!' +eagler_command_name: 'eagler' +use_register_command_text: '&aUse /eagler to set an Eaglercraft password on this account' +use_change_command_text: '&bUse /eagler to change your Eaglercraft password' +command_success_text: '&bYour eagler password was changed successfully.' +last_eagler_login_message: 'Your last Eaglercraft login was on $date from $ip' +too_many_registrations_message: '&cThe maximum number of registrations has been reached for your IP address' +need_vanilla_to_register_message: '&cYou need to log in with a vanilla account to use this command' +override_eagler_to_vanilla_skins: false +max_registration_per_ip: -1 \ No newline at end of file diff --git a/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_http_mime_types.json b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_http_mime_types.json new file mode 100644 index 0000000..38458bd --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_http_mime_types.json @@ -0,0 +1,180 @@ +{ + "text/html": { + "files": [ "html", "htm", "shtml" ], + "expires": 3600, + "charset": "utf-8" + }, + "application/javascript": { + "files": [ "js" ], + "expires": 3600, + "charset": "utf-8" + }, + "application/octet-stream": { + "files": [ "epk" ], + "expires": 14400 + }, + "text/css": { + "files": [ "css" ], + "expires": 14400, + "charset": "utf-8" + }, + "text/xml": { + "files": [ "xml" ], + "expires": 3600, + "charset": "utf-8" + }, + "text/plain": { + "files": [ "txt" ], + "expires": 3600, + "charset": "utf-8" + }, + "image/png": { + "files": [ "png" ], + "expires": 14400 + }, + "image/jpeg": { + "files": [ "jpeg", "jpg", "jfif" ], + "expires": 14400 + }, + "image/gif": { + "files": [ "gif" ], + "expires": 14400 + }, + "image/webp": { + "files": [ "webp" ], + "expires": 14400 + }, + "image/svg+xml": { + "files": [ "svg", "svgz" ], + "expires": 14400, + "charset": "utf-8" + }, + "image/tiff": { + "files": [ "tiff", "tif" ], + "expires": 14400 + }, + "image/avif": { + "files": [ "avif" ], + "expires": 14400 + }, + "image/x-ms-bmp": { + "files": [ "bmp" ], + "expires": 14400 + }, + "image/x-icon": { + "files": [ "ico" ], + "expires": 14400 + }, + "image/woff": { + "files": [ "woff" ], + "expires": 43200 + }, + "image/woff2": { + "files": [ "woff2" ], + "expires": 43200 + }, + "application/json": { + "files": [ "json" ], + "expires": 3600, + "charset": "utf-8" + }, + "application/pdf": { + "files": [ "pdf" ], + "expires": 14400 + }, + "application/rtf": { + "files": [ "rtf" ], + "expires": 14400 + }, + "application/java-archive": { + "files": [ "jar", "war", "ear" ], + "expires": 14400 + }, + "application/wasm": { + "files": [ "wasm" ], + "expires": 3600 + }, + "application/xhtml+xml": { + "files": [ "xhtml" ], + "expires": 3600, + "charset": "utf-8" + }, + "application/zip": { + "files": [ "zip" ], + "expires": 14400 + }, + "audio/midi": { + "files": [ "mid", "midi", "kar" ], + "expires": 43200 + }, + "audio/mpeg": { + "files": [ "mp3" ], + "expires": 43200 + }, + "audio/ogg": { + "files": [ "ogg" ], + "expires": 43200 + }, + "audio/x-m4a": { + "files": [ "m4a" ], + "expires": 43200 + }, + "application/atom+xml": { + "files": [ "atom" ], + "expires": 3600, + "charset": "utf-8" + }, + "application/rss+xml": { + "files": [ "rss" ], + "expires": 3600, + "charset": "utf-8" + }, + "application/x-shockwave-flash": { + "files": [ "swf" ], + "expires": 43200 + }, + "video/3gpp": { + "files": [ "3gpp", "3gp" ], + "expires": 43200 + }, + "video/mp4": { + "files": [ "mp4" ], + "expires": 43200 + }, + "video/mpeg": { + "files": [ "mpeg", "mpg" ], + "expires": 43200 + }, + "video/quicktime": { + "files": [ "mov" ], + "expires": 43200 + }, + "video/webm": { + "files": [ "webm" ], + "expires": 43200 + }, + "video/x-motion-jpeg": { + "files": [ "mjpg" ], + "expires": 14400 + }, + "video/x-flv": { + "files": [ "flv" ], + "expires": 43200 + }, + "video/x-m4v": { + "files": [ "m4v" ], + "expires": 43200 + }, + "video/x-mng": { + "files": [ "3mng" ], + "expires": 43200 + }, + "video/x-ms-wmv": { + "files": [ "wmv" ], + "expires": 43200 + }, + "video/x-msvideo": { + "files": [ "avi" ], + "expires": 43200 + } +} \ No newline at end of file diff --git a/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_listeners.yml b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_listeners.yml new file mode 100644 index 0000000..99e185d --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_listeners.yml @@ -0,0 +1,65 @@ +listener_01: + address: 0.0.0.0:8081 + address_v6: 'null' + max_players: 60 + tab_list: GLOBAL_PING + default_server: lobby + force_default_server: false + forward_ip: false + forward_ip_header: X-Real-IP + redirect_legacy_clients_to: 'null' + server_icon: server-icon.png + server_motd: + - '&6An EaglercraftX server' + allow_motd: true + allow_query: true + request_motd_cache: + cache_ttl: 7200 + online_server_list_animation: false + online_server_list_results: true + online_server_list_trending: true + online_server_list_portfolios: false + http_server: + enabled: false + root: 'web' + page_404_not_found: 'default' + page_index_name: + - 'index.html' + - 'index.htm' + ratelimit: + ip: + enable: true + period: 90 + limit: 60 + limit_lockout: 80 + lockout_duration: 1200 + exceptions: + - '127.*' + - '0:0:0:0:0:0:0:1' + login: + enable: true + period: 50 + limit: 5 + limit_lockout: 10 + lockout_duration: 300 + exceptions: + - '127.*' + - '0:0:0:0:0:0:0:1' + motd: + enable: true + period: 30 + limit: 5 + limit_lockout: 15 + lockout_duration: 300 + exceptions: + - '127.*' + - '0:0:0:0:0:0:0:1' + query: + enable: true + period: 30 + limit: 15 + limit_lockout: 25 + lockout_duration: 900 + exceptions: + - '127.*' + - '0:0:0:0:0:0:0:1' \ No newline at end of file diff --git a/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_settings.yml b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_settings.yml new file mode 100644 index 0000000..daa5411 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config/default_settings.yml @@ -0,0 +1,21 @@ +server_name: 'EaglercraftXBungee Server' +server_uuid: ${random_uuid} +websocket_connection_timeout: 15000 +websocket_handshake_timeout: 5000 +http_websocket_compression_level: 6 +download_vanilla_skins_to_clients: true +valid_skin_download_urls: + - 'textures.minecraft.net' +uuid_lookup_ratelimit_player: 50 +uuid_lookup_ratelimit_global: 175 +skin_download_ratelimit_player: 1000 +skin_download_ratelimit_global: 30000 +skin_cache_db_uri: 'jdbc:sqlite:eaglercraft_skins_cache.db' +skin_cache_keep_objects_days: 45 +skin_cache_keep_profiles_days: 7 +skin_cache_max_objects: 32768 +skin_cache_max_profiles: 32768 +skin_cache_antagonists_ratelimit: 15 +sql_driver_class: 'internal' +sql_driver_path: 'internal' +eagler_players_vanilla_skin: '' \ No newline at end of file diff --git a/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/sqlite/sqlite-jdbc.jar b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/sqlite/sqlite-jdbc.jar new file mode 100644 index 0000000..7aa750f Binary files /dev/null and b/gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/sqlite/sqlite-jdbc.jar differ diff --git a/gateway/EaglercraftXBungee/src/main/resources/plugin.yml b/gateway/EaglercraftXBungee/src/main/resources/plugin.yml new file mode 100644 index 0000000..e4a1722 --- /dev/null +++ b/gateway/EaglercraftXBungee/src/main/resources/plugin.yml @@ -0,0 +1,5 @@ +name: EaglercraftXBungee +main: net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee +version: 1.0.0 +description: Plugin to allow EaglercraftX 1.8 players to join your network, or allow EaglercraftX 1.8 players to use your network as a proxy to join other networks +author: lax1dude \ No newline at end of file diff --git a/gateway/PlaceholderServer/Java-WebSocket-1.5.1-with-dependencies.jar b/gateway/PlaceholderServer/Java-WebSocket-1.5.1-with-dependencies.jar new file mode 100644 index 0000000..8f103a7 Binary files /dev/null and b/gateway/PlaceholderServer/Java-WebSocket-1.5.1-with-dependencies.jar differ diff --git a/gateway/PlaceholderServer/PlaceholderServer-Latest.jar b/gateway/PlaceholderServer/PlaceholderServer-Latest.jar new file mode 100644 index 0000000..ec68b8b Binary files /dev/null and b/gateway/PlaceholderServer/PlaceholderServer-Latest.jar differ diff --git a/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/DummyConnection.java b/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/DummyConnection.java new file mode 100644 index 0000000..6d059f3 --- /dev/null +++ b/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/DummyConnection.java @@ -0,0 +1,112 @@ +package net.lax1dude.eaglercraft.v1_8.placeholder_server; + +import java.nio.ByteBuffer; + +import org.java_websocket.WebSocket; +import org.json.JSONArray; +import org.json.JSONObject; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class DummyConnection { + + public final long age; + public long sendRedirectAt = 0l; + public final WebSocket sock; + + private boolean hasFirstPacket = false; + + public DummyConnection(WebSocket sock) { + this.age = System.currentTimeMillis(); + this.sock = sock; + } + + public void processString(String str) { + if(!hasFirstPacket) { + hasFirstPacket = true; + str = str.toLowerCase(); + if(str.startsWith("accept:")) { + str = str.substring(7).trim(); + if(str.startsWith("motd")) { + String subType = "motd"; + int i = str.indexOf('.'); + if(i > 0 && i != str.length() - 1) { + subType = str.substring(i + 1); + } + JSONObject json = new JSONObject(); + json.put("name", PlaceholderServerConfig.serverName); + json.put("brand", "lax1dude"); + json.put("vers", "EaglerXPlaceholder/1.0"); + json.put("cracked", true); + json.put("secure", false); + json.put("time", System.currentTimeMillis()); + json.put("uuid", PlaceholderServerConfig.serverUUID); + json.put("type", str); + JSONObject motdData = new JSONObject(); + boolean icon = false; + if(subType.startsWith("cache.anim")) { + motdData.put("unsupported", true); + }else { + if(subType.startsWith("cache")) { + JSONArray arr = new JSONArray(); + arr.put("animation"); + arr.put("results"); + arr.put("trending"); + arr.put("portfolio"); + motdData.put("cache", arr); + motdData.put("ttl", 7200); + }else { + motdData.put("cache", true); + } + JSONArray motdLines = new JSONArray(); + String motd1 = PlaceholderServerConfig.motd1; + String motd2 = PlaceholderServerConfig.motd2; + if(motd1 != null && motd1.length() > 0) motdLines.put(motd1); + if(motd2 != null && motd2.length() > 0) motdLines.put(motd2); + motdData.put("motd", motdLines); + icon = PlaceholderServerConfig.cachedIconPacket != null && !subType.startsWith("noicon") + && !subType.startsWith("cache.noicon"); + motdData.put("icon", icon); + motdData.put("online", 0); + motdData.put("max", 0); + motdData.put("players", new JSONArray()); + } + json.put("data", motdData); + sock.send(json.toString()); + if(icon) { + sock.send(PlaceholderServerConfig.cachedIconPacket); + } + } + } + sock.close(); + } + } + + public void processBinary(ByteBuffer bin) { + if(!hasFirstPacket) { + hasFirstPacket = true; + if(bin.remaining() > 2) { + if(bin.get(0) == (byte)2 && bin.get(1) == (byte)69) { + sock.send(PlaceholderServerConfig.cachedLegacyKickRedirectPacket); + sendRedirectAt = System.currentTimeMillis(); + return; + }else if(bin.get(0) == (byte)1) { + sock.send(PlaceholderServerConfig.cachedKickPacket); + } + } + sock.close(); + } + } + +} diff --git a/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/PlaceholderServer.java b/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/PlaceholderServer.java new file mode 100644 index 0000000..6153725 --- /dev/null +++ b/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/PlaceholderServer.java @@ -0,0 +1,121 @@ +package net.lax1dude.eaglercraft.v1_8.placeholder_server; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlaceholderServer extends WebSocketServer { + + public static PlaceholderServer websocketServer = null; + private boolean isOpen = false; + + public static void main(String[] args) throws IOException { + System.out.println(); + System.out.println("Copyright (c) 2022 lax1dude"); + System.out.println("All rights reserved."); + System.out.println(); + System.out.println("Starting placeholder 1.8 server..."); + System.out.println(); + + PlaceholderServerConfig.load(); + + System.out.println("Starting WebSocket server..."); + System.out.println(); + websocketServer = new PlaceholderServer(new InetSocketAddress(PlaceholderServerConfig.host, PlaceholderServerConfig.port)); + websocketServer.start(); + + long redirTimeout = (PlaceholderServerConfig.redirect != null && PlaceholderServerConfig.redirect.length() > 0) ? 500l : 100l; + while(true) { + try { + Thread.sleep(200l); + long millis = System.currentTimeMillis(); + for(WebSocket ws : websocketServer.getConnections()) { + DummyConnection conn = ws.getAttachment(); + if (conn != null && ((conn.sendRedirectAt > 0l && millis - conn.sendRedirectAt > redirTimeout) + || millis - conn.age > (long) PlaceholderServerConfig.clientTimeout)) { + ws.close(); + } + } + } catch (Throwable t) { + } + } + } + + private PlaceholderServer(InetSocketAddress addr) { + super(addr); + setReuseAddr(true); + setTcpNoDelay(true); + } + + @Override + public void onClose(WebSocket arg0, int arg1, String arg2, boolean arg3) { + } + + @Override + public void onError(WebSocket arg0, Exception arg1) { + System.err.println(); + if(arg0 != null) { + System.err.println("Caught WebSocket exception on " + arg0.getRemoteSocketAddress() + "!"); + arg0.close(); + }else { + System.err.println("Caught WebSocket exception!"); + } + arg1.printStackTrace(); + if(!isOpen) { + System.exit(-1); + } + } + + @Override + public void onMessage(WebSocket arg0, String arg1) { + ((DummyConnection)arg0.getAttachment()).processString(arg1); + } + + @Override + public void onMessage(WebSocket arg0, ByteBuffer arg1) { + ((DummyConnection)arg0.getAttachment()).processBinary(arg1); + } + + @Override + public void onOpen(WebSocket arg0, ClientHandshake arg1) { + arg0.setAttachment(new DummyConnection(arg0)); + } + + @Override + public void onStart() { + System.out.println(); + System.out.println("Listening on: " + getAddress()); + isOpen = true; + + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + try { + PlaceholderServer.this.stop(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + }, "Shutdown Thread")); + + System.out.println("Use CTRL+C to exit"); + } + +} diff --git a/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/PlaceholderServerConfig.java b/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/PlaceholderServerConfig.java new file mode 100644 index 0000000..9b48cbe --- /dev/null +++ b/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/PlaceholderServerConfig.java @@ -0,0 +1,180 @@ +package net.lax1dude.eaglercraft.v1_8.placeholder_server; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +import org.json.JSONArray; +import org.json.JSONObject; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlaceholderServerConfig { + + public static final File configFile = new File("config.json"); + + public static String host = "0.0.0.0"; + public static int port = 8081; + public static String icon = null; + public static String serverName = "EaglercraftX 1.8 Server"; + public static String serverUUID = ""; + public static int clientTimeout = 3000; + public static String motd1 = "Coming Soon"; + public static String motd2 = ""; + public static String kick = "This server is still under construction"; + public static String redirect = ""; + + public static byte[] cachedIconPacket = null; + public static byte[] cachedLegacyKickRedirectPacket = null; + public static byte[] cachedKickPacket = null; + + public static void load() throws IOException { + if(!configFile.exists()) { + System.out.println("Writing new config file to: " + configFile.getName()); + int i; + try(Reader is = new InputStreamReader(PlaceholderServerConfig.class.getResourceAsStream("/config_default.json")); + OutputStream os = new FileOutputStream(configFile)) { + char[] copyBuffer = new char[1024]; + StringBuilder sb = new StringBuilder(); + while((i = is.read(copyBuffer)) != -1) { + sb.append(copyBuffer, 0, i); + } + String str = sb.toString(); + str = str.replace("${random_uuid}", UUID.randomUUID().toString()); + os.write(str.getBytes(StandardCharsets.UTF_8)); + } + try(InputStream is = PlaceholderServerConfig.class.getResourceAsStream("/server-icon_default.png"); + OutputStream os = new FileOutputStream(new File("server-icon.png"))) { + byte[] copyBuffer = new byte[1024]; + while((i = is.read(copyBuffer)) != -1) { + os.write(copyBuffer, 0, i); + } + } + } + + System.out.println("Reading config file: " + configFile.getName()); + byte[] fileBytes = new byte[(int)configFile.length()]; + try(InputStream is = new FileInputStream(configFile)) { + int i = 0, j; + while(i < fileBytes.length && (j = is.read(fileBytes, i, fileBytes.length - i)) != -1) { + i += j; + } + } + + try { + JSONObject loaded = new JSONObject(new String(fileBytes, StandardCharsets.UTF_8)); + host = loaded.getString("server_host"); + port = loaded.getInt("server_port"); + icon = loaded.getString("server_icon"); + serverName = loaded.getString("server_name"); + serverUUID = loaded.getString("server_uuid"); + clientTimeout = loaded.getInt("client_timeout"); + JSONArray motd = loaded.getJSONArray("server_motd"); + motd1 = motd.getString(0); + if(motd.length() > 1) { + motd2 = motd.getString(1); + } + kick = loaded.getString("kick_message"); + if(kick.length() > 255) { + kick = kick.substring(0, 255); + System.err.println("Warning: kick message was truncated to 255 characters"); + } + redirect = loaded.optString("redirect_legacy", null); + }catch(Throwable t) { + throw new IOException("Could not load config file \"" + configFile.getAbsolutePath() + "\"!", t); + } + + cacheKickPacket(); + cacheRedirectPacket(); + + if(icon != null && icon.length() > 0) { + cacheIconPacket(); + } + } + + private static void cacheKickPacket() throws IOException { + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bao); + dos.writeByte(0xFF); + dos.writeByte(0x08); + dos.writeByte(kick.length()); + for(int i = 0, l = kick.length(); i < l; ++i) { + dos.writeByte(kick.charAt(i) & 0xFF); + } + cachedKickPacket = bao.toByteArray(); + } + + private static void cacheRedirectPacket() throws IOException { + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bao); + if(redirect == null || redirect.length() == 0) { + String message = "This is an EaglercraftX 1.8 server, it is not compatible with 1.5.2!"; + dos.writeByte(0xFF); + dos.writeShort(message.length()); + for(int i = 0, l = message.length(), j; i < l; ++i) { + j = message.charAt(i); + dos.writeByte((j >> 8) & 0xFF); + dos.writeByte(j & 0xFF); + } + }else { + // Packet1Login + dos.writeByte(0x01); + dos.writeInt(0); + dos.writeShort(0); + dos.writeByte(0); + dos.writeByte(0); + dos.writeByte(0xFF); + dos.writeByte(0); + dos.writeByte(0); + // Packet250CustomPayload + dos.writeByte(0xFA); + String channel = "EAG|Reconnect"; + int cl = channel.length(); + dos.writeShort(cl); + for(int i = 0; i < cl; ++i) { + dos.writeChar(channel.charAt(i)); + } + byte[] redirect_ = redirect.getBytes(StandardCharsets.UTF_8); + dos.writeShort(redirect_.length); + dos.write(redirect_); + } + cachedLegacyKickRedirectPacket = bao.toByteArray(); + } + + private static void cacheIconPacket() throws IOException { + File f = new File(icon); + int[] iconPixels = ServerIconLoader.createServerIcon(f); + if(iconPixels != null) { + cachedIconPacket = new byte[16384]; + for(int i = 0, j; i < 4096; ++i) { + j = i << 2; + cachedIconPacket[j] = (byte)((iconPixels[i] >> 16) & 0xFF); + cachedIconPacket[j + 1] = (byte)((iconPixels[i] >> 8) & 0xFF); + cachedIconPacket[j + 2] = (byte)(iconPixels[i] & 0xFF); + cachedIconPacket[j + 3] = (byte)((iconPixels[i] >> 24) & 0xFF); + } + }else { + System.err.println("Could not load server icon \"" + f.getAbsolutePath() + "\"!"); + } + } +} diff --git a/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/ServerIconLoader.java b/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/ServerIconLoader.java new file mode 100644 index 0000000..5c698ad --- /dev/null +++ b/gateway/PlaceholderServer/src/main/java/net/lax1dude/eaglercraft/v1_8/placeholder_server/ServerIconLoader.java @@ -0,0 +1,79 @@ +package net.lax1dude.eaglercraft.v1_8.placeholder_server; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.InputStream; + +import javax.imageio.ImageIO; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +class ServerIconLoader { + + static int[] createServerIcon(BufferedImage awtIcon) { + BufferedImage icon = awtIcon; + boolean gotScaled = false; + if(icon.getWidth() != 64 || icon.getHeight() != 64) { + icon = new BufferedImage(64, 64, awtIcon.getType()); + Graphics2D g = (Graphics2D) icon.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, (awtIcon.getWidth() < 64 || awtIcon.getHeight() < 64) ? + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR : RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.setBackground(new Color(0, true)); + g.clearRect(0, 0, 64, 64); + int ow = awtIcon.getWidth(); + int oh = awtIcon.getHeight(); + int nw, nh; + float aspectRatio = (float)oh / (float)ow; + if(aspectRatio >= 1.0f) { + nw = (int)(64 / aspectRatio); + nh = 64; + }else { + nw = 64; + nh = (int)(64 * aspectRatio); + } + g.drawImage(awtIcon, (64 - nw) / 2, (64 - nh) / 2, (64 - nw) / 2 + nw, (64 - nh) / 2 + nh, 0, 0, awtIcon.getWidth(), awtIcon.getHeight(), null); + g.dispose(); + gotScaled = true; + } + int[] pxls = icon.getRGB(0, 0, 64, 64, new int[4096], 0, 64); + if(gotScaled) { + for(int i = 0; i < pxls.length; ++i) { + if((pxls[i] & 0xFFFFFF) == 0) { + pxls[i] = 0; + } + } + } + return pxls; + } + + static int[] createServerIcon(InputStream f) { + try { + return createServerIcon(ImageIO.read(f)); + }catch(Throwable t) { + return null; + } + } + + static int[] createServerIcon(File f) { + try { + return createServerIcon(ImageIO.read(f)); + }catch(Throwable t) { + return null; + } + } + +} diff --git a/gateway/PlaceholderServer/src/main/resources/config_default.json b/gateway/PlaceholderServer/src/main/resources/config_default.json new file mode 100644 index 0000000..b94378a --- /dev/null +++ b/gateway/PlaceholderServer/src/main/resources/config_default.json @@ -0,0 +1,13 @@ +{ + "server_host": "0.0.0.0", + "server_port": 8081, + "server_icon": "server-icon.png", + "server_motd": [ + "1.8 is still under construction" + ], + "server_name": "EaglercraftX 1.8 Server", + "server_uuid": "${random_uuid}", + "client_timeout": 3000, + "kick_message": "This EaglercraftX 1.8 server is still under construction!", + "redirect_legacy": null +} \ No newline at end of file diff --git a/gateway/PlaceholderServer/src/main/resources/server-icon_default.png b/gateway/PlaceholderServer/src/main/resources/server-icon_default.png new file mode 100644 index 0000000..c9cd76d Binary files /dev/null and b/gateway/PlaceholderServer/src/main/resources/server-icon_default.png differ diff --git a/gateway_version b/gateway_version new file mode 100644 index 0000000..afaf360 --- /dev/null +++ b/gateway_version @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/mcp918/assetsIndexTransformer.json b/mcp918/assetsIndexTransformer.json new file mode 100644 index 0000000..162270d --- /dev/null +++ b/mcp918/assetsIndexTransformer.json @@ -0,0 +1,72 @@ +{ + "rules": [ + { + "minecraft/sounds/music/menu/menu2.ogg": { + "action": "encode", + "ffmpeg": { + "samples": 16000, + "bitrate": 48 + } + } + }, + { + "minecraft/sounds/records/*": { + "action": "exclude" + } + }, + { + "minecraft/sounds/music/*": { + "action": "exclude" + } + }, + { + "minecraft/sounds/fire/fire.ogg": { + "action": "encode", + "ffmpeg": { + "samples": 16000, + "bitrate": 48, + "stereo": true + } + } + }, + { + "minecraft/sounds/minecart/inside.ogg": { + "action": "encode", + "ffmpeg": { + "samples": 16000, + "bitrate": 48, + "stereo": true + } + } + }, + { + "minecraft/sounds/*": { + "action": "encode", + "ffmpeg": { + "samples": 16000, + "bitrate": 48 + } + } + }, + { + "minecraft/icons/minecraft.icns": { + "action": "exclude" + } + }, + { + "minecraft/icons/*": { + "action": "include" + } + }, + { + "minecraft/sounds.json": { + "action": "include" + } + }, + { + "minecraft/lang/*": { + "action": "languages_zip" + } + } + ] +} \ No newline at end of file diff --git a/mcp918/readme.txt b/mcp918/readme.txt new file mode 100644 index 0000000..670b42d --- /dev/null +++ b/mcp918/readme.txt @@ -0,0 +1,30 @@ + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +To run build_init, place the following files in this directory: + + - 'mcp918.zip' (mod coder pack for minecraft 1.8.8) + - '1.8.8.jar` (jar file for minecraft 1.8.8) + - '1.8.json' (assets index for minecraft 1.8) + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +Notes on assetsIndexTransformer.json: + +Recommended allowed samples: + - 16000 + - 22050 + - 32000 + - 44100 + - 48000 + +Recommended allowed bitrates: + - 48 + - 64 + - 80 + - 96 + - 112 + - 128 diff --git a/patches/minecraft/delete.txt b/patches/minecraft/delete.txt new file mode 100644 index 0000000..a9b58f5 --- /dev/null +++ b/patches/minecraft/delete.txt @@ -0,0 +1,395 @@ +# 394 files to delete: +net/minecraft/world/gen/layer/GenLayerHills.java +net/minecraft/world/gen/MapGenBase.java +net/minecraft/client/renderer/VertexBufferUploader.java +net/minecraft/command/CommandWorldBorder.java +net/minecraft/command/IAdminCommand.java +net/minecraft/realms/DisconnectedRealmsScreen.java +net/minecraft/world/storage/SaveFormatOld.java +net/minecraft/world/gen/layer/GenLayerVoronoiZoom.java +net/minecraft/entity/ai/EntityAITargetNonTamed.java +net/minecraft/entity/ai/EntityMoveHelper.java +net/minecraft/entity/ai/EntityAIOcelotAttack.java +net/minecraft/client/stream/Metadata.java +net/minecraft/village/Village.java +net/minecraft/village/VillageSiege.java +net/minecraft/entity/ai/EntityAITradePlayer.java +net/minecraft/network/NetworkManager.java +net/minecraft/world/gen/MapGenCaves.java +net/minecraft/command/CommandTrigger.java +net/minecraft/world/gen/feature/WorldGenIcePath.java +net/minecraft/realms/RealmsServerPing.java +net/minecraft/client/renderer/chunk/ChunkRenderDispatcher.java +net/minecraft/entity/ai/EntityAIVillagerMate.java +net/minecraft/world/storage/DerivedWorldInfo.java +net/minecraft/server/network/NetHandlerStatusServer.java +net/minecraft/client/gui/ServerListEntryLanScan.java +net/minecraft/entity/ai/EntityAIBase.java +net/minecraft/world/gen/feature/WorldGenDungeons.java +net/minecraft/command/server/CommandListBans.java +net/minecraft/realms/RealmsServerStatusPinger.java +net/minecraft/command/CommandExecuteAt.java +net/minecraft/network/NetHandlerPlayServer.java +net/minecraft/world/gen/feature/WorldGenWaterlily.java +net/minecraft/entity/ai/EntityAIMoveThroughVillage.java +net/minecraft/client/renderer/GlStateManager.java +net/minecraft/world/gen/feature/WorldGenTaiga1.java +net/minecraft/entity/ai/EntityAICreeperSwell.java +net/minecraft/command/server/CommandSaveAll.java +net/minecraft/realms/RealmsVertexFormat.java +net/minecraft/network/NettyCompressionDecoder.java +net/minecraft/profiler/PlayerUsageSnooper.java +net/minecraft/client/gui/GuiSelectWorld.java +net/minecraft/entity/ai/EntityAIHarvestFarmland.java +net/minecraft/command/server/CommandPublishLocalServer.java +net/minecraft/command/CommandDebug.java +net/minecraft/world/gen/feature/WorldGenReed.java +net/minecraft/client/renderer/chunk/VboChunkFactory.java +net/minecraft/client/gui/GuiScreenCustomizePresets.java +net/minecraft/command/CommandGive.java +net/minecraft/world/gen/feature/WorldGenGlowStone1.java +net/minecraft/command/CommandGameRule.java +net/minecraft/world/gen/feature/WorldGenDesertWells.java +net/minecraft/server/management/PreYggdrasilConverter.java +net/minecraft/pathfinding/PathFinder.java +net/minecraft/network/PacketThreadUtil.java +net/minecraft/realms/RealmsClickableScrolledSelectionList.java +net/minecraft/client/shader/ShaderManager.java +net/minecraft/entity/ai/EntityAIWander.java +net/minecraft/server/management/UserListBans.java +net/minecraft/server/integrated/IntegratedServerCommandManager.java +net/minecraft/world/gen/structure/ComponentScatteredFeaturePieces.java +net/minecraft/command/CommandSpreadPlayers.java +net/minecraft/world/gen/structure/StructureNetherBridgePieces.java +net/minecraft/command/CommandWeather.java +net/minecraft/world/gen/feature/WorldGenTallGrass.java +net/minecraft/realms/RealmsMth.java +net/minecraft/world/gen/structure/MapGenStronghold.java +net/minecraft/realms/RealmsConnect.java +net/minecraft/client/stream/BroadcastController.java +net/minecraft/world/gen/feature/WorldGenSand.java +net/minecraft/entity/ai/EntityAIOcelotSit.java +net/minecraft/client/stream/IngestServerTester.java +net/minecraft/command/server/CommandDeOp.java +net/minecraft/world/gen/structure/MapGenStructureData.java +net/minecraft/client/renderer/ThreadDownloadImageData.java +net/minecraft/util/ThreadSafeBoundList.java +net/minecraft/command/ServerCommandManager.java +net/minecraft/client/shader/ShaderLinkHelper.java +net/minecraft/world/gen/structure/MapGenVillage.java +net/minecraft/client/gui/ServerListEntryLanDetected.java +net/minecraft/world/biome/WorldChunkManager.java +net/minecraft/command/CommandBase.java +net/minecraft/world/gen/ChunkProviderEnd.java +net/minecraft/entity/ai/EntityAIAttackOnCollide.java +net/minecraft/entity/ai/EntityAIMoveTowardsRestriction.java +net/minecraft/command/server/CommandOp.java +net/minecraft/world/gen/structure/MapGenMineshaft.java +net/minecraft/world/chunk/storage/RegionFileCache.java +net/minecraft/world/gen/structure/StructureBoundingBox.java +net/minecraft/world/biome/WorldChunkManagerHell.java +net/minecraft/world/gen/feature/WorldGenVines.java +net/minecraft/command/server/CommandSaveOn.java +net/minecraft/client/gui/stream/GuiIngestServers.java +net/minecraft/entity/ai/EntityAIPanic.java +net/minecraft/world/gen/structure/StructureMineshaftStart.java +net/minecraft/client/network/NetHandlerHandshakeMemory.java +net/minecraft/server/MinecraftServer.java +net/minecraft/command/server/CommandBanIp.java +net/minecraft/world/gen/layer/IntCache.java +net/minecraft/stats/StatisticsFile.java +net/minecraft/world/gen/feature/WorldGenHellLava.java +net/minecraft/world/gen/layer/GenLayerFuzzyZoom.java +net/minecraft/world/gen/feature/WorldGenMelon.java +net/minecraft/world/demo/DemoWorldServer.java +net/minecraft/util/MessageSerializer2.java +net/minecraft/command/server/CommandEmote.java +net/minecraft/network/NettyEncryptingDecoder.java +net/minecraft/world/gen/feature/WorldGenCactus.java +net/minecraft/command/WrongUsageException.java +net/minecraft/command/CommandReplaceItem.java +net/minecraft/entity/ai/EntityAIDoorInteract.java +net/minecraft/server/integrated/IntegratedPlayerList.java +net/minecraft/entity/ai/EntityAITarget.java +net/minecraft/command/CommandTime.java +net/minecraft/client/gui/GuiFlatPresets.java +net/minecraft/client/renderer/WorldVertexBufferUploader.java +net/minecraft/world/gen/ChunkProviderServer.java +net/minecraft/entity/ai/EntityJumpHelper.java +net/minecraft/network/PingResponseHandler.java +net/minecraft/entity/ai/EntityAIMate.java +net/minecraft/entity/ai/EntityAIOwnerHurtByTarget.java +net/minecraft/world/chunk/storage/IChunkLoader.java +net/minecraft/command/CommandXP.java +net/minecraft/world/gen/ChunkProviderFlat.java +net/minecraft/world/gen/layer/GenLayerDeepOcean.java +net/minecraft/client/stream/NullStream.java +net/minecraft/entity/ai/EntityAIOwnerHurtTarget.java +net/minecraft/world/gen/feature/WorldGenMegaJungle.java +net/minecraft/entity/ai/EntityAIMoveTowardsTarget.java +net/minecraft/entity/ai/EntityAIFindEntityNearest.java +net/minecraft/world/storage/SaveHandler.java +net/minecraft/command/server/CommandTeleport.java +net/minecraft/world/pathfinder/NodeProcessor.java +net/minecraft/network/NetworkSystem.java +net/minecraft/pathfinding/PathPoint.java +net/minecraft/command/CommandNotFoundException.java +net/minecraft/client/shader/Shader.java +net/minecraft/world/gen/feature/WorldGenLiquids.java +net/minecraft/entity/EntityTrackerEntry.java +net/minecraft/command/server/CommandMessage.java +net/minecraft/entity/ai/EntityAIBeg.java +net/minecraft/command/CommandParticle.java +net/minecraft/world/gen/layer/GenLayerZoom.java +net/minecraft/client/resources/SkinManager.java +net/minecraft/server/network/NetHandlerLoginServer.java +net/minecraft/command/CommandPlaySound.java +net/minecraft/world/gen/feature/WorldGenSwamp.java +net/minecraft/world/biome/BiomeCache.java +net/minecraft/command/CommandCompare.java +net/minecraft/world/pathfinder/WalkNodeProcessor.java +net/minecraft/entity/ai/EntityAIVillagerInteract.java +net/minecraft/command/CommandStats.java +net/minecraft/entity/ai/EntityAIFollowGolem.java +net/minecraft/command/CommandKill.java +net/minecraft/world/gen/layer/GenLayerRiverInit.java +net/minecraft/entity/ai/EntitySenses.java +net/minecraft/entity/ai/EntityAIFollowOwner.java +net/minecraft/util/MessageDeserializer2.java +net/minecraft/world/gen/feature/WorldGenPumpkin.java +net/minecraft/command/CommandEnchant.java +net/minecraft/pathfinding/Path.java +net/minecraft/world/gen/feature/WorldGenShrub.java +net/minecraft/world/gen/ChunkProviderDebug.java +net/minecraft/util/HttpUtil.java +net/minecraft/world/gen/layer/GenLayerRemoveTooMuchOcean.java +net/minecraft/command/CommandClearInventory.java +net/minecraft/entity/ai/EntityAIRestrictSun.java +net/minecraft/world/gen/feature/WorldGenDeadBush.java +net/minecraft/realms/RealmsButton.java +net/minecraft/village/VillageDoorInfo.java +net/minecraft/client/gui/GuiScreenDemo.java +net/minecraft/client/renderer/vertex/VertexFormatElement.java +net/minecraft/command/server/CommandSetBlock.java +net/minecraft/entity/ai/EntityAITasks.java +net/minecraft/entity/ai/EntityAILookAtVillager.java +net/minecraft/world/gen/feature/WorldGenFlowers.java +net/minecraft/world/WorldManager.java +net/minecraft/client/gui/GuiScreenRealmsProxy.java +net/minecraft/command/CommandToggleDownfall.java +net/minecraft/entity/ai/EntityAIWatchClosest2.java +net/minecraft/world/pathfinder/SwimNodeProcessor.java +net/minecraft/client/renderer/texture/TextureAtlasSprite.java +net/minecraft/entity/ai/EntityAILeapAtTarget.java +net/minecraft/entity/ai/EntityAIMoveIndoors.java +net/minecraft/entity/ai/EntityAIDefendVillage.java +net/minecraft/entity/ai/EntityAIWatchClosest.java +net/minecraft/util/JsonUtils.java +net/minecraft/client/shader/ShaderGroup.java +net/minecraft/server/management/ItemInWorldManager.java +net/minecraft/server/management/UserListWhitelist.java +net/minecraft/entity/ai/EntityAIMoveToBlock.java +net/minecraft/client/shader/ShaderLoader.java +net/minecraft/realms/RealmsEditBox.java +net/minecraft/entity/player/EntityPlayerMP.java +net/minecraft/server/management/UserListEntry.java +net/minecraft/command/CommandSetSpawnpoint.java +net/minecraft/network/NettyEncryptionTranslator.java +net/minecraft/entity/ai/EntityAIOpenDoor.java +net/minecraft/command/CommandDifficulty.java +net/minecraft/world/gen/feature/WorldGenHugeTrees.java +net/minecraft/world/gen/feature/WorldGenSavannaTree.java +net/minecraft/client/audio/SoundManager.java +net/minecraft/world/gen/structure/StructureComponent.java +net/minecraft/command/CommandFill.java +net/minecraft/world/biome/BiomeEndDecorator.java +net/minecraft/command/ICommand.java +net/minecraft/command/server/CommandPardonPlayer.java +net/minecraft/entity/ai/EntityAIFindEntityNearestPlayer.java +net/minecraft/realms/RealmsDefaultVertexFormat.java +net/minecraft/command/CommandServerKick.java +net/minecraft/world/gen/feature/WorldGenSpikes.java +net/minecraft/pathfinding/PathNavigateClimber.java +net/minecraft/world/gen/layer/GenLayerBiome.java +net/minecraft/realms/RealmsBridge.java +net/minecraft/client/resources/FileResourcePack.java +net/minecraft/world/gen/MapGenRavine.java +net/minecraft/server/management/UserListOpsEntry.java +net/minecraft/entity/ai/EntityAISwimming.java +net/minecraft/util/MessageSerializer.java +net/minecraft/client/renderer/OpenGlHelper.java +net/minecraft/server/management/PlayerManager.java +net/minecraft/client/gui/GuiCreateFlatWorld.java +net/minecraft/world/gen/structure/StructureOceanMonument.java +net/minecraft/command/CommandHandler.java +net/minecraft/server/management/PlayerProfileCache.java +net/minecraft/world/demo/DemoWorldManager.java +net/minecraft/entity/ai/EntityAIControlledByPlayer.java +net/minecraft/client/gui/stream/GuiStreamUnavailable.java +net/minecraft/command/CommandClone.java +net/minecraft/world/chunk/storage/AnvilChunkLoader.java +net/minecraft/client/multiplayer/ThreadLanServerPing.java +net/minecraft/command/server/CommandListPlayers.java +net/minecraft/command/server/CommandSummon.java +net/minecraft/entity/ai/EntityAILookIdle.java +net/minecraft/command/server/CommandMessageRaw.java +net/minecraft/world/chunk/storage/RegionFile.java +net/minecraft/network/NettyEncryptingEncoder.java +net/minecraft/util/EnumTypeAdapterFactory.java +net/minecraft/client/gui/stream/GuiStreamOptions.java +net/minecraft/scoreboard/ServerScoreboard.java +net/minecraft/command/ICommandManager.java +net/minecraft/village/VillageCollection.java +net/minecraft/client/network/NetHandlerLoginClient.java +net/minecraft/command/CommandDefaultGameMode.java +net/minecraft/world/gen/layer/GenLayerAddIsland.java +net/minecraft/world/gen/layer/GenLayerBiomeEdge.java +net/minecraft/world/gen/structure/MapGenNetherBridge.java +net/minecraft/world/gen/layer/GenLayerEdge.java +net/minecraft/server/management/ServerConfigurationManager.java +net/minecraft/realms/RealmsScreen.java +net/minecraft/client/stream/ChatController.java +net/minecraft/world/gen/structure/StructureVillagePieces.java +net/minecraft/world/gen/layer/GenLayerAddMushroomIsland.java +net/minecraft/world/gen/feature/WorldGenIceSpike.java +net/minecraft/world/gen/feature/WorldGenLakes.java +net/minecraft/command/server/CommandWhitelist.java +net/minecraft/client/gui/GuiShareToLan.java +net/minecraft/world/gen/ChunkProviderGenerate.java +net/minecraft/world/gen/feature/WorldGenDoublePlant.java +net/minecraft/command/NumberInvalidException.java +net/minecraft/profiler/IPlayerUsage.java +net/minecraft/world/gen/layer/GenLayer.java +net/minecraft/world/gen/layer/GenLayerRiverMix.java +net/minecraft/world/gen/GeneratorBushFeature.java +net/minecraft/realms/Tezzelator.java +net/minecraft/world/gen/layer/GenLayerRareBiome.java +net/minecraft/client/gui/GuiRenameWorld.java +net/minecraft/world/gen/feature/WorldGenForest.java +net/minecraft/command/server/CommandSaveOff.java +net/minecraft/pathfinding/PathEntity.java +net/minecraft/command/CommandSetPlayerTimeout.java +net/minecraft/realms/RealmsLevelSummary.java +net/minecraft/realms/RealmsServerAddress.java +net/minecraft/world/gen/feature/WorldGenBigTree.java +net/minecraft/world/gen/feature/WorldGenerator.java +net/minecraft/pathfinding/PathNavigate.java +net/minecraft/realms/RealmsAnvilLevelStorageSource.java +net/minecraft/realms/RealmsSliderButton.java +net/minecraft/entity/ai/EntityAIBreakDoor.java +net/minecraft/client/gui/GuiCustomizeWorldScreen.java +net/minecraft/world/storage/ThreadedFileIOBase.java +net/minecraft/client/renderer/vertex/VertexBuffer.java +net/minecraft/world/storage/SaveFormatComparator.java +net/minecraft/world/WorldServer.java +net/minecraft/network/rcon/RConConsoleSource.java +net/minecraft/world/chunk/storage/AnvilSaveHandler.java +net/minecraft/world/gen/feature/WorldGenFire.java +net/minecraft/entity/ai/EntityAIFleeSun.java +net/minecraft/world/gen/feature/WorldGenAbstractTree.java +net/minecraft/world/gen/feature/WorldGenGlowStone2.java +net/minecraft/world/gen/feature/WorldGenTrees.java +net/minecraft/realms/RealmsVertexFormatElement.java +net/minecraft/command/server/CommandTestFor.java +net/minecraft/network/NettyCompressionEncoder.java +net/minecraft/client/shader/Framebuffer.java +net/minecraft/client/gui/GuiSnooper.java +net/minecraft/world/gen/feature/WorldGenTaiga2.java +net/minecraft/world/gen/feature/WorldGenClay.java +net/minecraft/client/renderer/VboRenderList.java +net/minecraft/world/gen/layer/GenLayerShore.java +net/minecraft/entity/ai/RandomPositionGenerator.java +net/minecraft/command/CommandShowSeed.java +net/minecraft/server/network/NetHandlerHandshakeTCP.java +net/minecraft/world/gen/structure/MapGenStructure.java +net/minecraft/world/gen/layer/GenLayerAddSnow.java +net/minecraft/server/integrated/IntegratedServer.java +net/minecraft/client/renderer/WorldRenderer.java +net/minecraft/command/CommandEntityData.java +net/minecraft/client/stream/TwitchStream.java +net/minecraft/client/shader/ShaderDefault.java +net/minecraft/world/gen/ChunkProviderSettings.java +net/minecraft/command/server/CommandAchievement.java +net/minecraft/world/SpawnerAnimals.java +net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java +net/minecraft/world/gen/structure/MapGenStructureIO.java +net/minecraft/command/CommandGameMode.java +net/minecraft/server/management/BanEntry.java +net/minecraft/world/gen/feature/WorldGenBigMushroom.java +net/minecraft/client/gui/GuiSlotRealmsProxy.java +net/minecraft/command/server/CommandScoreboard.java +net/minecraft/entity/ai/EntityAIPlay.java +net/minecraft/command/server/CommandPardonIp.java +net/minecraft/client/stream/MetadataAchievement.java +net/minecraft/server/management/BanList.java +net/minecraft/entity/ai/EntityAINearestAttackableTarget.java +net/minecraft/server/management/IPBanEntry.java +net/minecraft/client/gui/GuiStreamIndicator.java +net/minecraft/util/CryptManager.java +net/minecraft/entity/ai/EntityAITempt.java +net/minecraft/server/management/UserListWhitelistEntry.java +net/minecraft/world/gen/layer/GenLayerIsland.java +net/minecraft/world/gen/structure/StructureStrongholdPieces.java +net/minecraft/command/server/CommandBroadcast.java +net/minecraft/command/CommandHelp.java +net/minecraft/command/server/CommandTestForBlock.java +net/minecraft/server/management/UserListOps.java +net/minecraft/world/WorldServerMulti.java +net/minecraft/world/gen/structure/StructureMineshaftPieces.java +net/minecraft/world/gen/feature/WorldGenCanopyTree.java +net/minecraft/client/gui/GuiSimpleScrolledSelectionListProxy.java +net/minecraft/world/chunk/storage/ChunkLoader.java +net/minecraft/client/stream/MetadataPlayerDeath.java +net/minecraft/entity/ai/EntityAIAvoidEntity.java +net/minecraft/world/gen/feature/WorldGenBlockBlob.java +net/minecraft/entity/ai/EntityAISit.java +net/minecraft/command/server/CommandBanPlayer.java +net/minecraft/client/shader/ShaderUniform.java +net/minecraft/world/storage/ISaveFormat.java +net/minecraft/realms/Realms.java +net/minecraft/world/chunk/storage/AnvilSaveConverter.java +net/minecraft/client/gui/GuiButtonRealmsProxy.java +net/minecraft/pathfinding/PathNavigateSwimmer.java +net/minecraft/client/network/OldServerPinger.java +net/minecraft/world/gen/ChunkProviderHell.java +net/minecraft/world/gen/feature/WorldGenMinable.java +net/minecraft/client/gui/GuiClickableScrolledSelectionListProxy.java +net/minecraft/command/CommandResultStats.java +net/minecraft/entity/ai/EntityAIRestrictOpenDoor.java +net/minecraft/entity/ai/EntityLookHelper.java +net/minecraft/server/management/UserListBansEntry.java +net/minecraft/client/renderer/vertex/VertexFormat.java +net/minecraft/client/gui/stream/GuiTwitchUserMode.java +net/minecraft/entity/ai/EntityAIFollowParent.java +net/minecraft/realms/RealmsScrolledSelectionList.java +net/minecraft/command/server/CommandStop.java +net/minecraft/realms/RealmsSharedConstants.java +net/minecraft/entity/ai/EntityAIArrowAttack.java +net/minecraft/world/gen/layer/GenLayerSmooth.java +net/minecraft/command/server/CommandSetDefaultSpawnpoint.java +net/minecraft/world/gen/structure/MapGenScatteredFeature.java +net/minecraft/entity/ai/EntityAIHurtByTarget.java +net/minecraft/world/gen/structure/StructureStart.java +net/minecraft/command/CommandEffect.java +net/minecraft/world/gen/feature/WorldGeneratorBonusChest.java +net/minecraft/world/Teleporter.java +net/minecraft/world/gen/layer/GenLayerRiver.java +net/minecraft/client/gui/GuiCreateWorld.java +net/minecraft/server/management/UserList.java +net/minecraft/realms/RealmsSimpleScrolledSelectionList.java +net/minecraft/entity/ai/EntityAIEatGrass.java +net/minecraft/world/gen/structure/StructureOceanMonumentPieces.java +net/minecraft/command/CommandTitle.java +net/minecraft/world/gen/MapGenCavesHell.java +net/minecraft/client/resources/FolderResourcePack.java +net/minecraft/command/SyntaxErrorException.java +net/minecraft/entity/EntityTracker.java +net/minecraft/client/stream/MetadataCombat.java +net/minecraft/client/network/LanServerDetector.java +net/minecraft/command/CommandBlockData.java +net/minecraft/util/MessageDeserializer.java +net/minecraft/pathfinding/PathNavigateGround.java +net/minecraft/world/gen/feature/WorldGenMegaPineTree.java +net/minecraft/world/biome/BiomeDecorator.java +net/minecraft/realms/RealmsBufferBuilder.java +net/minecraft/entity/ai/EntityAILookAtTradePlayer.java diff --git a/patches/minecraft/net/minecraft/block/Block.edit.java b/patches/minecraft/net/minecraft/block/Block.edit.java new file mode 100644 index 0000000..d807627 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/Block.edit.java @@ -0,0 +1,96 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 135 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 14 @ 144 : 146 + +> CHANGE 357 : 358 @ 489 : 490 + +~ public void randomTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 361 : 362 @ 493 : 494 + +~ public void updateTick(World var1, BlockPos var2, IBlockState var3, EaglercraftRandom var4) { + +> CHANGE 364 : 365 @ 496 : 497 + +~ public void randomDisplayTick(World worldIn, BlockPos pos, IBlockState state, EaglercraftRandom rand) { + +> CHANGE 383 : 384 @ 515 : 516 + +~ public int quantityDropped(EaglercraftRandom random) { + +> CHANGE 387 : 388 @ 519 : 520 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> DELETE 403 @ 535 : 537 + +> DELETE 404 @ 538 : 548 + +> CHANGE 407 : 408 @ 551 : 561 + +~ + +> DELETE 411 @ 564 : 572 + +> CHANGE 663 : 664 @ 824 : 825 + +~ public int quantityDroppedWithBonus(int fortune, EaglercraftRandom random) { + +> INSERT 800 : 801 @ 961 + ++ bootstrapStates(); + +> INSERT 1269 : 1309 @ 1429 + ++ public static void bootstrapStates() { ++ BlockBed.bootstrapStates(); ++ BlockDirt.bootstrapStates(); ++ BlockDoor.bootstrapStates(); ++ BlockDoublePlant.bootstrapStates(); ++ BlockFlowerPot.bootstrapStates(); ++ BlockHugeMushroom.bootstrapStates(); ++ BlockLever.bootstrapStates(); ++ BlockLog.bootstrapStates(); ++ BlockNewLeaf.bootstrapStates(); ++ BlockNewLog.bootstrapStates(); ++ BlockOldLeaf.bootstrapStates(); ++ BlockOldLog.bootstrapStates(); ++ BlockPistonExtension.bootstrapStates(); ++ BlockPistonMoving.bootstrapStates(); ++ BlockPlanks.bootstrapStates(); ++ BlockPrismarine.bootstrapStates(); ++ BlockQuartz.bootstrapStates(); ++ BlockRail.bootstrapStates(); ++ BlockRailDetector.bootstrapStates(); ++ BlockRailPowered.bootstrapStates(); ++ BlockRedSandstone.bootstrapStates(); ++ BlockRedstoneComparator.bootstrapStates(); ++ BlockRedstoneWire.bootstrapStates(); ++ BlockSand.bootstrapStates(); ++ BlockSandStone.bootstrapStates(); ++ BlockSapling.bootstrapStates(); ++ BlockSilverfish.bootstrapStates(); ++ BlockSlab.bootstrapStates(); ++ BlockStairs.bootstrapStates(); ++ BlockStone.bootstrapStates(); ++ BlockStoneBrick.bootstrapStates(); ++ BlockStoneSlab.bootstrapStates(); ++ BlockStoneSlabNew.bootstrapStates(); ++ BlockTallGrass.bootstrapStates(); ++ BlockTrapDoor.bootstrapStates(); ++ BlockWall.bootstrapStates(); ++ BlockWoodSlab.bootstrapStates(); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockAir.edit.java b/patches/minecraft/net/minecraft/block/BlockAir.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockAir.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockAnvil.edit.java b/patches/minecraft/net/minecraft/block/BlockAnvil.edit.java new file mode 100644 index 0000000..905467a --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockAnvil.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> DELETE 55 @ 55 : 64 + +> INSERT 96 : 101 @ 105 + ++ public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, ++ EnumFacing side, float hitX, float hitY, float hitZ) { ++ return true; ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBanner.edit.java b/patches/minecraft/net/minecraft/block/BlockBanner.edit.java new file mode 100644 index 0000000..b19e9a1 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBanner.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 68 : 69 @ 69 : 70 + +~ public Item getItemDropped(IBlockState state, EaglercraftRandom rand, int fortune) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBarrier.edit.java b/patches/minecraft/net/minecraft/block/BlockBarrier.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBarrier.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBasePressurePlate.edit.java b/patches/minecraft/net/minecraft/block/BlockBasePressurePlate.edit.java new file mode 100644 index 0000000..a96e28b --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBasePressurePlate.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 8 @ 9 : 10 + +> CHANGE 81 : 82 @ 83 : 84 + +~ public void randomTick(World worldIn, BlockPos pos, IBlockState state, EaglercraftRandom random) { + +> CHANGE 84 : 85 @ 86 : 92 + +~ public void updateTick(World worldIn, BlockPos pos, IBlockState state, EaglercraftRandom rand) { + +> DELETE 86 @ 93 : 94 + +> DELETE 88 @ 96 : 106 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBeacon.edit.java b/patches/minecraft/net/minecraft/block/BlockBeacon.edit.java new file mode 100644 index 0000000..8396096 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBeacon.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 8 @ 10 : 11 + +> DELETE 9 @ 12 : 13 + +> DELETE 14 @ 18 : 19 + +> DELETE 15 @ 20 : 22 + +> CHANGE 29 : 30 @ 36 : 47 + +~ return true; + +> DELETE 69 @ 86 : 115 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBed.edit.java b/patches/minecraft/net/minecraft/block/BlockBed.edit.java new file mode 100644 index 0000000..e84a973 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBed.edit.java @@ -0,0 +1,42 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 10 @ 11 : 12 + +> DELETE 14 @ 16 : 17 + +> DELETE 19 @ 22 : 23 + +> CHANGE 21 : 22 @ 25 : 27 + +~ public static PropertyEnum PART; + +> INSERT 31 : 35 @ 36 + ++ public static void bootstrapStates() { ++ PART = PropertyEnum.create("part", BlockBed.EnumPartType.class); ++ } ++ + +> CHANGE 37 : 38 @ 38 : 90 + +~ return true; + +> DELETE 40 @ 92 : 102 + +> DELETE 60 @ 122 : 125 + +> CHANGE 64 : 65 @ 129 : 130 + +~ public Item getItemDropped(IBlockState iblockstate, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBookshelf.edit.java b/patches/minecraft/net/minecraft/block/BlockBookshelf.edit.java new file mode 100644 index 0000000..186bed9 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBookshelf.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 16 : 17 @ 16 : 17 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 20 : 21 @ 20 : 21 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBreakable.edit.java b/patches/minecraft/net/minecraft/block/BlockBreakable.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBreakable.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBrewingStand.edit.java b/patches/minecraft/net/minecraft/block/BlockBrewingStand.edit.java new file mode 100644 index 0000000..2051c16 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBrewingStand.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 18 @ 18 : 19 + +> CHANGE 73 : 74 @ 74 : 85 + +~ return true; + +> CHANGE 87 : 88 @ 98 : 99 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom random) { + +> CHANGE 103 : 104 @ 114 : 115 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockBush.edit.java b/patches/minecraft/net/minecraft/block/BlockBush.edit.java new file mode 100644 index 0000000..9ea83c5 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockBush.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 45 : 46 @ 45 : 46 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockButton.edit.java b/patches/minecraft/net/minecraft/block/BlockButton.edit.java new file mode 100644 index 0000000..a25eee9 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockButton.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> DELETE 9 @ 12 : 13 + +> DELETE 11 @ 15 : 16 + +> DELETE 164 @ 169 : 189 + +> DELETE 171 @ 196 : 236 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockButtonStone.edit.java b/patches/minecraft/net/minecraft/block/BlockButtonStone.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockButtonStone.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockButtonWood.edit.java b/patches/minecraft/net/minecraft/block/BlockButtonWood.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockButtonWood.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCactus.edit.java b/patches/minecraft/net/minecraft/block/BlockCactus.edit.java new file mode 100644 index 0000000..cd32eb2 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCactus.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 29 : 30 @ 29 : 30 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCake.edit.java b/patches/minecraft/net/minecraft/block/BlockCake.edit.java new file mode 100644 index 0000000..6ac5c2b --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCake.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 103 : 104 @ 103 : 104 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 107 : 108 @ 107 : 108 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCarpet.edit.java b/patches/minecraft/net/minecraft/block/BlockCarpet.edit.java new file mode 100644 index 0000000..46d9776 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCarpet.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCarrot.edit.java b/patches/minecraft/net/minecraft/block/BlockCarrot.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCarrot.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCauldron.edit.java b/patches/minecraft/net/minecraft/block/BlockCauldron.edit.java new file mode 100644 index 0000000..731d368 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCauldron.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 12 @ 12 : 13 + +> DELETE 13 @ 14 : 15 + +> DELETE 15 @ 17 : 22 + +> DELETE 57 @ 64 : 74 + +> CHANGE 59 : 60 @ 76 : 164 + +~ return true; + +> CHANGE 77 : 78 @ 181 : 182 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockChest.edit.java b/patches/minecraft/net/minecraft/block/BlockChest.edit.java new file mode 100644 index 0000000..998bd2f --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockChest.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 17 @ 19 : 20 + +> CHANGE 131 : 132 @ 134 : 206 + +~ return state; + +> CHANGE 249 : 250 @ 323 : 338 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockClay.edit.java b/patches/minecraft/net/minecraft/block/BlockClay.edit.java new file mode 100644 index 0000000..f2c7ae4 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockClay.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 16 : 17 @ 16 : 17 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 20 : 21 @ 20 : 21 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCocoa.edit.java b/patches/minecraft/net/minecraft/block/BlockCocoa.edit.java new file mode 100644 index 0000000..e850a74 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCocoa.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 32 : 33 @ 35 : 36 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> CHANGE 150 : 151 @ 153 : 154 + +~ public boolean canUseBonemeal(World var1, EaglercraftRandom var2, BlockPos var3, IBlockState var4) { + +> CHANGE 154 : 155 @ 157 : 158 + +~ public void grow(World world, EaglercraftRandom var2, BlockPos blockpos, IBlockState iblockstate) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockColored.edit.java b/patches/minecraft/net/minecraft/block/BlockColored.edit.java new file mode 100644 index 0000000..46d9776 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockColored.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCommandBlock.edit.java b/patches/minecraft/net/minecraft/block/BlockCommandBlock.edit.java new file mode 100644 index 0000000..b68026d --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCommandBlock.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 32 : 33 @ 33 : 48 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom var4) { + +> DELETE 73 @ 88 : 92 + +> CHANGE 76 : 77 @ 95 : 96 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCompressedPowered.edit.java b/patches/minecraft/net/minecraft/block/BlockCompressedPowered.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCompressedPowered.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockContainer.edit.java b/patches/minecraft/net/minecraft/block/BlockContainer.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockContainer.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockCrops.edit.java b/patches/minecraft/net/minecraft/block/BlockCrops.edit.java new file mode 100644 index 0000000..56377ea --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockCrops.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 12 @ 15 : 16 + +> CHANGE 34 : 35 @ 38 : 39 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 116 : 117 @ 120 : 138 + +~ public Item getItemDropped(IBlockState iblockstate, EaglercraftRandom var2, int var3) { + +> CHANGE 128 : 129 @ 149 : 150 + +~ public boolean canUseBonemeal(World var1, EaglercraftRandom var2, BlockPos var3, IBlockState var4) { + +> CHANGE 132 : 133 @ 153 : 154 + +~ public void grow(World world, EaglercraftRandom var2, BlockPos blockpos, IBlockState iblockstate) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDaylightDetector.edit.java b/patches/minecraft/net/minecraft/block/BlockDaylightDetector.edit.java new file mode 100644 index 0000000..a65d6c5 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDaylightDetector.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 70 : 71 @ 70 : 86 + +~ return true; + +> CHANGE 76 : 77 @ 91 : 92 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDeadBush.edit.java b/patches/minecraft/net/minecraft/block/BlockDeadBush.edit.java new file mode 100644 index 0000000..cbca6fb --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDeadBush.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 7 @ 8 : 9 + +> DELETE 8 @ 10 : 11 + +> DELETE 9 @ 12 : 15 + +> CHANGE 32 : 33 @ 38 : 39 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> DELETE 36 @ 42 : 53 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDirectional.edit.java b/patches/minecraft/net/minecraft/block/BlockDirectional.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDirectional.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDirt.edit.java b/patches/minecraft/net/minecraft/block/BlockDirt.edit.java new file mode 100644 index 0000000..4908a9a --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDirt.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> CHANGE 21 : 22 @ 21 : 23 + +~ public static PropertyEnum VARIANT; + +> INSERT 31 : 35 @ 32 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockDirt.DirtType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDispenser.edit.java b/patches/minecraft/net/minecraft/block/BlockDispenser.edit.java new file mode 100644 index 0000000..829fb08 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDispenser.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 22 @ 25 : 26 + +> DELETE 24 @ 28 : 29 + +> CHANGE 34 : 35 @ 39 : 40 + +~ protected EaglercraftRandom rand = new EaglercraftRandom(); + +> DELETE 47 @ 52 : 81 + +> CHANGE 49 : 50 @ 83 : 98 + +~ return true; + +> DELETE 87 @ 135 : 142 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDoor.edit.java b/patches/minecraft/net/minecraft/block/BlockDoor.edit.java new file mode 100644 index 0000000..743ee41 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDoor.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 29 : 30 @ 29 : 31 + +~ public static PropertyEnum HINGE; + +> CHANGE 31 : 32 @ 32 : 34 + +~ public static PropertyEnum HALF; + +> INSERT 40 : 45 @ 42 + ++ public static void bootstrapStates() { ++ HINGE = PropertyEnum.create("hinge", BlockDoor.EnumHingePosition.class); ++ HALF = PropertyEnum.create("half", BlockDoor.EnumDoorHalf.class); ++ } ++ + +> CHANGE 180 : 181 @ 177 : 182 + +~ if (!flag1) { + +> CHANGE 196 : 197 @ 197 : 198 + +~ public Item getItemDropped(IBlockState iblockstate, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDoublePlant.edit.java b/patches/minecraft/net/minecraft/block/BlockDoublePlant.edit.java new file mode 100644 index 0000000..aacdd19 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDoublePlant.edit.java @@ -0,0 +1,46 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 17 @ 21 : 23 + +> CHANGE 25 : 27 @ 31 : 35 + +~ public static PropertyEnum VARIANT; +~ public static PropertyEnum HALF; + +> INSERT 40 : 45 @ 48 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockDoublePlant.EnumPlantType.class); ++ HALF = PropertyEnum.create("half", BlockDoublePlant.EnumBlockHalf.class); ++ } ++ + +> CHANGE 105 : 106 @ 108 : 109 + +~ public Item getItemDropped(IBlockState iblockstate, EaglercraftRandom random, int var3) { + +> DELETE 145 @ 148 : 158 + +> DELETE 155 @ 168 : 176 + +> DELETE 169 @ 190 : 206 + +> CHANGE 186 : 187 @ 223 : 224 + +~ public boolean canUseBonemeal(World var1, EaglercraftRandom var2, BlockPos var3, IBlockState var4) { + +> CHANGE 190 : 191 @ 227 : 228 + +~ public void grow(World world, EaglercraftRandom var2, BlockPos blockpos, IBlockState var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlab.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlab.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlabNew.edit.java b/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlabNew.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDoubleStoneSlabNew.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDoubleWoodSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockDoubleWoodSlab.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDoubleWoodSlab.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDragonEgg.edit.java b/patches/minecraft/net/minecraft/block/BlockDragonEgg.edit.java new file mode 100644 index 0000000..92baa02 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDragonEgg.edit.java @@ -0,0 +1,35 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 30 : 31 @ 31 : 32 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom var4) { + +> CHANGE 75 : 88 @ 76 : 94 + +~ for (int j = 0; j < 128; ++j) { +~ double d0 = worldIn.rand.nextDouble(); +~ float f = (worldIn.rand.nextFloat() - 0.5F) * 0.2F; +~ float f1 = (worldIn.rand.nextFloat() - 0.5F) * 0.2F; +~ float f2 = (worldIn.rand.nextFloat() - 0.5F) * 0.2F; +~ double d1 = (double) blockpos.getX() + (double) (pos.getX() - blockpos.getX()) * d0 +~ + (worldIn.rand.nextDouble() - 0.5D) * 1.0D + 0.5D; +~ double d2 = (double) blockpos.getY() + (double) (pos.getY() - blockpos.getY()) * d0 +~ + worldIn.rand.nextDouble() * 1.0D - 0.5D; +~ double d3 = (double) blockpos.getZ() + (double) (pos.getZ() - blockpos.getZ()) * d0 +~ + (worldIn.rand.nextDouble() - 0.5D) * 1.0D + 0.5D; +~ worldIn.spawnParticle(EnumParticleTypes.PORTAL, d1, d2, d3, (double) f, (double) f1, +~ (double) f2, new int[0]); + +> DELETE 89 @ 95 : 96 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDropper.edit.java b/patches/minecraft/net/minecraft/block/BlockDropper.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDropper.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockDynamicLiquid.edit.java b/patches/minecraft/net/minecraft/block/BlockDynamicLiquid.edit.java new file mode 100644 index 0000000..5731989 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockDynamicLiquid.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 5 : 6 @ 5 : 8 + +~ + +> CHANGE 26 : 27 @ 28 : 29 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 102 : 103 @ 104 : 105 + +~ Set set = this.getPossibleFlowDirections(world, blockpos); + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockEnchantmentTable.edit.java b/patches/minecraft/net/minecraft/block/BlockEnchantmentTable.edit.java new file mode 100644 index 0000000..7e733a7 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockEnchantmentTable.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 31 : 32 @ 31 : 32 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 75 : 76 @ 75 : 85 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockEndPortal.edit.java b/patches/minecraft/net/minecraft/block/BlockEndPortal.edit.java new file mode 100644 index 0000000..223e804 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockEndPortal.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 50 : 51 @ 50 : 51 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 54 : 55 @ 54 : 62 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockEndPortalFrame.edit.java b/patches/minecraft/net/minecraft/block/BlockEndPortalFrame.edit.java new file mode 100644 index 0000000..e3f91dc --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockEndPortalFrame.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 50 : 51 @ 50 : 51 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockEnderChest.edit.java b/patches/minecraft/net/minecraft/block/BlockEnderChest.edit.java new file mode 100644 index 0000000..952e260 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockEnderChest.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 13 @ 13 : 14 + +> DELETE 15 @ 16 : 17 + +> CHANGE 44 : 45 @ 46 : 47 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 48 : 49 @ 50 : 51 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 69 : 70 @ 71 : 87 + +~ return true; + +> CHANGE 76 : 77 @ 93 : 94 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockEventData.edit.java b/patches/minecraft/net/minecraft/block/BlockEventData.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockEventData.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockFalling.edit.java b/patches/minecraft/net/minecraft/block/BlockFalling.edit.java new file mode 100644 index 0000000..6bd53a9 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockFalling.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 30 @ 32 : 66 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockFarmland.edit.java b/patches/minecraft/net/minecraft/block/BlockFarmland.edit.java new file mode 100644 index 0000000..3643a31 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockFarmland.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 11 @ 14 : 15 + +> CHANGE 43 : 44 @ 47 : 48 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> DELETE 59 @ 63 : 71 + +> CHANGE 102 : 103 @ 114 : 115 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom random, int i) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockFence.edit.java b/patches/minecraft/net/minecraft/block/BlockFence.edit.java new file mode 100644 index 0000000..920665d --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockFence.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 5 + +~ + +> DELETE 14 @ 15 : 16 + +> CHANGE 144 : 145 @ 146 : 147 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockFenceGate.edit.java b/patches/minecraft/net/minecraft/block/BlockFenceGate.edit.java new file mode 100644 index 0000000..b50e7e9 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockFenceGate.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> DELETE 114 @ 117 : 139 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockFire.edit.java b/patches/minecraft/net/minecraft/block/BlockFire.edit.java new file mode 100644 index 0000000..0952594 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockFire.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 7 @ 4 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Maps; +~ + +> CHANGE 125 : 126 @ 125 : 126 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 133 : 134 @ 133 : 134 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 240 : 241 @ 240 : 241 + +~ private void catchOnFire(World worldIn, BlockPos pos, int chance, EaglercraftRandom random, int age) { + +> CHANGE 318 : 319 @ 318 : 319 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockFlower.edit.java b/patches/minecraft/net/minecraft/block/BlockFlower.edit.java new file mode 100644 index 0000000..3c6e51c --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockFlower.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import java.util.Collection; ++ import java.util.List; ++ + +> CHANGE 8 : 9 @ 5 : 9 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockFlowerPot.edit.java b/patches/minecraft/net/minecraft/block/BlockFlowerPot.edit.java new file mode 100644 index 0000000..ca31383 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockFlowerPot.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 29 : 30 @ 33 : 35 + +~ public static PropertyEnum CONTENTS; + +> INSERT 38 : 42 @ 43 + ++ public static void bootstrapStates() { ++ CONTENTS = PropertyEnum.create("contents", BlockFlowerPot.EnumFlowerType.class); ++ } ++ + +> CHANGE 166 : 167 @ 167 : 168 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockFurnace.edit.java b/patches/minecraft/net/minecraft/block/BlockFurnace.edit.java new file mode 100644 index 0000000..694bbd4 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockFurnace.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 16 @ 17 : 18 + +> CHANGE 34 : 35 @ 36 : 37 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 38 : 39 @ 40 : 66 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 69 : 70 @ 96 : 107 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockGlass.edit.java b/patches/minecraft/net/minecraft/block/BlockGlass.edit.java new file mode 100644 index 0000000..137bfa9 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockGlass.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 14 : 15 @ 14 : 15 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockGlowstone.edit.java b/patches/minecraft/net/minecraft/block/BlockGlowstone.edit.java new file mode 100644 index 0000000..2d571f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockGlowstone.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 18 : 19 @ 18 : 19 + +~ public int quantityDroppedWithBonus(int i, EaglercraftRandom random) { + +> CHANGE 22 : 23 @ 22 : 23 + +~ public int quantityDropped(EaglercraftRandom random) { + +> CHANGE 26 : 27 @ 26 : 27 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockGrass.edit.java b/patches/minecraft/net/minecraft/block/BlockGrass.edit.java new file mode 100644 index 0000000..e35e549 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockGrass.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 46 : 47 @ 50 : 75 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom random, int i) { + +> CHANGE 55 : 56 @ 83 : 84 + +~ public boolean canUseBonemeal(World var1, EaglercraftRandom var2, BlockPos var3, IBlockState var4) { + +> CHANGE 59 : 60 @ 87 : 88 + +~ public void grow(World world, EaglercraftRandom random, BlockPos blockpos, IBlockState var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockGravel.edit.java b/patches/minecraft/net/minecraft/block/BlockGravel.edit.java new file mode 100644 index 0000000..2d60240 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockGravel.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 10 : 11 @ 10 : 11 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom random, int i) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockHalfStoneSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockHalfStoneSlab.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockHalfStoneSlab.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockHalfStoneSlabNew.edit.java b/patches/minecraft/net/minecraft/block/BlockHalfStoneSlabNew.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockHalfStoneSlabNew.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockHalfWoodSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockHalfWoodSlab.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockHalfWoodSlab.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockHardenedClay.edit.java b/patches/minecraft/net/minecraft/block/BlockHardenedClay.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockHardenedClay.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockHay.edit.java b/patches/minecraft/net/minecraft/block/BlockHay.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockHay.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockHopper.edit.java b/patches/minecraft/net/minecraft/block/BlockHopper.edit.java new file mode 100644 index 0000000..b18abd9 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockHopper.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 6 + +~ +~ import com.google.common.base.Predicate; +~ + +> DELETE 20 @ 20 : 21 + +> CHANGE 97 : 98 @ 98 : 109 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockHugeMushroom.edit.java b/patches/minecraft/net/minecraft/block/BlockHugeMushroom.edit.java new file mode 100644 index 0000000..c203d83 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockHugeMushroom.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 18 : 19 @ 18 : 20 + +~ public static PropertyEnum VARIANT; + +> CHANGE 28 : 33 @ 29 : 30 + +~ public static void bootstrapStates() { +~ VARIANT = PropertyEnum.create("variant", BlockHugeMushroom.EnumType.class); +~ } +~ +~ public int quantityDropped(EaglercraftRandom random) { + +> CHANGE 49 : 50 @ 46 : 47 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockIce.edit.java b/patches/minecraft/net/minecraft/block/BlockIce.edit.java new file mode 100644 index 0000000..a30fb18 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockIce.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 55 : 56 @ 56 : 57 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 59 : 60 @ 60 : 61 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockJukebox.edit.java b/patches/minecraft/net/minecraft/block/BlockJukebox.edit.java new file mode 100644 index 0000000..a18a5c3 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockJukebox.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 9 @ 10 : 11 + +> DELETE 31 @ 33 : 34 + +> DELETE 40 @ 43 : 52 + +> DELETE 41 @ 53 : 75 + +> DELETE 43 @ 77 : 88 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockLadder.edit.java b/patches/minecraft/net/minecraft/block/BlockLadder.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockLadder.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockLeaves.edit.java b/patches/minecraft/net/minecraft/block/BlockLeaves.edit.java new file mode 100644 index 0000000..2e56bae --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockLeaves.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 10 @ 12 : 13 + +> CHANGE 70 : 71 @ 73 : 158 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom random) { + +> CHANGE 86 : 87 @ 173 : 174 + +~ public int quantityDropped(EaglercraftRandom random) { + +> CHANGE 90 : 91 @ 177 : 178 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> DELETE 94 @ 181 : 209 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockLeavesBase.edit.java b/patches/minecraft/net/minecraft/block/BlockLeavesBase.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockLeavesBase.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockLever.edit.java b/patches/minecraft/net/minecraft/block/BlockLever.edit.java new file mode 100644 index 0000000..fcb9667 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockLever.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 19 : 20 @ 21 : 23 + +~ public static PropertyEnum FACING; + +> INSERT 29 : 33 @ 32 + ++ public static void bootstrapStates() { ++ FACING = PropertyEnum.create("facing", BlockLever.EnumOrientation.class); ++ } ++ + +> CHANGE 154 : 155 @ 153 : 166 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockLilyPad.edit.java b/patches/minecraft/net/minecraft/block/BlockLilyPad.edit.java new file mode 100644 index 0000000..b7576f5 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockLilyPad.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 6 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockLiquid.edit.java b/patches/minecraft/net/minecraft/block/BlockLiquid.edit.java new file mode 100644 index 0000000..5d55729 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockLiquid.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 108 : 109 @ 110 : 111 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 112 : 113 @ 114 : 115 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 177 : 178 @ 179 : 180 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockLog.edit.java b/patches/minecraft/net/minecraft/block/BlockLog.edit.java new file mode 100644 index 0000000..0d793cb --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockLog.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 13 : 14 @ 15 : 17 + +~ public static PropertyEnum LOG_AXIS = null; + +> INSERT 22 : 26 @ 25 + ++ public static void bootstrapStates() { ++ LOG_AXIS = PropertyEnum.create("axis", BlockLog.EnumAxis.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockMelon.edit.java b/patches/minecraft/net/minecraft/block/BlockMelon.edit.java new file mode 100644 index 0000000..bb36a53 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockMelon.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 17 : 18 @ 17 : 18 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 21 : 22 @ 21 : 22 + +~ public int quantityDropped(EaglercraftRandom random) { + +> CHANGE 25 : 26 @ 25 : 26 + +~ public int quantityDroppedWithBonus(int i, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockMobSpawner.edit.java b/patches/minecraft/net/minecraft/block/BlockMobSpawner.edit.java new file mode 100644 index 0000000..9fecf57 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockMobSpawner.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 22 : 23 @ 22 : 23 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 26 : 27 @ 26 : 27 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockMushroom.edit.java b/patches/minecraft/net/minecraft/block/BlockMushroom.edit.java new file mode 100644 index 0000000..776b725 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockMushroom.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 8 @ 11 : 12 + +> CHANGE 16 : 17 @ 20 : 21 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom random) { + +> DELETE 69 @ 73 : 90 + +> CHANGE 73 : 74 @ 94 : 95 + +~ public boolean canUseBonemeal(World var1, EaglercraftRandom random, BlockPos var3, IBlockState var4) { + +> DELETE 76 @ 97 : 101 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockMycelium.edit.java b/patches/minecraft/net/minecraft/block/BlockMycelium.edit.java new file mode 100644 index 0000000..0edf3d6 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockMycelium.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 33 : 34 @ 34 : 60 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 43 : 44 @ 69 : 70 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom random, int i) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockNetherBrick.edit.java b/patches/minecraft/net/minecraft/block/BlockNetherBrick.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockNetherBrick.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockNetherWart.edit.java b/patches/minecraft/net/minecraft/block/BlockNetherWart.edit.java new file mode 100644 index 0000000..50c070b --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockNetherWart.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 14 @ 15 : 16 + +> CHANGE 37 : 38 @ 39 : 40 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 47 : 48 @ 49 : 67 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 51 : 52 @ 70 : 71 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockNetherrack.edit.java b/patches/minecraft/net/minecraft/block/BlockNetherrack.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockNetherrack.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockNewLeaf.edit.java b/patches/minecraft/net/minecraft/block/BlockNewLeaf.edit.java new file mode 100644 index 0000000..a11428f --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockNewLeaf.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 7 + +~ +~ import com.google.common.base.Predicate; +~ + +> DELETE 11 @ 12 : 13 + +> DELETE 14 @ 16 : 18 + +> CHANGE 18 : 19 @ 22 : 28 + +~ public static PropertyEnum VARIANT; + +> INSERT 25 : 33 @ 34 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockPlanks.EnumType.class, new Predicate() { ++ public boolean apply(BlockPlanks.EnumType blockplanks$enumtype) { ++ return blockplanks$enumtype.getMetadata() >= 4; ++ } ++ }); ++ } ++ + +> DELETE 87 @ 88 : 99 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockNewLog.edit.java b/patches/minecraft/net/minecraft/block/BlockNewLog.edit.java new file mode 100644 index 0000000..b2c937f --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockNewLog.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 6 + +~ +~ import com.google.common.base.Predicate; +~ + +> CHANGE 16 : 17 @ 16 : 22 + +~ public static PropertyEnum VARIANT; + +> INSERT 23 : 31 @ 28 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockPlanks.EnumType.class, new Predicate() { ++ public boolean apply(BlockPlanks.EnumType blockplanks$enumtype) { ++ return blockplanks$enumtype.getMetadata() >= 4; ++ } ++ }); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockNote.edit.java b/patches/minecraft/net/minecraft/block/BlockNote.edit.java new file mode 100644 index 0000000..5078aca --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockNote.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 6 + +~ +~ import com.google.common.collect.Lists; +~ + +> DELETE 10 @ 10 : 11 + +> CHANGE 44 : 45 @ 45 : 58 + +~ return true; + +> DELETE 47 @ 60 : 71 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockObsidian.edit.java b/patches/minecraft/net/minecraft/block/BlockObsidian.edit.java new file mode 100644 index 0000000..fa045e0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockObsidian.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 17 : 18 @ 17 : 18 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockOldLeaf.edit.java b/patches/minecraft/net/minecraft/block/BlockOldLeaf.edit.java new file mode 100644 index 0000000..017e438 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockOldLeaf.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 7 + +~ +~ import com.google.common.base.Predicate; +~ + +> DELETE 11 @ 12 : 13 + +> DELETE 14 @ 16 : 18 + +> CHANGE 20 : 21 @ 24 : 30 + +~ public static PropertyEnum VARIANT; + +> INSERT 27 : 35 @ 36 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockPlanks.EnumType.class, new Predicate() { ++ public boolean apply(BlockPlanks.EnumType blockplanks$enumtype) { ++ return blockplanks$enumtype.getMetadata() < 4; ++ } ++ }); ++ } ++ + +> DELETE 118 @ 119 : 130 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockOldLog.edit.java b/patches/minecraft/net/minecraft/block/BlockOldLog.edit.java new file mode 100644 index 0000000..273fff3 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockOldLog.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 6 + +~ +~ import com.google.common.base.Predicate; +~ + +> CHANGE 16 : 17 @ 16 : 22 + +~ public static PropertyEnum VARIANT; + +> INSERT 23 : 31 @ 28 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockPlanks.EnumType.class, new Predicate() { ++ public boolean apply(BlockPlanks.EnumType blockplanks$enumtype) { ++ return blockplanks$enumtype.getMetadata() < 4; ++ } ++ }); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockOre.edit.java b/patches/minecraft/net/minecraft/block/BlockOre.edit.java new file mode 100644 index 0000000..cdcef53 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockOre.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 26 : 27 @ 26 : 27 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 34 : 35 @ 34 : 35 + +~ public int quantityDropped(EaglercraftRandom random) { + +> CHANGE 38 : 39 @ 38 : 39 + +~ public int quantityDroppedWithBonus(int i, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPackedIce.edit.java b/patches/minecraft/net/minecraft/block/BlockPackedIce.edit.java new file mode 100644 index 0000000..137bfa9 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPackedIce.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 14 : 15 @ 14 : 15 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPane.edit.java b/patches/minecraft/net/minecraft/block/BlockPane.edit.java new file mode 100644 index 0000000..68becac --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPane.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 52 : 53 @ 52 : 53 + +~ public Item getItemDropped(IBlockState iblockstate, EaglercraftRandom random, int i) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPistonBase.edit.java b/patches/minecraft/net/minecraft/block/BlockPistonBase.edit.java new file mode 100644 index 0000000..e2f61d6 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPistonBase.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 7 + +~ + +> DELETE 48 @ 51 : 55 + +> DELETE 50 @ 57 : 71 + +> DELETE 95 @ 116 : 128 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPistonExtension.edit.java b/patches/minecraft/net/minecraft/block/BlockPistonExtension.edit.java new file mode 100644 index 0000000..662962c --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPistonExtension.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 25 : 26 @ 26 : 28 + +~ public static PropertyEnum TYPE; + +> INSERT 37 : 42 @ 39 + ++ public static void bootstrapStates() { ++ TYPE = PropertyEnum.create("type", ++ BlockPistonExtension.EnumPistonType.class); ++ } ++ + +> CHANGE 86 : 87 @ 83 : 84 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPistonMoving.edit.java b/patches/minecraft/net/minecraft/block/BlockPistonMoving.edit.java new file mode 100644 index 0000000..e93df63 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPistonMoving.edit.java @@ -0,0 +1,34 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 10 @ 13 : 14 + +> CHANGE 24 : 25 @ 28 : 29 + +~ public static PropertyEnum TYPE; + +> INSERT 33 : 37 @ 37 + ++ public static void bootstrapStates() { ++ TYPE = BlockPistonExtension.TYPE; ++ } ++ + +> CHANGE 82 : 83 @ 82 : 93 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> DELETE 86 @ 96 : 106 + +> DELETE 90 @ 110 : 117 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPlanks.edit.java b/patches/minecraft/net/minecraft/block/BlockPlanks.edit.java new file mode 100644 index 0000000..6bb7025 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPlanks.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> CHANGE 16 : 17 @ 16 : 18 + +~ public static PropertyEnum VARIANT; + +> INSERT 24 : 28 @ 25 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockPlanks.EnumType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPortal.edit.java b/patches/minecraft/net/minecraft/block/BlockPortal.edit.java new file mode 100644 index 0000000..c56e86f --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPortal.edit.java @@ -0,0 +1,30 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.cache.EaglerLoadingCache; +~ + +> CHANGE 34 : 35 @ 35 : 36 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 154 : 155 @ 155 : 156 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 169 : 170 @ 170 : 171 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom random) { + +> CHANGE 216 : 217 @ 217 : 218 + +~ EaglerLoadingCache loadingcache = BlockPattern.func_181627_a(parWorld, true); + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPotato.edit.java b/patches/minecraft/net/minecraft/block/BlockPotato.edit.java new file mode 100644 index 0000000..426efca --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPotato.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 4 @ 6 : 9 + +> DELETE 14 @ 19 : 28 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPressurePlate.edit.java b/patches/minecraft/net/minecraft/block/BlockPressurePlate.edit.java new file mode 100644 index 0000000..f42f38f --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPressurePlate.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> CHANGE 35 : 36 @ 35 : 36 + +~ List list; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPressurePlateWeighted.edit.java b/patches/minecraft/net/minecraft/block/BlockPressurePlateWeighted.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPressurePlateWeighted.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPrismarine.edit.java b/patches/minecraft/net/minecraft/block/BlockPrismarine.edit.java new file mode 100644 index 0000000..3c29b6c --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPrismarine.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> CHANGE 17 : 18 @ 17 : 19 + +~ public static PropertyEnum VARIANT; + +> INSERT 28 : 32 @ 29 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockPrismarine.EnumType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockPumpkin.edit.java b/patches/minecraft/net/minecraft/block/BlockPumpkin.edit.java new file mode 100644 index 0000000..46d9776 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockPumpkin.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockQuartz.edit.java b/patches/minecraft/net/minecraft/block/BlockQuartz.edit.java new file mode 100644 index 0000000..7c15696 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockQuartz.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> CHANGE 20 : 21 @ 20 : 22 + +~ public static PropertyEnum VARIANT; + +> INSERT 28 : 32 @ 29 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockQuartz.EnumType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRail.edit.java b/patches/minecraft/net/minecraft/block/BlockRail.edit.java new file mode 100644 index 0000000..20e7a48 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRail.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 10 : 11 @ 12 : 14 + +~ public static PropertyEnum SHAPE; + +> INSERT 18 : 22 @ 21 + ++ public static void bootstrapStates() { ++ SHAPE = PropertyEnum.create("shape", BlockRailBase.EnumRailDirection.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRailBase.edit.java b/patches/minecraft/net/minecraft/block/BlockRailBase.edit.java new file mode 100644 index 0000000..d1b1cec --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRailBase.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 5 + +~ +~ import com.google.common.collect.Lists; +~ + +> DELETE 75 @ 74 : 117 + +> CHANGE 80 : 81 @ 122 : 125 + +~ return parIBlockState; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRailDetector.edit.java b/patches/minecraft/net/minecraft/block/BlockRailDetector.edit.java new file mode 100644 index 0000000..df5c1e0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRailDetector.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 6 + +~ +~ import com.google.common.base.Predicate; +~ + +> CHANGE 24 : 25 @ 24 : 33 + +~ public static PropertyEnum SHAPE; + +> INSERT 34 : 46 @ 42 + ++ public static void bootstrapStates() { ++ SHAPE = PropertyEnum.create("shape", BlockRailBase.EnumRailDirection.class, ++ new Predicate() { ++ public boolean apply(BlockRailBase.EnumRailDirection blockrailbase$enumraildirection) { ++ return blockrailbase$enumraildirection != BlockRailBase.EnumRailDirection.NORTH_EAST ++ && blockrailbase$enumraildirection != BlockRailBase.EnumRailDirection.NORTH_WEST ++ && blockrailbase$enumraildirection != BlockRailBase.EnumRailDirection.SOUTH_EAST ++ && blockrailbase$enumraildirection != BlockRailBase.EnumRailDirection.SOUTH_WEST; ++ } ++ }); ++ } ++ + +> DELETE 54 @ 50 : 67 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRailPowered.edit.java b/patches/minecraft/net/minecraft/block/BlockRailPowered.edit.java new file mode 100644 index 0000000..8fc3b2c --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRailPowered.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 5 + +~ + +> CHANGE 13 : 14 @ 14 : 23 + +~ public static PropertyEnum SHAPE; + +> INSERT 23 : 35 @ 32 + ++ public static void bootstrapStates() { ++ SHAPE = PropertyEnum.create("shape", BlockRailBase.EnumRailDirection.class, ++ new Predicate() { ++ public boolean apply(BlockRailBase.EnumRailDirection blockrailbase$enumraildirection) { ++ return blockrailbase$enumraildirection != BlockRailBase.EnumRailDirection.NORTH_EAST ++ && blockrailbase$enumraildirection != BlockRailBase.EnumRailDirection.NORTH_WEST ++ && blockrailbase$enumraildirection != BlockRailBase.EnumRailDirection.SOUTH_EAST ++ && blockrailbase$enumraildirection != BlockRailBase.EnumRailDirection.SOUTH_WEST; ++ } ++ }); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedFlower.edit.java b/patches/minecraft/net/minecraft/block/BlockRedFlower.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedFlower.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedSandstone.edit.java b/patches/minecraft/net/minecraft/block/BlockRedSandstone.edit.java new file mode 100644 index 0000000..1d0c8fd --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedSandstone.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 5 + +~ + +> CHANGE 15 : 16 @ 16 : 18 + +~ public static PropertyEnum TYPE; + +> INSERT 23 : 27 @ 25 + ++ public static void bootstrapStates() { ++ TYPE = PropertyEnum.create("type", BlockRedSandstone.EnumType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneComparator.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneComparator.edit.java new file mode 100644 index 0000000..e69862e --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneComparator.edit.java @@ -0,0 +1,36 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 7 @ 4 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.base.Predicate; +~ + +> CHANGE 32 : 33 @ 33 : 35 + +~ public static PropertyEnum MODE; + +> INSERT 42 : 46 @ 44 + ++ public static void bootstrapStates() { ++ MODE = PropertyEnum.create("mode", BlockRedstoneComparator.Mode.class); ++ } ++ + +> CHANGE 50 : 51 @ 48 : 49 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 199 : 200 @ 197 : 198 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneDiode.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneDiode.edit.java new file mode 100644 index 0000000..7f8fb38 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneDiode.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 37 : 38 @ 39 : 40 + +~ public void randomTick(World var1, BlockPos var2, IBlockState var3, EaglercraftRandom var4) { + +> CHANGE 40 : 41 @ 42 : 43 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneLight.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneLight.edit.java new file mode 100644 index 0000000..5c78ba0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneLight.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 24 : 25 @ 24 : 56 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneOre.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneOre.edit.java new file mode 100644 index 0000000..e3da69e --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneOre.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 57 : 58 @ 57 : 58 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom var4) { + +> CHANGE 64 : 65 @ 64 : 65 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 68 : 69 @ 68 : 69 + +~ public int quantityDroppedWithBonus(int i, EaglercraftRandom random) { + +> CHANGE 72 : 73 @ 72 : 73 + +~ public int quantityDropped(EaglercraftRandom random) { + +> CHANGE 85 : 86 @ 85 : 86 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom var4) { + +> CHANGE 93 : 94 @ 93 : 94 + +~ EaglercraftRandom random = worldIn.rand; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneRepeater.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneRepeater.edit.java new file mode 100644 index 0000000..22f50a6 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneRepeater.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 68 : 69 @ 69 : 70 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 84 : 85 @ 85 : 86 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneTorch.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneTorch.edit.java new file mode 100644 index 0000000..c73497e --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneTorch.edit.java @@ -0,0 +1,34 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 4 : 9 @ 6 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ + +> CHANGE 85 : 86 @ 85 : 86 + +~ public void randomTick(World var1, BlockPos var2, IBlockState var3, EaglercraftRandom var4) { + +> CHANGE 88 : 89 @ 88 : 89 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 137 : 138 @ 137 : 138 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 145 : 146 @ 145 : 146 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRedstoneWire.edit.java b/patches/minecraft/net/minecraft/block/BlockRedstoneWire.edit.java new file mode 100644 index 0000000..c37582d --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRedstoneWire.edit.java @@ -0,0 +1,56 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 4 : 5 @ 6 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 6 : 10 @ 8 : 11 + +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Sets; +~ + +> CHANGE 30 : 34 @ 31 : 39 + +~ public static PropertyEnum NORTH; +~ public static PropertyEnum EAST; +~ public static PropertyEnum SOUTH; +~ public static PropertyEnum WEST; + +> INSERT 48 : 59 @ 53 + ++ public static void bootstrapStates() { ++ NORTH = PropertyEnum.create("north", ++ BlockRedstoneWire.EnumAttachPosition.class); ++ EAST = PropertyEnum.create("east", ++ BlockRedstoneWire.EnumAttachPosition.class); ++ SOUTH = PropertyEnum.create("south", ++ BlockRedstoneWire.EnumAttachPosition.class); ++ WEST = PropertyEnum.create("west", ++ BlockRedstoneWire.EnumAttachPosition.class); ++ } ++ + +> CHANGE 108 : 109 @ 102 : 103 + +~ ArrayList arraylist = Lists.newArrayList(this.blocksNeedingUpdate); + +> DELETE 189 @ 183 : 232 + +> CHANGE 198 : 199 @ 241 : 254 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 298 : 299 @ 353 : 354 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockReed.edit.java b/patches/minecraft/net/minecraft/block/BlockReed.edit.java new file mode 100644 index 0000000..81bd73c --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockReed.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 30 : 31 @ 30 : 31 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> CHANGE 93 : 94 @ 93 : 94 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockRotatedPillar.edit.java b/patches/minecraft/net/minecraft/block/BlockRotatedPillar.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockRotatedPillar.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSand.edit.java b/patches/minecraft/net/minecraft/block/BlockSand.edit.java new file mode 100644 index 0000000..82e623d --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSand.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> CHANGE 15 : 16 @ 15 : 17 + +~ public static PropertyEnum VARIANT; + +> INSERT 21 : 25 @ 22 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockSand.EnumType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSandStone.edit.java b/patches/minecraft/net/minecraft/block/BlockSandStone.edit.java new file mode 100644 index 0000000..1ff828a --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSandStone.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> CHANGE 16 : 17 @ 16 : 18 + +~ public static PropertyEnum TYPE; + +> INSERT 24 : 28 @ 25 + ++ public static void bootstrapStates() { ++ TYPE = PropertyEnum.create("type", BlockSandStone.EnumType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSapling.edit.java b/patches/minecraft/net/minecraft/block/BlockSapling.edit.java new file mode 100644 index 0000000..6f94f41 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSapling.edit.java @@ -0,0 +1,40 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 10 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 11 @ 16 : 17 + +> DELETE 16 @ 22 : 31 + +> CHANGE 18 : 19 @ 33 : 35 + +~ public static PropertyEnum TYPE; + +> INSERT 29 : 33 @ 45 + ++ public static void bootstrapStates() { ++ TYPE = PropertyEnum.create("type", BlockPlanks.EnumType.class); ++ } ++ + +> CHANGE 38 : 39 @ 50 : 58 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> DELETE 41 @ 60 : 161 + +> CHANGE 69 : 70 @ 189 : 190 + +~ public boolean canUseBonemeal(World world, EaglercraftRandom var2, BlockPos var3, IBlockState var4) { + +> DELETE 73 @ 193 : 197 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSeaLantern.edit.java b/patches/minecraft/net/minecraft/block/BlockSeaLantern.edit.java new file mode 100644 index 0000000..d81a357 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSeaLantern.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 18 : 19 @ 18 : 19 + +~ public int quantityDropped(EaglercraftRandom random) { + +> CHANGE 22 : 23 @ 22 : 23 + +~ public int quantityDroppedWithBonus(int i, EaglercraftRandom random) { + +> CHANGE 26 : 27 @ 26 : 27 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSign.edit.java b/patches/minecraft/net/minecraft/block/BlockSign.edit.java new file mode 100644 index 0000000..0b9e18d --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSign.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 54 : 55 @ 54 : 55 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 64 : 65 @ 64 : 71 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSilverfish.edit.java b/patches/minecraft/net/minecraft/block/BlockSilverfish.edit.java new file mode 100644 index 0000000..3653e54 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSilverfish.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 11 @ 13 : 14 + +> CHANGE 19 : 20 @ 22 : 24 + +~ public static PropertyEnum VARIANT; + +> CHANGE 28 : 33 @ 32 : 33 + +~ public static void bootstrapStates() { +~ VARIANT = PropertyEnum.create("variant", BlockSilverfish.EnumType.class); +~ } +~ +~ public int quantityDropped(EaglercraftRandom var1) { + +> DELETE 59 @ 59 : 70 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSkull.edit.java b/patches/minecraft/net/minecraft/block/BlockSkull.edit.java new file mode 100644 index 0000000..48432e1 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSkull.edit.java @@ -0,0 +1,41 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ + +> CHANGE 5 : 6 @ 3 : 5 + +~ + +> DELETE 17 @ 16 : 17 + +> DELETE 22 @ 22 : 25 + +> DELETE 27 @ 30 : 31 + +> DELETE 28 @ 32 : 33 + +> DELETE 122 @ 127 : 139 + +> DELETE 123 @ 140 : 146 + +> CHANGE 125 : 126 @ 148 : 149 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 130 : 131 @ 153 : 155 + +~ return false; + +> DELETE 134 @ 158 : 168 + +> DELETE 135 @ 169 : 210 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockSlab.edit.java new file mode 100644 index 0000000..f89ca53 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSlab.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 21 : 22 @ 21 : 23 + +~ public static PropertyEnum HALF; + +> INSERT 34 : 38 @ 35 + ++ public static void bootstrapStates() { ++ HALF = PropertyEnum.create("half", BlockSlab.EnumBlockHalf.class); ++ } ++ + +> CHANGE 86 : 87 @ 83 : 84 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSlime.edit.java b/patches/minecraft/net/minecraft/block/BlockSlime.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSlime.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSnow.edit.java b/patches/minecraft/net/minecraft/block/BlockSnow.edit.java new file mode 100644 index 0000000..1972225 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSnow.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 100 : 101 @ 100 : 101 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 104 : 105 @ 104 : 105 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 108 : 109 @ 108 : 109 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSnowBlock.edit.java b/patches/minecraft/net/minecraft/block/BlockSnowBlock.edit.java new file mode 100644 index 0000000..396e032 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSnowBlock.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 20 : 21 @ 20 : 21 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 24 : 25 @ 24 : 25 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> CHANGE 28 : 29 @ 28 : 29 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSoulSand.edit.java b/patches/minecraft/net/minecraft/block/BlockSoulSand.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSoulSand.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockSponge.edit.java b/patches/minecraft/net/minecraft/block/BlockSponge.edit.java new file mode 100644 index 0000000..dfe6d6c --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockSponge.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 5 : 9 @ 6 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ + +> CHANGE 61 : 62 @ 60 : 61 + +~ ArrayList arraylist = Lists.newArrayList(); + +> CHANGE 111 : 112 @ 110 : 111 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStainedGlass.edit.java b/patches/minecraft/net/minecraft/block/BlockStainedGlass.edit.java new file mode 100644 index 0000000..8925d29 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStainedGlass.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 48 : 49 @ 49 : 50 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> DELETE 65 @ 66 : 69 + +> DELETE 69 @ 73 : 76 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStainedGlassPane.edit.java b/patches/minecraft/net/minecraft/block/BlockStainedGlassPane.edit.java new file mode 100644 index 0000000..4a6a9cf --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStainedGlassPane.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 5 + +~ + +> DELETE 62 @ 63 : 66 + +> DELETE 66 @ 70 : 73 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStairs.edit.java b/patches/minecraft/net/minecraft/block/BlockStairs.edit.java new file mode 100644 index 0000000..5e7f0f0 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStairs.edit.java @@ -0,0 +1,34 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 4 : 6 @ 4 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 30 : 32 @ 30 : 34 + +~ public static PropertyEnum HALF; +~ public static PropertyEnum SHAPE; + +> INSERT 52 : 57 @ 54 + ++ public static void bootstrapStates() { ++ HALF = PropertyEnum.create("half", BlockStairs.EnumHalf.class); ++ SHAPE = PropertyEnum.create("shape", BlockStairs.EnumShape.class); ++ } ++ + +> CHANGE 401 : 402 @ 398 : 399 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> CHANGE 462 : 463 @ 459 : 460 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStandingSign.edit.java b/patches/minecraft/net/minecraft/block/BlockStandingSign.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStandingSign.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStaticLiquid.edit.java b/patches/minecraft/net/minecraft/block/BlockStaticLiquid.edit.java new file mode 100644 index 0000000..ae961cf --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStaticLiquid.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 34 : 35 @ 36 : 37 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState var3, EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStem.edit.java b/patches/minecraft/net/minecraft/block/BlockStem.edit.java new file mode 100644 index 0000000..a8217a3 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStem.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ + +> CHANGE 5 : 6 @ 3 : 8 + +~ + +> DELETE 16 @ 18 : 19 + +> CHANGE 58 : 59 @ 61 : 62 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom random) { + +> DELETE 119 @ 122 : 139 + +> CHANGE 124 : 125 @ 144 : 145 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 137 : 138 @ 157 : 158 + +~ public boolean canUseBonemeal(World var1, EaglercraftRandom var2, BlockPos var3, IBlockState var4) { + +> CHANGE 141 : 142 @ 161 : 162 + +~ public void grow(World world, EaglercraftRandom var2, BlockPos blockpos, IBlockState iblockstate) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStone.edit.java b/patches/minecraft/net/minecraft/block/BlockStone.edit.java new file mode 100644 index 0000000..83b5107 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStone.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 19 : 20 @ 19 : 21 + +~ public static PropertyEnum VARIANT; + +> INSERT 27 : 31 @ 28 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockStone.EnumType.class); ++ } ++ + +> CHANGE 40 : 41 @ 37 : 38 + +~ public Item getItemDropped(IBlockState iblockstate, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStoneBrick.edit.java b/patches/minecraft/net/minecraft/block/BlockStoneBrick.edit.java new file mode 100644 index 0000000..fabad13 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStoneBrick.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> CHANGE 15 : 16 @ 15 : 17 + +~ public static PropertyEnum VARIANT; + +> INSERT 27 : 31 @ 28 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockStoneBrick.EnumType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStoneSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockStoneSlab.edit.java new file mode 100644 index 0000000..376382d --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStoneSlab.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 22 : 23 @ 22 : 24 + +~ public static PropertyEnum VARIANT; + +> CHANGE 37 : 42 @ 38 : 39 + +~ public static void bootstrapStates() { +~ VARIANT = PropertyEnum.create("variant", BlockStoneSlab.EnumType.class); +~ } +~ +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockStoneSlabNew.edit.java b/patches/minecraft/net/minecraft/block/BlockStoneSlabNew.edit.java new file mode 100644 index 0000000..9360b67 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockStoneSlabNew.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 23 : 24 @ 24 : 26 + +~ public static PropertyEnum VARIANT; + +> INSERT 38 : 42 @ 40 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockStoneSlabNew.EnumType.class); ++ } ++ + +> CHANGE 46 : 47 @ 44 : 45 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockTNT.edit.java b/patches/minecraft/net/minecraft/block/BlockTNT.edit.java new file mode 100644 index 0000000..8a7ff91 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockTNT.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 8 @ 9 : 10 + +> DELETE 9 @ 11 : 12 + +> DELETE 10 @ 13 : 14 + +> DELETE 43 @ 47 : 57 + +> DELETE 48 @ 62 : 69 + +> DELETE 49 @ 70 : 71 + +> DELETE 71 @ 93 : 108 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockTallGrass.edit.java b/patches/minecraft/net/minecraft/block/BlockTallGrass.edit.java new file mode 100644 index 0000000..e331568 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockTallGrass.edit.java @@ -0,0 +1,46 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 11 @ 14 : 15 + +> DELETE 15 @ 19 : 21 + +> CHANGE 22 : 23 @ 28 : 30 + +~ public static PropertyEnum TYPE; + +> INSERT 31 : 35 @ 38 + ++ public static void bootstrapStates() { ++ TYPE = PropertyEnum.create("type", BlockTallGrass.EnumType.class); ++ } ++ + +> CHANGE 61 : 62 @ 64 : 65 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom random, int var3) { + +> CHANGE 65 : 66 @ 68 : 69 + +~ public int quantityDroppedWithBonus(int i, EaglercraftRandom random) { + +> DELETE 69 @ 72 : 85 + +> CHANGE 85 : 86 @ 101 : 102 + +~ public boolean canUseBonemeal(World var1, EaglercraftRandom var2, BlockPos var3, IBlockState var4) { + +> CHANGE 89 : 90 @ 105 : 106 + +~ public void grow(World world, EaglercraftRandom var2, BlockPos blockpos, IBlockState iblockstate) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockTorch.edit.java b/patches/minecraft/net/minecraft/block/BlockTorch.edit.java new file mode 100644 index 0000000..56e1cc3 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockTorch.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ + +> CHANGE 5 : 6 @ 3 : 6 + +~ + +> CHANGE 155 : 156 @ 155 : 156 + +~ public void randomDisplayTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockTrapDoor.edit.java b/patches/minecraft/net/minecraft/block/BlockTrapDoor.edit.java new file mode 100644 index 0000000..fcfe3e2 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockTrapDoor.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 26 : 27 @ 29 : 31 + +~ public static PropertyEnum HALF; + +> INSERT 38 : 42 @ 42 + ++ public static void bootstrapStates() { ++ HALF = PropertyEnum.create("half", BlockTrapDoor.DoorHalf.class); ++ } ++ + +> DELETE 119 @ 119 : 139 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockTripWire.edit.java b/patches/minecraft/net/minecraft/block/BlockTripWire.edit.java new file mode 100644 index 0000000..40b6dbf --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockTripWire.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 9 @ 11 : 13 + +> CHANGE 68 : 69 @ 72 : 73 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> DELETE 111 @ 115 : 125 + +> DELETE 131 @ 145 : 193 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockTripWireHook.edit.java b/patches/minecraft/net/minecraft/block/BlockTripWireHook.edit.java new file mode 100644 index 0000000..5807e62 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockTripWireHook.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ + +> CHANGE 5 : 6 @ 3 : 6 + +~ + +> CHANGE 173 : 174 @ 173 : 174 + +~ public void randomTick(World var1, BlockPos var2, IBlockState var3, EaglercraftRandom var4) { + +> CHANGE 176 : 177 @ 176 : 177 + +~ public void updateTick(World world, BlockPos blockpos, IBlockState iblockstate, EaglercraftRandom var4) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockVine.edit.java b/patches/minecraft/net/minecraft/block/BlockVine.edit.java new file mode 100644 index 0000000..6e546bf --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockVine.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 11 @ 11 : 14 + +> DELETE 12 @ 15 : 18 + +> DELETE 179 @ 185 : 319 + +> CHANGE 189 : 190 @ 329 : 330 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> CHANGE 193 : 194 @ 333 : 334 + +~ public int quantityDropped(EaglercraftRandom var1) { + +> DELETE 197 @ 337 : 349 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockWall.edit.java b/patches/minecraft/net/minecraft/block/BlockWall.edit.java new file mode 100644 index 0000000..26d9ba6 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockWall.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 5 + +~ + +> CHANGE 28 : 29 @ 29 : 31 + +~ public static PropertyEnum VARIANT; + +> INSERT 42 : 46 @ 44 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockWall.EnumType.class); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockWallSign.edit.java b/patches/minecraft/net/minecraft/block/BlockWallSign.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockWallSign.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockWeb.edit.java b/patches/minecraft/net/minecraft/block/BlockWeb.edit.java new file mode 100644 index 0000000..2710783 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockWeb.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 37 : 38 @ 37 : 38 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockWoodSlab.edit.java b/patches/minecraft/net/minecraft/block/BlockWoodSlab.edit.java new file mode 100644 index 0000000..e9953c1 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockWoodSlab.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 19 : 20 @ 20 : 22 + +~ public static PropertyEnum VARIANT; + +> INSERT 32 : 36 @ 34 + ++ public static void bootstrapStates() { ++ VARIANT = PropertyEnum.create("variant", BlockPlanks.EnumType.class); ++ } ++ + +> CHANGE 40 : 41 @ 38 : 39 + +~ public Item getItemDropped(IBlockState var1, EaglercraftRandom var2, int var3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockWorkbench.edit.java b/patches/minecraft/net/minecraft/block/BlockWorkbench.edit.java new file mode 100644 index 0000000..ccdba35 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockWorkbench.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 10 @ 11 : 12 + +> CHANGE 25 : 26 @ 27 : 34 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/block/BlockYellowFlower.edit.java b/patches/minecraft/net/minecraft/block/BlockYellowFlower.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/BlockYellowFlower.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/IGrowable.edit.java b/patches/minecraft/net/minecraft/block/IGrowable.edit.java new file mode 100644 index 0000000..eb404ec --- /dev/null +++ b/patches/minecraft/net/minecraft/block/IGrowable.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 11 : 12 @ 10 : 13 + +~ boolean canUseBonemeal(World var1, EaglercraftRandom var2, BlockPos var3, IBlockState var4); + +> EOF diff --git a/patches/minecraft/net/minecraft/block/material/Material.edit.java b/patches/minecraft/net/minecraft/block/material/Material.edit.java new file mode 100644 index 0000000..05d5136 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/material/Material.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/material/MaterialLiquid.edit.java b/patches/minecraft/net/minecraft/block/material/MaterialLiquid.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/material/MaterialLiquid.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/material/MaterialLogic.edit.java b/patches/minecraft/net/minecraft/block/material/MaterialLogic.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/material/MaterialLogic.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/material/MaterialPortal.edit.java b/patches/minecraft/net/minecraft/block/material/MaterialPortal.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/material/MaterialPortal.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/material/MaterialTransparent.edit.java b/patches/minecraft/net/minecraft/block/material/MaterialTransparent.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/material/MaterialTransparent.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/properties/IProperty.edit.java b/patches/minecraft/net/minecraft/block/properties/IProperty.edit.java new file mode 100644 index 0000000..d86cc37 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/properties/IProperty.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 11 : 12 @ 11 : 12 + +~ String getName(Object var1); + +> EOF diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyBool.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyBool.edit.java new file mode 100644 index 0000000..b75b366 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/properties/PropertyBool.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 5 + +> INSERT 4 : 6 @ 6 + ++ import com.google.common.collect.ImmutableSet; ++ + +> CHANGE 21 : 23 @ 21 : 23 + +~ public String getName(Object value) { +~ return ((Boolean) value).toString(); + +> EOF diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyDirection.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyDirection.edit.java new file mode 100644 index 0000000..66a48de --- /dev/null +++ b/patches/minecraft/net/minecraft/block/properties/PropertyDirection.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import java.util.Collection; ++ + +> CHANGE 8 : 9 @ 6 : 8 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyEnum.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyEnum.edit.java new file mode 100644 index 0000000..02ca5d6 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/properties/PropertyEnum.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import java.util.Collection; ++ import java.util.Map; ++ + +> CHANGE 11 : 12 @ 8 : 11 + +~ + +> CHANGE 22 : 23 @ 21 : 22 + +~ for (T oenum : allowedValues) { + +> CHANGE 37 : 38 @ 36 : 37 + +~ public String getName(Object oenum) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyHelper.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyHelper.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/properties/PropertyHelper.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/block/properties/PropertyInteger.edit.java b/patches/minecraft/net/minecraft/block/properties/PropertyInteger.edit.java new file mode 100644 index 0000000..7eda9ef --- /dev/null +++ b/patches/minecraft/net/minecraft/block/properties/PropertyInteger.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 4 @ 6 : 7 + +> INSERT 5 : 8 @ 8 + ++ import com.google.common.collect.ImmutableSet; ++ import com.google.common.collect.Sets; ++ + +> CHANGE 57 : 58 @ 57 : 58 + +~ public String getName(Object integer) { + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/BlockPistonStructureHelper.edit.java b/patches/minecraft/net/minecraft/block/state/BlockPistonStructureHelper.edit.java new file mode 100644 index 0000000..87f357f --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/BlockPistonStructureHelper.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/BlockState.edit.java b/patches/minecraft/net/minecraft/block/state/BlockState.edit.java new file mode 100644 index 0000000..c4f3e7f --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/BlockState.edit.java @@ -0,0 +1,39 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 12 @ 2 + ++ import java.util.ArrayList; ++ import java.util.Arrays; ++ import java.util.Collection; ++ import java.util.Collections; ++ import java.util.Comparator; ++ import java.util.HashMap; ++ import java.util.LinkedHashMap; ++ import java.util.List; ++ import java.util.Map; ++ + +> CHANGE 22 : 23 @ 12 : 21 + +~ + +> DELETE 25 @ 23 : 25 + +> CHANGE 48 : 49 @ 48 : 49 + +~ ArrayList arraylist = Lists.newArrayList(); + +> CHANGE 156 : 157 @ 156 : 157 + +~ for (Object comparable : iproperty.getAllowedValues()) { + +> CHANGE 159 : 160 @ 159 : 160 + +~ map.get(this.getPropertiesWithValue(iproperty, (Comparable) comparable))); + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java b/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java new file mode 100644 index 0000000..eeb9a2a --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/BlockStateBase.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 5 : 10 @ 8 + ++ ++ import com.google.common.base.Function; ++ import com.google.common.base.Joiner; ++ import com.google.common.collect.Iterables; ++ + +> DELETE 12 @ 10 : 11 + +> CHANGE 28 : 29 @ 27 : 28 + +~ (T) cyclePropertyValue(property.getAllowedValues(), this.getValue(property))); + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/BlockWorldState.edit.java b/patches/minecraft/net/minecraft/block/state/BlockWorldState.edit.java new file mode 100644 index 0000000..46d9776 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/BlockWorldState.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/IBlockState.edit.java b/patches/minecraft/net/minecraft/block/state/IBlockState.edit.java new file mode 100644 index 0000000..58780e9 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/IBlockState.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.ImmutableMap; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/pattern/BlockHelper.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/BlockHelper.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/pattern/BlockHelper.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/pattern/BlockPattern.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/BlockPattern.edit.java new file mode 100644 index 0000000..aa96d96 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/pattern/BlockPattern.edit.java @@ -0,0 +1,51 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 4 : 7 @ 4 : 6 + +~ +~ import net.lax1dude.eaglercraft.v1_8.cache.EaglerCacheProvider; +~ import net.lax1dude.eaglercraft.v1_8.cache.EaglerLoadingCache; + +> CHANGE 45 : 46 @ 44 : 45 + +~ EaglerLoadingCache lcache) { + +> CHANGE 49 : 50 @ 48 : 50 + +~ if (!this.blockMatches[k][j][i].apply(lcache.get(translateOffset(pos, finger, thumb, i, j, k)))) { + +> CHANGE 61 : 62 @ 61 : 62 + +~ EaglerLoadingCache loadingcache = func_181627_a(worldIn, false); + +> CHANGE 81 : 83 @ 81 : 83 + +~ public static EaglerLoadingCache func_181627_a(World parWorld, boolean parFlag) { +~ return new EaglerLoadingCache(new BlockPattern.CacheLoader(parWorld, parFlag)); + +> CHANGE 99 : 100 @ 99 : 100 + +~ static class CacheLoader implements EaglerCacheProvider { + +> CHANGE 108 : 109 @ 108 : 109 + +~ public BlockWorldState create(BlockPos parBlockPos) { + +> CHANGE 117 : 118 @ 117 : 118 + +~ private final EaglerLoadingCache lcache; + +> CHANGE 123 : 124 @ 123 : 124 + +~ EaglerLoadingCache parLoadingCache, int parInt1, int parInt2, int parInt3) { + +> CHANGE 154 : 155 @ 154 : 155 + +~ return (BlockWorldState) this.lcache.get(BlockPattern.translateOffset(this.pos, this.getFinger(), + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/pattern/BlockStateHelper.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/BlockStateHelper.edit.java new file mode 100644 index 0000000..a34c5fe --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/pattern/BlockStateHelper.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 4 : 8 @ 6 + ++ ++ import com.google.common.base.Predicate; ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java b/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java new file mode 100644 index 0000000..44e4735 --- /dev/null +++ b/patches/minecraft/net/minecraft/block/state/pattern/FactoryBlockPattern.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> CHANGE 7 : 8 @ 12 : 15 + +~ + +> INSERT 10 : 18 @ 17 + ++ import com.google.common.base.Joiner; ++ import com.google.common.base.Predicate; ++ import com.google.common.base.Predicates; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ ++ import net.minecraft.block.state.BlockWorldState; ++ + +> CHANGE 30 : 31 @ 29 : 30 + +~ if (aisle.length > 0 && !StringUtils.isEmpty(aisle[0])) { + +> CHANGE 49 : 50 @ 48 : 49 + +~ this.symbolMap.put(Character.valueOf(c0), (Predicate) null); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/ClientBrandRetriever.edit.java b/patches/minecraft/net/minecraft/client/ClientBrandRetriever.edit.java new file mode 100644 index 0000000..a3fdd92 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/ClientBrandRetriever.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 4 : 5 @ 4 : 5 + +~ return "eagler"; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/LoadingScreenRenderer.edit.java b/patches/minecraft/net/minecraft/client/LoadingScreenRenderer.edit.java new file mode 100644 index 0000000..0710154 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/LoadingScreenRenderer.edit.java @@ -0,0 +1,57 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 5 : 7 + +> DELETE 7 @ 8 : 9 + +> DELETE 8 @ 10 : 11 + +> DELETE 18 @ 21 : 22 + +> DELETE 22 @ 26 : 28 + +> CHANGE 44 : 47 @ 50 : 60 + +~ ScaledResolution scaledresolution = new ScaledResolution(this.mc); +~ GlStateManager.ortho(0.0D, scaledresolution.getScaledWidth_double(), +~ scaledresolution.getScaledHeight_double(), 0.0D, 100.0D, 300.0D); + +> INSERT 66 : 80 @ 79 + ++ public void eaglerShow(String line1, String line2) { ++ if (!this.mc.running) { ++ if (!this.field_73724_e) { ++ throw new MinecraftError(); ++ } ++ } else { ++ this.systemTime = 0L; ++ this.currentlyDisplayedText = line1; ++ this.message = line2; ++ this.setLoadingProgress(-1); ++ this.systemTime = 0L; ++ } ++ } ++ + +> CHANGE 93 : 94 @ 92 : 99 + +~ GlStateManager.clear(256); + +> CHANGE 101 : 103 @ 106 : 109 + +~ GlStateManager.clear(16640); +~ GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + +> DELETE 147 @ 153 : 158 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/Minecraft.edit.java b/patches/minecraft/net/minecraft/client/Minecraft.edit.java new file mode 100644 index 0000000..1aa3418 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/Minecraft.edit.java @@ -0,0 +1,581 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 17 + +> DELETE 4 @ 19 : 23 + +> DELETE 5 @ 24 : 25 + +> CHANGE 7 : 8 @ 27 : 31 + +~ import java.util.LinkedList; + +> DELETE 9 @ 32 : 35 + +> CHANGE 10 : 41 @ 36 : 39 + +~ +~ import org.apache.commons.lang3.Validate; +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.Display; +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.HString; +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; +~ import net.lax1dude.eaglercraft.v1_8.Keyboard; +~ import net.lax1dude.eaglercraft.v1_8.Mouse; +~ import net.lax1dude.eaglercraft.v1_8.futures.Executors; +~ import net.lax1dude.eaglercraft.v1_8.futures.FutureTask; +~ import net.lax1dude.eaglercraft.v1_8.futures.Futures; +~ import net.lax1dude.eaglercraft.v1_8.futures.ListenableFuture; +~ import net.lax1dude.eaglercraft.v1_8.futures.ListenableFutureTask; +~ import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; +~ import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFontRenderer; +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +~ import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; +~ import net.lax1dude.eaglercraft.v1_8.profile.GuiScreenEditProfile; +~ import net.lax1dude.eaglercraft.v1_8.profile.SkinPreviewRenderer; +~ import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; +~ import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; +~ import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; + +> DELETE 43 @ 41 : 43 + +> DELETE 56 @ 56 : 58 + +> DELETE 59 @ 61 : 62 + +> INSERT 62 : 63 @ 65 + ++ import net.minecraft.client.multiplayer.ServerAddress; + +> INSERT 64 : 65 @ 66 + ++ import net.minecraft.client.multiplayer.ServerList; + +> DELETE 66 @ 67 : 68 + +> DELETE 70 @ 72 : 73 + +> DELETE 71 @ 74 : 75 + +> DELETE 73 @ 77 : 78 + +> DELETE 88 @ 93 : 94 + +> DELETE 90 @ 96 : 97 + +> DELETE 104 @ 111 : 112 + +> DELETE 105 @ 113 : 115 + +> DELETE 126 @ 136 : 140 + +> DELETE 127 @ 141 : 143 + +> DELETE 128 @ 144 : 146 + +> DELETE 133 @ 151 : 152 + +> INSERT 144 : 145 @ 163 + ++ import net.minecraft.util.StringTranslate; + +> DELETE 151 @ 169 : 189 + +> CHANGE 152 : 153 @ 190 : 191 + +~ public class Minecraft implements IThreadListener { + +> CHANGE 155 : 156 @ 193 : 200 + +~ public static final boolean isRunningOnMac = false; + +> DELETE 168 @ 212 : 214 + +> DELETE 187 @ 233 : 234 + +> DELETE 193 @ 240 : 242 + +> DELETE 194 @ 243 : 245 + +> CHANGE 205 : 206 @ 256 : 257 + +~ private EaglercraftNetworkManager myNetworkManager; + +> DELETE 215 @ 266 : 268 + +> CHANGE 219 : 220 @ 272 : 275 + +~ private final List> scheduledTasks = new LinkedList(); + +> INSERT 234 : 236 @ 289 + ++ public int joinWorldTickCounter = 0; ++ private int dontPauseTimer = 0; + +> CHANGE 239 : 240 @ 292 : 295 + +~ StringTranslate.doCLINIT(); + +> CHANGE 241 : 242 @ 296 : 304 + +~ this.mcDefaultResourcePack = new DefaultResourcePack(); + +> CHANGE 243 : 244 @ 305 : 307 + +~ logger.info("Setting user: " + this.session.getProfile().getName()); + +> CHANGE 251 : 256 @ 314 : 318 + +~ String serverToJoin = EagRuntime.getConfiguration().getServerToJoin(); +~ if (serverToJoin != null) { +~ ServerAddress addr = AddressResolver.resolveAddressFromURI(serverToJoin); +~ this.serverName = addr.getIP(); +~ this.serverPort = addr.getPort(); + +> DELETE 258 @ 320 : 321 + +> CHANGE 273 : 275 @ 336 : 338 + +~ try { +~ while (true) { + +> DELETE 291 @ 354 : 371 + +> CHANGE 292 : 307 @ 372 : 374 + +~ } catch (MinecraftError var12) { +~ // ?? +~ } catch (ReportedException reportedexception) { +~ this.addGraphicsAndWorldToCrashReport(reportedexception.getCrashReport()); +~ this.freeMemory(); +~ logger.fatal("Reported exception thrown!", reportedexception); +~ this.displayCrashReport(reportedexception.getCrashReport()); +~ } catch (Throwable throwable1) { +~ CrashReport crashreport1 = this +~ .addGraphicsAndWorldToCrashReport(new CrashReport("Unexpected error", throwable1)); +~ this.freeMemory(); +~ logger.fatal("Unreported exception thrown!", throwable1); +~ this.displayCrashReport(crashreport1); +~ } finally { +~ this.shutdownMinecraftApplet(); + +> CHANGE 311 : 313 @ 378 : 380 + +~ private void startGame() throws IOException { +~ this.gameSettings = new GameSettings(this); + +> DELETE 314 @ 381 : 382 + +> CHANGE 319 : 320 @ 387 : 390 + +~ logger.info("EagRuntime Version: " + EagRuntime.getVersion()); + +> DELETE 321 @ 391 : 394 + +> CHANGE 322 : 323 @ 395 : 397 + +~ this.mcResourcePackRepository = new ResourcePackRepository(this.mcDefaultResourcePack, this.metadataSerializer_, + +> DELETE 331 @ 405 : 408 + +> CHANGE 334 : 336 @ 411 : 413 + +~ this.fontRendererObj = new EaglerFontRenderer(this.gameSettings, +~ new ResourceLocation("textures/font/ascii.png"), this.renderEngine, false); + +> CHANGE 341 : 342 @ 418 : 419 + +~ this.standardGalacticFontRenderer = new EaglerFontRenderer(this.gameSettings, + +> CHANGE 350 : 351 @ 427 : 428 + +~ return HString.format(parString1, new Object[] { GameSettings + +> CHANGE 361 : 362 @ 438 : 439 + +~ GlStateManager.clearDepth(1.0f); + +> INSERT 392 : 393 @ 469 + ++ SkinPreviewRenderer.initialize(); + +> INSERT 395 : 399 @ 471 + ++ ++ ServerList.initServerList(this); ++ EaglerProfile.read(); ++ + +> CHANGE 400 : 402 @ 472 : 473 + +~ this.displayGuiScreen(new GuiScreenEditProfile( +~ new GuiConnecting(new GuiMainMenu(), this, this.serverName, this.serverPort))); + +> CHANGE 403 : 404 @ 474 : 475 + +~ this.displayGuiScreen(new GuiScreenEditProfile(new GuiMainMenu())); + +> DELETE 409 @ 480 : 492 + +> CHANGE 425 : 426 @ 508 : 516 + +~ throw new UnsupportedOperationException("wtf u trying to twitch stream in a browser game?"); + +> CHANGE 428 : 431 @ 518 : 540 + +~ private void createDisplay() { +~ Display.create(); +~ Display.setTitle("Eaglercraft 1.8.8"); + +> DELETE 433 @ 542 : 579 + +> CHANGE 434 : 435 @ 580 : 590 + +~ return true; + +> DELETE 437 @ 592 : 596 + +> DELETE 441 @ 600 : 617 + +> CHANGE 447 : 459 @ 623 : 636 + +~ String report = crashReportIn.getCompleteReport(); +~ Bootstrap.printToSYSOUT(report); +~ PlatformRuntime.writeCrashReport(report); +~ if (PlatformRuntime.getPlatformType() == EnumPlatformType.JAVASCRIPT) { +~ System.err.println( +~ "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); +~ System.err.println("NATIVE BROWSER EXCEPTION:"); +~ if (!PlatformRuntime.printJSExceptionIfBrowser(crashReportIn.getCrashCause())) { +~ System.err.println(""); +~ } +~ System.err.println( +~ "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); + +> DELETE 460 @ 637 : 638 + +> INSERT 467 : 469 @ 645 + ++ GlStateManager.recompileShaders(); ++ + +> CHANGE 483 : 485 @ 659 : 660 + +~ logger.info("Caught error stitching, removing all assigned resourcepacks"); +~ logger.info(runtimeexception); + +> CHANGE 501 : 504 @ 676 : 688 + +~ private void updateDisplayMode() { +~ this.displayWidth = Display.getWidth(); +~ this.displayHeight = Display.getHeight(); + +> CHANGE 506 : 510 @ 690 : 734 + +~ private void drawSplashScreen(TextureManager textureManagerInstance) { +~ Display.update(); +~ updateDisplayMode(); +~ GlStateManager.viewport(0, 0, displayWidth, displayHeight); + +> DELETE 512 @ 736 : 739 + +> CHANGE 528 : 529 @ 755 : 756 + +~ new DynamicTexture(ImageData.loadImageFile(inputstream))); + +> DELETE 553 @ 780 : 782 + +> DELETE 579 @ 808 : 812 + +> CHANGE 610 : 611 @ 843 : 844 + +~ public void checkGLError(String message) { + +> CHANGE 612 : 613 @ 845 : 846 + +~ int i = EaglercraftGPU.glGetError(); + +> CHANGE 614 : 615 @ 847 : 848 + +~ String s = EaglercraftGPU.gluErrorString(i); + +> DELETE 625 @ 858 : 859 + +> CHANGE 635 : 636 @ 869 : 870 + +~ EagRuntime.destroy(); + +> CHANGE 637 : 638 @ 871 : 872 + +~ EagRuntime.exit(); + +> DELETE 641 @ 875 : 877 + +> CHANGE 646 : 647 @ 882 : 883 + +~ if (Display.isCloseRequested()) { + +> CHANGE 661 : 662 @ 897 : 898 + +~ Util.func_181617_a((FutureTask) this.scheduledTasks.remove(0), logger); + +> DELETE 680 @ 916 : 924 + +> CHANGE 681 : 691 @ 925 : 929 + +~ if (!Display.contextLost()) { +~ GlStateManager.clearColor(0.0f, 0.0f, 0.0f, 1.0f); +~ GlStateManager.pushMatrix(); +~ GlStateManager.clear(16640); +~ this.mcProfiler.startSection("display"); +~ GlStateManager.enableTexture2D(); +~ if (this.thePlayer != null && this.thePlayer.isEntityInsideOpaqueBlock()) { +~ this.gameSettings.thirdPersonView = 0; +~ } +~ + +> CHANGE 692 : 697 @ 930 : 931 + +~ if (!this.skipRenderWorld) { +~ this.mcProfiler.endStartSection("gameRenderer"); +~ this.entityRenderer.func_181560_a(this.timer.renderPartialTicks, i); +~ this.mcProfiler.endSection(); +~ } + +> CHANGE 698 : 710 @ 932 : 936 + +~ this.mcProfiler.endSection(); +~ if (this.gameSettings.showDebugInfo && this.gameSettings.showDebugProfilerChart +~ && !this.gameSettings.hideGUI) { +~ if (!this.mcProfiler.profilingEnabled) { +~ this.mcProfiler.clearProfiling(); +~ } +~ +~ this.mcProfiler.profilingEnabled = true; +~ this.displayDebugInfo(i1); +~ } else { +~ this.mcProfiler.profilingEnabled = false; +~ this.prevFrameTime = System.nanoTime(); + +> CHANGE 712 : 714 @ 938 : 943 + +~ this.guiAchievement.updateAchievementWindow(); +~ GlStateManager.popMatrix(); + +> DELETE 716 @ 945 : 954 + +> DELETE 718 @ 956 : 964 + +> INSERT 719 : 720 @ 965 + ++ + +> CHANGE 721 : 722 @ 966 : 968 + +~ this.isGamePaused = false; + +> CHANGE 728 : 729 @ 974 : 975 + +~ this.debug = HString.format("%d fps (%d chunk update%s) T: %s%s%s%s", + +> CHANGE 734 : 736 @ 980 : 984 + +~ this.gameSettings.fancyGraphics ? "" : " fast", this.gameSettings.clouds == 0 ? "" +~ : (this.gameSettings.clouds == 1 ? " fast-clouds" : " fancy-clouds") }); + +> DELETE 739 @ 987 : 991 + +> DELETE 788 @ 1040 : 1047 + +> CHANGE 836 : 837 @ 1095 : 1096 + +~ EaglercraftGPU.glLineWidth(1.0F); + +> CHANGE 846 : 847 @ 1105 : 1106 + +~ (double) ((float) j - (float) short1 * 0.6F - 16.0F), 0.0D).color(0, 0, 0, 100).endVertex(); + +> CHANGE 848 : 849 @ 1107 : 1108 + +~ .color(0, 0, 0, 100).endVertex(); + +> CHANGE 850 : 851 @ 1109 : 1110 + +~ .color(0, 0, 0, 100).endVertex(); + +> CHANGE 852 : 853 @ 1111 : 1112 + +~ (double) ((float) j - (float) short1 * 0.6F - 16.0F), 0.0D).color(0, 0, 0, 100).endVertex(); + +> DELETE 963 @ 1222 : 1226 + +> CHANGE 1071 : 1072 @ 1334 : 1374 + +~ logger.error("Use F11 to toggle fullscreen!"); + +> DELETE 1083 @ 1385 : 1386 + +> DELETE 1085 @ 1388 : 1396 + +> INSERT 1094 : 1096 @ 1405 + ++ RateLimitTracker.tick(); ++ + +> INSERT 1119 : 1125 @ 1428 + ++ if (this.currentScreen == null && this.dontPauseTimer <= 0) { ++ if (!Mouse.isMouseGrabbed()) { ++ this.setIngameNotInFocus(); ++ this.displayInGameMenu(); ++ } ++ } + +> INSERT 1132 : 1137 @ 1435 + ++ this.dontPauseTimer = 6; ++ } else { ++ if (this.dontPauseTimer > 0) { ++ --this.dontPauseTimer; ++ } + +> CHANGE 1147 : 1148 @ 1445 : 1446 + +~ return Minecraft.this.currentScreen.getClass().getName(); + +> CHANGE 1161 : 1162 @ 1459 : 1460 + +~ return Minecraft.this.currentScreen.getClass().getName(); + +> CHANGE 1202 : 1204 @ 1500 : 1501 + +~ if ((!this.inGameHasFocus || !Mouse.isActuallyGrabbed()) && Mouse.getEventButtonState()) { +~ this.inGameHasFocus = false; + +> CHANGE 1246 : 1247 @ 1543 : 1544 + +~ if (k == 1 || (k > -1 && k == this.gameSettings.keyBindClose.getKeyCode())) { + +> INSERT 1288 : 1289 @ 1585 + ++ GlStateManager.recompileShaders(); + +> INSERT 1495 : 1501 @ 1791 + ++ if (this.theWorld != null) { ++ ++joinWorldTickCounter; ++ } else { ++ joinWorldTickCounter = 0; ++ } ++ + +> CHANGE 1506 : 1507 @ 1796 : 1845 + +~ throw new UnsupportedOperationException("singleplayer has been removed"); + +> INSERT 1519 : 1520 @ 1857 + ++ session.reset(); + +> DELETE 1521 @ 1858 : 1864 + +> DELETE 1561 @ 1904 : 1905 + +> CHANGE 1581 : 1582 @ 1925 : 1927 + +~ this.thePlayer = this.playerController.func_178892_a(this.theWorld, new StatFileWriter()); + +> CHANGE 1747 : 1748 @ 2092 : 2093 + +~ return EagRuntime.getVersion(); + +> CHANGE 1752 : 1754 @ 2097 : 2098 + +~ return EaglercraftGPU.glGetString(7937) + " GL version " + EaglercraftGPU.glGetString(7938) + ", " +~ + EaglercraftGPU.glGetString(7936); + +> DELETE 1756 @ 2100 : 2110 + +> CHANGE 1758 : 1759 @ 2112 : 2116 + +~ return "Definitely Not; You're an eagler"; + +> DELETE 1795 @ 2152 : 2157 + +> INSERT 1809 : 1811 @ 2171 + ++ Minecraft.this.loadingScreen.eaglerShow(I18n.format("resourcePack.load.refreshing"), ++ I18n.format("resourcePack.load.pleaseWait")); + +> DELETE 1816 @ 2176 : 2203 + +> CHANGE 1817 : 1818 @ 2204 : 2209 + +~ return this.currentServerData != null ? "multiplayer" : "out_of_game"; + +> DELETE 1820 @ 2211 : 2428 + +> CHANGE 1837 : 1838 @ 2445 : 2446 + +~ return false; + +> DELETE 1840 @ 2448 : 2452 + +> DELETE 1841 @ 2453 : 2458 + +> DELETE 1842 @ 2459 : 2460 + +> DELETE 1844 @ 2462 : 2466 + +> CHANGE 1845 : 1846 @ 2467 : 2468 + +~ return System.currentTimeMillis(); + +> DELETE 1856 @ 2478 : 2495 + +> DELETE 1900 @ 2539 : 2543 + +> CHANGE 1906 : 1908 @ 2549 : 2589 + +~ if (i == this.gameSettings.keyBindScreenshot.getKeyCode()) { +~ this.ingameGUI.getChatGUI().printChatMessage(ScreenShotHelper.saveScreenshot()); + +> DELETE 1909 @ 2590 : 2592 + +> DELETE 1910 @ 2593 : 2594 + +> DELETE 1914 @ 2598 : 2606 + +> CHANGE 1925 : 1929 @ 2617 : 2629 + +~ ListenableFutureTask listenablefuturetask = ListenableFutureTask.create(callableToSchedule); +~ synchronized (this.scheduledTasks) { +~ this.scheduledTasks.add(listenablefuturetask); +~ return listenablefuturetask; + +> DELETE 1937 @ 2637 : 2641 + +> DELETE 1961 @ 2665 : 2673 + +> INSERT 1968 : 1972 @ 2680 + ++ ++ public static int getGLMaximumTextureSize() { ++ return EaglercraftGPU.glGetInteger(GL_MAX_TEXTURE_SIZE); ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/GuardianSound.edit.java b/patches/minecraft/net/minecraft/client/audio/GuardianSound.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/GuardianSound.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/ITickableSound.edit.java b/patches/minecraft/net/minecraft/client/audio/ITickableSound.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/ITickableSound.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/MovingSound.edit.java b/patches/minecraft/net/minecraft/client/audio/MovingSound.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/MovingSound.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecart.edit.java b/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecart.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecart.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecartRiding.edit.java b/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecartRiding.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/MovingSoundMinecartRiding.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/MusicTicker.edit.java b/patches/minecraft/net/minecraft/client/audio/MusicTicker.edit.java new file mode 100644 index 0000000..eb41a0b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/MusicTicker.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 5 @ 4 : 6 + +> CHANGE 10 : 11 @ 11 : 12 + +~ private final EaglercraftRandom rand = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/PositionedSound.edit.java b/patches/minecraft/net/minecraft/client/audio/PositionedSound.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/PositionedSound.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/PositionedSoundRecord.edit.java b/patches/minecraft/net/minecraft/client/audio/PositionedSoundRecord.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/PositionedSoundRecord.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/SoundCategory.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundCategory.edit.java new file mode 100644 index 0000000..e1b658b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/SoundCategory.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 6 @ 5 + ++ import com.google.common.collect.Maps; ++ + +> CHANGE 8 : 9 @ 7 : 8 + +~ MOBS("hostile", 5), ANIMALS("neutral", 6), PLAYERS("player", 7), AMBIENT("ambient", 8), VOICE("voice", 9); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/SoundEventAccessor.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundEventAccessor.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/SoundEventAccessor.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/SoundEventAccessorComposite.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundEventAccessorComposite.edit.java new file mode 100644 index 0000000..bc60f46 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/SoundEventAccessorComposite.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 7 @ 4 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ + +> CHANGE 11 : 12 @ 13 : 14 + +~ private final EaglercraftRandom rnd = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java new file mode 100644 index 0000000..83f7721 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/SoundHandler.edit.java @@ -0,0 +1,96 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> DELETE 5 @ 8 : 9 + +> INSERT 7 : 8 @ 11 + ++ import java.nio.charset.StandardCharsets; + +> DELETE 10 @ 13 : 14 + +> CHANGE 11 : 24 @ 15 : 25 + +~ import java.util.Set; +~ +~ import net.lax1dude.eaglercraft.v1_8.internal.PlatformAudio; +~ +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftSoundManager; +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 31 @ 32 : 36 + +> CHANGE 34 : 35 @ 39 : 41 + +~ + +> CHANGE 51 : 52 @ 57 : 58 + +~ private final EaglercraftSoundManager sndManager; + +> CHANGE 56 : 57 @ 62 : 63 + +~ this.sndManager = new EaglercraftSoundManager(gameSettingsIn, this); + +> CHANGE 69 : 70 @ 75 : 76 + +~ for (Entry entry : (Set) map.entrySet()) { + +> INSERT 84 : 94 @ 90 + ++ public static class SoundMap { ++ ++ protected final Map soundMap; ++ ++ public SoundMap(Map soundMap) { ++ this.soundMap = soundMap; ++ } ++ ++ } ++ + +> CHANGE 95 : 96 @ 91 : 92 + +~ Map map = null; + +> CHANGE 97 : 101 @ 93 : 94 + +~ map = JSONTypeProvider.deserialize(IOUtils.inputStreamToString(stream, StandardCharsets.UTF_8), +~ SoundMap.class).soundMap; +~ } catch (IOException e) { +~ throw new RuntimeException("Exception caught reading JSON", e); + +> INSERT 223 : 227 @ 216 + ++ if (category == SoundCategory.VOICE) { ++ PlatformAudio.setMicVol(volume); ++ } ++ + +> CHANGE 240 : 246 @ 229 : 231 + +~ SoundCategory cat = soundeventaccessorcomposite.getSoundCategory(); +~ for (int i = 0; i < categories.length; ++i) { +~ if (cat == categories[i]) { +~ arraylist.add(soundeventaccessorcomposite); +~ break; +~ } + +> CHANGE 252 : 253 @ 237 : 238 + +~ return (SoundEventAccessorComposite) arraylist.get((new EaglercraftRandom()).nextInt(arraylist.size())); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/SoundList.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundList.edit.java new file mode 100644 index 0000000..0e7a6e9 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/SoundList.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 5 + +> INSERT 4 : 6 @ 6 + ++ import com.google.common.collect.Lists; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/SoundListSerializer.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundListSerializer.edit.java new file mode 100644 index 0000000..8da5553 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/SoundListSerializer.edit.java @@ -0,0 +1,68 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 12 + +> INSERT 3 : 6 @ 13 + ++ import org.json.JSONArray; ++ import org.json.JSONException; ++ import org.json.JSONObject; + +> CHANGE 7 : 11 @ 14 : 18 + +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; +~ +~ public class SoundListSerializer implements JSONTypeDeserializer { +~ public SoundList deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 12 : 13 @ 19 : 20 + +~ soundlist.setReplaceExisting(jsonobject.optBoolean("replace", false)); + +> CHANGE 14 : 15 @ 21 : 22 + +~ .getCategory(jsonobject.optString("category", SoundCategory.MASTER.getCategoryName())); + +> CHANGE 18 : 19 @ 25 : 26 + +~ JSONArray jsonarray = jsonobject.getJSONArray("sounds"); + +> CHANGE 20 : 22 @ 27 : 29 + +~ for (int i = 0; i < jsonarray.length(); ++i) { +~ Object jsonelement = jsonarray.get(i); + +> CHANGE 23 : 28 @ 30 : 35 + +~ if (jsonelement instanceof String) { +~ soundlist$soundentry.setSoundEntryName((String) jsonelement); +~ } else if (jsonelement instanceof JSONObject) { +~ JSONObject jsonobject1 = (JSONObject) jsonelement; +~ soundlist$soundentry.setSoundEntryName(jsonobject1.getString("name")); + +> CHANGE 30 : 31 @ 37 : 38 + +~ .getType(jsonobject1.getString("type")); + +> CHANGE 36 : 37 @ 43 : 44 + +~ float f = jsonobject1.getFloat("volume"); + +> CHANGE 42 : 43 @ 49 : 50 + +~ float f1 = jsonobject1.getFloat("pitch"); + +> CHANGE 48 : 49 @ 55 : 56 + +~ int j = jsonobject1.getInt("weight"); + +> CHANGE 54 : 55 @ 61 : 62 + +~ soundlist$soundentry.setStreaming(jsonobject1.getBoolean("stream")); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/audio/SoundRegistry.edit.java b/patches/minecraft/net/minecraft/client/audio/SoundRegistry.edit.java new file mode 100644 index 0000000..c34ac5b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/audio/SoundRegistry.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 5 + +~ +~ import com.google.common.collect.Maps; +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/entity/AbstractClientPlayer.edit.java b/patches/minecraft/net/minecraft/client/entity/AbstractClientPlayer.edit.java new file mode 100644 index 0000000..8bdb3e3 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/entity/AbstractClientPlayer.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> DELETE 5 @ 6 : 10 + +> DELETE 11 @ 16 : 17 + +> DELETE 55 @ 61 : 79 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/entity/EntityOtherPlayerMP.edit.java b/patches/minecraft/net/minecraft/client/entity/EntityOtherPlayerMP.edit.java new file mode 100644 index 0000000..6ca22dd --- /dev/null +++ b/patches/minecraft/net/minecraft/client/entity/EntityOtherPlayerMP.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/entity/EntityPlayerSP.edit.java b/patches/minecraft/net/minecraft/client/entity/EntityPlayerSP.edit.java new file mode 100644 index 0000000..4c6308d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/entity/EntityPlayerSP.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> DELETE 56 @ 57 : 58 + +> INSERT 78 : 79 @ 80 + ++ private StatFileWriter statWriter; + +> CHANGE 80 : 81 @ 81 : 82 + +~ public EntityPlayerSP(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandler, StatFileWriter statWriter) { + +> DELETE 83 @ 84 : 85 + +> INSERT 85 : 86 @ 87 + ++ this.statWriter = statWriter; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/FontRenderer.edit.java b/patches/minecraft/net/minecraft/client/gui/FontRenderer.edit.java new file mode 100644 index 0000000..6d4e0d2 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/FontRenderer.edit.java @@ -0,0 +1,159 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 6 + +> CHANGE 6 : 13 @ 10 : 12 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.HString; +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 14 @ 13 : 14 + +> DELETE 15 @ 15 : 16 + +> DELETE 22 @ 23 : 25 + +> CHANGE 24 : 26 @ 27 : 29 + +~ protected static final ResourceLocation[] unicodePageLocations = new ResourceLocation[256]; +~ protected int[] charWidth = new int[256]; + +> CHANGE 27 : 46 @ 30 : 49 + +~ public EaglercraftRandom fontRandom = new EaglercraftRandom(); +~ protected byte[] glyphWidth = new byte[65536]; +~ protected int[] colorCode = new int[32]; +~ protected final ResourceLocation locationFontTexture; +~ protected final TextureManager renderEngine; +~ protected float posX; +~ protected float posY; +~ protected boolean unicodeFlag; +~ protected boolean bidiFlag; +~ protected float red; +~ protected float blue; +~ protected float green; +~ protected float alpha; +~ protected int textColor; +~ protected boolean randomStyle; +~ protected boolean boldStyle; +~ protected boolean italicStyle; +~ protected boolean underlineStyle; +~ protected boolean strikethroughStyle; + +> CHANGE 89 : 90 @ 92 : 93 + +~ ImageData bufferedimage; + +> CHANGE 97 : 100 @ 100 : 104 + +~ int i = bufferedimage.width; +~ int j = bufferedimage.height; +~ int[] aint = bufferedimage.pixels; + +> CHANGE 168 : 187 @ 172 : 182 + +~ Tessellator tessellator = Tessellator.getInstance(); +~ WorldRenderer worldrenderer = tessellator.getWorldRenderer(); +~ +~ worldrenderer.begin(Tessellator.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX); +~ +~ worldrenderer.pos(this.posX + (float) k, this.posY, 0.0F).tex((float) i / 128.0F, (float) j / 128.0F) +~ .endVertex(); +~ +~ worldrenderer.pos(this.posX - (float) k, this.posY + 7.99F, 0.0F) +~ .tex((float) i / 128.0F, ((float) j + 7.99F) / 128.0F).endVertex(); +~ +~ worldrenderer.pos(this.posX + f - 1.0F + (float) k, this.posY, 0.0F) +~ .tex(((float) i + f - 1.0F) / 128.0F, (float) j / 128.0F).endVertex(); +~ +~ worldrenderer.pos(this.posX + f - 1.0F - (float) k, this.posY + 7.99F, 0.0F) +~ .tex(((float) i + f - 1.0F) / 128.0F, ((float) j + 7.99F) / 128.0F).endVertex(); +~ +~ tessellator.draw(); +~ + +> CHANGE 193 : 194 @ 188 : 189 + +~ HString.format("textures/font/unicode_page_%02x.png", new Object[] { Integer.valueOf(parInt1) })); + +> CHANGE 217 : 235 @ 212 : 222 + +~ Tessellator tessellator = Tessellator.getInstance(); +~ WorldRenderer worldrenderer = tessellator.getWorldRenderer(); +~ +~ worldrenderer.begin(Tessellator.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX); +~ +~ worldrenderer.pos(this.posX + f5, this.posY, 0.0F).tex(f2 / 256.0F, f3 / 256.0F).endVertex(); +~ +~ worldrenderer.pos(this.posX - f5, this.posY + 7.99F, 0.0F).tex(f2 / 256.0F, (f3 + 15.98F) / 256.0F) +~ .endVertex(); +~ +~ worldrenderer.pos(this.posX + f4 / 2.0F + f5, this.posY, 0.0F).tex((f2 + f4) / 256.0F, f3 / 256.0F) +~ .endVertex(); +~ +~ worldrenderer.pos(this.posX + f4 / 2.0F - f5, this.posY + 7.99F, 0.0F) +~ .tex((f2 + f4) / 256.0F, (f3 + 15.98F) / 256.0F).endVertex(); +~ +~ tessellator.draw(); +~ + +> CHANGE 262 : 270 @ 249 : 256 + +~ // try { +~ // Bidi bidi = new Bidi((new ArabicShaping(8)).shape(parString1), 127); +~ // bidi.setReorderingMode(0); +~ // return bidi.writeReordered(2); +~ // } catch (ArabicShapingException var3) { +~ // return parString1; +~ // } +~ return parString1; + +> CHANGE 272 : 273 @ 258 : 259 + +~ protected void resetStyles() { + +> CHANGE 280 : 281 @ 266 : 267 + +~ protected void renderStringAtPos(String parString1, boolean parFlag) { + +> CHANGE 284 : 285 @ 270 : 271 + +~ int i1 = "0123456789abcdefklmnor".indexOf(Character.toLowerCase(parString1.charAt(i + 1))); + +> CHANGE 429 : 431 @ 415 : 416 + +~ this.posX = x; +~ this.posY = y; + +> DELETE 452 @ 437 : 438 + +> INSERT 453 : 454 @ 439 + ++ return (int) this.posX; + +> CHANGE 603 : 604 @ 588 : 589 + +~ return Arrays.asList(this.wrapFormattedStringToWidth(str, wrapWidth, 0).split("\n")); + +> CHANGE 606 : 610 @ 591 : 592 + +~ String wrapFormattedStringToWidth(String str, int wrapWidth, int depthCheck) { // TODO: fix recursive +~ if (depthCheck > 20) { +~ return str; +~ } + +> CHANGE 618 : 619 @ 600 : 601 + +~ return s + "\n" + this.wrapFormattedStringToWidth(s1, wrapWidth, ++depthCheck); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/Gui.edit.java b/patches/minecraft/net/minecraft/client/gui/Gui.edit.java new file mode 100644 index 0000000..5decd2c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/Gui.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 5 : 7 + +> CHANGE 141 : 142 @ 142 : 143 + +~ public void drawTexturedModalRect(int xCoord, int yCoord, EaglerTextureAtlasSprite textureSprite, int widthIn, + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiButton.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiButton.edit.java new file mode 100644 index 0000000..ef0d172 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiButton.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiButtonLanguage.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiButtonLanguage.edit.java new file mode 100644 index 0000000..23934c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiButtonLanguage.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiChat.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiChat.edit.java new file mode 100644 index 0000000..424b328 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiChat.edit.java @@ -0,0 +1,67 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 4 : 15 @ 5 : 7 + +~ +~ import org.apache.commons.lang3.StringUtils; +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.Keyboard; +~ import net.lax1dude.eaglercraft.v1_8.Mouse; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.minecraft.client.resources.I18n; + +> DELETE 21 @ 13 : 18 + +> INSERT 33 : 35 @ 30 + ++ private GuiButton exitButton; ++ + +> INSERT 44 : 47 @ 39 + ++ if (!(this instanceof GuiSleepMP)) { ++ this.buttonList.add(exitButton = new GuiButton(69, this.width - 100, 3, 97, 20, I18n.format("chat.exit"))); ++ } + +> CHANGE 65 : 66 @ 57 : 58 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 73 : 74 @ 65 : 68 + +~ if (parInt1 != 28 && parInt1 != 156) { + +> CHANGE 117 : 118 @ 111 : 112 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> INSERT 129 : 135 @ 123 + ++ protected void actionPerformed(GuiButton par1GuiButton) { ++ if (par1GuiButton.id == 69) { ++ this.mc.displayGuiScreen(null); ++ } ++ } ++ + +> INSERT 220 : 221 @ 208 + ++ GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + +> INSERT 226 : 228 @ 213 + ++ exitButton.yPosition = 3 + mc.guiAchievement.getHeight(); ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiCommandBlock.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiCommandBlock.edit.java new file mode 100644 index 0000000..9a3fce1 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiCommandBlock.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +~ import net.lax1dude.eaglercraft.v1_8.Keyboard; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 11 @ 12 : 15 + +> CHANGE 55 : 56 @ 59 : 60 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 80 : 81 @ 84 : 85 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 94 : 95 @ 98 : 99 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiConfirmOpenLink.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiConfirmOpenLink.edit.java new file mode 100644 index 0000000..c488800 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiConfirmOpenLink.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 6 + +> CHANGE 32 : 33 @ 36 : 37 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiControls.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiControls.edit.java new file mode 100644 index 0000000..944386f --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiControls.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 9 + +> CHANGE 54 : 55 @ 58 : 59 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 71 : 72 @ 75 : 76 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> CHANGE 89 : 90 @ 93 : 94 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiCustomizeSkin.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiCustomizeSkin.edit.java new file mode 100644 index 0000000..dfa0f97 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiCustomizeSkin.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 32 : 33 @ 35 : 36 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiDisconnected.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiDisconnected.edit.java new file mode 100644 index 0000000..1dbc9ae --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiDisconnected.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 4 @ 4 : 6 + +~ + +> INSERT 5 : 6 @ 7 + ++ import net.minecraft.util.ChatComponentTranslation; + +> CHANGE 21 : 22 @ 22 : 23 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 34 : 35 @ 35 : 36 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> INSERT 55 : 59 @ 56 + ++ ++ public static GuiScreen createRateLimitKick(GuiScreen prev) { ++ return new GuiDisconnected(prev, "connect.failed", new ChatComponentTranslation("disconnect.tooManyRequests")); ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiDownloadTerrain.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiDownloadTerrain.edit.java new file mode 100644 index 0000000..00e33e5 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiDownloadTerrain.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 14 : 15 @ 16 : 17 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiEnchantment.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiEnchantment.edit.java new file mode 100644 index 0000000..4fc81a5 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiEnchantment.edit.java @@ -0,0 +1,48 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 3 : 8 @ 5 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 10 @ 10 : 11 + +> DELETE 23 @ 24 : 25 + +> CHANGE 31 : 32 @ 33 : 34 + +~ private EaglercraftRandom random = new EaglercraftRandom(); + +> CHANGE 61 : 62 @ 63 : 64 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> CHANGE 87 : 90 @ 89 : 92 + +~ GlStateManager.viewport((scaledresolution.getScaledWidth() - 290 - 110) / 2 * scaledresolution.getScaleFactor(), +~ (scaledresolution.getScaledHeight() - 220 + 60) / 2 * scaledresolution.getScaleFactor(), +~ 290 * scaledresolution.getScaleFactor(), 220 * scaledresolution.getScaleFactor()); + +> CHANGE 91 : 92 @ 93 : 94 + +~ GlStateManager.gluPerspective(90.0F, 1.3333334F, 9.0F, 80.0F); + +> INSERT 128 : 129 @ 130 + ++ GlStateManager.enableDepth(); + +> INSERT 130 : 131 @ 131 + ++ GlStateManager.disableDepth(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiErrorScreen.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiErrorScreen.edit.java new file mode 100644 index 0000000..eef24ee --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiErrorScreen.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 25 : 26 @ 28 : 29 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 28 : 29 @ 31 : 32 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiGameOver.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiGameOver.edit.java new file mode 100644 index 0000000..7a6c8eb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiGameOver.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 9 : 10 + +> CHANGE 37 : 38 @ 43 : 44 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 40 : 41 @ 46 : 47 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> INSERT 48 : 50 @ 54 + ++ this.mc.theWorld.sendQuittingDisconnectingPacket(); ++ this.mc.loadWorld((WorldClient) null); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiHopper.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiHopper.edit.java new file mode 100644 index 0000000..f84fcd9 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiHopper.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 5 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiIngame.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiIngame.edit.java new file mode 100644 index 0000000..bfb24ee --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiIngame.edit.java @@ -0,0 +1,63 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 8 @ 2 + ++ import java.util.ArrayList; ++ import java.util.Collection; ++ import java.util.List; ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ + +> CHANGE 11 : 14 @ 5 : 8 + +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 16 @ 10 : 19 + +> DELETE 18 @ 21 : 22 + +> DELETE 19 @ 23 : 24 + +> CHANGE 51 : 52 @ 56 : 57 + +~ private final EaglercraftRandom rand = new EaglercraftRandom(); + +> DELETE 55 @ 60 : 61 + +> DELETE 82 @ 88 : 89 + +> CHANGE 177 : 178 @ 184 : 187 + +~ this.overlayDebug.renderDebugInfo(scaledresolution, partialTicks); + +> INSERT 265 : 268 @ 274 + ++ if (this.mc.gameSettings.hudWorld && (mc.currentScreen == null || !(mc.currentScreen instanceof GuiChat))) { ++ j -= 10; ++ } + +> DELETE 434 @ 440 : 444 + +> CHANGE 451 : 452 @ 461 : 462 + +~ for (Score score : (List) arraylist1) { + +> CHANGE 464 : 465 @ 474 : 475 + +~ for (Score score1 : (List) arraylist1) { + +> CHANGE 808 : 809 @ 818 : 819 + +~ EaglerTextureAtlasSprite textureatlassprite = this.mc.getBlockRendererDispatcher().getBlockModelShapes() + +> DELETE 866 @ 876 : 877 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiIngameMenu.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiIngameMenu.edit.java new file mode 100644 index 0000000..f672bc9 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiIngameMenu.edit.java @@ -0,0 +1,40 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.Mouse; + +> DELETE 7 @ 13 : 14 + +> DELETE 9 @ 16 : 18 + +> DELETE 11 @ 20 : 21 + +> CHANGE 31 : 32 @ 41 : 42 + +~ guibutton.enabled = false; + +> CHANGE 34 : 35 @ 44 : 45 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> DELETE 41 @ 51 : 52 + +> DELETE 46 @ 57 : 60 + +> CHANGE 64 : 65 @ 78 : 79 + +~ break; + +> CHANGE 71 : 74 @ 85 : 86 + +~ if (Mouse.isActuallyGrabbed()) { +~ Mouse.setGrabbed(false); +~ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiKeyBindingList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiKeyBindingList.edit.java new file mode 100644 index 0000000..97c966b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiKeyBindingList.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 5 @ 3 + ++ ++ import net.lax1dude.eaglercraft.v1_8.ArrayUtils; + +> DELETE 6 @ 4 : 7 + +> DELETE 10 @ 11 : 12 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiLabel.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiLabel.edit.java new file mode 100644 index 0000000..3260063 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiLabel.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 7 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 8 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiLanguage.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiLanguage.edit.java new file mode 100644 index 0000000..55c8960 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiLanguage.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 4 : 8 @ 6 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> DELETE 9 @ 7 : 12 + +> CHANGE 43 : 44 @ 46 : 47 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> INSERT 100 : 102 @ 103 + ++ this.mc.loadingScreen.eaglerShow(I18n.format("resourcePack.load.refreshing"), ++ I18n.format("resourcePack.load.pleaseWait")); + +> INSERT 111 : 112 @ 112 + ++ GuiLanguage.this.mc.displayGuiScreen(GuiLanguage.this); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiListButton.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiListButton.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiListButton.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiListExtended.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiListExtended.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiListExtended.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiLockIconButton.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiLockIconButton.edit.java new file mode 100644 index 0000000..23934c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiLockIconButton.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.edit.java new file mode 100644 index 0000000..8006c8a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.edit.java @@ -0,0 +1,294 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 5 @ 6 : 7 + +> INSERT 6 : 7 @ 8 + ++ import java.util.Arrays; + +> CHANGE 9 : 26 @ 10 : 12 + +~ +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion; +~ +~ import com.google.common.base.Charsets; +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.crypto.MD5Digest; +~ import net.lax1dude.eaglercraft.v1_8.crypto.SHA1Digest; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +~ import net.lax1dude.eaglercraft.v1_8.profile.GuiScreenEditProfile; + +> CHANGE 27 : 28 @ 13 : 25 + +~ import net.minecraft.client.audio.PositionedSoundRecord; + +> DELETE 29 @ 26 : 27 + +> DELETE 32 @ 30 : 32 + +> DELETE 34 @ 34 : 43 + +> DELETE 36 @ 45 : 46 + +> CHANGE 37 : 38 @ 47 : 48 + +~ private static final EaglercraftRandom RANDOM = new EaglercraftRandom(); + +> INSERT 39 : 45 @ 49 + ++ private boolean isDefault; ++ private static final int lendef = 5987; ++ private static final byte[] md5def = new byte[] { -61, -53, -36, 27, 24, 27, 103, -31, -58, -116, 113, -60, -67, -8, ++ -77, 30 }; ++ private static final byte[] sha1def = new byte[] { -107, 77, 108, 49, 11, -100, -8, -119, -1, -100, -85, -55, 18, ++ -69, -107, 113, -93, -101, -79, 32 }; + +> CHANGE 48 : 49 @ 52 : 53 + +~ private static DynamicTexture viewportTexture = null; + +> DELETE 50 @ 54 : 55 + +> DELETE 52 @ 57 : 58 + +> DELETE 62 @ 68 : 70 + +> CHANGE 68 : 69 @ 76 : 78 + +~ private static ResourceLocation backgroundTexture = null; + +> DELETE 71 @ 80 : 81 + +> DELETE 110 @ 120 : 126 + +> INSERT 111 : 131 @ 127 + ++ MD5Digest md5 = new MD5Digest(); ++ SHA1Digest sha1 = new SHA1Digest(); ++ byte[] md5out = new byte[16]; ++ byte[] sha1out = new byte[20]; ++ try { ++ byte[] bytes = EaglerInputStream.inputStreamToBytesQuiet( ++ Minecraft.getMinecraft().getResourceManager().getResource(minecraftTitleTextures).getInputStream()); ++ if (bytes != null) { ++ md5.update(bytes, 0, bytes.length); ++ sha1.update(bytes, 0, bytes.length); ++ md5.doFinal(md5out, 0); ++ sha1.doFinal(sha1out, 0); ++ this.isDefault = bytes.length == lendef && Arrays.equals(md5out, md5def) ++ && Arrays.equals(sha1out, sha1def); ++ } else { ++ this.isDefault = false; ++ } ++ } catch (IOException e) { ++ this.isDefault = false; ++ } + +> CHANGE 141 : 142 @ 137 : 138 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 145 : 149 @ 141 : 144 + +~ if (viewportTexture == null) { +~ viewportTexture = new DynamicTexture(256, 256); +~ backgroundTexture = this.mc.getTextureManager().getDynamicTextureLocation("background", viewportTexture); +~ } + +> DELETE 159 @ 154 : 155 + +> CHANGE 160 : 165 @ 156 : 160 + +~ +~ boolean isFork = !EaglercraftVersion.projectOriginAuthor.equalsIgnoreCase(EaglercraftVersion.projectForkVendor); +~ +~ if (isFork && EaglercraftVersion.mainMenuStringF != null && EaglercraftVersion.mainMenuStringF.length() > 0) { +~ i += 11; + +> INSERT 167 : 169 @ 162 + ++ this.addSingleplayerMultiplayerButtons(i, 24); ++ + +> CHANGE 171 : 174 @ 164 : 166 + +~ this.buttonList.add(new GuiButton(4, this.width / 2 + 2, i + 72 + 12, 98, 20, +~ I18n.format("menu.editProfile", new Object[0]))); +~ + +> CHANGE 175 : 180 @ 167 : 168 + +~ +~ if (isFork) { +~ this.openGLWarning1 = EaglercraftVersion.mainMenuStringE; +~ this.openGLWarning2 = EaglercraftVersion.mainMenuStringF; +~ boolean line2 = this.openGLWarning2 != null && this.openGLWarning2.length() > 0; + +> CHANGE 184 : 185 @ 172 : 173 + +~ this.field_92021_u = ((GuiButton) this.buttonList.get(0)).yPosition - (line2 ? 32 : 21); + +> CHANGE 186 : 187 @ 174 : 175 + +~ this.field_92019_w = this.field_92021_u + (line2 ? 24 : 11); + +> CHANGE 193 : 197 @ 181 : 184 + +~ // this.buttonList +~ // .add(new GuiButton(1, this.width / 2 - 100, parInt1, +~ // I18n.format("menu.singleplayer", new Object[0]))); +~ this.buttonList.add(new GuiButton(2, this.width / 2 - 100, parInt1 + parInt2 * 0, + +> CHANGE 198 : 202 @ 185 : 187 + +~ GuiButton btn; +~ this.buttonList.add(btn = new GuiButton(14, this.width / 2 - 100, parInt1 + parInt2 * 1, +~ I18n.format("menu.forkOnGitlab", new Object[0]))); +~ btn.enabled = EaglercraftVersion.mainMenuEnableGithubButton; + +> CHANGE 204 : 205 @ 189 : 203 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 214 : 215 @ 212 : 213 + +~ logger.error("Singleplayer was removed dumbass"); + +> DELETE 221 @ 219 : 223 + +> CHANGE 222 : 223 @ 224 : 225 + +~ this.mc.displayGuiScreen(new GuiScreenEditProfile(this)); + +> CHANGE 225 : 227 @ 227 : 229 + +~ if (parGuiButton.id == 14) { +~ EagRuntime.openLink(EaglercraftVersion.projectForkURL); + +> DELETE 229 @ 231 : 240 + +> DELETE 231 @ 242 : 270 + +> CHANGE 237 : 238 @ 276 : 277 + +~ GlStateManager.gluPerspective(120.0F, 1.0F, 0.05F, 10.0F); + +> CHANGE 311 : 315 @ 350 : 354 + +~ this.mc.getTextureManager().bindTexture(backgroundTexture); +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +~ EaglercraftGPU.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 256, 256); + +> DELETE 345 @ 384 : 385 + +> DELETE 354 @ 394 : 395 + +> DELETE 378 @ 419 : 421 + +> CHANGE 385 : 386 @ 428 : 429 + +~ if (this.isDefault || (double) this.updateCounter < 1.0E-4D) { + +> INSERT 396 : 409 @ 439 + ++ boolean isForkLabel = ((this.openGLWarning1 != null && this.openGLWarning1.length() > 0) ++ || (this.openGLWarning2 != null && this.openGLWarning2.length() > 0)); ++ ++ if (isForkLabel) { ++ drawRect(this.field_92022_t - 3, this.field_92021_u - 3, this.field_92020_v + 3, this.field_92019_w, ++ 1428160512); ++ if (this.openGLWarning1 != null) ++ this.drawString(this.fontRendererObj, this.openGLWarning1, this.field_92022_t, this.field_92021_u, -1); ++ if (this.openGLWarning2 != null) ++ this.drawString(this.fontRendererObj, this.openGLWarning2, (this.width - this.field_92024_r) / 2, ++ this.field_92021_u + 12, -1); ++ } ++ + +> CHANGE 411 : 412 @ 441 : 442 + +~ GlStateManager.rotate(isForkLabel ? -12.0F : -20.0F, 0.0F, 0.0F, 1.0F); + +> INSERT 415 : 418 @ 445 + ++ if (isForkLabel) { ++ f1 *= 0.8f; ++ } + +> DELETE 421 @ 448 : 452 + +> INSERT 422 : 425 @ 453 + ++ String s = EaglercraftVersion.mainMenuStringA; ++ this.drawString(this.fontRendererObj, s, 2, this.height - 20, -1); ++ s = EaglercraftVersion.mainMenuStringB; + +> CHANGE 426 : 428 @ 454 : 455 + +~ +~ String s1 = EaglercraftVersion.mainMenuStringC; + +> INSERT 429 : 432 @ 456 + ++ this.height - 20, -1); ++ s1 = EaglercraftVersion.mainMenuStringD; ++ this.drawString(this.fontRendererObj, s1, this.width - this.fontRendererObj.getStringWidth(s1) - 2, + +> CHANGE 433 : 441 @ 457 : 463 + +~ +~ String lbl = "CREDITS.txt"; +~ int w = fontRendererObj.getStringWidth(lbl) * 3 / 4; +~ +~ if (i >= (this.width - w - 4) && i <= this.width && j >= 0 && j <= 9) { +~ drawRect((this.width - w - 4), 0, this.width, 10, 0x55000099); +~ } else { +~ drawRect((this.width - w - 4), 0, this.width, 10, 0x55200000); + +> INSERT 443 : 449 @ 465 + ++ GlStateManager.pushMatrix(); ++ GlStateManager.translate((this.width - w - 2), 2.0f, 0.0f); ++ GlStateManager.scale(0.75f, 0.75f, 0.75f); ++ drawString(fontRendererObj, lbl, 0, 0, 16777215); ++ GlStateManager.popMatrix(); ++ + +> CHANGE 452 : 464 @ 468 : 476 + +~ protected void mouseClicked(int par1, int par2, int par3) { +~ if (par3 == 0) { +~ String lbl = "CREDITS.txt"; +~ int w = fontRendererObj.getStringWidth(lbl) * 3 / 4; +~ if (par1 >= (this.width - w - 4) && par1 <= this.width && par2 >= 0 && par2 <= 10) { +~ String resStr = EagRuntime.getResourceString("/assets/eagler/CREDITS.txt"); +~ if (resStr != null) { +~ EagRuntime.openCreditsPopup(resStr); +~ } +~ mc.getSoundHandler() +~ .playSound(PositionedSoundRecord.create(new ResourceLocation("gui.button.press"), 1.0F)); +~ return; + +> DELETE 465 @ 477 : 478 + +> INSERT 466 : 467 @ 479 + ++ super.mouseClicked(par1, par2, par3); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMemoryErrorScreen.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMemoryErrorScreen.edit.java new file mode 100644 index 0000000..0f2981c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiMemoryErrorScreen.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> CHANGE 13 : 14 @ 18 : 19 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 22 : 23 @ 27 : 28 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMerchant.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMerchant.edit.java new file mode 100644 index 0000000..a18e361 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiMerchant.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 7 @ 5 : 6 + +> DELETE 8 @ 7 : 8 + +> DELETE 21 @ 21 : 23 + +> CHANGE 65 : 66 @ 67 : 68 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java new file mode 100644 index 0000000..df574bc --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiMultiplayer.edit.java @@ -0,0 +1,114 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import java.io.IOException; ++ + +> CHANGE 6 : 10 @ 4 : 17 + +~ +~ import net.lax1dude.eaglercraft.v1_8.Keyboard; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 13 @ 20 : 22 + +> DELETE 14 @ 23 : 26 + +> DELETE 17 @ 29 : 30 + +> DELETE 29 @ 42 : 44 + +> INSERT 30 : 31 @ 45 + ++ private static long lastRefreshCommit = 0l; + +> CHANGE 41 : 42 @ 55 : 56 + +~ this.savedServerList = ServerList.getServerList(); + +> DELETE 43 @ 57 : 66 + +> CHANGE 78 : 79 @ 101 : 108 + +~ this.savedServerList.updateServerPing(); + +> DELETE 83 @ 112 : 118 + +> CHANGE 85 : 86 @ 120 : 121 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 122 : 127 @ 157 : 158 + +~ long millis = System.currentTimeMillis(); +~ if (millis - lastRefreshCommit > 700l) { +~ lastRefreshCommit = millis; +~ this.refreshServerList(); +~ } + +> CHANGE 132 : 133 @ 163 : 164 + +~ public void refreshServerList() { + +> CHANGE 147 : 152 @ 178 : 180 + +~ long millis = System.currentTimeMillis(); +~ if (millis - lastRefreshCommit > 700l) { +~ lastRefreshCommit = millis; +~ this.refreshServerList(); +~ } + +> CHANGE 167 : 172 @ 195 : 197 + +~ long millis = System.currentTimeMillis(); +~ if (millis - lastRefreshCommit > 700l) { +~ lastRefreshCommit = millis; +~ this.refreshServerList(); +~ } + +> CHANGE 182 : 187 @ 207 : 209 + +~ long millis = System.currentTimeMillis(); +~ if (millis - lastRefreshCommit > 700l) { +~ lastRefreshCommit = millis; +~ this.refreshServerList(); +~ } + +> DELETE 188 @ 210 : 211 + +> CHANGE 190 : 191 @ 213 : 214 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> DELETE 209 @ 232 : 241 + +> CHANGE 220 : 221 @ 252 : 253 + +~ } else if (i < this.serverListSelector.getSize() - 1) { + +> DELETE 223 @ 255 : 264 + +> DELETE 256 @ 297 : 302 + +> DELETE 257 @ 303 : 304 + +> CHANGE 270 : 271 @ 317 : 319 + +~ if (guilistextended$iguilistentry != null) { + +> DELETE 277 @ 325 : 326 + +> DELETE 279 @ 328 : 332 + +> CHANGE 283 : 284 @ 336 : 337 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiNewChat.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiNewChat.edit.java new file mode 100644 index 0000000..07ec3b9 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiNewChat.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 10 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 11 @ 6 : 12 + +> DELETE 15 @ 16 : 18 + +> CHANGE 123 : 124 @ 126 : 127 + +~ for (IChatComponent ichatcomponent : (List) list) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOptionButton.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOptionButton.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiOptionButton.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOptionSlider.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOptionSlider.edit.java new file mode 100644 index 0000000..23934c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiOptionSlider.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOptions.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOptions.edit.java new file mode 100644 index 0000000..620f1fb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiOptions.edit.java @@ -0,0 +1,69 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.vfs.SYS; + +> DELETE 8 @ 7 : 24 + +> DELETE 10 @ 26 : 27 + +> INSERT 22 : 24 @ 39 + ++ private GuiButton notSoSuperSecret; ++ private GuiButton broadcastSettings; + +> CHANGE 70 : 72 @ 85 : 87 + +~ this.buttonList.add(notSoSuperSecret = new GuiButton(8675309, this.width / 2 + 5, this.height / 6 + 48 - 6, 150, +~ 20, "Super Secret Settings...") { + +> CHANGE 85 : 88 @ 100 : 102 + +~ this.buttonList.add(broadcastSettings = new GuiButton(107, this.width / 2 + 5, this.height / 6 + 72 - 6, 150, +~ 20, I18n.format(EagRuntime.getRecText(), new Object[0]))); +~ broadcastSettings.enabled = EagRuntime.recSupported(); + +> CHANGE 96 : 98 @ 110 : 111 + +~ GuiButton rp; +~ this.buttonList.add(rp = new GuiButton(105, this.width / 2 - 155, this.height / 6 + 144 - 6, 150, 20, + +> CHANGE 99 : 101 @ 112 : 113 + +~ GuiButton b; +~ this.buttonList.add(b = new GuiButton(104, this.width / 2 + 5, this.height / 6 + 144 - 6, 150, 20, + +> INSERT 102 : 103 @ 114 + ++ b.enabled = false; + +> INSERT 105 : 107 @ 116 + ++ ++ rp.enabled = SYS.VFS != null; + +> CHANGE 129 : 130 @ 138 : 139 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 162 : 163 @ 171 : 172 + +~ notSoSuperSecret.displayString = "Nope!"; + +> DELETE 185 @ 194 : 199 + +> CHANGE 201 : 203 @ 215 : 222 + +~ EagRuntime.toggleRec(); +~ broadcastSettings.displayString = I18n.format(EagRuntime.getRecText(), new Object[0]); + +> DELETE 204 @ 223 : 224 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOptionsRowList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOptionsRowList.edit.java new file mode 100644 index 0000000..d9325ac --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiOptionsRowList.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 7 @ 5 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java new file mode 100644 index 0000000..0d32d82 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiOverlayDebug.edit.java @@ -0,0 +1,384 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import java.text.SimpleDateFormat; + +> INSERT 4 : 6 @ 5 + ++ import java.util.Calendar; ++ import java.util.Iterator; + +> INSERT 7 : 8 @ 6 + ++ import java.util.Locale; + +> INSERT 9 : 21 @ 7 + ++ import java.util.TimeZone; ++ ++ import com.google.common.base.Strings; ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.Display; ++ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> CHANGE 26 : 29 @ 12 : 17 + +~ import net.minecraft.client.renderer.RenderHelper; +~ import net.minecraft.client.renderer.entity.RenderManager; +~ import net.minecraft.client.resources.I18n; + +> CHANGE 30 : 32 @ 18 : 19 + +~ import net.minecraft.entity.EntityLivingBase; +~ import net.minecraft.potion.PotionEffect; + +> CHANGE 40 : 41 @ 27 : 28 + +~ import net.minecraft.world.biome.BiomeGenBase; + +> DELETE 42 @ 29 : 31 + +> CHANGE 52 : 55 @ 41 : 42 + +~ public void renderDebugInfo(ScaledResolution scaledResolutionIn, float partialTicks) { +~ int ww = scaledResolutionIn.getScaledWidth(); +~ int hh = scaledResolutionIn.getScaledHeight(); + +> CHANGE 56 : 79 @ 43 : 49 + +~ if (this.mc.gameSettings.showDebugInfo) { +~ GlStateManager.pushMatrix(); +~ this.renderDebugInfoLeft(); +~ this.renderDebugInfoRight(scaledResolutionIn); +~ GlStateManager.popMatrix(); +~ if (this.mc.gameSettings.field_181657_aC) { +~ this.func_181554_e(); +~ } +~ } else { +~ int i = 2; +~ +~ if (this.mc.gameSettings.hudFps) { +~ drawFPS(2, i); +~ i += 9; +~ } +~ +~ if (this.mc.gameSettings.hudCoords) { +~ drawXYZ(2, i); +~ } +~ +~ if (this.mc.gameSettings.hudPlayer) { +~ drawPlayer(ww - 3, 3, partialTicks); +~ } + +> INSERT 81 : 105 @ 51 + ++ if (this.mc.currentScreen == null || !(this.mc.currentScreen instanceof GuiChat)) { ++ if (this.mc.gameSettings.hudStats) { ++ drawStatsHUD(ww - 2, hh - 2); ++ } ++ ++ if (this.mc.gameSettings.hudWorld) { ++ drawWorldHUD(2, hh - 2); ++ } ++ } ++ ++ if (this.mc.gameSettings.hudCoords && this.mc.joinWorldTickCounter < 80) { ++ if (this.mc.joinWorldTickCounter > 70) { ++ GlStateManager.enableBlend(); ++ GlStateManager.blendFunc(770, 771); ++ } ++ int i = this.mc.joinWorldTickCounter - 70; ++ if (i < 0) ++ i = 0; ++ drawHideHUD(ww / 2, hh - 70, (10 - i) * 0xFF / 10); ++ if (this.mc.joinWorldTickCounter > 70) { ++ GlStateManager.disableBlend(); ++ } ++ } ++ + +> INSERT 108 : 300 @ 54 + ++ private void drawFPS(int x, int y) { ++ this.fontRenderer.drawStringWithShadow(this.mc.renderGlobal.getDebugInfoShort(), x, y, 0xFFFFFF); ++ } ++ ++ private void drawXYZ(int x, int y) { ++ Entity e = mc.getRenderViewEntity(); ++ BlockPos blockpos = new BlockPos(e.posX, e.getEntityBoundingBox().minY, e.posZ); ++ this.fontRenderer.drawStringWithShadow( ++ "x: " + blockpos.getX() + ", y: " + blockpos.getY() + ", z: " + blockpos.getZ(), x, y, 0xFFFFFF); ++ } ++ ++ private void drawStatsHUD(int x, int y) { ++ int i = 9; ++ ++ String line = "Walk: " + EnumChatFormatting.YELLOW + HString.format("%.2f", mc.thePlayer.getAIMoveSpeed()) ++ + EnumChatFormatting.WHITE + " Flight: " ++ + (mc.thePlayer.capabilities.allowFlying ++ ? ("" + EnumChatFormatting.YELLOW + mc.thePlayer.capabilities.getFlySpeed()) ++ : EnumChatFormatting.RED + "No"); ++ int lw = fontRenderer.getStringWidth(line); ++ this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF); ++ i += 11; ++ ++ line = "Food: " + EnumChatFormatting.YELLOW + mc.thePlayer.getFoodStats().getFoodLevel() ++ + EnumChatFormatting.WHITE + ", Sat: " + EnumChatFormatting.YELLOW ++ + HString.format("%.1f", mc.thePlayer.getFoodStats().getSaturationLevel()); ++ lw = fontRenderer.getStringWidth(line); ++ this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF); ++ i += 11; ++ ++ line = "Amr: " + EnumChatFormatting.YELLOW + mc.thePlayer.getTotalArmorValue() + EnumChatFormatting.WHITE ++ + ", Health: " + EnumChatFormatting.RED + HString.format("%.1f", mc.thePlayer.getHealth()); ++ lw = fontRenderer.getStringWidth(line); ++ this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF); ++ i += 11; ++ ++ int xpc = mc.thePlayer.xpBarCap(); ++ line = "XP: " + EnumChatFormatting.GREEN + MathHelper.floor_float(mc.thePlayer.experience * xpc) ++ + EnumChatFormatting.WHITE + " / " + EnumChatFormatting.GREEN + xpc; ++ lw = fontRenderer.getStringWidth(line); ++ this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF); ++ i += 11; ++ ++ Iterator potions = mc.thePlayer.getActivePotionEffects().iterator(); ++ if (potions.hasNext()) { ++ while (potions.hasNext()) { ++ i += 11; ++ PotionEffect e = potions.next(); ++ int t = e.getDuration() / 20; ++ int m = t / 60; ++ int s = t % 60; ++ int j = e.getAmplifier(); ++ if (j > 0) { ++ line = I18n.format(e.getEffectName()) ++ + (j > 0 ? (" " + EnumChatFormatting.YELLOW + EnumChatFormatting.BOLD ++ + I18n.format("potion.potency." + j) + EnumChatFormatting.RESET) : "") ++ + " [" + EnumChatFormatting.YELLOW + HString.format("%02d:%02d", m, s) ++ + EnumChatFormatting.RESET + "]"; ++ } else { ++ line = I18n.format(e.getEffectName()) + " [" + EnumChatFormatting.YELLOW ++ + HString.format("%02d:%02d", m, s) + EnumChatFormatting.RESET + "]"; ++ } ++ lw = fontRenderer.getStringWidth(line); ++ this.fontRenderer.drawStringWithShadow(line, x - lw, y - i, 0xFFFFFF); ++ } ++ } ++ ++ } ++ ++ public static final int ticksAtMidnight = 18000; ++ public static final int ticksPerDay = 24000; ++ public static final int ticksPerHour = 1000; ++ public static final double ticksPerMinute = 1000d / 60d; ++ public static final double ticksPerSecond = 1000d / 60d / 60d; ++ private static final SimpleDateFormat SDFTwentyFour = new SimpleDateFormat("HH:mm", Locale.ENGLISH); ++ private static final SimpleDateFormat SDFTwelve = new SimpleDateFormat("h:mm aa", Locale.ENGLISH); ++ ++ private void drawWorldHUD(int x, int y) { ++ /* ++ * Math was taken from: https://github.com/EssentialsX/Essentials/blob/ ++ * dc7fb919391d62de45e17b51ae1e6fe3e66d7ac6/Essentials/src/main/java/com/ ++ * earth2me/essentials/utils/DescParseTickFormat.java ++ */ ++ long totalTicks = mc.theWorld.getWorldTime(); ++ long ticks = totalTicks; ++ ticks = ticks - ticksAtMidnight + ticksPerDay; ++ final long days = ticks / ticksPerDay; ++ ticks -= days * ticksPerDay; ++ final long hours = ticks / ticksPerHour; ++ ticks -= hours * ticksPerHour; ++ final long minutes = (long) Math.floor(ticks / ticksPerMinute); ++ final double dticks = ticks - minutes * ticksPerMinute; ++ final long seconds = (long) Math.floor(dticks / ticksPerSecond); ++ ++ // TODO: why does desktop JRE not apply "GMT" correctly? ++ final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.ENGLISH); ++ ++ cal.setLenient(true); ++ cal.set(0, Calendar.JANUARY, 1, 0, 0, 0); ++ cal.add(Calendar.DAY_OF_YEAR, (int) days); ++ cal.add(Calendar.HOUR_OF_DAY, (int) hours); ++ cal.add(Calendar.MINUTE, (int) minutes); ++ cal.add(Calendar.SECOND, (int) seconds + 1); ++ ++ String timeString = EnumChatFormatting.WHITE + "Day " + ((totalTicks + 30000l) / 24000l) + " (" ++ + EnumChatFormatting.YELLOW ++ + (this.mc.gameSettings.hud24h ? SDFTwentyFour : SDFTwelve).format(cal.getTime()) ++ + EnumChatFormatting.WHITE + ")"; ++ ++ Entity e = mc.getRenderViewEntity(); ++ BlockPos blockpos = new BlockPos(e.posX, MathHelper.clamp_double(e.getEntityBoundingBox().minY, 0.0D, 254.0D), ++ e.posZ); ++ BiomeGenBase biome = mc.theWorld.getBiomeGenForCoords(blockpos); ++ ++ Chunk c = mc.theWorld.getChunkFromBlockCoords(blockpos); ++ int blockLight = c.getLightFor(EnumSkyBlock.BLOCK, blockpos); ++ int skyLight = c.getLightFor(EnumSkyBlock.SKY, blockpos) - mc.theWorld.calculateSkylightSubtracted(1.0f); ++ int totalLight = Math.max(blockLight, skyLight); ++ EnumChatFormatting lightColor = blockLight < 8 ++ ? ((skyLight < 8 || !mc.theWorld.isDaytime()) ? EnumChatFormatting.RED : EnumChatFormatting.YELLOW) ++ : EnumChatFormatting.GREEN; ++ String lightString = "Light: " + lightColor + totalLight + EnumChatFormatting.WHITE; ++ ++ float temp = biome.getFloatTemperature(blockpos); ++ ++ String tempString = "Temp: " ++ + ((blockLight > 11 || temp > 0.15f) ? EnumChatFormatting.YELLOW : EnumChatFormatting.AQUA) ++ + HString.format("%.2f", temp) + EnumChatFormatting.WHITE; ++ ++ this.fontRenderer.drawStringWithShadow(timeString, x, y - 30, 0xFFFFFF); ++ this.fontRenderer.drawStringWithShadow("Biome: " + EnumChatFormatting.AQUA + biome.biomeName, x, y - 19, ++ 0xFFFFFF); ++ this.fontRenderer.drawStringWithShadow(lightString + " " + tempString, x, y - 8, 0xFFFFFF); ++ } ++ ++ private void drawPlayer(int x, int y, float partialTicks) { ++ Entity e = mc.getRenderViewEntity(); ++ if (e != null && e instanceof EntityLivingBase) { ++ EntityLivingBase ent = (EntityLivingBase) e; ++ GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); ++ GlStateManager.enableDepth(); ++ GlStateManager.enableColorMaterial(); ++ GlStateManager.pushMatrix(); ++ GlStateManager.translate((float) x - 10, (float) y + 36, 50.0F); ++ GlStateManager.scale(-17.0F, 17.0F, 17.0F); ++ GlStateManager.rotate(180.0F, 0.0F, 0.0F, 1.0F); ++ float f = ent.renderYawOffset; ++ float f1 = ent.rotationYaw; ++ float f2 = ent.prevRotationYaw; ++ float f3 = ent.prevRotationYawHead; ++ float f4 = ent.rotationYawHead; ++ float f5 = ent.prevRenderYawOffset; ++ GlStateManager.rotate(115.0F, 0.0F, 1.0F, 0.0F); ++ RenderHelper.enableStandardItemLighting(); ++ float f6 = ent.prevRenderYawOffset + (ent.renderYawOffset - ent.prevRenderYawOffset) * partialTicks; ++ ent.rotationYawHead -= f6; ++ ent.prevRotationYawHead -= f6; ++ ent.rotationYawHead *= 0.5f; ++ ent.prevRotationYawHead *= 0.5f; ++ ent.renderYawOffset = 0.0f; ++ ent.prevRenderYawOffset = 0.0f; ++ ent.prevRotationYaw = 0.0f; ++ ent.rotationYaw = 0.0f; ++ GlStateManager.rotate(-135.0F ++ - (ent.prevRotationYawHead + (ent.rotationYawHead - ent.prevRotationYawHead) * partialTicks) * 0.5F, ++ 0.0F, 1.0F, 0.0F); ++ GlStateManager.rotate(ent.rotationPitch * 0.2f, 1.0F, 0.0F, 0.0F); ++ RenderManager rendermanager = Minecraft.getMinecraft().getRenderManager(); ++ rendermanager.setPlayerViewY(180.0F); ++ rendermanager.setRenderShadow(false); ++ rendermanager.renderEntityWithPosYaw(ent, 0.0D, 0.0D, 0.0D, 0.0F, partialTicks); ++ rendermanager.setRenderShadow(true); ++ ent.renderYawOffset = f; ++ ent.rotationYaw = f1; ++ ent.prevRotationYaw = f2; ++ ent.prevRotationYawHead = f3; ++ ent.rotationYawHead = f4; ++ ent.prevRenderYawOffset = f5; ++ GlStateManager.popMatrix(); ++ RenderHelper.disableStandardItemLighting(); ++ GlStateManager.disableDepth(); ++ GlStateManager.disableRescaleNormal(); ++ GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit); ++ GlStateManager.disableTexture2D(); ++ GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit); ++ } ++ } ++ ++ private void drawHideHUD(int x, int y, int fade) { ++ drawCenteredString(fontRenderer, I18n.format("options.hud.note"), x, y, 0xEECC00 | (fade << 24)); ++ } ++ + +> INSERT 339 : 346 @ 93 + ++ if (!this.mc.gameSettings.showDebugInfo) { ++ BlockPos blockpos = new BlockPos(this.mc.getRenderViewEntity().posX, ++ this.mc.getRenderViewEntity().getEntityBoundingBox().minY, this.mc.getRenderViewEntity().posZ); ++ return Lists.newArrayList(new String[] { this.mc.renderGlobal.getDebugInfoShort(), ++ "x: " + blockpos.getX() + ", y: " + blockpos.getY() + ", z: " + blockpos.getZ() }); ++ } ++ + +> CHANGE 356 : 357 @ 103 : 104 + +~ HString.format("Chunk-relative: %d %d %d", new Object[] { Integer.valueOf(blockpos.getX() & 15), + +> CHANGE 382 : 383 @ 129 : 130 + +~ HString.format("XYZ: %.3f / %.5f / %.3f", + +> CHANGE 386 : 387 @ 133 : 134 + +~ HString.format("Block: %d %d %d", + +> CHANGE 389 : 390 @ 136 : 137 + +~ HString.format("Chunk: %d %d %d in %d %d %d", + +> CHANGE 393 : 394 @ 140 : 141 + +~ HString.format("Facing: %s (%s) (%.1f / %.1f)", + +> CHANGE 399 : 400 @ 146 : 147 + +~ arraylist.add("Biome: " + chunk.getBiome(blockpos).biomeName); + +> CHANGE 404 : 405 @ 151 : 161 + +~ arraylist.add(HString.format("Local Difficulty: %.2f (Day %d)", + +> DELETE 409 @ 165 : 169 + +> CHANGE 413 : 414 @ 173 : 174 + +~ arraylist.add(HString.format("Looking at: %d %d %d", new Object[] { Integer.valueOf(blockpos1.getX()), + +> CHANGE 422 : 450 @ 182 : 199 + +~ ArrayList arraylist; +~ if (EagRuntime.getPlatformType() != EnumPlatformType.JAVASCRIPT) { +~ long i = EagRuntime.maxMemory(); +~ long j = EagRuntime.totalMemory(); +~ long k = EagRuntime.freeMemory(); +~ long l = j - k; +~ arraylist = Lists.newArrayList(new String[] { +~ HString.format("Java: %s %dbit", +~ new Object[] { System.getProperty("java.version"), +~ Integer.valueOf(this.mc.isJava64bit() ? 64 : 32) }), +~ HString.format("Mem: % 2d%% %03d/%03dMB", +~ new Object[] { Long.valueOf(l * 100L / i), Long.valueOf(bytesToMb(l)), +~ Long.valueOf(bytesToMb(i)) }), +~ HString.format("Allocated: % 2d%% %03dMB", +~ new Object[] { Long.valueOf(j * 100L / i), Long.valueOf(bytesToMb(j)) }), +~ "", HString.format("CPU: %s", new Object[] { "eaglercraft" }), "", +~ HString.format("Display: %dx%d (%s)", +~ new Object[] { Integer.valueOf(Display.getWidth()), Integer.valueOf(Display.getHeight()), +~ EaglercraftGPU.glGetString(7936) }), +~ EaglercraftGPU.glGetString(7937), EaglercraftGPU.glGetString(7938) }); +~ } else { +~ arraylist = Lists.newArrayList( +~ new String[] { "Java: TeaVM", "", HString.format("CPU: %s", new Object[] { "eaglercraft" }), "", +~ HString.format("Display: %dx%d (%s)", +~ new Object[] { Integer.valueOf(Display.getWidth()), +~ Integer.valueOf(Display.getHeight()), EaglercraftGPU.glGetString(7936) }), +~ EaglercraftGPU.glGetString(7937), EaglercraftGPU.glGetString(7938) }); +~ } + +> DELETE 458 @ 207 : 211 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiPageButtonList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiPageButtonList.edit.java new file mode 100644 index 0000000..45ec92e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiPageButtonList.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import java.util.List; ++ + +> CHANGE 8 : 9 @ 6 : 7 + +~ + +> DELETE 10 @ 8 : 16 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiPlayerTabOverlay.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiPlayerTabOverlay.edit.java new file mode 100644 index 0000000..c3e3ab1 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiPlayerTabOverlay.edit.java @@ -0,0 +1,51 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 4 : 10 @ 7 + ++ ++ import com.google.common.collect.ComparisonChain; ++ import com.google.common.collect.Ordering; ++ ++ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 11 @ 8 : 10 + +> DELETE 13 @ 12 : 13 + +> CHANGE 59 : 60 @ 59 : 60 + +~ for (NetworkPlayerInfo networkplayerinfo : (List) list) { + +> CHANGE 80 : 81 @ 80 : 82 + +~ boolean flag = true; + +> CHANGE 101 : 102 @ 102 : 103 + +~ for (String s : (List) list1) { + +> CHANGE 109 : 110 @ 110 : 111 + +~ for (String s2 : (List) list2) { + +> CHANGE 118 : 119 @ 119 : 120 + +~ for (String s3 : (List) list1) { + +> CHANGE 151 : 152 @ 152 : 153 + +~ if (entityplayer == null || entityplayer.isWearing(EnumPlayerModelParts.HAT)) { + +> CHANGE 185 : 186 @ 186 : 187 + +~ for (String s4 : (List) list2) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiRepair.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiRepair.edit.java new file mode 100644 index 0000000..6e36ea3 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiRepair.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 3 : 7 @ 5 + ++ ++ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; ++ import net.lax1dude.eaglercraft.v1_8.Keyboard; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 8 @ 6 : 7 + +> DELETE 9 @ 8 : 9 + +> DELETE 21 @ 21 : 22 + +> CHANGE 91 : 92 @ 92 : 93 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 113 : 114 @ 114 : 115 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackAvailable.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackAvailable.edit.java new file mode 100644 index 0000000..681a62a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackAvailable.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackList.edit.java new file mode 100644 index 0000000..681a62a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackList.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiResourcePackSelected.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackSelected.edit.java new file mode 100644 index 0000000..681a62a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiResourcePackSelected.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreen.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreen.edit.java new file mode 100644 index 0000000..e23cddc --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreen.edit.java @@ -0,0 +1,149 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 11 + +> DELETE 3 @ 12 : 14 + +> INSERT 7 : 22 @ 18 + ++ ++ import org.apache.commons.lang3.StringUtils; ++ ++ import com.google.common.base.Splitter; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Sets; ++ ++ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ++ import net.lax1dude.eaglercraft.v1_8.Keyboard; ++ import net.lax1dude.eaglercraft.v1_8.Mouse; ++ import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> CHANGE 23 : 24 @ 19 : 27 + +~ import net.minecraft.client.gui.inventory.GuiContainer; + +> DELETE 26 @ 29 : 30 + +> INSERT 28 : 29 @ 32 + ++ import net.minecraft.client.resources.I18n; + +> DELETE 42 @ 45 : 51 + +> CHANGE 59 : 61 @ 68 : 69 + +~ private String clickedLinkURI; +~ protected long showingCloseKey = 0; + +> INSERT 71 : 101 @ 79 + ++ long millis = System.currentTimeMillis(); ++ long closeKeyTimeout = millis - showingCloseKey; ++ if (closeKeyTimeout < 3000l) { ++ int alpha1 = 0xC0000000; ++ int alpha2 = 0xFF000000; ++ if (closeKeyTimeout > 2500l) { ++ float f = (float) (3000l - closeKeyTimeout) * 0.002f; ++ if (f < 0.03f) ++ f = 0.03f; ++ alpha1 = (int) (f * 192.0f) << 24; ++ alpha2 = (int) (f * 255.0f) << 24; ++ } ++ String str; ++ int k = getCloseKey(); ++ if (k == KeyboardConstants.KEY_GRAVE) { ++ str = I18n.format("gui.exitKeyRetarded"); ++ } else { ++ str = I18n.format("gui.exitKey", Keyboard.getKeyName(k)); ++ } ++ int w = fontRendererObj.getStringWidth(str); ++ int x = (width - w - 4) / 2; ++ int y = 10; ++ drawRect(x, y, x + w + 4, y + 12, alpha1); ++ if (closeKeyTimeout > 2500l) ++ GlStateManager.enableBlend(); ++ fontRendererObj.drawStringWithShadow(str, x + 2, y + 2, 0xFFAAAA | alpha2); ++ if (closeKeyTimeout > 2500l) ++ GlStateManager.disableBlend(); ++ } ++ + +> CHANGE 103 : 115 @ 81 : 83 + +~ protected int getCloseKey() { +~ if (this instanceof GuiContainer) { +~ return this.mc.gameSettings.keyBindInventory.getKeyCode(); +~ } else { +~ return this.mc.gameSettings.keyBindClose.getKeyCode(); +~ } +~ } +~ +~ protected void keyTyped(char parChar1, int parInt1) { +~ if (((this.mc.theWorld == null || this.mc.thePlayer.getHealth() <= 0.0F) && parInt1 == 1) +~ || parInt1 == this.mc.gameSettings.keyBindClose.getKeyCode() +~ || (parInt1 == 1 && this.mc.gameSettings.keyBindClose.getKeyCode() == 0)) { + +> INSERT 119 : 121 @ 87 + ++ } else if (parInt1 == 1) { ++ showingCloseKey = System.currentTimeMillis(); + +> DELETE 122 @ 88 : 89 + +> CHANGE 125 : 126 @ 92 : 102 + +~ return EagRuntime.getClipboard(); + +> CHANGE 130 : 131 @ 106 : 113 + +~ EagRuntime.setClipboard(copyText); + +> INSERT 307 : 308 @ 289 + ++ String uri = clickevent.getValue(); + +> CHANGE 309 : 314 @ 290 : 311 + +~ if (this.mc.gameSettings.chatLinksPrompt) { +~ this.clickedLinkURI = uri; +~ this.mc.displayGuiScreen(new GuiConfirmOpenLink(this, clickevent.getValue(), 31102009, false)); +~ } else { +~ this.openWebLink(uri); + +> CHANGE 316 : 317 @ 313 : 315 + +~ // rip + +> CHANGE 322 : 329 @ 320 : 326 + +~ /* +~ * ChatUserInfo chatuserinfo = +~ * this.mc.getTwitchStream().func_152926_a(clickevent.getValue()); if +~ * (chatuserinfo != null) { this.mc.displayGuiScreen(new +~ * GuiTwitchUserMode(this.mc.getTwitchStream(), chatuserinfo)); } else { } +~ */ +~ LOGGER.error("Tried to handle twitch user but couldn\'t find them!"); + +> CHANGE 352 : 353 @ 349 : 350 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> CHANGE 377 : 378 @ 374 : 375 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 497 : 499 @ 494 : 503 + +~ private void openWebLink(String parURI) { +~ EagRuntime.openLink(parURI); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenAddServer.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenAddServer.edit.java new file mode 100644 index 0000000..26d1a58 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenAddServer.edit.java @@ -0,0 +1,91 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.Keyboard; + +> DELETE 6 @ 10 : 11 + +> CHANGE 13 : 14 @ 18 : 37 + +~ private GuiButton hideAddress; + +> INSERT 27 : 28 @ 50 + ++ int i = 80; + +> CHANGE 29 : 32 @ 51 : 52 + +~ GuiButton done; +~ GuiButton cancel; +~ this.buttonList.add(done = new GuiButton(0, this.width / 2 - 100, i + 96 + 12, + +> CHANGE 33 : 34 @ 53 : 54 + +~ this.buttonList.add(cancel = new GuiButton(1, this.width / 2 - 100, i + 120 + 12, + +> CHANGE 35 : 42 @ 55 : 56 + +~ if (EagRuntime.requireSSL()) { +~ done.yPosition = cancel.yPosition; +~ done.width = (done.width / 2) - 2; +~ cancel.width = (cancel.width / 2) - 2; +~ done.xPosition += cancel.width + 4; +~ } +~ this.buttonList.add(this.serverResourcePacks = new GuiButton(2, this.width / 2 - 100, i + 54, + +> INSERT 44 : 47 @ 58 + ++ this.buttonList.add(this.hideAddress = new GuiButton(3, this.width / 2 - 100, i + 78, ++ I18n.format("addServer.hideAddress", new Object[0]) + ": " ++ + I18n.format(this.serverData.hideAddress ? "gui.yes" : "gui.no", new Object[0]))); + +> CHANGE 53 : 54 @ 64 : 67 + +~ ((GuiButton) this.buttonList.get(0)).enabled = this.serverIPField.getText().trim().length() > 0; + +> CHANGE 60 : 61 @ 73 : 74 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 62 : 67 @ 75 : 76 + +~ if (parGuiButton.id == 3) { +~ this.serverData.hideAddress = !this.serverData.hideAddress; +~ this.hideAddress.displayString = I18n.format("addServer.hideAddress", new Object[0]) + ": " +~ + I18n.format(this.serverData.hideAddress ? "gui.yes" : "gui.no", new Object[0]); +~ } else if (parGuiButton.id == 2) { + +> CHANGE 75 : 77 @ 84 : 86 + +~ this.serverData.serverName = this.serverNameField.getText().trim(); +~ this.serverData.serverIP = this.serverIPField.getText().trim(); + +> CHANGE 83 : 84 @ 92 : 93 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 95 : 96 @ 104 : 106 + +~ ((GuiButton) this.buttonList.get(0)).enabled = this.serverIPField.getText().trim().length() > 0; + +> CHANGE 98 : 99 @ 108 : 109 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> INSERT 112 : 118 @ 122 + ++ if (EagRuntime.requireSSL()) { ++ this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn1"), this.width / 2, 184, ++ 0xccccff); ++ this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn2"), this.width / 2, 196, ++ 0xccccff); ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenBook.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenBook.edit.java new file mode 100644 index 0000000..98735f5 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenBook.edit.java @@ -0,0 +1,43 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 6 + +> INSERT 3 : 13 @ 7 + ++ ++ import org.json.JSONException; ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; ++ import net.lax1dude.eaglercraft.v1_8.Keyboard; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 14 @ 8 : 12 + +> DELETE 30 @ 28 : 31 + +> CHANGE 169 : 170 @ 170 : 171 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 208 : 209 @ 209 : 210 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 346 : 347 @ 347 : 348 + +~ } catch (JSONException var13) { + +> CHANGE 381 : 382 @ 382 : 383 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenOptionsSounds.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenOptionsSounds.edit.java new file mode 100644 index 0000000..a126206 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenOptionsSounds.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 7 @ 7 : 10 + +> CHANGE 44 : 45 @ 47 : 48 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenResourcePacks.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenResourcePacks.edit.java new file mode 100644 index 0000000..599ec22 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenResourcePacks.edit.java @@ -0,0 +1,104 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import java.io.ByteArrayInputStream; + +> DELETE 4 @ 5 : 6 + +> CHANGE 7 : 15 @ 9 : 14 + +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.vfs.SYS; +~ import net.lax1dude.eaglercraft.v1_8.internal.FileChooserResult; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 20 @ 19 : 23 + +> CHANGE 35 : 37 @ 38 : 39 + +~ GuiButton btn; +~ this.buttonList.add(btn = new GuiOptionButton(2, this.width / 2 - 154, this.height - 48, + +> INSERT 38 : 39 @ 40 + ++ btn.enabled = SYS.VFS != null; + +> CHANGE 49 : 50 @ 50 : 51 + +~ for (ResourcePackRepository.Entry resourcepackrepository$entry : (List) arraylist) { + +> CHANGE 94 : 95 @ 95 : 96 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 97 : 100 @ 98 : 135 + +~ if (SYS.VFS == null) +~ return; +~ EagRuntime.displayFileChooser("application/zip", "zip"); + +> CHANGE 115 : 116 @ 150 : 151 + +~ for (ResourcePackRepository.Entry resourcepackrepository$entry : (List) arraylist) { + +> INSERT 122 : 124 @ 157 + ++ this.mc.loadingScreen.eaglerShow(I18n.format("resourcePack.load.refreshing"), ++ I18n.format("resourcePack.load.pleaseWait")); + +> DELETE 127 @ 160 : 161 + +> CHANGE 133 : 173 @ 167 : 168 + +~ public void updateScreen() { +~ FileChooserResult packFile = null; +~ if (EagRuntime.fileChooserHasResult()) { +~ packFile = EagRuntime.getFileChooserResult(); +~ } +~ if (packFile == null) +~ return; +~ logger.info("Loading resource pack: {}", packFile.fileName); +~ mc.loadingScreen.eaglerShow(I18n.format("resourcePack.load.loading"), packFile.fileName); +~ SYS.loadResourcePack(packFile.fileName, new ByteArrayInputStream(packFile.fileData), null); +~ +~ ArrayList arraylist = Lists.newArrayList(); +~ +~ for (ResourcePackListEntry resourcepacklistentry : this.selectedResourcePacks) { +~ if (resourcepacklistentry instanceof ResourcePackListEntryFound) { +~ arraylist.add(((ResourcePackListEntryFound) resourcepacklistentry).func_148318_i()); +~ } +~ } +~ +~ Collections.reverse(arraylist); +~ this.mc.getResourcePackRepository().setRepositories(arraylist); +~ this.mc.gameSettings.resourcePacks.clear(); +~ this.mc.gameSettings.field_183018_l.clear(); +~ +~ for (ResourcePackRepository.Entry resourcepackrepository$entry : (List) arraylist) { +~ this.mc.gameSettings.resourcePacks.add(resourcepackrepository$entry.getResourcePackName()); +~ if (resourcepackrepository$entry.func_183027_f() != 1) { +~ this.mc.gameSettings.field_183018_l.add(resourcepackrepository$entry.getResourcePackName()); +~ } +~ } +~ +~ this.mc.gameSettings.saveOptions(); +~ +~ boolean wasChanged = this.changed; +~ this.changed = false; +~ this.initGui(); +~ this.changed = wasChanged; +~ } +~ +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenServerList.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenServerList.edit.java new file mode 100644 index 0000000..c0cc544 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenServerList.edit.java @@ -0,0 +1,62 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.Keyboard; + +> DELETE 6 @ 8 : 9 + +> CHANGE 28 : 34 @ 31 : 32 + +~ if (EagRuntime.requireSSL()) { +~ this.field_146302_g = new GuiTextField(2, this.fontRendererObj, this.width / 2 - 100, this.height / 4 + 35, +~ 200, 20); +~ } else { +~ this.field_146302_g = new GuiTextField(2, this.fontRendererObj, this.width / 2 - 100, 116, 200, 20); +~ } + +> CHANGE 37 : 38 @ 35 : 37 + +~ ((GuiButton) this.buttonList.get(0)).enabled = this.field_146302_g.getText().trim().length() > 0; + +> CHANGE 46 : 47 @ 45 : 46 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 51 : 52 @ 50 : 51 + +~ this.field_146301_f.serverIP = this.field_146302_g.getText().trim(); + +> CHANGE 58 : 59 @ 57 : 58 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 60 : 61 @ 59 : 61 + +~ ((GuiButton) this.buttonList.get(0)).enabled = this.field_146302_g.getText().trim().length() > 0; + +> CHANGE 67 : 68 @ 67 : 68 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> CHANGE 76 : 87 @ 76 : 78 + +~ if (EagRuntime.requireSSL()) { +~ this.drawString(this.fontRendererObj, I18n.format("addServer.enterIp", new Object[0]), this.width / 2 - 100, +~ this.height / 4 + 19, 10526880); +~ this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn1"), this.width / 2, +~ this.height / 4 + 30 + 37, 0xccccff); +~ this.drawCenteredString(this.fontRendererObj, I18n.format("addServer.SSLWarn2"), this.width / 2, +~ this.height / 4 + 30 + 49, 0xccccff); +~ } else { +~ this.drawString(this.fontRendererObj, I18n.format("addServer.enterIp", new Object[0]), this.width / 2 - 100, +~ 100, 10526880); +~ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiScreenWorking.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiScreenWorking.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiScreenWorking.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSleepMP.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSleepMP.edit.java new file mode 100644 index 0000000..b76dcef --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiSleepMP.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 13 : 14 @ 16 : 17 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 30 : 31 @ 33 : 34 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSlider.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSlider.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiSlider.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSlot.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSlot.edit.java new file mode 100644 index 0000000..757e8ff --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiSlot.edit.java @@ -0,0 +1,38 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 7 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.Mouse; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 8 @ 3 : 6 + +> DELETE 9 @ 7 : 8 + +> DELETE 11 @ 10 : 11 + +> INSERT 13 : 16 @ 13 + ++ ++ private static final Logger excLogger = LogManager.getLogger("GuiSlotRenderer"); ++ + +> CHANGE 397 : 404 @ 394 : 395 + +~ try { +~ this.drawSlot(j, mouseXIn, k, l, parInt3, parInt4); +~ } catch (Throwable t) { +~ excLogger.error( +~ "Exception caught rendering a slot of a list on the screen! Game will continue running due to the suspicion that this could be an intentional crash attempt, and therefore it would be inconvenient if the user were to be locked out of this gui due to repeatedly triggering a full crash report"); +~ excLogger.error(t); +~ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiSpectator.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiSpectator.edit.java new file mode 100644 index 0000000..0b0539a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiSpectator.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 5 + +> DELETE 8 @ 9 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiTextField.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiTextField.edit.java new file mode 100644 index 0000000..cc97fc8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiTextField.edit.java @@ -0,0 +1,47 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 4 : 7 @ 4 : 9 + +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 8 @ 10 : 11 + +> CHANGE 19 : 20 @ 22 : 23 + +~ protected String text = ""; + +> INSERT 64 : 76 @ 67 + ++ public void updateText(String parString1) { ++ if (this.field_175209_y.apply(parString1)) { ++ if (parString1.length() > this.maxStringLength) { ++ this.text = parString1.substring(0, this.maxStringLength); ++ } else { ++ this.text = parString1; ++ } ++ ++ this.setCursorPosition(cursorPosition); ++ } ++ } ++ + +> CHANGE 429 : 432 @ 420 : 421 + +~ GlStateManager.color(0.2F, 0.2F, 1.0F, 1.0F); +~ GlStateManager.enableBlend(); +~ GlStateManager.blendFunc(775, 770); + +> DELETE 433 @ 422 : 424 + +> CHANGE 439 : 440 @ 430 : 431 + +~ GlStateManager.disableBlend(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiUtilRenderComponents.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiUtilRenderComponents.edit.java new file mode 100644 index 0000000..6ed2506 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiUtilRenderComponents.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 8 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java new file mode 100644 index 0000000..0eb863f --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiVideoSettings.edit.java @@ -0,0 +1,44 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 9 + +~ + +> CHANGE 16 : 20 @ 21 : 24 + +~ GameSettings.Options.PARTICLES, GameSettings.Options.MIPMAP_LEVELS, GameSettings.Options.BLOCK_ALTERNATIVES, +~ GameSettings.Options.ENTITY_SHADOWS, GameSettings.Options.FOG, GameSettings.Options.HUD_FPS, +~ GameSettings.Options.HUD_COORDS, GameSettings.Options.HUD_PLAYER, GameSettings.Options.HUD_STATS, +~ GameSettings.Options.HUD_WORLD, GameSettings.Options.HUD_24H, GameSettings.Options.CHUNK_FIX }; + +> CHANGE 31 : 33 @ 35 : 38 + +~ GameSettings.Options[] agamesettings$options = new GameSettings.Options[videoOptions.length]; +~ int i = 0; + +> CHANGE 34 : 37 @ 39 : 53 + +~ for (GameSettings.Options gamesettings$options : videoOptions) { +~ agamesettings$options[i] = gamesettings$options; +~ ++i; + +> INSERT 39 : 42 @ 55 + ++ this.optionsRowList = new GuiOptionsRowList(this.mc, this.width, this.height, 32, this.height - 32, 25, ++ agamesettings$options); ++ + +> CHANGE 49 : 50 @ 62 : 63 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 59 : 60 @ 72 : 73 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiWinGame.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiWinGame.edit.java new file mode 100644 index 0000000..b1648cd --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiWinGame.edit.java @@ -0,0 +1,47 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 5 + +> CHANGE 6 : 16 @ 8 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.base.Charsets; +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +~ import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; + +> DELETE 18 @ 11 : 14 + +> DELETE 19 @ 15 : 16 + +> DELETE 23 @ 20 : 23 + +> CHANGE 51 : 52 @ 51 : 52 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 80 : 81 @ 80 : 81 + +~ EaglercraftRandom random = new EaglercraftRandom(8124371L); + +> CHANGE 85 : 86 @ 85 : 86 + +~ for (s = s.replaceAll("PLAYERNAME", EaglerProfile.getName()); s + +> CHANGE 108 : 109 @ 108 : 109 + +~ s = s.replaceAll("PLAYERNAME", EaglerProfile.getName()); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/GuiYesNo.edit.java b/patches/minecraft/net/minecraft/client/gui/GuiYesNo.edit.java new file mode 100644 index 0000000..f4c43e1 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/GuiYesNo.edit.java @@ -0,0 +1,40 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 3 : 6 @ 5 : 9 + +~ +~ import com.google.common.collect.Lists; +~ + +> INSERT 17 : 18 @ 20 + ++ private boolean opaqueBackground = false; + +> CHANGE 46 : 47 @ 48 : 49 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 51 : 56 @ 53 : 54 + +~ if (opaqueBackground) { +~ this.drawBackground(0); +~ } else { +~ this.drawDefaultBackground(); +~ } + +> INSERT 85 : 90 @ 83 + ++ ++ public GuiYesNo withOpaqueBackground() { ++ opaqueBackground = true; ++ return this; ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/MapItemRenderer.edit.java b/patches/minecraft/net/minecraft/client/gui/MapItemRenderer.edit.java new file mode 100644 index 0000000..0fe1c8c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/MapItemRenderer.edit.java @@ -0,0 +1,38 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 8 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 9 @ 5 : 6 + +> DELETE 10 @ 7 : 8 + +> INSERT 75 : 76 @ 73 + ++ int c; + +> CHANGE 77 : 78 @ 74 : 75 + +~ c = (i + i / 128 & 1) * 8 + 16 << 24; + +> CHANGE 79 : 80 @ 76 : 77 + +~ c = MapColor.mapColorArray[j / 4].func_151643_b(j & 3); + +> INSERT 81 : 82 @ 78 + ++ this.mapTextureData[i] = (c & 0xFF00FF00) | ((c & 0x00FF0000) >> 16) | ((c & 0x000000FF) << 16); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/ScreenChatOptions.edit.java b/patches/minecraft/net/minecraft/client/gui/ScreenChatOptions.edit.java new file mode 100644 index 0000000..bea096f --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/ScreenChatOptions.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> CHANGE 41 : 42 @ 46 : 47 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/ServerListEntryNormal.edit.java b/patches/minecraft/net/minecraft/client/gui/ServerListEntryNormal.edit.java new file mode 100644 index 0000000..3b027ac --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/ServerListEntryNormal.edit.java @@ -0,0 +1,76 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 10 + +> CHANGE 3 : 7 @ 11 : 13 + +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 8 @ 14 : 18 + +> CHANGE 9 : 10 @ 19 : 22 + +~ import net.minecraft.client.resources.I18n; + +> DELETE 12 @ 24 : 27 + +> DELETE 15 @ 30 : 32 + +> DELETE 21 @ 38 : 39 + +> DELETE 22 @ 40 : 41 + +> DELETE 28 @ 47 : 49 + +> DELETE 36 @ 57 : 74 + +> CHANGE 44 : 54 @ 82 : 85 + +~ for (int k1 = 0; k1 < 2; ++k1) { +~ if (k1 < list.size()) { +~ this.mc.fontRendererObj.drawString((String) list.get(k1), j + 32 + 3, +~ k + 12 + this.mc.fontRendererObj.FONT_HEIGHT * k1, 8421504); +~ } else if (k1 == 1) { +~ this.mc.fontRendererObj.drawString( +~ this.field_148301_e.hideAddress ? I18n.format("selectServer.hiddenAddress", new Object[0]) +~ : this.field_148301_e.serverIP, +~ j + 32 + 3, k + 12 + this.mc.fontRendererObj.FONT_HEIGHT * k1 + k1, 0x444444); +~ } + +> CHANGE 103 : 107 @ 134 : 139 + +~ if (this.mc.gameSettings.touchscreen || flag) { +~ GlStateManager.enableShaderBlendAdd(); +~ GlStateManager.setShaderBlendSrc(0.6f, 0.6f, 0.6f, 1.0f); +~ GlStateManager.setShaderBlendAdd(0.3f, 0.3f, 0.3f, 0.0f); + +> CHANGE 108 : 110 @ 140 : 143 + +~ if (field_148301_e.iconTextureObject != null) { +~ this.func_178012_a(j, k, field_148301_e.iconResourceLocation); + +> INSERT 113 : 116 @ 146 + ++ if (this.mc.gameSettings.touchscreen || flag) { ++ GlStateManager.disableShaderBlendAdd(); ++ } + +> CHANGE 127 : 128 @ 157 : 158 + +~ // Gui.drawRect(j, k, j + 32, k + 32, -1601138544); + +> INSERT 161 : 162 @ 191 + ++ GlStateManager.blendFunc(770, 771); + +> DELETE 170 @ 199 : 238 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/ServerSelectionList.edit.java b/patches/minecraft/net/minecraft/client/gui/ServerSelectionList.edit.java new file mode 100644 index 0000000..71fadd9 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/ServerSelectionList.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 7 @ 5 : 10 + +> DELETE 8 @ 11 : 12 + +> DELETE 12 @ 16 : 18 + +> CHANGE 21 : 22 @ 27 : 38 + +~ return (GuiListExtended.IGuiListEntry) this.field_148198_l.get(i); + +> CHANGE 25 : 26 @ 41 : 42 + +~ return this.field_148198_l.size(); + +> DELETE 49 @ 65 : 74 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievement.edit.java b/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievement.edit.java new file mode 100644 index 0000000..31c62b4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievement.edit.java @@ -0,0 +1,49 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 5 : 6 + +> INSERT 120 : 152 @ 120 + ++ public int getHeight() { ++ if (this.theAchievement != null && this.notificationTime != 0L && Minecraft.getMinecraft().thePlayer != null) { ++ double d0 = (double) (Minecraft.getSystemTime() - this.notificationTime) / 3000.0D; ++ if (!this.permanentNotification) { ++ if (d0 < 0.0D || d0 > 1.0D) { ++ this.notificationTime = 0L; ++ return 0; ++ } ++ } else if (d0 > 0.5D) { ++ d0 = 0.5D; ++ } ++ ++ double d1 = d0 * 2.0D; ++ if (d1 > 1.0D) { ++ d1 = 2.0D - d1; ++ } ++ ++ d1 = d1 * 4.0D; ++ d1 = 1.0D - d1; ++ if (d1 < 0.0D) { ++ d1 = 0.0D; ++ } ++ ++ d1 = d1 * d1; ++ d1 = d1 * d1; ++ ++ return 32 - (int) (d1 * 32.0D); ++ } else { ++ return 0; ++ } ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievements.edit.java b/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievements.edit.java new file mode 100644 index 0000000..6d54191 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/achievement/GuiAchievements.edit.java @@ -0,0 +1,65 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 7 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.Mouse; +~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 13 @ 10 : 11 + +> DELETE 14 @ 12 : 13 + +> DELETE 24 @ 23 : 24 + +> CHANGE 67 : 68 @ 67 : 68 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 76 : 78 @ 76 : 84 + +~ protected int getCloseKey() { +~ return this.mc.gameSettings.keyBindInventory.getKeyCode(); + +> CHANGE 154 : 155 @ 160 : 161 + +~ GlStateManager.disableLighting(); + +> INSERT 216 : 220 @ 222 + ++ GlStateManager.enableDepth(); ++ GlStateManager.clearDepth(0.0f); ++ GlStateManager.clear(256); ++ GlStateManager.clearDepth(1.0f); + +> CHANGE 237 : 238 @ 239 : 240 + +~ EaglercraftRandom random = new EaglercraftRandom(); + +> CHANGE 246 : 248 @ 248 : 249 + +~ random.setSeed( +~ (long) (this.mc.getSession().getProfile().getId().hashCode() + k1 + l2 + (l1 + k2) * 16)); + +> CHANGE 249 : 250 @ 250 : 251 + +~ EaglerTextureAtlasSprite textureatlassprite = this.func_175371_a(Blocks.sand); + +> DELETE 276 @ 277 : 278 + +> CHANGE 434 : 435 @ 436 : 437 + +~ GlStateManager.disableBlend(); + +> CHANGE 438 : 439 @ 440 : 441 + +~ private EaglerTextureAtlasSprite func_175371_a(Block parBlock) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/achievement/GuiStats.edit.java b/patches/minecraft/net/minecraft/client/gui/achievement/GuiStats.edit.java new file mode 100644 index 0000000..8037666 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/achievement/GuiStats.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 6 : 12 @ 7 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.Mouse; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 18 @ 13 : 14 + +> DELETE 20 @ 16 : 17 + +> DELETE 31 @ 28 : 29 + +> CHANGE 102 : 103 @ 100 : 101 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/CreativeCrafting.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/CreativeCrafting.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/CreativeCrafting.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiBeacon.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiBeacon.edit.java new file mode 100644 index 0000000..9f63b8b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiBeacon.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 9 @ 7 : 9 + +> DELETE 21 @ 21 : 23 + +> CHANGE 101 : 102 @ 103 : 104 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiBrewingStand.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiBrewingStand.edit.java new file mode 100644 index 0000000..5dbc961 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiBrewingStand.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiChest.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiChest.edit.java new file mode 100644 index 0000000..23934c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiChest.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainer.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainer.edit.java new file mode 100644 index 0000000..faab928 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainer.edit.java @@ -0,0 +1,63 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 3 : 11 @ 5 + ++ ++ import com.google.common.collect.Sets; ++ ++ import net.lax1dude.eaglercraft.v1_8.Keyboard; ++ import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> DELETE 13 @ 7 : 9 + +> DELETE 14 @ 10 : 11 + +> DELETE 22 @ 19 : 20 + +> CHANGE 210 : 211 @ 208 : 209 + +~ EaglerTextureAtlasSprite textureatlassprite = this.mc.getTextureMapBlocks().getAtlasSprite(s1); + +> CHANGE 267 : 268 @ 265 : 266 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> CHANGE 503 : 507 @ 501 : 503 + +~ protected void keyTyped(char parChar1, int parInt1) { +~ if (parInt1 == this.mc.gameSettings.keyBindClose.getKeyCode() +~ || parInt1 == this.mc.gameSettings.keyBindInventory.getKeyCode() +~ || (parInt1 == 1 && this.mc.gameSettings.keyBindClose.getKeyCode() == 0)) { + +> CHANGE 508 : 510 @ 504 : 512 + +~ if (this.mc.currentScreen == null) { +~ this.mc.setIngameFocus(); + +> INSERT 511 : 522 @ 513 + ++ } else if (parInt1 == 1) { ++ showingCloseKey = System.currentTimeMillis(); ++ } else { ++ this.checkHotbarKeys(parInt1); ++ if (this.theSlot != null && this.theSlot.getHasStack()) { ++ if (parInt1 == this.mc.gameSettings.keyBindPickBlock.getKeyCode()) { ++ this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, 0, 3); ++ } else if (parInt1 == this.mc.gameSettings.keyBindDrop.getKeyCode()) { ++ this.handleMouseClick(this.theSlot, this.theSlot.slotNumber, isCtrlKeyDown() ? 1 : 0, 4); ++ } ++ } + +> DELETE 523 @ 514 : 515 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainerCreative.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainerCreative.edit.java new file mode 100644 index 0000000..ad8bfd3 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiContainerCreative.edit.java @@ -0,0 +1,49 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 6 : 12 @ 7 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.Keyboard; ++ import net.lax1dude.eaglercraft.v1_8.Mouse; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 16 @ 11 : 14 + +> DELETE 35 @ 33 : 35 + +> CHANGE 221 : 222 @ 221 : 222 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 235 : 238 @ 235 : 236 + +~ if (parInt1 == getCloseKey()) { +~ mc.displayGuiScreen(null); +~ } else if (!this.checkHotbarKeys(parInt1)) { + +> INSERT 248 : 253 @ 246 + ++ protected int getCloseKey() { ++ return selectedTabIndex != CreativeTabs.tabAllSearch.getTabIndex() ? super.getCloseKey() ++ : mc.gameSettings.keyBindClose.getKeyCode(); ++ } ++ + +> CHANGE 302 : 303 @ 295 : 296 + +~ protected void mouseClicked(int parInt1, int parInt2, int parInt3) { + +> CHANGE 625 : 626 @ 618 : 619 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiCrafting.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiCrafting.edit.java new file mode 100644 index 0000000..5dbc961 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiCrafting.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiDispenser.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiDispenser.edit.java new file mode 100644 index 0000000..5dbc961 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiDispenser.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiEditSign.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiEditSign.edit.java new file mode 100644 index 0000000..f6532b6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiEditSign.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.Keyboard; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 8 @ 7 : 8 + +> DELETE 15 @ 15 : 16 + +> CHANGE 49 : 50 @ 50 : 51 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 59 : 60 @ 60 : 61 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiFurnace.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiFurnace.edit.java new file mode 100644 index 0000000..5dbc961 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiFurnace.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiInventory.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiInventory.edit.java new file mode 100644 index 0000000..0d9d03e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiInventory.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> DELETE 8 @ 7 : 9 + +> DELETE 9 @ 10 : 11 + +> INSERT 58 : 59 @ 60 + ++ GlStateManager.enableDepth(); + +> INSERT 61 : 62 @ 62 + ++ GlStateManager.disableDepth(); + +> CHANGE 104 : 105 @ 104 : 105 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.edit.java b/patches/minecraft/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/inventory/GuiScreenHorseInventory.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/BaseSpectatorGroup.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/BaseSpectatorGroup.edit.java new file mode 100644 index 0000000..89ce3a0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/BaseSpectatorGroup.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 6 + +~ +~ import com.google.common.collect.Lists; +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuObject.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuObject.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuObject.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuRecipient.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuView.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuView.edit.java new file mode 100644 index 0000000..46d9776 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/ISpectatorMenuView.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/PlayerMenuObject.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/PlayerMenuObject.edit.java new file mode 100644 index 0000000..1742fa4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/PlayerMenuObject.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 7 @ 6 : 9 + +> DELETE 14 @ 16 : 17 + +> DELETE 17 @ 20 : 22 + +> CHANGE 28 : 30 @ 33 : 34 + +~ Minecraft.getMinecraft().getTextureManager().bindTexture( +~ Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(profile).getResourceLocation()); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/SpectatorMenu.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/SpectatorMenu.edit.java new file mode 100644 index 0000000..4cc7924 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/SpectatorMenu.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 4 : 8 @ 6 + ++ ++ import com.google.common.base.Objects; ++ import com.google.common.collect.Lists; ++ + +> DELETE 11 @ 9 : 13 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/categories/SpectatorDetails.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/categories/SpectatorDetails.edit.java new file mode 100644 index 0000000..ffa8afe --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/categories/SpectatorDetails.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.base.Objects; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.edit.java new file mode 100644 index 0000000..f31a944 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToPlayer.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 5 : 10 @ 8 + ++ ++ import com.google.common.collect.ComparisonChain; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Ordering; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToTeam.edit.java b/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToTeam.edit.java new file mode 100644 index 0000000..086f276 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/gui/spectator/categories/TeleportToTeam.edit.java @@ -0,0 +1,41 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 8 @ 4 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 16 @ 13 : 14 + +> DELETE 17 @ 15 : 16 + +> INSERT 83 : 87 @ 82 + ++ this.field_178677_c = DefaultPlayerSkin.getDefaultSkinLegacy(); ++ ++ // TODO: program team skins ++ + +> CHANGE 88 : 93 @ 83 : 87 + +~ // String s1 = ((NetworkPlayerInfo) this.field_178675_d +~ // .get((new +~ // EaglercraftRandom()).nextInt(this.field_178675_d.size()))).getGameProfile().getName(); +~ // this.field_178677_c = AbstractClientPlayer.getLocationSkin(s1); +~ // AbstractClientPlayer.getDownloadImageSkin(this.field_178677_c, s1); + +> CHANGE 94 : 95 @ 88 : 89 + +~ // this.field_178677_c = DefaultPlayerSkin.getDefaultSkinLegacy(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/main/GameConfiguration.edit.java b/patches/minecraft/net/minecraft/client/main/GameConfiguration.edit.java new file mode 100644 index 0000000..ac5fed0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/main/GameConfiguration.edit.java @@ -0,0 +1,34 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> DELETE 7 @ 10 : 11 + +> DELETE 8 @ 12 : 13 + +> CHANGE 10 : 11 @ 15 : 17 + +~ GameConfiguration.DisplayInformation displayInfoIn, GameConfiguration.GameInformation gameInfoIn) { + +> DELETE 13 @ 19 : 20 + +> DELETE 14 @ 21 : 22 + +> DELETE 30 @ 38 : 52 + +> DELETE 40 @ 62 : 72 + +> DELETE 42 @ 74 : 77 + +> CHANGE 43 : 44 @ 78 : 80 + +~ public UserInformation(Session parSession) { + +> DELETE 45 @ 81 : 84 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/main/Main.edit.java b/patches/minecraft/net/minecraft/client/main/Main.edit.java new file mode 100644 index 0000000..88042d4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/main/Main.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 17 + +~ import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; + +> DELETE 4 @ 18 : 19 + +> CHANGE 7 : 8 @ 22 : 23 + +~ public static void appMain(String[] astring) { + +> DELETE 9 @ 24 : 68 + +> DELETE 10 @ 69 : 119 + +> CHANGE 11 : 15 @ 120 : 131 + +~ new GameConfiguration.UserInformation(new Session()), +~ new GameConfiguration.DisplayInformation(854, 480, false, true), +~ new GameConfiguration.GameInformation(false, "1.8.8")); +~ PlatformRuntime.setThreadName("Client thread"); + +> DELETE 17 @ 133 : 137 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelArmorStand.edit.java b/patches/minecraft/net/minecraft/client/model/ModelArmorStand.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelArmorStand.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelArmorStandArmor.edit.java b/patches/minecraft/net/minecraft/client/model/ModelArmorStandArmor.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelArmorStandArmor.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelBanner.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBanner.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelBanner.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelBase.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBase.edit.java new file mode 100644 index 0000000..e4e66ef --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelBase.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 4 : 9 @ 6 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ + +> CHANGE 30 : 31 @ 30 : 31 + +~ public ModelRenderer getRandomModelBox(EaglercraftRandom rand) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelBat.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBat.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelBat.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelBiped.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBiped.edit.java new file mode 100644 index 0000000..7437e1f --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelBiped.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> CHANGE 74 : 75 @ 76 : 77 + +~ if (entity != null && entity.isSneaking()) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelBlaze.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBlaze.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelBlaze.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelBoat.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBoat.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelBoat.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelBook.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBook.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelBook.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelBox.edit.java b/patches/minecraft/net/minecraft/client/model/ModelBox.edit.java new file mode 100644 index 0000000..27f04db --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelBox.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelChest.edit.java b/patches/minecraft/net/minecraft/client/model/ModelChest.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelChest.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelChicken.edit.java b/patches/minecraft/net/minecraft/client/model/ModelChicken.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelChicken.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelCow.edit.java b/patches/minecraft/net/minecraft/client/model/ModelCow.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelCow.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelCreeper.edit.java b/patches/minecraft/net/minecraft/client/model/ModelCreeper.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelCreeper.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelDragon.edit.java b/patches/minecraft/net/minecraft/client/model/ModelDragon.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelDragon.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelEnderCrystal.edit.java b/patches/minecraft/net/minecraft/client/model/ModelEnderCrystal.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelEnderCrystal.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelEnderMite.edit.java b/patches/minecraft/net/minecraft/client/model/ModelEnderMite.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelEnderMite.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelEnderman.edit.java b/patches/minecraft/net/minecraft/client/model/ModelEnderman.edit.java new file mode 100644 index 0000000..f89cd31 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelEnderman.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 39 : 40 @ 41 : 42 + +~ this.bipedBody.rotationPointZ = 0.0F; + +> CHANGE 92 : 93 @ 94 : 95 + +~ this.bipedHead.rotationPointZ = 0.0F; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelGhast.edit.java b/patches/minecraft/net/minecraft/client/model/ModelGhast.edit.java new file mode 100644 index 0000000..c947a6b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelGhast.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> CHANGE 17 : 18 @ 18 : 19 + +~ EaglercraftRandom random = new EaglercraftRandom(1660L); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelGuardian.edit.java b/patches/minecraft/net/minecraft/client/model/ModelGuardian.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelGuardian.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelHorse.edit.java b/patches/minecraft/net/minecraft/client/model/ModelHorse.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelHorse.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelHumanoidHead.edit.java b/patches/minecraft/net/minecraft/client/model/ModelHumanoidHead.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelHumanoidHead.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelIronGolem.edit.java b/patches/minecraft/net/minecraft/client/model/ModelIronGolem.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelIronGolem.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelLargeChest.edit.java b/patches/minecraft/net/minecraft/client/model/ModelLargeChest.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelLargeChest.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelLeashKnot.edit.java b/patches/minecraft/net/minecraft/client/model/ModelLeashKnot.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelLeashKnot.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelMagmaCube.edit.java b/patches/minecraft/net/minecraft/client/model/ModelMagmaCube.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelMagmaCube.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelMinecart.edit.java b/patches/minecraft/net/minecraft/client/model/ModelMinecart.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelMinecart.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelOcelot.edit.java b/patches/minecraft/net/minecraft/client/model/ModelOcelot.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelOcelot.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelPig.edit.java b/patches/minecraft/net/minecraft/client/model/ModelPig.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelPig.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelPlayer.edit.java b/patches/minecraft/net/minecraft/client/model/ModelPlayer.edit.java new file mode 100644 index 0000000..3049a84 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelPlayer.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> CHANGE 75 : 76 @ 77 : 78 + +~ if (entity != null && entity.isSneaking()) { + +> CHANGE 107 : 108 @ 109 : 110 + +~ if (entity != null && entity.isSneaking()) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelQuadruped.edit.java b/patches/minecraft/net/minecraft/client/model/ModelQuadruped.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelQuadruped.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelRabbit.edit.java b/patches/minecraft/net/minecraft/client/model/ModelRabbit.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelRabbit.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java b/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java new file mode 100644 index 0000000..67d6020 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelRenderer.edit.java @@ -0,0 +1,35 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +~ + +> CHANGE 5 : 11 @ 4 : 7 + +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 12 @ 8 : 9 + +> DELETE 13 @ 10 : 12 + +> CHANGE 227 : 229 @ 226 : 228 + +~ this.displayList = GLAllocation.generateDisplayLists(); +~ EaglercraftGPU.glNewList(this.displayList, GL_COMPILE); + +> CHANGE 235 : 236 @ 234 : 235 + +~ EaglercraftGPU.glEndList(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSheep1.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSheep1.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSheep1.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSheep2.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSheep2.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSheep2.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSign.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSign.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSign.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSilverfish.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSilverfish.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSilverfish.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSkeleton.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSkeleton.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSkeleton.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSkeletonHead.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSkeletonHead.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSkeletonHead.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSlime.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSlime.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSlime.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSnowMan.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSnowMan.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSnowMan.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSpider.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSpider.edit.java new file mode 100644 index 0000000..d92f45e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSpider.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 83 : 84 @ 85 : 86 + +~ float f5 = 0.0F; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelSquid.edit.java b/patches/minecraft/net/minecraft/client/model/ModelSquid.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelSquid.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelVillager.edit.java b/patches/minecraft/net/minecraft/client/model/ModelVillager.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelVillager.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelWitch.edit.java b/patches/minecraft/net/minecraft/client/model/ModelWitch.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelWitch.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelWither.edit.java b/patches/minecraft/net/minecraft/client/model/ModelWither.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelWither.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelWolf.edit.java b/patches/minecraft/net/minecraft/client/model/ModelWolf.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelWolf.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelZombie.edit.java b/patches/minecraft/net/minecraft/client/model/ModelZombie.edit.java new file mode 100644 index 0000000..d6cd15d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelZombie.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 7 : 8 @ 8 : 9 + +~ this(0.0F, true); + +> CHANGE 15 : 16 @ 16 : 17 + +~ super(modelSize, 0.0F, 64, parFlag ? 64 : 32); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/ModelZombieVillager.edit.java b/patches/minecraft/net/minecraft/client/model/ModelZombieVillager.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/ModelZombieVillager.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/model/TexturedQuad.edit.java b/patches/minecraft/net/minecraft/client/model/TexturedQuad.edit.java new file mode 100644 index 0000000..fa21f3d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/model/TexturedQuad.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java new file mode 100644 index 0000000..bf2e6e8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/multiplayer/ChunkProviderClient.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 8 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 18 @ 14 : 16 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/multiplayer/GuiConnecting.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/GuiConnecting.edit.java new file mode 100644 index 0000000..be5df63 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/multiplayer/GuiConnecting.edit.java @@ -0,0 +1,230 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 13 @ 3 : 6 + +~ +~ import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState; +~ import net.lax1dude.eaglercraft.v1_8.internal.EnumServerRateLimit; +~ import net.lax1dude.eaglercraft.v1_8.internal.PlatformNetworking; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; +~ import net.lax1dude.eaglercraft.v1_8.socket.ConnectionHandshake; +~ import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; +~ import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; + +> CHANGE 17 : 18 @ 10 : 14 + +~ import net.minecraft.client.network.NetHandlerPlayClient; + +> DELETE 20 @ 16 : 19 + +> DELETE 21 @ 20 : 23 + +> DELETE 23 @ 25 : 26 + +> CHANGE 24 : 28 @ 27 : 28 + +~ private EaglercraftNetworkManager networkManager; +~ private String currentAddress; +~ private String currentPassword; +~ private boolean allowPlaintext; + +> INSERT 29 : 30 @ 29 + ++ private boolean hasOpened; + +> INSERT 31 : 32 @ 30 + ++ private int timer = 0; + +> INSERT 34 : 47 @ 32 + ++ this(parGuiScreen, mcIn, parServerData, false); ++ } ++ ++ public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, boolean allowPlaintext) { ++ this(parGuiScreen, mcIn, parServerData, null, allowPlaintext); ++ } ++ ++ public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, String password) { ++ this(parGuiScreen, mcIn, parServerData, password, false); ++ } ++ ++ public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, ServerData parServerData, String password, ++ boolean allowPlaintext) { + +> CHANGE 49 : 50 @ 34 : 35 + +~ String serveraddress = AddressResolver.resolveURI(parServerData); + +> CHANGE 52 : 57 @ 37 : 38 + +~ if (RateLimitTracker.isLockedOut(serveraddress)) { +~ logger.error("Server locked this client out on a previous connection, will not attempt to reconnect"); +~ } else { +~ this.connect(serveraddress, password, allowPlaintext); +~ } + +> INSERT 60 : 73 @ 41 + ++ this(parGuiScreen, mcIn, hostName, port, false); ++ } ++ ++ public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, boolean allowPlaintext) { ++ this(parGuiScreen, mcIn, hostName, port, null, allowPlaintext); ++ } ++ ++ public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, String password) { ++ this(parGuiScreen, mcIn, hostName, port, password, false); ++ } ++ ++ public GuiConnecting(GuiScreen parGuiScreen, Minecraft mcIn, String hostName, int port, String password, ++ boolean allowPlaintext) { + +> CHANGE 76 : 77 @ 44 : 45 + +~ this.connect(hostName, password, allowPlaintext); + +> CHANGE 79 : 82 @ 47 : 52 + +~ public GuiConnecting(GuiConnecting previous, String password) { +~ this(previous, password, false); +~ } + +> CHANGE 83 : 88 @ 53 : 57 + +~ public GuiConnecting(GuiConnecting previous, String password, boolean allowPlaintext) { +~ this.mc = previous.mc; +~ this.previousGuiScreen = previous.previousGuiScreen; +~ this.connect(previous.currentAddress, password, allowPlaintext); +~ } + +> CHANGE 89 : 94 @ 58 : 72 + +~ private void connect(String ip, String password, boolean allowPlaintext) { +~ this.currentAddress = ip; +~ this.currentPassword = password; +~ this.allowPlaintext = allowPlaintext; +~ } + +> CHANGE 95 : 131 @ 73 : 80 + +~ public void updateScreen() { +~ ++timer; +~ if (timer > 1) { +~ if (this.currentAddress == null) { +~ mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); +~ } else if (this.networkManager == null) { +~ logger.info("Connecting to: {}", currentAddress); +~ this.networkManager = new EaglercraftNetworkManager(currentAddress); +~ this.networkManager.connect(); +~ } else { +~ if (this.networkManager.isChannelOpen()) { +~ if (!hasOpened) { +~ hasOpened = true; +~ logger.info("Logging in: {}", currentAddress); +~ if (ConnectionHandshake.attemptHandshake(this.mc, this, previousGuiScreen, currentPassword, +~ allowPlaintext)) { +~ logger.info("Handshake Success"); +~ this.networkManager.setConnectionState(EnumConnectionState.PLAY); +~ this.networkManager.setNetHandler(new NetHandlerPlayClient(this.mc, previousGuiScreen, +~ this.networkManager, this.mc.getSession().getProfile())); +~ } else { +~ if (mc.currentScreen == this) { +~ checkLowLevelRatelimit(); +~ } +~ if (mc.currentScreen == this) { +~ logger.info("Handshake Failure"); +~ mc.getSession().reset(); +~ mc.displayGuiScreen( +~ new GuiDisconnected(previousGuiScreen, "connect.failed", new ChatComponentText( +~ "Handshake Failure\n\nAre you sure this is an eagler 1.8 server?"))); +~ } +~ if (!PlatformNetworking.playConnectionState().isClosed()) { +~ PlatformNetworking.playDisconnect(); +~ } +~ return; +~ } + +> CHANGE 132 : 135 @ 81 : 87 + +~ try { +~ this.networkManager.processReceivedPackets(); +~ } catch (IOException ex) { + +> CHANGE 136 : 164 @ 88 : 92 + +~ } else { +~ if (PlatformNetworking.playConnectionState() == EnumEaglerConnectionState.FAILED) { +~ if (!hasOpened) { +~ mc.getSession().reset(); +~ checkLowLevelRatelimit(); +~ if (mc.currentScreen == this) { +~ if (RateLimitTracker.isProbablyLockedOut(currentAddress)) { +~ mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); +~ } else { +~ mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", +~ new ChatComponentText("Connection Refused"))); +~ } +~ } +~ } +~ } else { +~ if (this.networkManager.checkDisconnected()) { +~ this.mc.getSession().reset(); +~ checkLowLevelRatelimit(); +~ if (mc.currentScreen == this) { +~ if (RateLimitTracker.isProbablyLockedOut(currentAddress)) { +~ mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); +~ } else { +~ mc.displayGuiScreen(new GuiDisconnected(previousGuiScreen, "connect.failed", +~ new ChatComponentText("Connection Refused"))); +~ } +~ } +~ } +~ } + +> DELETE 165 @ 93 : 94 + +> DELETE 166 @ 95 : 105 + +> CHANGE 170 : 171 @ 109 : 110 + +~ protected void keyTyped(char parChar1, int parInt1) { + +> CHANGE 175 : 177 @ 114 : 116 + +~ this.buttonList.add( +~ new GuiButton(0, this.width / 2 - 100, this.height / 2 - 10, I18n.format("gui.cancel", new Object[0]))); + +> CHANGE 179 : 180 @ 118 : 119 + +~ protected void actionPerformed(GuiButton parGuiButton) { + +> CHANGE 193 : 194 @ 132 : 133 + +~ if (this.networkManager == null || !this.networkManager.isChannelOpen()) { + +> INSERT 203 : 217 @ 142 + ++ ++ private void checkLowLevelRatelimit() { ++ EnumServerRateLimit rateLimit = PlatformNetworking.getRateLimit(); ++ if (rateLimit == EnumServerRateLimit.BLOCKED) { ++ RateLimitTracker.registerBlock(currentAddress); ++ mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); ++ logger.info("Handshake Failure: Too Many Requests!"); ++ } else if (rateLimit == EnumServerRateLimit.LOCKED_OUT) { ++ RateLimitTracker.registerLockOut(currentAddress); ++ mc.displayGuiScreen(GuiDisconnected.createRateLimitKick(previousGuiScreen)); ++ logger.info("Handshake Failure: Too Many Requests!"); ++ logger.info("Server has locked this client out"); ++ } ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/multiplayer/PlayerControllerMP.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/PlayerControllerMP.edit.java new file mode 100644 index 0000000..f532552 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/multiplayer/PlayerControllerMP.edit.java @@ -0,0 +1,39 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import java.io.IOException; ++ ++ import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; + +> DELETE 11 @ 8 : 9 + +> INSERT 27 : 28 @ 25 + ++ import net.minecraft.util.ChatComponentText; + +> CHANGE 256 : 267 @ 253 : 254 + +~ try { +~ this.netClientHandler.getNetworkManager().processReceivedPackets(); +~ } catch (IOException ex) { +~ EaglercraftNetworkManager.logger +~ .fatal("Unhandled IOException was thrown " + "while processing multiplayer packets!"); +~ EaglercraftNetworkManager.logger.fatal(ex); +~ EaglercraftNetworkManager.logger.fatal("Disconnecting..."); +~ this.netClientHandler.getNetworkManager() +~ .closeChannel(new ChatComponentText("Exception thrown: " + ex.toString())); +~ } +~ this.netClientHandler.getSkinCache().flush(); + +> CHANGE 363 : 365 @ 350 : 352 + +~ public EntityPlayerSP func_178892_a(World worldIn, StatFileWriter statWriter) { +~ return new EntityPlayerSP(this.mc, worldIn, this.netClientHandler, statWriter); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ServerAddress.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ServerAddress.edit.java new file mode 100644 index 0000000..6b93762 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/multiplayer/ServerAddress.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> CHANGE 6 : 7 @ 11 : 12 + +~ public ServerAddress(String parString1, int parInt1) { + +> CHANGE 12 : 13 @ 17 : 18 + +~ return this.ipAddress; + +> DELETE 19 @ 24 : 84 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ServerData.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ServerData.edit.java new file mode 100644 index 0000000..7151320 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/multiplayer/ServerData.edit.java @@ -0,0 +1,151 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 13 @ 2 + ++ import java.io.IOException; ++ ++ import org.json.JSONArray; ++ import org.json.JSONObject; ++ ++ import net.lax1dude.eaglercraft.v1_8.internal.IServerQuery; ++ import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.profile.EaglerSkinTexture; ++ import net.minecraft.client.Minecraft; + +> INSERT 16 : 17 @ 5 + ++ import net.minecraft.util.ResourceLocation; + +> CHANGE 21 : 24 @ 9 : 12 + +~ public String populationInfo = ""; +~ public String serverMOTD = ""; +~ public long pingToServer = -1l; + +> CHANGE 29 : 30 @ 17 : 18 + +~ public boolean hideAddress = false; + +> INSERT 31 : 39 @ 19 + ++ public IServerQuery currentQuery = null; ++ public final ResourceLocation iconResourceLocation; ++ public EaglerSkinTexture iconTextureObject = null; ++ public long pingSentTime = -1l; ++ public boolean serverIconDirty = false; ++ public boolean hasPing = false; ++ public boolean serverIconEnabled = false; ++ public boolean isDefault = false; + +> INSERT 40 : 44 @ 20 + ++ private static final Logger logger = LogManager.getLogger("MOTDQuery"); ++ ++ private static int serverTextureId = 0; ++ + +> INSERT 48 : 49 @ 24 + ++ this.iconResourceLocation = new ResourceLocation("eagler:servers/icons/tex_" + serverTextureId++); + +> DELETE 55 @ 30 : 33 + +> INSERT 62 : 64 @ 40 + ++ nbttagcompound.setBoolean("hideAddress", this.hideAddress); ++ + +> DELETE 77 @ 53 : 56 + +> INSERT 88 : 94 @ 67 + ++ if (nbtCompound.hasKey("hideAddress", 1)) { ++ serverdata.hideAddress = nbtCompound.getBoolean("hideAddress"); ++ } else { ++ serverdata.hideAddress = false; ++ } ++ + +> DELETE 97 @ 70 : 78 + +> CHANGE 105 : 106 @ 86 : 87 + +~ this.hideAddress = serverDataIn.hideAddress; + +> INSERT 122 : 188 @ 103 + ++ ++ public void setMOTDFromQuery(QueryResponse pkt) { ++ try { ++ if (pkt.isResponseJSON()) { ++ JSONObject motdData = pkt.getResponseJSON(); ++ JSONArray motd = motdData.getJSONArray("motd"); ++ this.serverMOTD = motd.length() > 0 ++ ? (motd.length() > 1 ? motd.getString(0) + "\n" + motd.getString(1) : motd.getString(0)) ++ : ""; ++ this.populationInfo = "" + motdData.getInt("online") + "/" + motdData.getInt("max"); ++ this.playerList = null; ++ JSONArray players = motdData.optJSONArray("players"); ++ if (players.length() > 0) { ++ StringBuilder builder = new StringBuilder(); ++ for (int i = 0, l = players.length(); i < l; ++i) { ++ if (i > 0) { ++ builder.append('\n'); ++ } ++ builder.append(players.getString(i)); ++ } ++ this.playerList = builder.toString(); ++ } ++ serverIconEnabled = motdData.getBoolean("icon"); ++ if (!serverIconEnabled) { ++ if (iconTextureObject != null) { ++ Minecraft.getMinecraft().getTextureManager().deleteTexture(iconResourceLocation); ++ iconTextureObject = null; ++ } ++ } ++ } else { ++ throw new IOException("Response was not JSON!"); ++ } ++ } catch (Throwable t) { ++ pingToServer = -1l; ++ logger.error("Could not decode QueryResponse from: {}", serverIP); ++ logger.error(t); ++ } ++ } ++ ++ public void setIconPacket(byte[] pkt) { ++ try { ++ if (!serverIconEnabled) { ++ throw new IOException("Unexpected icon packet on text-only MOTD"); ++ } ++ if (pkt.length != 16384) { ++ throw new IOException("MOTD icon packet is the wrong size!"); ++ } ++ int[] pixels = new int[4096]; ++ for (int i = 0, j; i < 4096; ++i) { ++ j = i << 2; ++ pixels[i] = ((int) pkt[j] & 0xFF) | (((int) pkt[j + 1] & 0xFF) << 8) | (((int) pkt[j + 2] & 0xFF) << 16) ++ | (((int) pkt[j + 3] & 0xFF) << 24); ++ } ++ if (iconTextureObject != null) { ++ iconTextureObject.copyPixelsIn(pixels); ++ } else { ++ iconTextureObject = new EaglerSkinTexture(pixels, 64, 64); ++ Minecraft.getMinecraft().getTextureManager().loadTexture(iconResourceLocation, iconTextureObject); ++ } ++ } catch (Throwable t) { ++ pingToServer = -1l; ++ logger.error("Could not decode MOTD icon from: {}", serverIP); ++ logger.error(t); ++ } ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/multiplayer/ServerList.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/ServerList.edit.java new file mode 100644 index 0000000..bf25e8a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/multiplayer/ServerList.edit.java @@ -0,0 +1,230 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import java.io.ByteArrayOutputStream; +~ import java.util.Iterator; + +> INSERT 5 : 18 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ++ import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; ++ import net.lax1dude.eaglercraft.v1_8.internal.EnumServerRateLimit; ++ import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter.DefaultServer; ++ import net.lax1dude.eaglercraft.v1_8.internal.QueryResponse; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.socket.AddressResolver; ++ import net.lax1dude.eaglercraft.v1_8.socket.RateLimitTracker; ++ import net.lax1dude.eaglercraft.v1_8.socket.ServerQueryDispatch; + +> CHANGE 19 : 20 @ 6 : 7 + +~ import net.minecraft.client.renderer.texture.TextureManager; + +> CHANGE 23 : 24 @ 10 : 12 + +~ import net.minecraft.util.EnumChatFormatting; + +> INSERT 28 : 29 @ 16 + ++ private final List allServers = Lists.newArrayList(); + +> CHANGE 31 : 34 @ 18 : 19 + +~ private static ServerList instance = null; +~ +~ private ServerList(Minecraft mcIn) { + +> INSERT 38 : 46 @ 23 + ++ public static void initServerList(Minecraft mc) { ++ instance = new ServerList(mc); ++ } ++ ++ public static ServerList getServerList() { ++ return instance; ++ } ++ + +> CHANGE 48 : 55 @ 25 : 29 + +~ freeServerIcons(); +~ +~ this.allServers.clear(); +~ for (DefaultServer srv : EagRuntime.getConfiguration().getDefaultServerList()) { +~ ServerData dat = new ServerData(srv.name, srv.addr, true); +~ dat.isDefault = true; +~ this.allServers.add(dat); + +> CHANGE 57 : 58 @ 31 : 32 + +~ byte[] localStorage = EagRuntime.getStorage("s"); + +> CHANGE 59 : 72 @ 33 : 35 + +~ if (localStorage != null) { +~ NBTTagCompound nbttagcompound = CompressedStreamTools +~ .readCompressed(new EaglerInputStream(localStorage)); +~ if (nbttagcompound == null) { +~ return; +~ } +~ +~ NBTTagList nbttaglist = nbttagcompound.getTagList("servers", 10); +~ +~ for (int i = 0; i < nbttaglist.tagCount(); ++i) { +~ ServerData srv = ServerData.getServerDataFromNBTCompound(nbttaglist.getCompoundTagAt(i)); +~ this.allServers.add(srv); +~ } + +> INSERT 73 : 74 @ 36 + ++ + +> INSERT 76 : 78 @ 38 + ++ } finally { ++ refreshServerPing(); + +> CHANGE 87 : 90 @ 47 : 48 + +~ if (!serverdata.isDefault) { +~ nbttaglist.appendTag(serverdata.getNBTCompound()); +~ } + +> CHANGE 94 : 99 @ 52 : 53 + +~ +~ ByteArrayOutputStream bao = new ByteArrayOutputStream(); +~ CompressedStreamTools.writeCompressed(nbttagcompound, bao); +~ EagRuntime.setStorage("s", bao.toByteArray()); +~ + +> CHANGE 110 : 115 @ 64 : 65 + +~ ServerData data = this.servers.remove(parInt1); +~ if (data != null && data.iconTextureObject != null) { +~ mc.getTextureManager().deleteTexture(data.iconResourceLocation); +~ data.iconTextureObject = null; +~ } + +> INSERT 151 : 259 @ 101 + ++ ++ public void freeServerIcons() { ++ TextureManager mgr = mc.getTextureManager(); ++ for (int i = 0, l = allServers.size(); i < l; ++i) { ++ ServerData server = allServers.get(i); ++ if (server.iconTextureObject != null) { ++ mgr.deleteTexture(server.iconResourceLocation); ++ server.iconTextureObject = null; ++ } ++ } ++ } ++ ++ public void refreshServerPing() { ++ this.servers.clear(); ++ this.servers.addAll(this.allServers); ++ for (ServerData dat : servers) { ++ if (dat.currentQuery != null) { ++ if (dat.currentQuery.isOpen()) { ++ dat.currentQuery.close(); ++ } ++ dat.currentQuery = null; ++ } ++ dat.hasPing = false; ++ dat.pingSentTime = -1l; ++ } ++ } ++ ++ public void updateServerPing() { ++ int total = 0; ++ Iterator itr = servers.iterator(); ++ while (itr.hasNext()) { ++ ServerData dat = itr.next(); ++ if (dat.pingSentTime <= 0l) { ++ dat.pingSentTime = System.currentTimeMillis(); ++ if (RateLimitTracker.isLockedOut(dat.serverIP)) { ++ logger.error( ++ "Server {} locked this client out on a previous connection, will not attempt to reconnect", ++ dat.serverIP); ++ dat.serverMOTD = EnumChatFormatting.RED + "Too Many Requests!\nTry again later"; ++ dat.pingToServer = -1l; ++ dat.hasPing = true; ++ dat.field_78841_f = true; ++ } else { ++ dat.pingToServer = -2l; ++ String addr = AddressResolver.resolveURI(dat.serverIP); ++ dat.currentQuery = ServerQueryDispatch.sendServerQuery(addr, "MOTD"); ++ if (dat.currentQuery == null) { ++ dat.pingToServer = -1l; ++ dat.hasPing = true; ++ dat.field_78841_f = true; ++ } else { ++ ++total; ++ } ++ } ++ } else if (dat.currentQuery != null) { ++ if (!dat.hasPing) { ++ ++total; ++ EnumServerRateLimit rateLimit = dat.currentQuery.getRateLimit(); ++ if (rateLimit != EnumServerRateLimit.OK) { ++ if (rateLimit == EnumServerRateLimit.BLOCKED) { ++ RateLimitTracker.registerBlock(dat.serverIP); ++ } else if (rateLimit == EnumServerRateLimit.LOCKED_OUT) { ++ RateLimitTracker.registerLockOut(dat.serverIP); ++ } ++ dat.serverMOTD = EnumChatFormatting.RED + "Too Many Requests!\nTry again later"; ++ dat.pingToServer = -1l; ++ dat.hasPing = true; ++ return; ++ } ++ } ++ if (dat.currentQuery.responsesAvailable() > 0) { ++ QueryResponse pkt; ++ do { ++ pkt = dat.currentQuery.getResponse(); ++ } while (dat.currentQuery.responsesAvailable() > 0); ++ if (pkt.responseType.equalsIgnoreCase("MOTD") && pkt.isResponseJSON()) { ++ dat.setMOTDFromQuery(pkt); ++ if (!dat.hasPing) { ++ dat.pingToServer = pkt.clientTime - dat.pingSentTime; ++ dat.hasPing = true; ++ } ++ } ++ } ++ if (dat.currentQuery.binaryResponsesAvailable() > 0) { ++ byte[] r; ++ do { ++ r = dat.currentQuery.getBinaryResponse(); ++ } while (dat.currentQuery.binaryResponsesAvailable() > 0); ++ dat.setIconPacket(r); ++ } ++ if (!dat.currentQuery.isOpen() && dat.pingSentTime > 0l ++ && (System.currentTimeMillis() - dat.pingSentTime) > 2000l && !dat.hasPing) { ++ if (RateLimitTracker.isProbablyLockedOut(dat.serverIP)) { ++ logger.error("Server {} ratelimited this client out on a previous connection, assuming lockout", ++ dat.serverIP); ++ dat.serverMOTD = EnumChatFormatting.RED + "Too Many Requests!\nTry again later"; ++ } ++ dat.pingToServer = -1l; ++ dat.hasPing = true; ++ } ++ } ++ if (total >= 4) { ++ break; ++ } ++ } ++ ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/multiplayer/WorldClient.edit.java b/patches/minecraft/net/minecraft/client/multiplayer/WorldClient.edit.java new file mode 100644 index 0000000..e8ad952 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/multiplayer/WorldClient.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> INSERT 5 : 8 @ 6 + ++ ++ import com.google.common.collect.Sets; ++ + +> DELETE 13 @ 11 : 12 + +> CHANGE 224 : 225 @ 223 : 224 + +~ EaglercraftRandom random = new EaglercraftRandom(); + +> CHANGE 308 : 309 @ 307 : 309 + +~ return "Non-integrated multiplayer server"; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/network/NetHandlerPlayClient.edit.java b/patches/minecraft/net/minecraft/client/network/NetHandlerPlayClient.edit.java new file mode 100644 index 0000000..453adce --- /dev/null +++ b/patches/minecraft/net/minecraft/client/network/NetHandlerPlayClient.edit.java @@ -0,0 +1,334 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 8 + +> DELETE 6 @ 12 : 14 + +> INSERT 7 : 19 @ 15 + ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.netty.Unpooled; ++ import net.lax1dude.eaglercraft.v1_8.profile.ServerSkinCache; ++ import net.lax1dude.eaglercraft.v1_8.profile.SkinPackets; ++ import net.lax1dude.eaglercraft.v1_8.socket.EaglercraftNetworkManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> DELETE 33 @ 29 : 31 + +> DELETE 42 @ 40 : 41 + +> DELETE 47 @ 46 : 49 + +> DELETE 94 @ 96 : 97 + +> DELETE 96 @ 99 : 100 + +> DELETE 174 @ 178 : 179 + +> DELETE 206 @ 211 : 213 + +> CHANGE 209 : 210 @ 216 : 217 + +~ private final EaglercraftNetworkManager netManager; + +> CHANGE 215 : 216 @ 222 : 223 + +~ private final Map playerInfoMap = Maps.newHashMap(); + +> CHANGE 218 : 220 @ 225 : 226 + +~ private final EaglercraftRandom avRandomizer = new EaglercraftRandom(); +~ private final ServerSkinCache skinCache; + +> CHANGE 221 : 222 @ 227 : 228 + +~ public NetHandlerPlayClient(Minecraft mcIn, GuiScreen parGuiScreen, EaglercraftNetworkManager parNetworkManager, + +> INSERT 227 : 228 @ 233 + ++ this.skinCache = new ServerSkinCache(parNetworkManager, mcIn.getTextureManager()); + +> INSERT 232 : 233 @ 237 + ++ this.skinCache.destroy(); + +> INSERT 235 : 239 @ 239 + ++ public ServerSkinCache getSkinCache() { ++ return this.skinCache; ++ } ++ + +> DELETE 240 @ 240 : 241 + +> DELETE 259 @ 260 : 261 + +> DELETE 364 @ 366 : 367 + +> DELETE 376 @ 379 : 380 + +> DELETE 397 @ 401 : 402 + +> DELETE 403 @ 408 : 409 + +> DELETE 411 @ 417 : 418 + +> DELETE 419 @ 426 : 427 + +> DELETE 450 @ 458 : 459 + +> DELETE 472 @ 481 : 482 + +> DELETE 480 @ 490 : 491 + +> DELETE 497 @ 508 : 509 + +> DELETE 505 @ 517 : 519 + +> DELETE 512 @ 526 : 527 + +> DELETE 559 @ 574 : 576 + +> DELETE 568 @ 585 : 586 + +> DELETE 590 @ 608 : 609 + +> CHANGE 598 : 601 @ 617 : 618 + +~ if (this.gameController.theWorld != null) { +~ this.gameController.loadWorld((WorldClient) null); +~ } + +> CHANGE 602 : 604 @ 619 : 627 + +~ this.gameController +~ .displayGuiScreen(new GuiDisconnected(this.guiScreenServer, "disconnect.lost", ichatcomponent)); + +> DELETE 608 @ 631 : 632 + +> DELETE 615 @ 639 : 640 + +> DELETE 638 @ 663 : 664 + +> DELETE 647 @ 673 : 674 + +> DELETE 667 @ 694 : 695 + +> DELETE 671 @ 699 : 700 + +> DELETE 706 @ 735 : 736 + +> DELETE 711 @ 741 : 742 + +> DELETE 716 @ 747 : 748 + +> DELETE 755 @ 787 : 788 + +> DELETE 767 @ 800 : 801 + +> DELETE 773 @ 807 : 808 + +> DELETE 778 @ 813 : 814 + +> DELETE 795 @ 831 : 832 + +> DELETE 804 @ 841 : 842 + +> DELETE 831 @ 869 : 870 + +> DELETE 858 @ 897 : 898 + +> DELETE 874 @ 914 : 915 + +> DELETE 884 @ 925 : 926 + +> DELETE 895 @ 937 : 938 + +> DELETE 917 @ 960 : 961 + +> DELETE 933 @ 977 : 978 + +> DELETE 941 @ 986 : 987 + +> DELETE 949 @ 995 : 996 + +> DELETE 953 @ 1000 : 1001 + +> DELETE 958 @ 1006 : 1007 + +> DELETE 963 @ 1012 : 1014 + +> DELETE 981 @ 1032 : 1033 + +> CHANGE 1002 : 1005 @ 1054 : 1073 + +~ +~ // minecraft demo screen +~ + +> DELETE 1023 @ 1091 : 1092 + +> DELETE 1029 @ 1098 : 1099 + +> DELETE 1040 @ 1110 : 1111 + +> DELETE 1049 @ 1120 : 1121 + +> DELETE 1074 @ 1146 : 1147 + +> DELETE 1084 @ 1157 : 1173 + +> INSERT 1085 : 1087 @ 1174 + ++ // used by twitch stream ++ + +> DELETE 1090 @ 1177 : 1178 + +> CHANGE 1095 : 1096 @ 1183 : 1184 + +~ + +> DELETE 1104 @ 1192 : 1193 + +> DELETE 1108 @ 1197 : 1198 + +> DELETE 1144 @ 1234 : 1235 + +> DELETE 1152 @ 1243 : 1245 + +> CHANGE 1154 : 1157 @ 1247 : 1248 + +~ EaglercraftUUID uuid = s38packetplayerlistitem$addplayerdata.getProfile().getId(); +~ this.playerInfoMap.remove(uuid); +~ this.skinCache.evictSkin(uuid); + +> DELETE 1191 @ 1282 : 1283 + +> DELETE 1201 @ 1293 : 1294 + +> DELETE 1210 @ 1303 : 1304 + +> CHANGE 1218 : 1240 @ 1312 : 1335 + +~ this.netManager +~ .sendPacket(new C19PacketResourcePackStatus(s1, C19PacketResourcePackStatus.Action.DECLINED)); +~ return; +~ } +~ if (this.gameController.getCurrentServerData() != null && this.gameController.getCurrentServerData() +~ .getResourceMode() == ServerData.ServerResourceMode.ENABLED) { +~ NetHandlerPlayClient.this.netManager +~ .sendPacket(new C19PacketResourcePackStatus(s1, C19PacketResourcePackStatus.Action.ACCEPTED)); +~ NetHandlerPlayClient.this.gameController.getResourcePackRepository().downloadResourcePack(s, s1, +~ success -> { +~ if (success) { +~ NetHandlerPlayClient.this.netManager.sendPacket(new C19PacketResourcePackStatus(s1, +~ C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED)); +~ } else { +~ NetHandlerPlayClient.this.netManager.sendPacket(new C19PacketResourcePackStatus(s1, +~ C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD)); +~ } +~ }); +~ } else if (this.gameController.getCurrentServerData() != null && this.gameController.getCurrentServerData() +~ .getResourceMode() != ServerData.ServerResourceMode.PROMPT) { +~ this.netManager +~ .sendPacket(new C19PacketResourcePackStatus(s1, C19PacketResourcePackStatus.Action.DECLINED)); + +> CHANGE 1241 : 1249 @ 1336 : 1346 + +~ NetHandlerPlayClient.this.gameController.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() { +~ public void confirmClicked(boolean flag, int var2) { +~ NetHandlerPlayClient.this.gameController = Minecraft.getMinecraft(); +~ if (flag) { +~ if (NetHandlerPlayClient.this.gameController.getCurrentServerData() != null) { +~ NetHandlerPlayClient.this.gameController.getCurrentServerData() +~ .setResourceMode(ServerData.ServerResourceMode.ENABLED); +~ } + +> CHANGE 1250 : 1260 @ 1347 : 1366 + +~ NetHandlerPlayClient.this.netManager.sendPacket( +~ new C19PacketResourcePackStatus(s1, C19PacketResourcePackStatus.Action.ACCEPTED)); +~ NetHandlerPlayClient.this.gameController.getResourcePackRepository().downloadResourcePack(s, s1, +~ success -> { +~ if (success) { +~ NetHandlerPlayClient.this.netManager.sendPacket(new C19PacketResourcePackStatus( +~ s1, C19PacketResourcePackStatus.Action.SUCCESSFULLY_LOADED)); +~ } else { +~ NetHandlerPlayClient.this.netManager.sendPacket(new C19PacketResourcePackStatus( +~ s1, C19PacketResourcePackStatus.Action.FAILED_DOWNLOAD)); + +> INSERT 1261 : 1267 @ 1367 + ++ }); ++ } else { ++ if (NetHandlerPlayClient.this.gameController.getCurrentServerData() != null) { ++ NetHandlerPlayClient.this.gameController.getCurrentServerData() ++ .setResourceMode(ServerData.ServerResourceMode.DISABLED); ++ } + +> CHANGE 1268 : 1270 @ 1368 : 1401 + +~ NetHandlerPlayClient.this.netManager.sendPacket( +~ new C19PacketResourcePackStatus(s1, C19PacketResourcePackStatus.Action.DECLINED)); + +> DELETE 1271 @ 1402 : 1404 + +> INSERT 1272 : 1277 @ 1405 + ++ ServerList.func_147414_b(NetHandlerPlayClient.this.gameController.getCurrentServerData()); ++ NetHandlerPlayClient.this.gameController.displayGuiScreen((GuiScreen) null); ++ } ++ }, I18n.format("multiplayer.texturePrompt.line1", new Object[0]), ++ I18n.format("multiplayer.texturePrompt.line2", new Object[0]), 0)); + +> DELETE 1281 @ 1409 : 1410 + +> DELETE 1289 @ 1418 : 1419 + +> DELETE 1291 @ 1421 : 1422 + +> DELETE 1302 @ 1433 : 1435 + +> INSERT 1311 : 1318 @ 1444 + ++ } else if ("EAG|Skins-1.8".equals(packetIn.getChannelName())) { ++ try { ++ SkinPackets.readPluginMessage(packetIn.getBufferData(), skinCache); ++ } catch (IOException e) { ++ logger.error("Couldn't read EAG|Skins-1.8 packet!"); ++ logger.error(e); ++ } + +> DELETE 1323 @ 1449 : 1450 + +> DELETE 1342 @ 1469 : 1470 + +> DELETE 1358 @ 1486 : 1487 + +> DELETE 1369 @ 1498 : 1499 + +> DELETE 1408 @ 1538 : 1539 + +> DELETE 1443 @ 1574 : 1575 + +> CHANGE 1472 : 1473 @ 1604 : 1605 + +~ public EaglercraftNetworkManager getNetworkManager() { + +> CHANGE 1480 : 1481 @ 1612 : 1613 + +~ public NetworkPlayerInfo getPlayerInfo(EaglercraftUUID parUUID) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/network/NetworkPlayerInfo.edit.java b/patches/minecraft/net/minecraft/client/network/NetworkPlayerInfo.edit.java new file mode 100644 index 0000000..a29be2e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/network/NetworkPlayerInfo.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 6 @ 3 : 6 + +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; + +> DELETE 8 @ 8 : 9 + +> DELETE 18 @ 19 : 22 + +> CHANGE 58 : 59 @ 62 : 63 + +~ return true; + +> CHANGE 62 : 64 @ 66 : 67 + +~ return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile) +~ .getSkinModel().profileSkinType; + +> CHANGE 67 : 68 @ 70 : 76 + +~ return Minecraft.getMinecraft().getNetHandler().getSkinCache().getSkin(this.gameProfile).getResourceLocation(); + +> CHANGE 71 : 72 @ 79 : 84 + +~ return null; + +> DELETE 78 @ 90 : 117 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/Barrier.edit.java b/patches/minecraft/net/minecraft/client/particle/Barrier.edit.java new file mode 100644 index 0000000..054c592 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/Barrier.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.IAcceleratedParticleEngine; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 5 @ 3 : 6 + +> INSERT 59 : 67 @ 60 + ++ public boolean renderAccelerated(IAcceleratedParticleEngine accelerator, Entity var2, float f, float f1, float f2, ++ float f3, float f4, float f5) { ++ accelerator.drawParticle(this, particleIcon.getOriginX(), particleIcon.getOriginY(), getBrightnessForRender(f), ++ Math.min(particleIcon.getIconWidth(), particleIcon.getIconHeight()), 0.5f, this.particleRed, ++ this.particleGreen, this.particleBlue, this.particleAlpha); ++ return true; ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EffectRenderer.edit.java b/patches/minecraft/net/minecraft/client/particle/EffectRenderer.edit.java new file mode 100644 index 0000000..e14a941 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EffectRenderer.edit.java @@ -0,0 +1,105 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 5 : 8 @ 7 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.minecraft.AcceleratedEffectRenderer; +~ + +> INSERT 9 : 15 @ 9 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 18 @ 12 : 45 + +> DELETE 19 @ 46 : 47 + +> DELETE 20 @ 48 : 49 + +> CHANGE 40 : 41 @ 69 : 70 + +~ private EaglercraftRandom rand = new EaglercraftRandom(); + +> INSERT 43 : 45 @ 72 + ++ private AcceleratedEffectRenderer acceleratedParticleRenderer = new AcceleratedEffectRenderer(); ++ + +> CHANGE 215 : 217 @ 242 : 244 + +~ for (int i = 0; i < 3; ++i) { +~ for (int j = 1; j >= 0; --j) { + +> CHANGE 218 : 225 @ 245 : 252 + +~ // switch (j) { +~ // case 0: +~ // GlStateManager.depthMask(false); +~ // break; +~ // case 1: +~ // GlStateManager.depthMask(true); +~ // } + +> INSERT 226 : 228 @ 253 + ++ float texCoordWidth = 0.001f; ++ float texCoordHeight = 0.001f; + +> INSERT 232 : 233 @ 257 + ++ texCoordWidth = texCoordHeight = 1.0f / 256.0f; + +> INSERT 236 : 239 @ 260 + ++ TextureMap blockMap = (TextureMap) this.renderer.getTexture(TextureMap.locationBlocksTexture); ++ texCoordWidth = 1.0f / blockMap.getWidth(); ++ texCoordHeight = 1.0f / blockMap.getHeight(); + +> INSERT 246 : 250 @ 267 + ++ boolean legacyRenderingHasOccured = false; ++ ++ acceleratedParticleRenderer.begin(partialTicks); ++ + +> CHANGE 254 : 259 @ 271 : 272 + +~ if (!entityfx.renderAccelerated(acceleratedParticleRenderer, entityIn, partialTicks, f, f4, +~ f1, f2, f3)) { +~ entityfx.renderParticle(worldrenderer, entityIn, partialTicks, f, f4, f1, f2, f3); +~ legacyRenderingHasOccured = true; +~ } + +> INSERT 268 : 269 @ 281 + ++ final int l = i; + +> CHANGE 271 : 274 @ 283 : 286 + +~ return l == 0 ? "MISC_TEXTURE" +~ : (l == 1 ? "TERRAIN_TEXTURE" +~ : (l == 3 ? "ENTITY_PARTICLE_TEXTURE" : "Unknown - " + l)); + +> CHANGE 280 : 287 @ 292 : 293 + +~ if (legacyRenderingHasOccured) { +~ tessellator.draw(); +~ } else { +~ worldrenderer.finishDrawing(); +~ } +~ +~ acceleratedParticleRenderer.draw(texCoordWidth, texCoordHeight); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityAuraFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityAuraFX.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityAuraFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityBlockDustFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityBlockDustFX.edit.java new file mode 100644 index 0000000..8f13c7e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityBlockDustFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityBreakingFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityBreakingFX.edit.java new file mode 100644 index 0000000..e220fe4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityBreakingFX.edit.java @@ -0,0 +1,35 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.IAcceleratedParticleEngine; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 5 @ 3 : 6 + +> INSERT 9 : 10 @ 10 + ++ import net.minecraft.util.MathHelper; + +> INSERT 83 : 96 @ 83 + ++ public boolean renderAccelerated(IAcceleratedParticleEngine accelerator, Entity var2, float f, float f1, float f2, ++ float f3, float f4, float f5) { ++ int w = this.particleIcon.getIconWidth(); ++ int h = this.particleIcon.getIconHeight(); ++ int xOffset = MathHelper.floor_float(w * this.particleTextureJitterX * 4.0f * 0.0625f); ++ int yOffset = MathHelper.floor_float(h * this.particleTextureJitterY * 4.0f * 0.0625f); ++ int texSize = Math.min(w, h) / 4; ++ accelerator.drawParticle(this, this.particleIcon.getOriginX() + xOffset, ++ this.particleIcon.getOriginY() + yOffset, getBrightnessForRender(f), texSize, particleScale * 0.1f, ++ this.particleRed, this.particleGreen, this.particleBlue, 1.0f); ++ return true; ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityBubbleFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityBubbleFX.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityBubbleFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityCloudFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityCloudFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityCloudFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityCrit2FX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityCrit2FX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityCrit2FX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityCritFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityCritFX.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityCritFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityDiggingFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityDiggingFX.edit.java new file mode 100644 index 0000000..40b696d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityDiggingFX.edit.java @@ -0,0 +1,35 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.IAcceleratedParticleEngine; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 7 @ 5 : 8 + +> INSERT 10 : 11 @ 11 + ++ import net.minecraft.util.MathHelper; + +> INSERT 101 : 114 @ 101 + ++ public boolean renderAccelerated(IAcceleratedParticleEngine accelerator, Entity var2, float f, float f1, float f2, ++ float f3, float f4, float f5) { ++ int w = this.particleIcon.getIconWidth(); ++ int h = this.particleIcon.getIconHeight(); ++ int xOffset = MathHelper.floor_float(w * this.particleTextureJitterX * 4.0f * 0.0625f); ++ int yOffset = MathHelper.floor_float(h * this.particleTextureJitterY * 4.0f * 0.0625f); ++ int texSize = Math.min(w, h) / 4; ++ accelerator.drawParticle(this, this.particleIcon.getOriginX() + xOffset, ++ this.particleIcon.getOriginY() + yOffset, getBrightnessForRender(f), texSize, particleScale * 0.1f, ++ this.particleRed, this.particleGreen, this.particleBlue, 1.0f); ++ return true; ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityDropParticleFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityDropParticleFX.edit.java new file mode 100644 index 0000000..75f4d7b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityDropParticleFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityEnchantmentTableParticleFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityExplodeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityExplodeFX.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityExplodeFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFX.edit.java new file mode 100644 index 0000000..df86d75 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityFX.edit.java @@ -0,0 +1,39 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.minecraft.IAcceleratedParticleEngine; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 3 : 5 + +> CHANGE 24 : 25 @ 23 : 24 + +~ protected EaglerTextureAtlasSprite particleIcon; + +> INSERT 177 : 189 @ 176 + ++ public boolean renderAccelerated(IAcceleratedParticleEngine accelerator, Entity var2, float f, float f1, float f2, ++ float f3, float f4, float f5) { ++ if (getFXLayer() == 3) { ++ return false; ++ } else { ++ accelerator.drawParticle(this, particleTextureIndexX * 16, particleTextureIndexY * 16, ++ getBrightnessForRender(f), 16, particleScale * 0.1f, this.particleRed, this.particleGreen, ++ this.particleBlue, this.particleAlpha); ++ return true; ++ } ++ } ++ + +> CHANGE 199 : 200 @ 186 : 187 + +~ public void setParticleIcon(EaglerTextureAtlasSprite icon) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFirework.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFirework.edit.java new file mode 100644 index 0000000..602bef8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityFirework.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.IAcceleratedParticleEngine; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 5 @ 3 : 7 + +> INSERT 69 : 78 @ 71 + ++ ++ public boolean renderAccelerated(IAcceleratedParticleEngine accelerator, Entity var2, float f, float f1, ++ float f2, float f3, float f4, float f5) { ++ accelerator.drawParticle(this, 64, 32, getBrightnessForRender(f), 64, ++ 7.1F * MathHelper.sin(((float) this.particleAge + f - 1.0F) * 0.25F * 3.1415927F) * 0.0625f * 0.25f, ++ this.particleRed, this.particleGreen, this.particleBlue, ++ 0.6F - ((float) this.particleAge + f - 1.0F) * 0.25F * 0.5F); ++ return true; ++ } + +> INSERT 300 : 301 @ 293 + ++ entityfirework$overlayfx.particleAlpha = 0.99f; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFishWakeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFishWakeFX.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityFishWakeFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFlameFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFlameFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityFlameFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityFootStepFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityFootStepFX.edit.java new file mode 100644 index 0000000..d7a9732 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityFootStepFX.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 5 @ 3 : 6 + +> DELETE 6 @ 7 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityHeartFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityHeartFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityHeartFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityHugeExplodeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityHugeExplodeFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityHugeExplodeFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityLargeExplodeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityLargeExplodeFX.edit.java new file mode 100644 index 0000000..f7e2aa7 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityLargeExplodeFX.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 5 @ 3 : 6 + +> DELETE 7 @ 8 : 9 + +> DELETE 9 @ 11 : 12 + +> DELETE 15 @ 18 : 22 + +> CHANGE 45 : 46 @ 52 : 53 + +~ worldrenderer.begin(7, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityLavaFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityLavaFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityLavaFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityNoteFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityNoteFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityNoteFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityParticleEmitter.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityParticleEmitter.edit.java new file mode 100644 index 0000000..6d6fb99 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityParticleEmitter.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityPickupFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityPickupFX.edit.java new file mode 100644 index 0000000..346006e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityPickupFX.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 3 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityPortalFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityPortalFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityPortalFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityRainFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityRainFX.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityRainFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntityReddustFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntityReddustFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntityReddustFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySmokeFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySmokeFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntitySmokeFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySnowShovelFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySnowShovelFX.edit.java new file mode 100644 index 0000000..7b93b06 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntitySnowShovelFX.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySpellParticleFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySpellParticleFX.edit.java new file mode 100644 index 0000000..b985883 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntitySpellParticleFX.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> CHANGE 9 : 10 @ 11 : 12 + +~ private static final EaglercraftRandom RANDOM = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySplashFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySplashFX.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntitySplashFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/EntitySuspendFX.edit.java b/patches/minecraft/net/minecraft/client/particle/EntitySuspendFX.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/EntitySuspendFX.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/IParticleFactory.edit.java b/patches/minecraft/net/minecraft/client/particle/IParticleFactory.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/IParticleFactory.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/particle/MobAppearance.edit.java b/patches/minecraft/net/minecraft/client/particle/MobAppearance.edit.java new file mode 100644 index 0000000..1f88b64 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/particle/MobAppearance.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 3 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/player/inventory/ContainerLocalMenu.edit.java b/patches/minecraft/net/minecraft/client/player/inventory/ContainerLocalMenu.edit.java new file mode 100644 index 0000000..3827cac --- /dev/null +++ b/patches/minecraft/net/minecraft/client/player/inventory/ContainerLocalMenu.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.edit.java b/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.edit.java new file mode 100644 index 0000000..287c86b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/ActiveRenderInfo.edit.java @@ -0,0 +1,40 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 8 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 11 @ 7 : 9 + +> DELETE 17 @ 15 : 17 + +> CHANGE 19 : 23 @ 19 : 23 + +~ private static final int[] VIEWPORT = new int[4]; +~ private static final float[] MODELVIEW = new float[16]; +~ private static final float[] PROJECTION = new float[16]; +~ private static final float[] OBJECTCOORDS = new float[3]; + +> CHANGE 33 : 38 @ 33 : 38 + +~ EaglercraftGPU.glGetInteger(GL_VIEWPORT, VIEWPORT); +~ float f = (float) ((VIEWPORT[0] + VIEWPORT[2]) / 2); +~ float f1 = (float) ((VIEWPORT[1] + VIEWPORT[3]) / 2); +~ GlStateManager.gluUnProject(f, f1, 0.0F, MODELVIEW, PROJECTION, VIEWPORT, OBJECTCOORDS); +~ position = new Vec3((double) OBJECTCOORDS[0], (double) OBJECTCOORDS[1], (double) OBJECTCOORDS[2]); + +> CHANGE 50 : 51 @ 50 : 51 + +~ double d1 = parEntity.prevPosY + (parEntity.posY - parEntity.prevPosY) * parDouble1 + parEntity.getEyeHeight(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockFluidRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockFluidRenderer.edit.java new file mode 100644 index 0000000..cf47ae2 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/BlockFluidRenderer.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 8 @ 6 : 8 + +> CHANGE 15 : 17 @ 15 : 17 + +~ private EaglerTextureAtlasSprite[] atlasSpritesLava = new EaglerTextureAtlasSprite[2]; +~ private EaglerTextureAtlasSprite[] atlasSpritesWater = new EaglerTextureAtlasSprite[2]; + +> CHANGE 34 : 36 @ 34 : 35 + +~ EaglerTextureAtlasSprite[] atextureatlassprite = blockliquid.getMaterial() == Material.lava +~ ? this.atlasSpritesLava + +> CHANGE 67 : 68 @ 66 : 67 + +~ EaglerTextureAtlasSprite textureatlassprite = atextureatlassprite[0]; + +> CHANGE 173 : 174 @ 172 : 173 + +~ EaglerTextureAtlasSprite textureatlassprite1 = atextureatlassprite[1]; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java new file mode 100644 index 0000000..775b567 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/BlockModelRenderer.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 7 @ 4 + ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 10 @ 7 : 11 + +> INSERT 85 : 86 @ 86 + ++ BlockPos.MutableBlockPos pointer = new BlockPos.MutableBlockPos(); + +> CHANGE 89 : 90 @ 89 : 90 + +~ BlockPos blockpos = blockPosIn.offsetEvenFaster(enumfacing, pointer); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java new file mode 100644 index 0000000..ac8e734 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/BlockModelShapes.edit.java @@ -0,0 +1,40 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 5 : 9 @ 6 + ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> DELETE 54 @ 51 : 52 + +> CHANGE 75 : 76 @ 73 : 74 + +~ public EaglerTextureAtlasSprite getTexture(IBlockState state) { + +> CHANGE 130 : 131 @ 128 : 129 + +~ this.bakedModelStore.put((IBlockState) entry.getKey(), + +> CHANGE 306 : 307 @ 304 : 305 + +~ String s = BlockDirt.VARIANT.getName((BlockDirt.DirtType) linkedhashmap.remove(BlockDirt.VARIANT)); + +> CHANGE 317 : 319 @ 315 : 316 + +~ String s = BlockStoneSlab.VARIANT +~ .getName((BlockStoneSlab.EnumType) linkedhashmap.remove(BlockStoneSlab.VARIANT)); + +> CHANGE 328 : 329 @ 325 : 326 + +~ .getName((BlockStoneSlabNew.EnumType) linkedhashmap.remove(BlockStoneSlabNew.VARIANT)); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.edit.java b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.edit.java new file mode 100644 index 0000000..2d6bf14 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/BlockRendererDispatcher.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 4 : 11 + +> DELETE 18 @ 23 : 24 + +> CHANGE 35 : 36 @ 41 : 42 + +~ public void renderBlockDamage(IBlockState state, BlockPos pos, EaglerTextureAtlasSprite texture, + +> CHANGE 91 : 95 @ 97 : 103 + +~ +~ try { +~ state = block.getActualState(state, worldIn, pos); +~ } catch (Exception eeeee) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ChestRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/ChestRenderer.edit.java new file mode 100644 index 0000000..35c39b8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/ChestRenderer.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ChunkRenderContainer.edit.java b/patches/minecraft/net/minecraft/client/renderer/ChunkRenderContainer.edit.java new file mode 100644 index 0000000..6c8fb64 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/ChunkRenderContainer.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 7 @ 4 : 5 + +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java new file mode 100644 index 0000000..7bef347 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/EntityRenderer.edit.java @@ -0,0 +1,214 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; + +> CHANGE 4 : 6 @ 8 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.HString; + +> INSERT 7 : 19 @ 10 + ++ ++ import com.google.common.base.Predicate; ++ import com.google.common.base.Predicates; ++ ++ import net.lax1dude.eaglercraft.v1_8.Display; ++ import net.lax1dude.eaglercraft.v1_8.Mouse; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 29 @ 20 : 29 + +> DELETE 36 @ 36 : 38 + +> DELETE 43 @ 45 : 48 + +> DELETE 63 @ 68 : 75 + +> CHANGE 72 : 73 @ 84 : 85 + +~ private EaglercraftRandom random = new EaglercraftRandom(); + +> DELETE 116 @ 128 : 129 + +> DELETE 146 @ 159 : 160 + +> INSERT 147 : 156 @ 161 + ++ GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit); ++ GlStateManager.matrixMode(5890); ++ GlStateManager.loadIdentity(); ++ float f3 = 0.00390625F; ++ GlStateManager.scale(f3, f3, f3); ++ GlStateManager.translate(8.0F, 8.0F, 8.0F); ++ GlStateManager.matrixMode(5888); ++ GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit); ++ + +> CHANGE 169 : 170 @ 174 : 175 + +~ return false; + +> DELETE 173 @ 178 : 184 + +> DELETE 180 @ 191 : 206 + +> DELETE 183 @ 209 : 224 + +> CHANGE 186 : 187 @ 227 : 242 + +~ this.useShader = false; + +> DELETE 190 @ 245 : 256 + +> DELETE 193 @ 259 : 263 + +> DELETE 237 @ 307 : 311 + +> DELETE 238 @ 312 : 319 + +> CHANGE 356 : 357 @ 437 : 438 + +~ f = this.mc.gameSettings.keyBindZoomCamera.isKeyDown() ? 17.0f : this.mc.gameSettings.fovSetting; + +> CHANGE 526 : 527 @ 607 : 608 + +~ GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), + +> CHANGE 528 : 529 @ 609 : 610 + +~ this.farPlaneDistance * 2.0f * MathHelper.SQRT_2); + +> CHANGE 586 : 587 @ 667 : 668 + +~ GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, false), + +> DELETE 630 @ 711 : 723 + +> CHANGE 751 : 752 @ 844 : 845 + +~ this.lightmapColors[i] = short1 << 24 | j | k << 8 | l << 16; + +> INSERT 755 : 769 @ 848 + ++ ++ GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit); ++ this.mc.getTextureManager().bindTexture(this.locationLightMap); ++ if (mc.gameSettings.fancyGraphics || mc.gameSettings.ambientOcclusion > 0) { ++ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ++ } else { ++ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); ++ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); ++ } ++ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); ++ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ++ GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit); ++ + +> DELETE 792 @ 871 : 876 + +> INSERT 796 : 799 @ 880 + ++ if (this.mc.gameSettings.keyBindZoomCamera.isKeyDown()) { ++ f *= 0.7f; ++ } + +> DELETE 838 @ 919 : 932 + +> CHANGE 866 : 867 @ 960 : 961 + +~ return EntityRenderer.this.mc.currentScreen.getClass().getName(); + +> CHANGE 871 : 872 @ 965 : 966 + +~ return HString.format("Scaled: (%d, %d). Absolute: (%d, %d)", + +> CHANGE 878 : 879 @ 972 : 973 + +~ return HString.format("Scaled: (%d, %d). Absolute: (%d, %d). Scale factor of %d", + +> DELETE 894 @ 988 : 990 + +> CHANGE 926 : 927 @ 1022 : 1023 + +~ EaglercraftGPU.glLineWidth(1.0F); + +> CHANGE 997 : 999 @ 1093 : 1095 + +~ GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), +~ (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, this.farPlaneDistance * 4.0F); + +> CHANGE 1003 : 1004 @ 1099 : 1100 + +~ GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), + +> INSERT 1031 : 1032 @ 1127 + ++ GlStateManager.disableBlend(); + +> DELETE 1038 @ 1133 : 1134 + +> INSERT 1039 : 1040 @ 1135 + ++ GlStateManager.shadeModel(7424); + +> CHANGE 1131 : 1132 @ 1226 : 1227 + +~ GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), + +> CHANGE 1141 : 1142 @ 1236 : 1237 + +~ GlStateManager.gluPerspective(this.getFOVModifier(partialTicks, true), + +> CHANGE 1229 : 1230 @ 1324 : 1325 + +~ EaglercraftGPU.glNormal3f(0.0F, 1.0F, 0.0F); + +> CHANGE 1277 : 1278 @ 1372 : 1373 + +~ if (f2 >= 0.15F) { + +> CHANGE 1514 : 1515 @ 1609 : 1610 + +~ GlStateManager.clearColor(this.fogColorRed, this.fogColorGreen, this.fogColorBlue, 1.0F); + +> CHANGE 1524 : 1527 @ 1619 : 1621 + +~ EaglercraftGPU.glFog(GL_FOG_COLOR, +~ this.setFogColorBuffer(this.fogColorRed, this.fogColorGreen, this.fogColorBlue, 1.0F)); +~ EaglercraftGPU.glNormal3f(0.0F, -1.0F, 0.0F); + +> CHANGE 1544 : 1545 @ 1638 : 1642 + +~ EaglercraftGPU.glFogi('\u855a', '\u855b'); + +> INSERT 1559 : 1562 @ 1656 + ++ } else if (!this.mc.gameSettings.fog) { ++ GlStateManager.setFog(2048); ++ GlStateManager.setFogDensity(0.0F); + +> INSERT 1563 : 1564 @ 1657 + ++ GlStateManager.setFogDensity(0.001F); + +> CHANGE 1574 : 1575 @ 1667 : 1670 + +~ EaglercraftGPU.glFogi('\u855a', '\u855b'); + +> DELETE 1584 @ 1679 : 1680 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/GLAllocation.edit.java b/patches/minecraft/net/minecraft/client/renderer/GLAllocation.edit.java new file mode 100644 index 0000000..b3064b2 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/GLAllocation.edit.java @@ -0,0 +1,45 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; + +> CHANGE 6 : 8 @ 9 : 18 + +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; + +> CHANGE 9 : 12 @ 19 : 24 + +~ public class GLAllocation { +~ public static int generateDisplayLists() { +~ return EaglercraftGPU.glGenLists(); + +> CHANGE 14 : 16 @ 26 : 28 + +~ public static void deleteDisplayLists(int list) { +~ EaglercraftGPU.glDeleteLists(list); + +> CHANGE 18 : 20 @ 30 : 32 + +~ public static ByteBuffer createDirectByteBuffer(int capacity) { +~ return EagRuntime.allocateByteBuffer(capacity); + +> DELETE 22 @ 34 : 38 + +> CHANGE 23 : 24 @ 39 : 40 + +~ return EagRuntime.allocateIntBuffer(capacity); + +> CHANGE 27 : 28 @ 43 : 44 + +~ return EagRuntime.allocateFloatBuffer(capacity); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/IImageBuffer.edit.java b/patches/minecraft/net/minecraft/client/renderer/IImageBuffer.edit.java new file mode 100644 index 0000000..3f0947a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/IImageBuffer.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> CHANGE 5 : 6 @ 5 : 6 + +~ ImageData parseUserSkin(ImageData var1); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ImageBufferDownload.edit.java b/patches/minecraft/net/minecraft/client/renderer/ImageBufferDownload.edit.java new file mode 100644 index 0000000..cba4fa6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/ImageBufferDownload.edit.java @@ -0,0 +1,39 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> CHANGE 9 : 10 @ 13 : 14 + +~ public ImageData parseUserSkin(ImageData bufferedimage) { + +> CHANGE 15 : 31 @ 19 : 35 + +~ ImageData bufferedimage1 = new ImageData(this.imageWidth, this.imageHeight, true); +~ bufferedimage1.copyPixelsFrom(bufferedimage, 0, 0, bufferedimage.width, bufferedimage.height, 0, 0, +~ bufferedimage.width, bufferedimage.height); +~ if (bufferedimage.height == 32) { +~ bufferedimage1.drawLayer(bufferedimage, 24, 48, 20, 52, 4, 16, 8, 20); +~ bufferedimage1.drawLayer(bufferedimage, 28, 48, 24, 52, 8, 16, 12, 20); +~ bufferedimage1.drawLayer(bufferedimage, 20, 52, 16, 64, 8, 20, 12, 32); +~ bufferedimage1.drawLayer(bufferedimage, 24, 52, 20, 64, 4, 20, 8, 32); +~ bufferedimage1.drawLayer(bufferedimage, 28, 52, 24, 64, 0, 20, 4, 32); +~ bufferedimage1.drawLayer(bufferedimage, 32, 52, 28, 64, 12, 20, 16, 32); +~ bufferedimage1.drawLayer(bufferedimage, 40, 48, 36, 52, 44, 16, 48, 20); +~ bufferedimage1.drawLayer(bufferedimage, 44, 48, 40, 52, 48, 16, 52, 20); +~ bufferedimage1.drawLayer(bufferedimage, 36, 52, 32, 64, 48, 20, 52, 32); +~ bufferedimage1.drawLayer(bufferedimage, 40, 52, 36, 64, 44, 20, 48, 32); +~ bufferedimage1.drawLayer(bufferedimage, 44, 52, 40, 64, 40, 20, 44, 32); +~ bufferedimage1.drawLayer(bufferedimage, 48, 52, 44, 64, 52, 20, 56, 32); + +> CHANGE 33 : 34 @ 37 : 39 + +~ this.imageData = bufferedimage1.pixels; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java new file mode 100644 index 0000000..890f3b2 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/InventoryEffectRenderer.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 5 @ 3 + ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 4 : 5 + +> INSERT 50 : 51 @ 49 + ++ GlStateManager.enableAlpha(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java b/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java new file mode 100644 index 0000000..3439419 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/ItemModelMesher.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 4 : 8 @ 5 : 7 + +~ +~ import com.google.common.collect.Maps; +~ +~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> CHANGE 24 : 25 @ 23 : 24 + +~ public EaglerTextureAtlasSprite getParticleIcon(Item item) { + +> CHANGE 28 : 29 @ 27 : 28 + +~ public EaglerTextureAtlasSprite getParticleIcon(Item item, int meta) { + +> CHANGE 78 : 79 @ 77 : 78 + +~ this.simpleShapesCache.put((Integer) entry.getKey(), + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.edit.java new file mode 100644 index 0000000..0973bed --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/ItemRenderer.edit.java @@ -0,0 +1,46 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 7 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 13 @ 8 : 13 + +> DELETE 18 @ 18 : 19 + +> DELETE 30 @ 31 : 32 + +> CHANGE 56 : 59 @ 58 : 61 + +~ // if (this.isBlockTranslucent(block)) { //TODO: figure out why this code exists, it breaks slime blocks +~ // GlStateManager.depthMask(false); +~ // } + +> CHANGE 62 : 65 @ 64 : 67 + +~ // if (this.isBlockTranslucent(block)) { +~ // GlStateManager.depthMask(true); +~ // } + +> CHANGE 166 : 167 @ 168 : 169 + +~ EaglercraftGPU.glNormal3f(0.0F, 0.0F, -1.0F); + +> CHANGE 361 : 362 @ 363 : 364 + +~ private void func_178108_a(float parFloat1, EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> CHANGE 427 : 428 @ 429 : 430 + +~ EaglerTextureAtlasSprite textureatlassprite = this.mc.getTextureMapBlocks() + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/RegionRenderCache.edit.java b/patches/minecraft/net/minecraft/client/renderer/RegionRenderCache.edit.java new file mode 100644 index 0000000..cdca76e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/RegionRenderCache.edit.java @@ -0,0 +1,62 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> CHANGE 14 : 15 @ 13 : 14 + +~ private final IBlockState DEFAULT_STATE = Blocks.air.getDefaultState(); + +> INSERT 56 : 70 @ 55 + ++ /** ++ * only use with a regular "net.minecraft.util.BlockPos"! ++ */ ++ public IBlockState getBlockStateFaster(BlockPos blockpos) { ++ int i = this.getPositionIndexFaster(blockpos); ++ IBlockState iblockstate = this.blockStates[i]; ++ if (iblockstate == null) { ++ iblockstate = this.getBlockStateRawFaster(blockpos); ++ this.blockStates[i] = iblockstate; ++ } ++ ++ return iblockstate; ++ } ++ + +> INSERT 80 : 93 @ 65 + ++ /** ++ * only use with a regular "net.minecraft.util.BlockPos"! ++ */ ++ private IBlockState getBlockStateRawFaster(BlockPos pos) { ++ if (pos.y >= 0 && pos.y < 256) { ++ int i = (pos.x >> 4) - this.chunkX; ++ int j = (pos.z >> 4) - this.chunkZ; ++ return this.chunkArray[i][j].getBlockState(pos); ++ } else { ++ return DEFAULT_STATE; ++ } ++ } ++ + +> INSERT 99 : 109 @ 71 + ++ ++ /** ++ * only use with a regular "net.minecraft.util.BlockPos"! ++ */ ++ private int getPositionIndexFaster(BlockPos parBlockPos) { ++ int i = parBlockPos.x - this.position.x; ++ int j = parBlockPos.y - this.position.y; ++ int k = parBlockPos.z - this.position.z; ++ return i * 400 + k * 20 + j; ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/RegionRenderCacheBuilder.edit.java b/patches/minecraft/net/minecraft/client/renderer/RegionRenderCacheBuilder.edit.java new file mode 100644 index 0000000..4260e1b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/RegionRenderCacheBuilder.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java b/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java new file mode 100644 index 0000000..889e39b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/RenderGlobal.edit.java @@ -0,0 +1,217 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> CHANGE 8 : 10 @ 13 : 14 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.HString; + +> INSERT 12 : 26 @ 16 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ import com.google.common.collect.Sets; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.minecraft.ChunkUpdateManager; ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; ++ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; ++ import net.lax1dude.eaglercraft.v1_8.vector.Vector4f; + +> DELETE 38 @ 28 : 41 + +> DELETE 42 @ 45 : 46 + +> DELETE 48 @ 52 : 53 + +> DELETE 52 @ 57 : 60 + +> DELETE 54 @ 62 : 65 + +> DELETE 83 @ 94 : 99 + +> DELETE 103 @ 119 : 123 + +> CHANGE 106 : 107 @ 126 : 129 + +~ private final EaglerTextureAtlasSprite[] destroyBlockIcons = new EaglerTextureAtlasSprite[10]; + +> CHANGE 118 : 119 @ 140 : 141 + +~ private final ChunkUpdateManager renderDispatcher = new ChunkUpdateManager(); + +> CHANGE 141 : 143 @ 163 : 165 + +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + +> CHANGE 145 : 148 @ 167 : 179 + +~ this.vboEnabled = false; +~ this.renderContainer = new RenderList(); +~ this.renderChunkFactory = new ListChunkFactory(); + +> DELETE 167 @ 198 : 202 + +> DELETE 168 @ 203 : 224 + +> DELETE 171 @ 227 : 233 + +> CHANGE 175 : 176 @ 237 : 239 + +~ return false; + +> DELETE 181 @ 244 : 247 + +> CHANGE 187 : 192 @ 253 : 266 + +~ this.glSkyList2 = GLAllocation.generateDisplayLists(); +~ EaglercraftGPU.glNewList(this.glSkyList2, GL_COMPILE); +~ this.renderSky(worldrenderer, -16.0F, true); +~ tessellator.draw(); +~ EaglercraftGPU.glEndList(); + +> DELETE 198 @ 272 : 275 + +> CHANGE 204 : 209 @ 281 : 294 + +~ this.glSkyList = GLAllocation.generateDisplayLists(); +~ EaglercraftGPU.glNewList(this.glSkyList, GL_COMPILE); +~ this.renderSky(worldrenderer, 16.0F, false); +~ tessellator.draw(); +~ EaglercraftGPU.glEndList(); + +> DELETE 238 @ 323 : 326 + +> CHANGE 244 : 251 @ 332 : 347 + +~ this.starGLCallList = GLAllocation.generateDisplayLists(); +~ GlStateManager.pushMatrix(); +~ EaglercraftGPU.glNewList(this.starGLCallList, GL_COMPILE); +~ this.renderStars(worldrenderer); +~ tessellator.draw(); +~ EaglercraftGPU.glEndList(); +~ GlStateManager.popMatrix(); + +> CHANGE 255 : 256 @ 351 : 352 + +~ EaglercraftRandom random = new EaglercraftRandom(10842L); + +> DELETE 326 @ 422 : 431 + +> DELETE 327 @ 432 : 438 + +> DELETE 355 @ 466 : 470 + +> DELETE 356 @ 471 : 472 + +> DELETE 397 @ 513 : 550 + +> CHANGE 450 : 451 @ 603 : 604 + +~ for (TileEntity tileentity2 : (List) list1) { + +> CHANGE 503 : 504 @ 656 : 657 + +~ return HString.format("C: %d/%d %sD: %d, %s", + +> DELETE 619 @ 772 : 773 + +> CHANGE 621 : 622 @ 775 : 776 + +~ if ((!flag1 || !renderglobal$containerlocalrenderinformation1.setFacing // TODO: + +> DELETE 644 @ 798 : 799 + +> CHANGE 651 : 653 @ 806 : 807 + +~ if (this.mc.gameSettings.chunkFix ? this.isPositionInRenderChunkHack(blockpos1, renderchunk4) +~ : this.isPositionInRenderChunk(blockpos, renderchunk4)) { + +> INSERT 674 : 684 @ 828 + ++ /** ++ * WARNING: use only in the above "build near" logic ++ */ ++ private boolean isPositionInRenderChunkHack(BlockPos pos, RenderChunk renderChunkIn) { ++ BlockPos blockpos = renderChunkIn.getPosition(); ++ return MathHelper.abs_int(pos.getX() - blockpos.getX() - 8) > 11 ? false ++ : (MathHelper.abs_int(pos.getY() - blockpos.getY() - 8) > 11 ? false ++ : MathHelper.abs_int(pos.getZ() - blockpos.getZ() - 8) <= 11); ++ } ++ + +> INSERT 713 : 714 @ 857 + ++ ((ClippingHelperImpl) this.debugFixedClippingHelper).destroy(); + +> DELETE 808 @ 951 : 961 + +> DELETE 809 @ 962 : 982 + +> CHANGE 902 : 903 @ 1075 : 1085 + +~ GlStateManager.callList(this.glSkyList); + +> CHANGE 941 : 942 @ 1123 : 1124 + +~ .pos((double) (f12 * 120.0F), (double) (f13 * 120.0F), (double) (f13 * 40.0F * afloat[3])) + +> CHANGE 984 : 985 @ 1166 : 1176 + +~ GlStateManager.callList(this.starGLCallList); + +> CHANGE 998 : 999 @ 1189 : 1199 + +~ GlStateManager.callList(this.glSkyList2); + +> CHANGE 1371 : 1372 @ 1571 : 1572 + +~ this.displayListEntitiesDirty |= this.renderDispatcher.updateChunks(finishTimeNano); + +> DELETE 1389 @ 1589 : 1590 + +> CHANGE 1564 : 1565 @ 1765 : 1766 + +~ EaglerTextureAtlasSprite textureatlassprite = this.destroyBlockIcons[i]; + +> CHANGE 1586 : 1587 @ 1787 : 1788 + +~ EaglercraftGPU.glLineWidth(2.0F); + +> CHANGE 1827 : 1828 @ 2028 : 2029 + +~ EaglercraftRandom random = this.theWorld.rand; + +> INSERT 2057 : 2074 @ 2258 + ++ ++ public String getDebugInfoShort() { ++ int i = this.viewFrustum.renderChunks.length; ++ int j = 0; ++ int k = 0; ++ ++ for (RenderGlobal.ContainerLocalRenderInformation renderglobal$containerlocalrenderinformation : this.renderInfos) { ++ CompiledChunk compiledchunk = renderglobal$containerlocalrenderinformation.renderChunk.compiledChunk; ++ if (compiledchunk != CompiledChunk.DUMMY && !compiledchunk.isEmpty()) { ++ ++j; ++ k += compiledchunk.getTileEntities().size(); ++ } ++ } ++ ++ return "" + Minecraft.getDebugFPS() + "fps | C: " + j + "/" + i + ", E: " + this.countEntitiesRendered + "+" + k ++ + ", " + renderDispatcher.getDebugInfo(); ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/RenderHelper.edit.java b/patches/minecraft/net/minecraft/client/renderer/RenderHelper.edit.java new file mode 100644 index 0000000..9b65b60 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/RenderHelper.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 6 : 7 + +> CHANGE 14 : 16 @ 15 : 17 + +~ GlStateManager.disableMCLight(0); +~ GlStateManager.disableMCLight(1); + +> CHANGE 21 : 24 @ 22 : 24 + +~ GlStateManager.enableMCLight(0, 0.6f, LIGHT0_POS.xCoord, LIGHT0_POS.yCoord, LIGHT0_POS.zCoord, 0.0D); +~ GlStateManager.enableMCLight(1, 0.6f, LIGHT1_POS.xCoord, LIGHT1_POS.yCoord, LIGHT1_POS.zCoord, 0.0D); +~ GlStateManager.setMCLightAmbient(0.4f, 0.4f, 0.4f); + +> DELETE 25 @ 25 : 39 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/RenderList.edit.java b/patches/minecraft/net/minecraft/client/renderer/RenderList.edit.java new file mode 100644 index 0000000..0880281 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/RenderList.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 7 @ 7 : 8 + +> CHANGE 15 : 16 @ 16 : 17 + +~ EaglercraftGPU.glCallList( + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/Tessellator.edit.java b/patches/minecraft/net/minecraft/client/renderer/Tessellator.edit.java new file mode 100644 index 0000000..58f3265 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/Tessellator.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldVertexBufferUploader; + +> INSERT 9 : 18 @ 8 + ++ ++ public static final int GL_TRIANGLES = RealOpenGLEnums.GL_TRIANGLES; ++ public static final int GL_TRIANGLE_STRIP = RealOpenGLEnums.GL_TRIANGLE_STRIP; ++ public static final int GL_TRIANGLE_FAN = RealOpenGLEnums.GL_TRIANGLE_FAN; ++ public static final int GL_QUADS = RealOpenGLEnums.GL_QUADS; ++ public static final int GL_LINES = RealOpenGLEnums.GL_LINES; ++ public static final int GL_LINE_STRIP = RealOpenGLEnums.GL_LINE_STRIP; ++ public static final int GL_LINE_LOOP = RealOpenGLEnums.GL_LINE_LOOP; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/ViewFrustum.edit.java b/patches/minecraft/net/minecraft/client/renderer/ViewFrustum.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/ViewFrustum.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockFaceUV.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockFaceUV.edit.java new file mode 100644 index 0000000..918d391 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockFaceUV.edit.java @@ -0,0 +1,47 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 10 + +~ import org.json.JSONArray; +~ import org.json.JSONException; +~ import org.json.JSONObject; + +> INSERT 6 : 8 @ 11 + ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; ++ + +> CHANGE 50 : 52 @ 53 : 57 + +~ public static class Deserializer implements JSONTypeDeserializer { +~ public BlockFaceUV deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 57 : 59 @ 62 : 64 + +~ protected int parseRotation(JSONObject parJsonObject) { +~ int i = parJsonObject.optInt("rotation", 0); + +> CHANGE 62 : 63 @ 67 : 68 + +~ throw new JSONException("Invalid rotation " + i + " found, only 0/90/180/270 allowed"); + +> CHANGE 66 : 67 @ 71 : 72 + +~ private float[] parseUV(JSONObject parJsonObject) { + +> CHANGE 70 : 73 @ 75 : 78 + +~ JSONArray jsonarray = parJsonObject.getJSONArray("uv"); +~ if (jsonarray.length() != 4) { +~ throw new JSONException("Expected 4 uv values, found: " + jsonarray.length()); + +> CHANGE 77 : 78 @ 82 : 83 + +~ afloat[i] = jsonarray.getFloat(i); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPart.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPart.edit.java new file mode 100644 index 0000000..57e3fd5 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPart.edit.java @@ -0,0 +1,127 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 10 + +> CHANGE 5 : 15 @ 13 : 15 + +~ +~ import org.json.JSONArray; +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import com.google.common.collect.Maps; +~ +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; +~ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; + +> DELETE 16 @ 16 : 17 + +> DELETE 17 @ 18 : 19 + +> CHANGE 67 : 69 @ 69 : 73 + +~ public static class Deserializer implements JSONTypeDeserializer { +~ public BlockPart deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 72 : 75 @ 76 : 79 + +~ Map map = this.parseFacesCheck(jsonobject); +~ if (jsonobject.has("shade") && !(jsonobject.get("shade") instanceof Boolean)) { +~ throw new JSONException("Expected shade to be a Boolean"); + +> CHANGE 76 : 77 @ 80 : 81 + +~ boolean flag = jsonobject.optBoolean("shade", true); + +> CHANGE 81 : 82 @ 85 : 86 + +~ private BlockPartRotation parseRotation(JSONObject parJsonObject) { + +> CHANGE 84 : 85 @ 88 : 89 + +~ JSONObject jsonobject = parJsonObject.getJSONObject("rotation"); + +> CHANGE 89 : 90 @ 93 : 94 + +~ boolean flag = jsonobject.optBoolean("rescale", false); + +> CHANGE 96 : 98 @ 100 : 102 + +~ private float parseAngle(JSONObject parJsonObject) { +~ float f = parJsonObject.getFloat("angle"); + +> CHANGE 99 : 100 @ 103 : 104 + +~ throw new JSONException("Invalid rotation " + f + " found, only -45/-22.5/0/22.5/45 allowed"); + +> CHANGE 105 : 107 @ 109 : 111 + +~ private EnumFacing.Axis parseAxis(JSONObject parJsonObject) { +~ String s = parJsonObject.getString("axis"); + +> CHANGE 109 : 110 @ 113 : 114 + +~ throw new JSONException("Invalid rotation axis: " + s); + +> CHANGE 115 : 117 @ 119 : 122 + +~ private Map parseFacesCheck(JSONObject parJsonObject) { +~ Map map = this.parseFaces(parJsonObject); + +> CHANGE 118 : 119 @ 123 : 124 + +~ throw new JSONException("Expected between 1 and 6 unique faces, got 0"); + +> CHANGE 124 : 125 @ 129 : 131 + +~ private Map parseFaces(JSONObject parJsonObject) { + +> CHANGE 126 : 127 @ 132 : 133 + +~ JSONObject jsonobject = parJsonObject.getJSONObject("faces"); + +> CHANGE 128 : 132 @ 134 : 138 + +~ for (String entry : jsonobject.keySet()) { +~ EnumFacing enumfacing = this.parseEnumFacing(entry); +~ enummap.put(enumfacing, +~ JSONTypeProvider.deserialize(jsonobject.getJSONObject(entry), BlockPartFace.class)); + +> CHANGE 140 : 141 @ 146 : 147 + +~ throw new JSONException("Unknown facing: " + name); + +> CHANGE 146 : 147 @ 152 : 153 + +~ private Vector3f parsePositionTo(JSONObject parJsonObject) { + +> CHANGE 152 : 153 @ 158 : 159 + +~ throw new JSONException("\'to\' specifier exceeds the allowed boundaries: " + vector3f); + +> CHANGE 156 : 157 @ 162 : 163 + +~ private Vector3f parsePositionFrom(JSONObject parJsonObject) { + +> CHANGE 162 : 163 @ 168 : 169 + +~ throw new JSONException("\'from\' specifier exceeds the allowed boundaries: " + vector3f); + +> CHANGE 166 : 170 @ 172 : 176 + +~ private Vector3f parsePosition(JSONObject parJsonObject, String parString1) { +~ JSONArray jsonarray = parJsonObject.getJSONArray(parString1); +~ if (jsonarray.length() != 3) { +~ throw new JSONException("Expected 3 " + parString1 + " values, found: " + jsonarray.length()); + +> CHANGE 174 : 175 @ 180 : 181 + +~ afloat[i] = jsonarray.getFloat(i); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartFace.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartFace.edit.java new file mode 100644 index 0000000..31c4726 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartFace.edit.java @@ -0,0 +1,42 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 7 @ 2 : 9 + +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; + +> DELETE 8 @ 10 : 11 + +> CHANGE 23 : 25 @ 26 : 30 + +~ public static class Deserializer implements JSONTypeDeserializer { +~ public BlockPartFace deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 28 : 29 @ 33 : 35 + +~ BlockFaceUV blockfaceuv = JSONTypeProvider.deserialize(jsonobject, BlockFaceUV.class); + +> CHANGE 32 : 34 @ 38 : 40 + +~ protected int parseTintIndex(JSONObject parJsonObject) { +~ return parJsonObject.optInt("tintindex", -1); + +> CHANGE 36 : 38 @ 42 : 44 + +~ private String parseTexture(JSONObject parJsonObject) { +~ return parJsonObject.getString("texture"); + +> CHANGE 40 : 42 @ 46 : 48 + +~ private EnumFacing parseCullFace(JSONObject parJsonObject) { +~ String s = parJsonObject.optString("cullface", ""); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartRotation.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartRotation.edit.java new file mode 100644 index 0000000..989fffa --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BlockPartRotation.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; + +> DELETE 4 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java new file mode 100644 index 0000000..a5efcf6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/BreakingFour.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> INSERT 4 : 6 @ 7 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ + +> CHANGE 7 : 8 @ 8 : 9 + +~ private final EaglerTextureAtlasSprite texture; + +> CHANGE 9 : 10 @ 10 : 11 + +~ public BreakingFour(BakedQuad parBakedQuad, EaglerTextureAtlasSprite textureIn) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java new file mode 100644 index 0000000..8d85f9a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/FaceBakery.edit.java @@ -0,0 +1,46 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 6 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; ++ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; ++ import net.lax1dude.eaglercraft.v1_8.vector.Vector4f; + +> DELETE 7 @ 3 : 8 + +> DELETE 11 @ 12 : 15 + +> CHANGE 16 : 19 @ 20 : 23 + +~ public BakedQuad makeBakedQuad(Vector3f posFrom, Vector3f posTo, BlockPartFace face, +~ EaglerTextureAtlasSprite sprite, EnumFacing facing, ModelRotation modelRotationIn, +~ BlockPartRotation partRotation, boolean uvLocked, boolean shade) { + +> CHANGE 33 : 34 @ 37 : 38 + +~ private int[] makeQuadVertexData(BlockPartFace partFace, EaglerTextureAtlasSprite sprite, EnumFacing facing, + +> CHANGE 81 : 83 @ 85 : 87 + +~ float[] sprite, EaglerTextureAtlasSprite modelRotationIn, ModelRotation partRotation, +~ BlockPartRotation uvLocked, boolean shade, boolean parFlag2) { + +> CHANGE 96 : 97 @ 100 : 101 + +~ EaglerTextureAtlasSprite sprite, BlockFaceUV faceUV) { + +> CHANGE 210 : 211 @ 214 : 215 + +~ EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> CHANGE 287 : 288 @ 291 : 292 + +~ EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.edit.java new file mode 100644 index 0000000..e5c420c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemCameraTransforms.edit.java @@ -0,0 +1,39 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 10 + +~ import org.json.JSONException; +~ import org.json.JSONObject; + +> INSERT 5 : 9 @ 11 + ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ + +> CHANGE 90 : 98 @ 92 : 108 + +~ public static class Deserializer implements JSONTypeDeserializer { +~ public ItemCameraTransforms deserialize(JSONObject jsonobject) throws JSONException { +~ ItemTransformVec3f itemtransformvec3f = this.func_181683_a(jsonobject, "thirdperson"); +~ ItemTransformVec3f itemtransformvec3f1 = this.func_181683_a(jsonobject, "firstperson"); +~ ItemTransformVec3f itemtransformvec3f2 = this.func_181683_a(jsonobject, "head"); +~ ItemTransformVec3f itemtransformvec3f3 = this.func_181683_a(jsonobject, "gui"); +~ ItemTransformVec3f itemtransformvec3f4 = this.func_181683_a(jsonobject, "ground"); +~ ItemTransformVec3f itemtransformvec3f5 = this.func_181683_a(jsonobject, "fixed"); + +> CHANGE 102 : 103 @ 112 : 114 + +~ private ItemTransformVec3f func_181683_a(JSONObject parJsonObject, String parString1) { + +> CHANGE 104 : 105 @ 115 : 117 + +~ ? JSONTypeProvider.deserialize(parJsonObject.get(parString1), ItemTransformVec3f.class) + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemModelGenerator.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemModelGenerator.edit.java new file mode 100644 index 0000000..bf40412 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemModelGenerator.edit.java @@ -0,0 +1,40 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 5 : 11 @ 7 : 13 + +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ +~ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; +~ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; + +> DELETE 14 @ 16 : 17 + +> CHANGE 31 : 33 @ 34 : 35 + +~ EaglerTextureAtlasSprite textureatlassprite = textureMapIn +~ .getAtlasSprite((new ResourceLocation(s1)).toString()); + +> CHANGE 45 : 47 @ 47 : 48 + +~ private List func_178394_a(int parInt1, String parString1, +~ EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> CHANGE 59 : 61 @ 60 : 61 + +~ private List func_178397_a(EaglerTextureAtlasSprite parTextureAtlasSprite, String parString1, +~ int parInt1) { + +> CHANGE 163 : 164 @ 163 : 164 + +~ private List func_178393_a(EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.edit.java new file mode 100644 index 0000000..bb4ffc2 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ItemTransformVec3f.edit.java @@ -0,0 +1,41 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 8 @ 2 : 10 + +~ import org.json.JSONArray; +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; +~ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; + +> DELETE 9 @ 11 : 12 + +> CHANGE 43 : 44 @ 46 : 47 + +~ public static class Deserializer implements JSONTypeDeserializer { + +> CHANGE 48 : 49 @ 51 : 54 + +~ public ItemTransformVec3f deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 62 : 63 @ 67 : 68 + +~ private Vector3f parseVector3f(JSONObject jsonObject, String key, Vector3f defaultValue) { + +> CHANGE 66 : 69 @ 71 : 74 + +~ JSONArray jsonarray = jsonObject.getJSONArray(key); +~ if (jsonarray.length() != 3) { +~ throw new JSONException("Expected 3 " + key + " values, found: " + jsonarray.length()); + +> CHANGE 73 : 74 @ 78 : 79 + +~ afloat[i] = jsonarray.getFloat(i); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlock.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlock.edit.java new file mode 100644 index 0000000..3525963 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlock.edit.java @@ -0,0 +1,98 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 11 + +> CHANGE 11 : 12 @ 20 : 27 + +~ + +> CHANGE 13 : 15 @ 28 : 30 + +~ import org.json.JSONException; +~ import org.json.JSONObject; + +> INSERT 16 : 25 @ 31 + ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.minecraft.util.ResourceLocation; ++ + +> CHANGE 27 : 28 @ 33 : 40 + +~ + +> DELETE 37 @ 49 : 53 + +> CHANGE 38 : 39 @ 54 : 55 + +~ return (ModelBlock) JSONTypeProvider.deserialize(new JSONObject(parString1), ModelBlock.class); + +> CHANGE 163 : 166 @ 179 : 180 + +~ } catch (ModelBlock.LoopException var5) { +~ throw var5; +~ } catch (Throwable var6) { + +> CHANGE 181 : 184 @ 195 : 200 + +~ public static class Deserializer implements JSONTypeDeserializer { +~ public ModelBlock deserialize(JSONObject jsonobject) throws JSONException { +~ List list = this.getModelElements(jsonobject); + +> CHANGE 188 : 189 @ 204 : 205 + +~ throw new JSONException("BlockModel requires either elements or parent, found neither"); + +> CHANGE 190 : 191 @ 206 : 207 + +~ throw new JSONException("BlockModel requires either elements or parent, found both"); + +> CHANGE 196 : 198 @ 212 : 215 + +~ JSONObject jsonobject1 = jsonobject.getJSONObject("display"); +~ itemcameratransforms = JSONTypeProvider.deserialize(jsonobject1, ItemCameraTransforms.class); + +> CHANGE 205 : 206 @ 222 : 223 + +~ private Map getTextures(JSONObject parJsonObject) { + +> CHANGE 208 : 209 @ 225 : 226 + +~ JSONObject jsonobject = parJsonObject.getJSONObject("textures"); + +> CHANGE 210 : 212 @ 227 : 229 + +~ for (String entry : jsonobject.keySet()) { +~ hashmap.put(entry, jsonobject.getString(entry)); + +> CHANGE 218 : 220 @ 235 : 237 + +~ private String getParent(JSONObject parJsonObject) { +~ return parJsonObject.optString("parent", ""); + +> CHANGE 222 : 224 @ 239 : 241 + +~ protected boolean getAmbientOcclusionEnabled(JSONObject parJsonObject) { +~ return parJsonObject.optBoolean("ambientocclusion", true); + +> CHANGE 226 : 227 @ 243 : 245 + +~ protected List getModelElements(JSONObject parJsonObject) { + +> CHANGE 229 : 231 @ 247 : 249 + +~ for (Object jsonelement : parJsonObject.getJSONArray("elements")) { +~ arraylist.add((BlockPart) JSONTypeProvider.deserialize(jsonelement, BlockPart.class)); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.edit.java new file mode 100644 index 0000000..e77b0cb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.edit.java @@ -0,0 +1,94 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 11 + +> INSERT 9 : 19 @ 18 + ++ ++ import org.json.JSONArray; ++ import org.json.JSONException; ++ import org.json.JSONObject; ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; + +> DELETE 20 @ 19 : 20 + +> CHANGE 23 : 24 @ 23 : 27 + +~ + +> CHANGE 27 : 28 @ 30 : 31 + +~ return (ModelBlockDefinition) JSONTypeProvider.deserialize(parReader, ModelBlockDefinition.class); + +> CHANGE 69 : 73 @ 72 : 78 + +~ public static class Deserializer implements JSONTypeDeserializer { +~ public ModelBlockDefinition deserialize(JSONObject jsonobject) throws JSONException { +~ List list = this.parseVariantsList(jsonobject); +~ return new ModelBlockDefinition((Collection) list); + +> CHANGE 75 : 77 @ 80 : 83 + +~ protected List parseVariantsList(JSONObject parJsonObject) { +~ JSONObject jsonobject = parJsonObject.getJSONObject("variants"); + +> CHANGE 79 : 81 @ 85 : 87 + +~ for (String entry : jsonobject.keySet()) { +~ arraylist.add(this.parseVariants(entry, jsonobject.get(entry))); + +> CHANGE 86 : 87 @ 92 : 95 + +~ protected ModelBlockDefinition.Variants parseVariants(String s, Object jsonelement) { + +> CHANGE 88 : 91 @ 96 : 101 + +~ if (jsonelement instanceof JSONArray) { +~ for (Object jsonelement1 : (JSONArray) jsonelement) { +~ arraylist.add(JSONTypeProvider.deserialize(jsonelement1, ModelBlockDefinition.Variant.class)); + +> CHANGE 93 : 94 @ 103 : 105 + +~ arraylist.add(JSONTypeProvider.deserialize(jsonelement, ModelBlockDefinition.Variant.class)); + +> CHANGE 153 : 155 @ 164 : 168 + +~ public static class Deserializer implements JSONTypeDeserializer { +~ public ModelBlockDefinition.Variant deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 169 : 171 @ 182 : 184 + +~ private boolean parseUvLock(JSONObject parJsonObject) { +~ return parJsonObject.optBoolean("uvlock", false); + +> CHANGE 173 : 176 @ 186 : 189 + +~ protected ModelRotation parseRotation(JSONObject parJsonObject) { +~ int i = parJsonObject.optInt("x", 0); +~ int j = parJsonObject.optInt("y", 0); + +> CHANGE 178 : 179 @ 191 : 192 + +~ throw new JSONException("Invalid BlockModelRotation x: " + i + ", y: " + j); + +> CHANGE 184 : 186 @ 197 : 199 + +~ protected String parseModel(JSONObject parJsonObject) { +~ return parJsonObject.getString("model"); + +> CHANGE 188 : 190 @ 201 : 203 + +~ protected int parseWeight(JSONObject parJsonObject) { +~ return parJsonObject.optInt("weight", 1); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java new file mode 100644 index 0000000..a2a73c2 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/BlockStateMapper.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 6 : 11 @ 9 + ++ ++ import com.google.common.base.Objects; ++ import com.google.common.collect.Maps; ++ import com.google.common.collect.Sets; ++ + +> DELETE 13 @ 11 : 13 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/DefaultStateMapper.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/IStateMapper.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/IStateMapper.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/IStateMapper.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java new file mode 100644 index 0000000..e7bb329 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMap.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 5 : 9 @ 7 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> DELETE 12 @ 10 : 11 + +> CHANGE 27 : 28 @ 26 : 27 + +~ LinkedHashMap linkedhashmap = Maps.newLinkedHashMap(iblockstate.getProperties()); + +> CHANGE 32 : 33 @ 31 : 32 + +~ s = this.name.getName(linkedhashmap.remove(this.name)); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java new file mode 100644 index 0000000..b774dd4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/block/statemap/StateMapperBase.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Maps; ++ + +> DELETE 10 @ 8 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.edit.java new file mode 100644 index 0000000..66f8e68 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkCompileTaskGenerator.edit.java @@ -0,0 +1,71 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 5 + +~ +~ import com.google.common.collect.Lists; +~ + +> CHANGE 7 : 8 @ 6 : 8 + +~ import net.minecraft.util.EnumWorldBlockLayer; + +> DELETE 11 @ 11 : 12 + +> INSERT 17 : 19 @ 18 + ++ public long goddamnFuckingTimeout = 0l; ++ public long time = 0; + +> CHANGE 50 : 51 @ 49 : 57 + +~ this.status = statusIn; + +> CHANGE 54 : 58 @ 60 : 61 + +~ if (this.type == ChunkCompileTaskGenerator.Type.REBUILD_CHUNK +~ && this.status != ChunkCompileTaskGenerator.Status.DONE) { +~ this.renderChunk.setNeedsUpdate(true); +~ } + +> CHANGE 59 : 61 @ 62 : 67 + +~ this.finished = true; +~ this.status = ChunkCompileTaskGenerator.Status.DONE; + +> CHANGE 62 : 64 @ 68 : 76 + +~ for (Runnable runnable : this.listFinishRunnables) { +~ runnable.run(); + +> DELETE 65 @ 77 : 78 + +> CHANGE 68 : 71 @ 81 : 90 + +~ this.listFinishRunnables.add(parRunnable); +~ if (this.finished) { +~ parRunnable.run(); + +> DELETE 72 @ 91 : 92 + +> DELETE 74 @ 94 : 98 + +> INSERT 82 : 90 @ 106 + ++ public boolean canExecuteYet() { ++ if (this.type == ChunkCompileTaskGenerator.Type.RESORT_TRANSPARENCY) { ++ return !this.renderChunk.getCompiledChunk().isLayerEmpty(EnumWorldBlockLayer.TRANSLUCENT); ++ } else { ++ return true; ++ } ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderWorker.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderWorker.edit.java new file mode 100644 index 0000000..a67da61 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ChunkRenderWorker.edit.java @@ -0,0 +1,104 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.minecraft.ChunkUpdateManager; + +> DELETE 7 @ 11 : 15 + +> DELETE 9 @ 17 : 19 + +> CHANGE 10 : 11 @ 20 : 21 + +~ public class ChunkRenderWorker { + +> CHANGE 12 : 13 @ 22 : 23 + +~ private final ChunkUpdateManager chunkRenderDispatcher; + +> CHANGE 15 : 16 @ 25 : 26 + +~ public ChunkRenderWorker(ChunkUpdateManager parChunkRenderDispatcher) { + +> CHANGE 19 : 20 @ 29 : 30 + +~ public ChunkRenderWorker(ChunkUpdateManager chunkRenderDispatcherIn, + +> DELETE 25 @ 35 : 51 + +> CHANGE 26 : 30 @ 52 : 62 + +~ if (generator.getStatus() != ChunkCompileTaskGenerator.Status.PENDING) { +~ if (!generator.isFinished()) { +~ LOGGER.warn("Chunk render task was " + generator.getStatus() +~ + " when I expected it to be pending; ignoring task"); + +> CHANGE 32 : 33 @ 64 : 67 + +~ return; + +> INSERT 35 : 37 @ 69 + ++ generator.setStatus(ChunkCompileTaskGenerator.Status.COMPILING); ++ + +> CHANGE 52 : 56 @ 84 : 95 + +~ if (generator.getStatus() != ChunkCompileTaskGenerator.Status.COMPILING) { +~ if (!generator.isFinished()) { +~ LOGGER.warn("Chunk render task was " + generator.getStatus() +~ + " when I expected it to be compiling; aborting task"); + +> CHANGE 58 : 60 @ 97 : 100 + +~ this.freeRenderBuilder(generator); +~ return; + +> INSERT 62 : 64 @ 102 + ++ generator.setStatus(ChunkCompileTaskGenerator.Status.UPLOADING); ++ + +> CHANGE 65 : 68 @ 103 : 104 + +~ if (compiledchunk == null) { +~ System.out.println(chunkcompiletaskgenerator$type); +~ } + +> CHANGE 70 : 72 @ 106 : 108 + +~ if (!compiledchunk.isLayerEmpty(enumworldblocklayer)) { +~ this.chunkRenderDispatcher.uploadChunk(enumworldblocklayer, + +> CHANGE 73 : 76 @ 109 : 110 + +~ generator.getRenderChunk(), compiledchunk); +~ generator.getRenderChunk().setCompiledChunk(compiledchunk); +~ generator.setStatus(ChunkCompileTaskGenerator.Status.DONE); + +> CHANGE 79 : 80 @ 113 : 114 + +~ this.chunkRenderDispatcher.uploadChunk( + +> CHANGE 82 : 85 @ 116 : 117 + +~ generator.getRenderChunk(), compiledchunk); +~ generator.getRenderChunk().setCompiledChunk(compiledchunk); +~ generator.setStatus(ChunkCompileTaskGenerator.Status.DONE); + +> DELETE 87 @ 119 : 159 + +> CHANGE 91 : 92 @ 163 : 165 + +~ return this.regionRenderCacheBuilder; + +> DELETE 95 @ 168 : 171 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/CompiledChunk.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/CompiledChunk.edit.java new file mode 100644 index 0000000..0733b1d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/CompiledChunk.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 7 @ 4 : 6 + +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> CHANGE 22 : 23 @ 21 : 22 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/IRenderChunkFactory.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/IRenderChunkFactory.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/IRenderChunkFactory.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ListChunkFactory.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/ListChunkFactory.edit.java new file mode 100644 index 0000000..4890f7c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ListChunkFactory.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/ListedRenderChunk.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/ListedRenderChunk.edit.java new file mode 100644 index 0000000..bcfb39e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/ListedRenderChunk.edit.java @@ -0,0 +1,48 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; + +> DELETE 5 @ 4 : 6 + +> CHANGE 10 : 11 @ 11 : 12 + +~ private final int[] baseDisplayList; + +> INSERT 14 : 18 @ 15 + ++ this.baseDisplayList = new int[EnumWorldBlockLayer.values().length]; ++ for (int i = 0; i < this.baseDisplayList.length; ++i) { ++ this.baseDisplayList[i] = GLAllocation.generateDisplayLists(); ++ } + +> CHANGE 21 : 22 @ 18 : 19 + +~ return !parCompiledChunk.isLayerEmpty(layer) ? this.baseDisplayList[layer.ordinal()] : -1; + +> CHANGE 26 : 29 @ 23 : 24 + +~ for (int i = 0; i < this.baseDisplayList.length; ++i) { +~ GLAllocation.deleteDisplayLists(this.baseDisplayList[i]); +~ } + +> INSERT 30 : 40 @ 25 + ++ ++ public void rebuildChunk(float x, float y, float z, ChunkCompileTaskGenerator generator) { ++ super.rebuildChunk(x, y, z, generator); ++ EnumWorldBlockLayer[] layers = EnumWorldBlockLayer.values(); ++ for (int i = 0; i < layers.length; ++i) { ++ if (generator.getCompiledChunk().isLayerEmpty(layers[i])) { ++ EaglercraftGPU.flushDisplayList(this.baseDisplayList[i]); ++ } ++ } ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.edit.java new file mode 100644 index 0000000..4297047 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/RenderChunk.edit.java @@ -0,0 +1,101 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; + +> CHANGE 6 : 12 @ 8 : 9 + +~ +~ import com.google.common.collect.Maps; +~ import com.google.common.collect.Sets; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 16 @ 13 : 16 + +> DELETE 18 @ 18 : 22 + +> DELETE 21 @ 25 : 26 + +> DELETE 34 @ 39 : 41 + +> CHANGE 37 : 38 @ 44 : 46 + +~ private final float[] modelviewMatrix = new float[16]; + +> DELETE 51 @ 59 : 65 + +> DELETE 62 @ 76 : 80 + +> DELETE 93 @ 111 : 112 + +> CHANGE 95 : 97 @ 114 : 123 + +~ if (generator.getStatus() != ChunkCompileTaskGenerator.Status.COMPILING) { +~ return; + +> INSERT 99 : 102 @ 125 + ++ regionrendercache = new RegionRenderCache(this.world, blockpos.add(-1, -1, -1), blockpos1.add(1, 1, 1), 1); ++ generator.setCompiledChunk(compiledchunk); ++ + +> CHANGE 109 : 111 @ 132 : 134 + +~ for (BlockPos blockpos$mutableblockpos : BlockPos.getAllInBox(blockpos, blockpos1)) { +~ IBlockState iblockstate = regionrendercache.getBlockStateFaster(blockpos$mutableblockpos); + +> CHANGE 117 : 118 @ 140 : 141 + +~ TileEntity tileentity = regionrendercache.getTileEntity(blockpos$mutableblockpos); + +> DELETE 156 @ 179 : 180 + +> CHANGE 157 : 164 @ 181 : 192 + +~ HashSet hashset1 = Sets.newHashSet(hashset); +~ HashSet hashset2 = Sets.newHashSet(this.field_181056_j); +~ hashset1.removeAll(this.field_181056_j); +~ hashset2.removeAll(hashset); +~ this.field_181056_j.clear(); +~ this.field_181056_j.addAll(hashset); +~ this.renderGlobal.func_181023_a(hashset2, hashset1); + +> CHANGE 168 : 171 @ 196 : 205 + +~ if (this.compileTask != null && this.compileTask.getStatus() != ChunkCompileTaskGenerator.Status.DONE) { +~ this.compileTask.finish(); +~ this.compileTask = null; + +> DELETE 172 @ 206 : 207 + +> DELETE 174 @ 209 : 213 + +> DELETE 175 @ 214 : 216 + +> CHANGE 176 : 179 @ 217 : 225 + +~ this.finishCompileTask(); +~ this.compileTask = new ChunkCompileTaskGenerator(this, ChunkCompileTaskGenerator.Type.REBUILD_CHUNK); +~ chunkcompiletaskgenerator = this.compileTask; + +> CHANGE 183 : 186 @ 229 : 252 + +~ this.compileTask = new ChunkCompileTaskGenerator(this, ChunkCompileTaskGenerator.Type.RESORT_TRANSPARENCY); +~ this.compileTask.setCompiledChunk(this.compiledChunk); +~ return this.compileTask; + +> CHANGE 223 : 224 @ 289 : 297 + +~ this.compiledChunk = compiledChunkIn; + +> DELETE 234 @ 307 : 314 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/SetVisibility.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/SetVisibility.edit.java new file mode 100644 index 0000000..78b3da1 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/SetVisibility.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/chunk/VisGraph.edit.java b/patches/minecraft/net/minecraft/client/renderer/chunk/VisGraph.edit.java new file mode 100644 index 0000000..7c7a500 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/chunk/VisGraph.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 6 : 9 @ 7 : 8 + +~ +~ import com.google.common.collect.Lists; +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/culling/ClippingHelperImpl.edit.java b/patches/minecraft/net/minecraft/client/renderer/culling/ClippingHelperImpl.edit.java new file mode 100644 index 0000000..9673445 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/culling/ClippingHelperImpl.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +~ +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 7 @ 4 : 6 + +> DELETE 11 @ 10 : 13 + +> INSERT 14 : 15 @ 16 + ++ instance.destroy(); + +> DELETE 28 @ 29 : 34 + +> CHANGE 30 : 32 @ 36 : 40 + +~ GlStateManager.getFloat(2983, afloat); +~ GlStateManager.getFloat(2982, afloat1); + +> INSERT 101 : 105 @ 109 + ++ ++ public void destroy() { ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/culling/Frustum.edit.java b/patches/minecraft/net/minecraft/client/renderer/culling/Frustum.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/culling/Frustum.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/ArmorStandRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/ArmorStandRenderer.edit.java new file mode 100644 index 0000000..f3b8c08 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/ArmorStandRenderer.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 5 @ 4 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/Render.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/Render.edit.java new file mode 100644 index 0000000..45a8aca --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/Render.edit.java @@ -0,0 +1,36 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 6 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 9 @ 5 : 6 + +> DELETE 10 @ 7 : 8 + +> DELETE 11 @ 9 : 11 + +> DELETE 21 @ 21 : 22 + +> CHANGE 82 : 84 @ 83 : 85 + +~ EaglerTextureAtlasSprite textureatlassprite = texturemap.getAtlasSprite("minecraft:blocks/fire_layer_0"); +~ EaglerTextureAtlasSprite textureatlassprite1 = texturemap.getAtlasSprite("minecraft:blocks/fire_layer_1"); + +> CHANGE 102 : 103 @ 103 : 104 + +~ EaglerTextureAtlasSprite textureatlassprite2 = i % 2 == 0 ? textureatlassprite : textureatlassprite1; + +> CHANGE 282 : 283 @ 283 : 284 + +~ EaglercraftGPU.glNormal3f(0.0F, 1.0F, 0.0F); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderArrow.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderArrow.edit.java new file mode 100644 index 0000000..3072a84 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderArrow.edit.java @@ -0,0 +1,30 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 4 : 7 + +> DELETE 10 @ 11 : 12 + +> CHANGE 51 : 52 @ 53 : 54 + +~ EaglercraftGPU.glNormal3f(f10, 0.0F, 0.0F); + +> CHANGE 58 : 59 @ 60 : 61 + +~ EaglercraftGPU.glNormal3f(-f10, 0.0F, 0.0F); + +> CHANGE 68 : 69 @ 70 : 71 + +~ EaglercraftGPU.glNormal3f(0.0F, 0.0F, f10); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBat.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBat.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBat.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBiped.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBiped.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBiped.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBlaze.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBlaze.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBlaze.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderBoat.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBoat.edit.java new file mode 100644 index 0000000..f3b8c08 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderBoat.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 5 @ 4 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCaveSpider.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCaveSpider.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCaveSpider.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderChicken.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderChicken.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderChicken.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCow.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCow.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCow.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderCreeper.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCreeper.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderCreeper.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderDragon.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderDragon.edit.java new file mode 100644 index 0000000..df0d3ca --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderDragon.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 5 @ 3 : 4 + +> DELETE 7 @ 6 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEnderman.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEnderman.edit.java new file mode 100644 index 0000000..5857c31 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEnderman.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 6 @ 5 : 7 + +> CHANGE 15 : 16 @ 16 : 17 + +~ private EaglercraftRandom rnd = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEndermite.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEndermite.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEndermite.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntity.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntity.edit.java new file mode 100644 index 0000000..ec2b569 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntity.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntityItem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntityItem.edit.java new file mode 100644 index 0000000..64bf1ce --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderEntityItem.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 5 : 8 + +> CHANGE 16 : 17 @ 18 : 19 + +~ private EaglercraftRandom field_177079_e = new EaglercraftRandom(); + +> CHANGE 45 : 47 @ 47 : 49 + +~ float f6 = 0.0F * (float) (i - 1) * 0.5F; +~ float f4 = 0.0F * (float) (i - 1) * 0.5F; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFallingBlock.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFallingBlock.edit.java new file mode 100644 index 0000000..650f964 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFallingBlock.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 8 @ 6 : 7 + +> DELETE 9 @ 8 : 11 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFireball.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFireball.edit.java new file mode 100644 index 0000000..851f807 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFireball.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 3 : 4 + +> DELETE 7 @ 5 : 9 + +> CHANGE 27 : 28 @ 29 : 30 + +~ EaglerTextureAtlasSprite textureatlassprite = Minecraft.getMinecraft().getRenderItem().getItemModelMesher() + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderFish.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFish.edit.java new file mode 100644 index 0000000..16b90d8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderFish.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 5 @ 3 : 4 + +> DELETE 6 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGhast.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGhast.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGhast.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGiantZombie.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGiantZombie.edit.java new file mode 100644 index 0000000..2866203 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGiantZombie.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 5 @ 4 : 7 + +> CHANGE 20 : 22 @ 22 : 24 + +~ this.field_177189_c = new ModelZombie(0.5F, false); +~ this.field_177186_d = new ModelZombie(1.0F, false); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderGuardian.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGuardian.edit.java new file mode 100644 index 0000000..b137cc4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderGuardian.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 6 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 7 @ 3 : 5 + +> DELETE 8 @ 6 : 7 + +> DELETE 9 @ 8 : 10 + +> DELETE 15 @ 16 : 17 + +> CHANGE 72 : 74 @ 74 : 76 + +~ EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 10497.0F); +~ EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 10497.0F); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderHorse.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderHorse.edit.java new file mode 100644 index 0000000..ec230e5 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderHorse.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 7 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 9 @ 6 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderIronGolem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderIronGolem.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderIronGolem.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java new file mode 100644 index 0000000..d52eb72 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderItem.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 7 @ 4 + ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 28 @ 25 : 26 + +> DELETE 31 @ 29 : 30 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLeashKnot.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLeashKnot.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLeashKnot.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLightningBolt.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLightningBolt.edit.java new file mode 100644 index 0000000..38ecafd --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLightningBolt.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 7 @ 5 : 8 + +> CHANGE 28 : 29 @ 29 : 30 + +~ EaglercraftRandom random = new EaglercraftRandom(entitylightningbolt.boltVertex); + +> CHANGE 38 : 39 @ 39 : 40 + +~ EaglercraftRandom random1 = new EaglercraftRandom(entitylightningbolt.boltVertex); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderLiving.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLiving.edit.java new file mode 100644 index 0000000..7d89666 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderLiving.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 3 : 5 + +> DELETE 7 @ 6 : 7 + +> DELETE 8 @ 8 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMagmaCube.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMagmaCube.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMagmaCube.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java new file mode 100644 index 0000000..f0859ed --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderManager.edit.java @@ -0,0 +1,38 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 9 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 25 @ 20 : 22 + +> DELETE 27 @ 24 : 25 + +> DELETE 28 @ 26 : 80 + +> CHANGE 191 : 193 @ 243 : 244 + +~ this.skinMap.put("slim", new RenderPlayer(this, true, false)); +~ this.skinMap.put("zombie", new RenderPlayer(this, false, true)); + +> CHANGE 204 : 205 @ 255 : 256 + +~ render = this.getEntityClassRenderObject((Class) parClass1.getSuperclass()); + +> CHANGE 211 : 212 @ 262 : 263 + +~ public Render getEntityRenderObject(Entity entityIn) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecart.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecart.edit.java new file mode 100644 index 0000000..e086ddc --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecart.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 7 @ 6 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMinecartMobSpawner.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderMooshroom.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMooshroom.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderMooshroom.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderOcelot.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderOcelot.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderOcelot.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPainting.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPainting.edit.java new file mode 100644 index 0000000..e3c80b1 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPainting.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPig.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPig.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPig.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPigZombie.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPigZombie.edit.java new file mode 100644 index 0000000..7ce3c0d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPigZombie.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> CHANGE 17 : 19 @ 19 : 21 + +~ this.field_177189_c = new ModelZombie(0.5F, false); +~ this.field_177186_d = new ModelZombie(1.0F, false); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPlayer.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPlayer.edit.java new file mode 100644 index 0000000..f5814ec --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPlayer.edit.java @@ -0,0 +1,83 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> INSERT 5 : 6 @ 4 + ++ import net.minecraft.client.model.ModelBiped; + +> CHANGE 7 : 8 @ 5 : 8 + +~ import net.minecraft.client.model.ModelZombie; + +> INSERT 24 : 25 @ 24 + ++ private boolean zombieModel; + +> CHANGE 27 : 28 @ 26 : 27 + +~ this(renderManager, false, false); + +> CHANGE 30 : 32 @ 29 : 31 + +~ public RenderPlayer(RenderManager renderManager, boolean useSmallArms, boolean zombieModel) { +~ super(renderManager, zombieModel ? new ModelZombie(0.0F, true) : new ModelPlayer(0.0F, useSmallArms), 0.5F); + +> INSERT 33 : 34 @ 32 + ++ this.zombieModel = zombieModel; + +> CHANGE 42 : 44 @ 40 : 42 + +~ public ModelBiped getMainModel() { +~ return (ModelBiped) super.getMainModel(); + +> CHANGE 60 : 61 @ 58 : 59 + +~ ModelBiped modelplayer = this.getMainModel(); + +> CHANGE 69 : 77 @ 67 : 72 + +~ if (!zombieModel) { +~ ModelPlayer modelplayer_ = (ModelPlayer) modelplayer; +~ modelplayer_.bipedBodyWear.showModel = clientPlayer.isWearing(EnumPlayerModelParts.JACKET); +~ modelplayer_.bipedLeftLegwear.showModel = clientPlayer.isWearing(EnumPlayerModelParts.LEFT_PANTS_LEG); +~ modelplayer_.bipedRightLegwear.showModel = clientPlayer.isWearing(EnumPlayerModelParts.RIGHT_PANTS_LEG); +~ modelplayer_.bipedLeftArmwear.showModel = clientPlayer.isWearing(EnumPlayerModelParts.LEFT_SLEEVE); +~ modelplayer_.bipedRightArmwear.showModel = clientPlayer.isWearing(EnumPlayerModelParts.RIGHT_SLEEVE); +~ } + +> CHANGE 127 : 137 @ 122 : 130 + +~ if (!zombieModel) { +~ float f = 1.0F; +~ GlStateManager.color(f, f, f); +~ ModelBiped modelplayer = this.getMainModel(); +~ this.setModelVisibilities(clientPlayer); +~ modelplayer.swingProgress = 0.0F; +~ modelplayer.isSneak = false; +~ modelplayer.setRotationAngles(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0625F, clientPlayer); +~ ((ModelPlayer) modelplayer).renderRightArm(); +~ } + +> CHANGE 140 : 150 @ 133 : 141 + +~ if (!zombieModel) { +~ float f = 1.0F; +~ GlStateManager.color(f, f, f); +~ ModelBiped modelplayer = this.getMainModel(); +~ this.setModelVisibilities(clientPlayer); +~ modelplayer.isSneak = false; +~ modelplayer.swingProgress = 0.0F; +~ modelplayer.setRotationAngles(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0625F, clientPlayer); +~ ((ModelPlayer) modelplayer).renderLeftArm(); +~ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderPotion.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPotion.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderPotion.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderRabbit.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderRabbit.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderRabbit.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSheep.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSheep.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSheep.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSilverfish.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSilverfish.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSilverfish.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSkeleton.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSkeleton.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSkeleton.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSlime.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSlime.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSlime.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowMan.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowMan.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowMan.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowball.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowball.edit.java new file mode 100644 index 0000000..58aaaa0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSnowball.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 4 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSpider.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSpider.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSpider.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderSquid.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSquid.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderSquid.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderTNTPrimed.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderTNTPrimed.edit.java new file mode 100644 index 0000000..f3b8c08 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderTNTPrimed.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 5 @ 4 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderTntMinecart.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderTntMinecart.edit.java new file mode 100644 index 0000000..ef0d172 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderTntMinecart.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderVillager.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderVillager.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderVillager.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWitch.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWitch.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWitch.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWither.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWither.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWither.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderWolf.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWolf.edit.java new file mode 100644 index 0000000..49ebceb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderWolf.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderXPOrb.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderXPOrb.edit.java new file mode 100644 index 0000000..e3c80b1 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderXPOrb.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 6 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RenderZombie.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RenderZombie.edit.java new file mode 100644 index 0000000..a8c8300 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RenderZombie.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 9 @ 7 : 9 + +> CHANGE 34 : 36 @ 34 : 36 + +~ this.field_177189_c = new ModelZombie(0.5F, false); +~ this.field_177186_d = new ModelZombie(1.0F, false); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/RendererLivingEntity.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/RendererLivingEntity.edit.java new file mode 100644 index 0000000..5720a26 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/RendererLivingEntity.edit.java @@ -0,0 +1,52 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; + +> INSERT 4 : 13 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 18 @ 10 : 12 + +> DELETE 19 @ 13 : 16 + +> DELETE 29 @ 26 : 29 + +> CHANGE 45 : 46 @ 45 : 46 + +~ return this.layerRenderers.add((LayerRenderer) layer); + +> CHANGE 240 : 247 @ 240 : 296 + +~ GlStateManager.enableShaderBlendAdd(); +~ float f1 = 1.0F - (float) (i >> 24 & 255) / 255.0F; +~ float f2 = (float) (i >> 16 & 255) / 255.0F; +~ float f3 = (float) (i >> 8 & 255) / 255.0F; +~ float f4 = (float) (i & 255) / 255.0F; +~ GlStateManager.setShaderBlendSrc(f1, f1, f1, 1.0F); +~ GlStateManager.setShaderBlendAdd(f2 * f1 + 0.4F, f3 * f1, f4 * f1, 0.0f); + +> CHANGE 252 : 253 @ 301 : 338 + +~ GlStateManager.disableShaderBlendAdd(); + +> CHANGE 326 : 327 @ 411 : 412 + +~ EaglercraftGPU.glNormal3f(0.0F, 1.0F, 0.0F); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java new file mode 100644 index 0000000..c2936a6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArmorBase.edit.java @@ -0,0 +1,38 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 8 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 9 @ 5 : 6 + +> DELETE 10 @ 7 : 8 + +> CHANGE 57 : 58 @ 55 : 56 + +~ this.func_177179_a((T) modelbase, parInt1); + +> CHANGE 77 : 78 @ 75 : 76 + +~ this.func_177183_a(entitylivingbaseIn, (T) modelbase, armorSlot, parFloat2, parFloat3, parFloat4, + +> CHANGE 136 : 137 @ 134 : 135 + +~ String s = HString.format("textures/models/armor/%s_layer_%d%s.png", + +> CHANGE 138 : 139 @ 136 : 137 + +~ parString1 == null ? "" : HString.format("_%s", new Object[] { parString1 }) }); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArrow.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArrow.edit.java new file mode 100644 index 0000000..af91309 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerArrow.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 7 @ 5 : 6 + +> DELETE 9 @ 8 : 9 + +> CHANGE 26 : 27 @ 26 : 27 + +~ EaglercraftRandom random = new EaglercraftRandom((long) entitylivingbase.getEntityId()); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerBipedArmor.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCape.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCape.edit.java new file mode 100644 index 0000000..aa90635 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCape.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> CHANGE 4 : 5 @ 3 : 4 + +~ import net.minecraft.client.model.ModelPlayer; + +> DELETE 6 @ 5 : 6 + +> CHANGE 20 : 22 @ 20 : 21 + +~ && abstractclientplayer.getLocationCape() != null +~ && this.playerRenderer.getMainModel() instanceof ModelPlayer) { + +> CHANGE 64 : 65 @ 63 : 64 + +~ ((ModelPlayer) this.playerRenderer.getMainModel()).renderCape(0.0625F); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.edit.java new file mode 100644 index 0000000..27cd162 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCreeperCharge.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 4 + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCustomHead.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCustomHead.edit.java new file mode 100644 index 0000000..7020e6d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerCustomHead.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 8 @ 6 : 7 + +> DELETE 9 @ 8 : 9 + +> CHANGE 78 : 79 @ 78 : 79 + +~ gameprofile = TileEntitySkull.updateGameprofile(new GameProfile((EaglercraftUUID) null, s)); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.edit.java new file mode 100644 index 0000000..628614e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerDeadmau5Head.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> CHANGE 4 : 5 @ 3 : 4 + +~ import net.minecraft.client.model.ModelPlayer; + +> DELETE 6 @ 5 : 6 + +> CHANGE 17 : 18 @ 17 : 18 + +~ && !abstractclientplayer.isInvisible() && this.playerRenderer.getMainModel() instanceof ModelPlayer) { + +> CHANGE 37 : 38 @ 37 : 38 + +~ ((ModelPlayer) this.playerRenderer.getMainModel()).renderDeadmau5Head(0.0625F); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.edit.java new file mode 100644 index 0000000..26e1b4d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonDeath.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 8 @ 6 : 8 + +> CHANGE 24 : 25 @ 24 : 25 + +~ EaglercraftRandom random = new EaglercraftRandom(432L); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.edit.java new file mode 100644 index 0000000..7f6db67 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEnderDragonEyes.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.edit.java new file mode 100644 index 0000000..7f6db67 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerEndermanEyes.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.edit.java new file mode 100644 index 0000000..94c615e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldBlock.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> DELETE 8 @ 6 : 8 + +> DELETE 9 @ 9 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItem.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItem.edit.java new file mode 100644 index 0000000..323913c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItem.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 5 : 6 + +> DELETE 8 @ 8 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.edit.java new file mode 100644 index 0000000..323913c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerHeldItemWitch.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 5 : 6 + +> DELETE 8 @ 8 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.edit.java new file mode 100644 index 0000000..c0b443d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerIronGolemFlower.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> DELETE 7 @ 5 : 7 + +> DELETE 8 @ 8 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.edit.java new file mode 100644 index 0000000..51a6afb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerMooshroomMushroom.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 6 @ 5 : 6 + +> DELETE 7 @ 7 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSaddle.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSaddle.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSaddle.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSheepWool.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSheepWool.edit.java new file mode 100644 index 0000000..27cd162 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSheepWool.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 4 + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.edit.java new file mode 100644 index 0000000..3f0c81e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSlimeGel.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 5 @ 4 : 5 + +> DELETE 6 @ 6 : 7 + +> DELETE 20 @ 21 : 22 + +> DELETE 25 @ 27 : 28 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.edit.java new file mode 100644 index 0000000..c5a9a60 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSnowmanHead.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 4 + +> DELETE 6 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.edit.java new file mode 100644 index 0000000..7f6db67 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerSpiderEyes.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerVillagerArmor.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWitherAura.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWitherAura.edit.java new file mode 100644 index 0000000..27cd162 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWitherAura.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 4 + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.edit.java b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.edit.java new file mode 100644 index 0000000..599647f --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/entity/layers/LayerWolfCollar.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/AbstractTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/AbstractTexture.edit.java new file mode 100644 index 0000000..f68beb4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/AbstractTexture.edit.java @@ -0,0 +1,38 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; + +> INSERT 4 : 6 @ 6 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ + +> CHANGE 14 : 26 @ 14 : 25 + +~ if (blur != parFlag || mipmap != parFlag2) { +~ this.blur = parFlag; +~ this.mipmap = parFlag2; +~ int i = -1; +~ short short1 = -1; +~ if (parFlag) { +~ i = parFlag2 ? 9987 : 9729; +~ short1 = 9729; +~ } else { +~ i = parFlag2 ? 9986 : 9728; +~ short1 = 9728; +~ } + +> CHANGE 27 : 30 @ 26 : 28 + +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, i); +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, short1); +~ } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/DynamicTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/DynamicTexture.edit.java new file mode 100644 index 0000000..98abd07 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/DynamicTexture.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 5 @ 4 : 6 + +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> CHANGE 12 : 15 @ 13 : 17 + +~ public DynamicTexture(ImageData bufferedImage) { +~ this(bufferedImage.width, bufferedImage.height); +~ System.arraycopy(bufferedImage.pixels, 0, dynamicTextureData, 0, bufferedImage.pixels.length); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/IIconCreator.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/IIconCreator.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/IIconCreator.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/ITextureObject.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/ITextureObject.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/ITextureObject.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/ITickableTextureObject.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/ITickableTextureObject.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/ITickableTextureObject.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.edit.java new file mode 100644 index 0000000..495f6fe --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/LayeredColorMaskTexture.edit.java @@ -0,0 +1,57 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 5 : 9 @ 8 + ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> DELETE 10 @ 9 : 11 + +> DELETE 14 @ 15 : 17 + +> CHANGE 31 : 32 @ 34 : 35 + +~ ImageData bufferedimage; + +> CHANGE 33 : 34 @ 36 : 37 + +~ ImageData bufferedimage1 = TextureUtil + +> DELETE 35 @ 38 : 42 + +> CHANGE 36 : 39 @ 43 : 46 + +~ bufferedimage = new ImageData(bufferedimage1.width, bufferedimage1.height, false); +~ bufferedimage.drawLayer(bufferedimage1, 0, 0, bufferedimage1.width, bufferedimage1.height, 0, 0, +~ bufferedimage1.width, bufferedimage1.height); + +> CHANGE 45 : 50 @ 52 : 59 + +~ ImageData bufferedimage2 = TextureUtil.readBufferedImage(inputstream); +~ if (bufferedimage2.width == bufferedimage.width && bufferedimage2.height == bufferedimage.height) { +~ for (int k = 0; k < bufferedimage2.height; ++k) { +~ for (int l = 0; l < bufferedimage2.width; ++l) { +~ int i1 = bufferedimage2.pixels[k * bufferedimage2.width + l]; + +> CHANGE 52 : 56 @ 61 : 64 + +~ int k1 = bufferedimage1.pixels[k * bufferedimage1.width + l]; +~ int l1 = MathHelper.func_180188_d(k1, ImageData.swapRB(mapcolor.colorValue)) +~ & 16777215; +~ bufferedimage2.pixels[k * bufferedimage2.width + l] = j1 | l1; + +> CHANGE 60 : 62 @ 68 : 69 + +~ bufferedimage.drawLayer(bufferedimage2, 0, 0, bufferedimage2.width, bufferedimage2.height, 0, 0, +~ bufferedimage2.width, bufferedimage2.height); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/LayeredTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/LayeredTexture.edit.java new file mode 100644 index 0000000..f16b77a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/LayeredTexture.edit.java @@ -0,0 +1,38 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 5 : 11 @ 8 : 10 + +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> DELETE 13 @ 12 : 14 + +> CHANGE 24 : 25 @ 25 : 26 + +~ ImageData bufferedimage = null; + +> CHANGE 30 : 31 @ 31 : 32 + +~ ImageData bufferedimage1 = TextureUtil.readBufferedImage(inputstream); + +> CHANGE 32 : 33 @ 33 : 34 + +~ bufferedimage = new ImageData(bufferedimage1.width, bufferedimage1.height, true); + +> CHANGE 35 : 37 @ 36 : 37 + +~ bufferedimage.drawLayer(bufferedimage1, 0, 0, bufferedimage1.width, bufferedimage1.height, 0, 0, +~ bufferedimage1.width, bufferedimage1.height); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/SimpleTexture.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/SimpleTexture.edit.java new file mode 100644 index 0000000..c3ae816 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/SimpleTexture.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 4 : 8 @ 5 : 7 + +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> DELETE 12 @ 11 : 13 + +> CHANGE 28 : 29 @ 29 : 30 + +~ ImageData bufferedimage = TextureUtil.readBufferedImage(inputstream); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/Stitcher.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/Stitcher.edit.java new file mode 100644 index 0000000..ab1b84c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/Stitcher.edit.java @@ -0,0 +1,54 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 6 : 13 @ 8 + ++ ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Sets; ++ + +> DELETE 14 @ 9 : 10 + +> CHANGE 43 : 44 @ 39 : 40 + +~ public void addSprite(EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> CHANGE 59 : 60 @ 55 : 56 + +~ String s = HString.format("Unable to fit: %s - size: %dx%d - Maybe try a lowerresolution resourcepack?", + +> CHANGE 74 : 75 @ 70 : 71 + +~ public List getStichSlots() { + +> CHANGE 83 : 84 @ 79 : 80 + +~ for (Stitcher.Slot stitcher$slot1 : (List) arraylist) { + +> CHANGE 85 : 86 @ 81 : 82 + +~ EaglerTextureAtlasSprite textureatlassprite = stitcher$holder.getAtlasSprite(); + +> CHANGE 176 : 177 @ 172 : 173 + +~ private final EaglerTextureAtlasSprite theTexture; + +> CHANGE 183 : 184 @ 179 : 180 + +~ public Holder(EaglerTextureAtlasSprite parTextureAtlasSprite, int parInt1) { + +> CHANGE 192 : 193 @ 188 : 189 + +~ public EaglerTextureAtlasSprite getAtlasSprite() { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java new file mode 100644 index 0000000..5aaf9ab --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureClock.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> DELETE 4 @ 3 : 5 + +> CHANGE 6 : 7 @ 7 : 8 + +~ public class TextureClock extends EaglerTextureAtlasSprite { + +> CHANGE 48 : 50 @ 49 : 51 + +~ animationCache.copyFrameLevelsToTex2D(this.frameCounter, this.originX, this.originY, this.width, +~ this.height); + +> INSERT 54 : 55 @ 55 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java new file mode 100644 index 0000000..3d859eb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureCompass.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> DELETE 4 @ 3 : 5 + +> CHANGE 8 : 9 @ 9 : 10 + +~ public class TextureCompass extends EaglerTextureAtlasSprite { + +> CHANGE 71 : 73 @ 72 : 74 + +~ animationCache.copyFrameLevelsToTex2D(this.frameCounter, this.originX, this.originY, this.width, +~ this.height); + +> INSERT 77 : 78 @ 78 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureManager.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureManager.edit.java new file mode 100644 index 0000000..dc09b14 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureManager.edit.java @@ -0,0 +1,73 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 7 : 14 @ 9 : 15 + +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ +~ import net.lax1dude.eaglercraft.v1_8.HString; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 20 @ 21 : 23 + +> CHANGE 33 : 41 @ 36 : 41 + +~ if (resource.cachedPointer != null) { +~ TextureUtil.bindTexture(((ITextureObject) resource.cachedPointer).getGlTextureId()); // unsafe, lol +~ } else { +~ Object object = (ITextureObject) this.mapTextureObjects.get(resource); +~ if (object == null) { +~ object = new SimpleTexture(resource); +~ this.loadTexture(resource, (ITextureObject) object); +~ } + +> CHANGE 42 : 45 @ 42 : 43 + +~ resource.cachedPointer = object; +~ TextureUtil.bindTexture(((ITextureObject) object).getGlTextureId()); +~ } + +> CHANGE 56 : 57 @ 54 : 55 + +~ public boolean loadTexture(ResourceLocation textureLocation, ITextureObject textureObj) { + +> INSERT 70 : 71 @ 68 + ++ final ITextureObject textureObj2 = textureObj; + +> CHANGE 73 : 74 @ 70 : 71 + +~ return textureObj2.getClass().getName(); + +> INSERT 79 : 80 @ 76 + ++ textureLocation.cachedPointer = textureObj; + +> CHANGE 85 : 90 @ 81 : 82 + +~ if (textureLocation.cachedPointer != null) { +~ return (ITextureObject) textureLocation.cachedPointer; +~ } else { +~ return (ITextureObject) (textureLocation.cachedPointer = this.mapTextureObjects.get(textureLocation)); +~ } + +> CHANGE 102 : 103 @ 94 : 95 + +~ HString.format("dynamic/%s_%d", new Object[] { name, integer })); + +> CHANGE 115 : 116 @ 107 : 108 + +~ ITextureObject itextureobject = this.mapTextureObjects.remove(textureLocation); + +> DELETE 119 @ 111 : 112 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java new file mode 100644 index 0000000..cd6a1ba --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureMap.edit.java @@ -0,0 +1,144 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 3 : 4 @ 6 + ++ import java.util.Collection; + +> INSERT 10 : 19 @ 12 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> DELETE 21 @ 14 : 20 + +> DELETE 30 @ 29 : 31 + +> CHANGE 35 : 38 @ 36 : 39 + +~ private final List listAnimatedSprites; +~ private final Map mapRegisteredSprites; +~ private final Map mapUploadedSprites; + +> CHANGE 41 : 44 @ 42 : 43 + +~ private final EaglerTextureAtlasSprite missingImage; +~ private int width; +~ private int height; + +> CHANGE 53 : 54 @ 52 : 53 + +~ this.missingImage = new EaglerTextureAtlasSprite("missingno"); + +> DELETE 71 @ 70 : 71 + +> INSERT 74 : 75 @ 74 + ++ destroyAnimationCaches(); + +> CHANGE 91 : 92 @ 90 : 91 + +~ EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) entry.getValue(); + +> CHANGE 97 : 98 @ 96 : 97 + +~ ImageData[] abufferedimage = new ImageData[1 + this.mipmapLevels]; + +> CHANGE 104 : 106 @ 103 : 105 + +~ int l = abufferedimage[0].width; +~ int i1 = abufferedimage[0].height; + +> CHANGE 124 : 126 @ 123 : 124 + +~ new Object[] { Integer.valueOf(i2), resourcelocation2 }); +~ logger.error(ioexception); + +> CHANGE 135 : 137 @ 133 : 134 + +~ logger.error("Unable to parse metadata from " + resourcelocation1); +~ logger.error(runtimeexception); + +> CHANGE 139 : 141 @ 136 : 137 + +~ logger.error("Using missing texture, unable to load " + resourcelocation1); +~ logger.error(ioexception1); + +> CHANGE 167 : 168 @ 163 : 164 + +~ for (final EaglerTextureAtlasSprite textureatlassprite1 : this.mapRegisteredSprites.values()) { + +> CHANGE 208 : 212 @ 204 : 205 + +~ width = stitcher.getCurrentWidth(); +~ height = stitcher.getCurrentHeight(); +~ +~ for (EaglerTextureAtlasSprite textureatlassprite2 : stitcher.getStichSlots()) { + +> CHANGE 233 : 234 @ 226 : 227 + +~ for (EaglerTextureAtlasSprite textureatlassprite3 : (Collection) hashmap.values()) { + +> CHANGE 242 : 244 @ 235 : 237 + +~ HString.format("%s/%s%s", new Object[] { this.basePath, location.getResourcePath(), ".png" })) +~ : new ResourceLocation(location.getResourceDomain(), HString.format("%s/mipmaps/%s.%d%s", + +> CHANGE 247 : 249 @ 240 : 242 + +~ public EaglerTextureAtlasSprite getAtlasSprite(String iconName) { +~ EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.mapUploadedSprites.get(iconName); + +> CHANGE 259 : 260 @ 252 : 253 + +~ for (EaglerTextureAtlasSprite textureatlassprite : this.listAnimatedSprites) { + +> CHANGE 265 : 272 @ 258 : 259 + +~ private void destroyAnimationCaches() { +~ for (EaglerTextureAtlasSprite textureatlassprite : this.listAnimatedSprites) { +~ textureatlassprite.clearFramesTextureData(); +~ } +~ } +~ +~ public EaglerTextureAtlasSprite registerSprite(ResourceLocation location) { + +> CHANGE 275 : 277 @ 262 : 263 + +~ EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.mapRegisteredSprites +~ .get(location); + +> CHANGE 278 : 279 @ 264 : 265 + +~ textureatlassprite = EaglerTextureAtlasSprite.makeAtlasSprite(location); + +> CHANGE 294 : 295 @ 280 : 281 + +~ public EaglerTextureAtlasSprite getMissingSprite() { + +> INSERT 297 : 306 @ 283 + ++ ++ public int getWidth() { ++ return width; ++ } ++ ++ public int getHeight() { ++ return height; ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/texture/TextureUtil.edit.java b/patches/minecraft/net/minecraft/client/renderer/texture/TextureUtil.edit.java new file mode 100644 index 0000000..5dbf57e --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/texture/TextureUtil.edit.java @@ -0,0 +1,121 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*; +~ + +> CHANGE 6 : 14 @ 5 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; +~ +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> DELETE 16 @ 9 : 11 + +> DELETE 18 @ 13 : 17 + +> CHANGE 34 : 35 @ 33 : 34 + +~ public static int uploadTextureImage(int parInt1, ImageData parBufferedImage) { + +> CHANGE 155 : 157 @ 154 : 155 + +~ EaglercraftGPU.glTexSubImage2D(GL_TEXTURE_2D, parInt1, parInt4, parInt5 + k, parInt2, l, GL_RGBA, +~ GL_UNSIGNED_BYTE, dataBuffer); + +> CHANGE 161 : 162 @ 159 : 160 + +~ public static int uploadTextureImageAllocate(int parInt1, ImageData parBufferedImage, boolean parFlag, + +> CHANGE 163 : 164 @ 161 : 162 + +~ allocateTexture(parInt1, parBufferedImage.width, parBufferedImage.height); + +> CHANGE 172 : 173 @ 170 : 171 + +~ // deleteTexture(parInt1); //TODO: why + +> CHANGE 175 : 179 @ 173 : 177 + +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, '\u813d', parInt2); +~ EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u813a', 0.0F); +~ EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u813b', (float) parInt2); +~ // EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, '\u8501', 0.0F); + +> CHANGE 182 : 184 @ 180 : 181 + +~ EaglercraftGPU.glTexImage2D(GL_TEXTURE_2D, i, GL_RGBA, parInt3 >> i, parInt4 >> i, 0, GL_RGBA, +~ GL_UNSIGNED_BYTE, (IntBuffer) null); + +> CHANGE 188 : 189 @ 185 : 186 + +~ public static int uploadTextureImageSub(int textureId, ImageData parBufferedImage, int parInt2, int parInt3, + +> CHANGE 195 : 199 @ 192 : 196 + +~ private static void uploadTextureImageSubImpl(ImageData parBufferedImage, int parInt1, int parInt2, boolean parFlag, +~ boolean parFlag2) { +~ int i = parBufferedImage.width; +~ int j = parBufferedImage.height; + +> CHANGE 210 : 212 @ 207 : 208 + +~ EaglercraftGPU.glTexSubImage2D(GL_TEXTURE_2D, 0, parInt1, parInt2 + i1, i, j1, GL_RGBA, GL_UNSIGNED_BYTE, +~ dataBuffer); + +> CHANGE 218 : 220 @ 214 : 216 + +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + +> CHANGE 221 : 223 @ 217 : 219 + +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + +> CHANGE 233 : 235 @ 229 : 231 + +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, parFlag2 ? 9987 : 9729); +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + +> CHANGE 236 : 238 @ 232 : 234 + +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, parFlag2 ? 9986 : 9728); +~ EaglercraftGPU.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + +> CHANGE 263 : 264 @ 259 : 265 + +~ return readBufferedImage(resourceManager.getResource(imageLocation).getInputStream()).pixels; + +> CHANGE 266 : 268 @ 267 : 269 + +~ public static ImageData readBufferedImage(InputStream imageStream) throws IOException { +~ ImageData bufferedimage; + +> CHANGE 269 : 270 @ 270 : 271 + +~ bufferedimage = ImageData.loadImageFile(imageStream); + +> INSERT 310 : 318 @ 311 + ++ public static int[] convertComponentOrder(int[] arr) { ++ for (int i = 0; i < arr.length; ++i) { ++ int j = arr[i]; ++ arr[i] = (j & 0xFF000000) | ((j >> 16) & 0xFF) | (j & 0xFF00) | ((j << 16) & 0xFF0000); ++ } ++ return arr; ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.edit.java new file mode 100644 index 0000000..f84fcd9 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderEnderCrystal.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 5 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderItemFrame.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderItemFrame.edit.java new file mode 100644 index 0000000..69a4b85 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderItemFrame.edit.java @@ -0,0 +1,47 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 6 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 9 @ 5 : 6 + +> DELETE 11 @ 8 : 9 + +> DELETE 15 @ 13 : 14 + +> DELETE 31 @ 30 : 31 + +> INSERT 67 : 69 @ 67 + ++ GlStateManager.enableLighting(); ++ GlStateManager.enableColorMaterial(); + +> INSERT 76 : 78 @ 74 + ++ GlStateManager.enableLighting(); ++ GlStateManager.enableColorMaterial(); + +> DELETE 111 @ 107 : 128 + +> CHANGE 116 : 117 @ 133 : 134 + +~ GlStateManager.pushLightCoords(); + +> CHANGE 120 : 121 @ 137 : 141 + +~ GlStateManager.popLightCoords(); + +> CHANGE 142 : 143 @ 162 : 163 + +~ EaglercraftGPU.glNormal3f(0.0F, 1.0F, 0.0F); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderWitherSkull.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderWitherSkull.edit.java new file mode 100644 index 0000000..35c39b8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/RenderWitherSkull.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.edit.java new file mode 100644 index 0000000..a15be46 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBannerRenderer.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 6 : 11 @ 8 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 13 @ 10 : 11 + +> DELETE 14 @ 12 : 13 + +> CHANGE 109 : 110 @ 108 : 109 + +~ for (TileEntityBanner.EnumBannerPattern tileentitybanner$enumbannerpattern : (List) list1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer.edit.java new file mode 100644 index 0000000..4007436 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityBeaconRenderer.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 7 @ 3 : 4 + +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 8 @ 5 : 7 + +> DELETE 12 @ 11 : 12 + +> CHANGE 31 : 33 @ 31 : 33 + +~ EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 10497.0F); +~ EaglercraftGPU.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 10497.0F); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityChestRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityChestRenderer.edit.java new file mode 100644 index 0000000..a9e36da --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityChestRenderer.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 5 @ 3 + ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 9 @ 7 : 9 + +> INSERT 58 : 59 @ 58 + ++ isChristams = true; + +> DELETE 66 @ 65 : 67 + +> INSERT 68 : 70 @ 69 + ++ } else if (tileentitychest.getChestType() == 1) { ++ this.bindTexture(textureTrapped); + +> DELETE 82 @ 81 : 83 + +> INSERT 84 : 86 @ 85 + ++ } else if (tileentitychest.getChestType() == 1) { ++ this.bindTexture(textureTrappedDouble); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.edit.java new file mode 100644 index 0000000..23934c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnchantmentTableRenderer.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.edit.java new file mode 100644 index 0000000..9158631 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEndPortalRenderer.edit.java @@ -0,0 +1,36 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 7 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; +~ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 10 @ 7 : 8 + +> DELETE 11 @ 9 : 11 + +> CHANGE 18 : 19 @ 18 : 19 + +~ private static final EaglercraftRandom field_147527_e = new EaglercraftRandom(31100L); + +> CHANGE 53 : 54 @ 53 : 54 + +~ float f7 = (float) (-(d1 + (double) f3 - 1.25)); + +> CHANGE 67 : 68 @ 67 : 71 + +~ GlStateManager.enableTexGen(); + +> CHANGE 102 : 103 @ 105 : 109 + +~ GlStateManager.disableTexGen(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.edit.java new file mode 100644 index 0000000..23934c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityEnderChestRenderer.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.edit.java new file mode 100644 index 0000000..2c6ffdb --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityItemStackRenderer.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 7 @ 5 : 8 + +> CHANGE 38 : 39 @ 39 : 40 + +~ gameprofile = new GameProfile((EaglercraftUUID) null, nbttagcompound.getString("SkullOwner")); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.edit.java new file mode 100644 index 0000000..23934c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityMobSpawnerRenderer.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 3 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 4 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.edit.java new file mode 100644 index 0000000..8e76321 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityPistonRenderer.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; + +> DELETE 11 @ 9 : 10 + +> DELETE 13 @ 12 : 13 + +> DELETE 14 @ 14 : 15 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.edit.java new file mode 100644 index 0000000..845a257 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntityRendererDispatcher.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 8 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ import net.lax1dude.eaglercraft.v1_8.opengl.OpenGlHelper; + +> DELETE 9 @ 5 : 7 + +> DELETE 10 @ 8 : 19 + +> CHANGE 68 : 70 @ 77 : 78 + +~ tileentityspecialrenderer = this +~ .getSpecialRendererByClass((Class) teClass.getSuperclass()); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.edit.java new file mode 100644 index 0000000..46a316a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySignRenderer.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 6 @ 3 + ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 10 @ 7 : 9 + +> DELETE 14 @ 13 : 14 + +> CHANGE 69 : 70 @ 69 : 70 + +~ EaglercraftGPU.glNormal3f(0.0F, 0.0F, -1.0F * f3); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.edit.java new file mode 100644 index 0000000..ccc7053 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySkullRenderer.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> CHANGE 7 : 8 @ 10 : 13 + +~ import net.minecraft.client.network.NetHandlerPlayClient; + +> DELETE 10 @ 15 : 16 + +> CHANGE 65 : 69 @ 71 : 80 + +~ if (parGameProfile != null && parGameProfile.getId() != null) { +~ NetHandlerPlayClient netHandler = Minecraft.getMinecraft().getNetHandler(); +~ if (netHandler != null) { +~ resourcelocation = netHandler.getSkinCache().getSkin(parGameProfile).getResourceLocation(); + +> DELETE 71 @ 82 : 83 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.edit.java b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/tileentity/TileEntitySpecialRenderer.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/renderer/vertex/DefaultVertexFormats.edit.java b/patches/minecraft/net/minecraft/client/renderer/vertex/DefaultVertexFormats.edit.java new file mode 100644 index 0000000..f7b3463 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/renderer/vertex/DefaultVertexFormats.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.opengl.VertexFormat; + +> CHANGE 5 : 17 @ 6 : 74 + +~ public static final VertexFormat BLOCK = VertexFormat.BLOCK; +~ public static final VertexFormat ITEM = VertexFormat.ITEM; +~ public static final VertexFormat OLDMODEL_POSITION_TEX_NORMAL = VertexFormat.OLDMODEL_POSITION_TEX_NORMAL; +~ public static final VertexFormat PARTICLE_POSITION_TEX_COLOR_LMAP = VertexFormat.PARTICLE_POSITION_TEX_COLOR_LMAP; +~ public static final VertexFormat POSITION = VertexFormat.POSITION; +~ public static final VertexFormat POSITION_COLOR = VertexFormat.POSITION_COLOR; +~ public static final VertexFormat POSITION_TEX = VertexFormat.POSITION_TEX; +~ public static final VertexFormat POSITION_NORMAL = VertexFormat.POSITION_NORMAL; +~ public static final VertexFormat POSITION_TEX_COLOR = VertexFormat.POSITION_TEX_COLOR; +~ public static final VertexFormat POSITION_TEX_NORMAL = VertexFormat.POSITION_TEX_NORMAL; +~ public static final VertexFormat POSITION_TEX_LMAP_COLOR = VertexFormat.POSITION_TEX_LMAP_COLOR; +~ public static final VertexFormat POSITION_TEX_COLOR_NORMAL = VertexFormat.POSITION_TEX_COLOR_NORMAL; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java new file mode 100644 index 0000000..1930df7 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/AbstractResourcePack.edit.java @@ -0,0 +1,75 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 9 + +> CHANGE 4 : 15 @ 11 : 12 + +~ import java.nio.charset.StandardCharsets; +~ +~ import net.lax1dude.eaglercraft.v1_8.vfs.SYS; +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; +~ import net.lax1dude.eaglercraft.v1_8.HString; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> DELETE 16 @ 13 : 14 + +> DELETE 19 @ 17 : 20 + +> CHANGE 22 : 23 @ 23 : 24 + +~ protected final String resourcePackFile; + +> CHANGE 24 : 25 @ 25 : 26 + +~ public AbstractResourcePack(String resourcePackFileIn) { + +> CHANGE 29 : 30 @ 30 : 31 + +~ return HString.format("%s/%s/%s", + +> DELETE 33 @ 34 : 38 + +> CHANGE 52 : 60 @ 57 : 58 + +~ try { +~ return readMetadata(parIMetadataSerializer, this.getInputStreamByName("pack.mcmeta"), parString1); +~ } catch (JSONException e) { +~ if (SYS.VFS != null) { +~ SYS.deleteResourcePack(this.resourcePackFile); +~ } +~ throw e; +~ } + +> CHANGE 64 : 65 @ 62 : 64 + +~ JSONObject jsonobject = null; + +> CHANGE 67 : 70 @ 66 : 70 + +~ jsonobject = new JSONObject(IOUtils.inputStreamToString(parInputStream, StandardCharsets.UTF_8)); +~ } catch (RuntimeException | IOException runtimeexception) { +~ throw new JSONException(runtimeexception); + +> CHANGE 71 : 72 @ 71 : 72 + +~ IOUtils.closeQuietly(parInputStream); + +> CHANGE 77 : 78 @ 77 : 78 + +~ public ImageData getPackImage() throws IOException { + +> CHANGE 82 : 83 @ 82 : 83 + +~ return this.resourcePackFile; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/DefaultPlayerSkin.edit.java b/patches/minecraft/net/minecraft/client/resources/DefaultPlayerSkin.edit.java new file mode 100644 index 0000000..bbf4ff6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/DefaultPlayerSkin.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> CHANGE 14 : 15 @ 13 : 14 + +~ public static ResourceLocation getDefaultSkin(EaglercraftUUID playerUUID) { + +> CHANGE 18 : 19 @ 17 : 18 + +~ public static String getSkinType(EaglercraftUUID playerUUID) { + +> CHANGE 22 : 23 @ 21 : 22 + +~ private static boolean isSlimSkin(EaglercraftUUID playerUUID) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java new file mode 100644 index 0000000..11a8ce3 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/DefaultResourcePack.edit.java @@ -0,0 +1,53 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 6 + +> DELETE 5 @ 9 : 10 + +> INSERT 6 : 11 @ 11 + ++ ++ import com.google.common.collect.ImmutableSet; ++ ++ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ++ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> DELETE 12 @ 12 : 14 + +> CHANGE 17 : 18 @ 19 : 21 + +~ public static final Set defaultResourceDomains = ImmutableSet.of("minecraft", "eagler"); + +> DELETE 19 @ 22 : 26 + +> CHANGE 34 : 35 @ 41 : 43 + +~ return null; + +> CHANGE 38 : 40 @ 46 : 48 + +~ return EagRuntime +~ .getResourceStream("/assets/" + location.getResourceDomain() + "/" + location.getResourcePath()); + +> CHANGE 43 : 44 @ 51 : 53 + +~ return this.getResourceStream(resourcelocation) != null; + +> CHANGE 53 : 55 @ 62 : 64 + +~ return AbstractResourcePack.readMetadata(parIMetadataSerializer, +~ EagRuntime.getResourceStream("pack.mcmeta"), parString1); + +> DELETE 57 @ 66 : 68 + +> CHANGE 60 : 62 @ 71 : 74 + +~ public ImageData getPackImage() throws IOException { +~ return TextureUtil.readBufferedImage(EagRuntime.getResourceStream("pack.png")); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/FallbackResourceManager.edit.java b/patches/minecraft/net/minecraft/client/resources/FallbackResourceManager.edit.java new file mode 100644 index 0000000..9cba5cc --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/FallbackResourceManager.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 5 @ 7 : 8 + +> CHANGE 8 : 13 @ 11 : 15 + +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 15 @ 17 : 19 + +> CHANGE 58 : 59 @ 62 : 67 + +~ return resourcePack.getInputStream(location); + +> DELETE 86 @ 94 : 125 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/FoliageColorReloadListener.edit.java b/patches/minecraft/net/minecraft/client/resources/FoliageColorReloadListener.edit.java new file mode 100644 index 0000000..246f0c4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/FoliageColorReloadListener.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 6 + +> CHANGE 13 : 15 @ 14 : 15 + +~ ColorizerFoliage.setFoliageBiomeColorizer( +~ TextureUtil.convertComponentOrder(TextureUtil.readImageData(iresourcemanager, LOC_FOLIAGE_PNG))); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/GrassColorReloadListener.edit.java b/patches/minecraft/net/minecraft/client/resources/GrassColorReloadListener.edit.java new file mode 100644 index 0000000..ea58d29 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/GrassColorReloadListener.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 6 + +> CHANGE 13 : 15 @ 14 : 15 + +~ ColorizerGrass.setGrassBiomeColorizer( +~ TextureUtil.convertComponentOrder(TextureUtil.readImageData(iresourcemanager, LOC_GRASS_PNG))); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/I18n.edit.java b/patches/minecraft/net/minecraft/client/resources/I18n.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/I18n.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/IReloadableResourceManager.edit.java b/patches/minecraft/net/minecraft/client/resources/IReloadableResourceManager.edit.java new file mode 100644 index 0000000..4890f7c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/IReloadableResourceManager.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/IResource.edit.java b/patches/minecraft/net/minecraft/client/resources/IResource.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/IResource.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/IResourceManager.edit.java b/patches/minecraft/net/minecraft/client/resources/IResourceManager.edit.java new file mode 100644 index 0000000..52ac7f5 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/IResourceManager.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 5 : 6 @ 5 : 6 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/IResourceManagerReloadListener.edit.java b/patches/minecraft/net/minecraft/client/resources/IResourceManagerReloadListener.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/IResourceManagerReloadListener.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java b/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java new file mode 100644 index 0000000..d1ac688 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/IResourcePack.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 5 : 7 @ 6 + ++ ++ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> CHANGE 20 : 21 @ 19 : 20 + +~ ImageData getPackImage() throws IOException; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/Language.edit.java b/patches/minecraft/net/minecraft/client/resources/Language.edit.java new file mode 100644 index 0000000..3d1f770 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/Language.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ + +> CHANGE 26 : 27 @ 24 : 25 + +~ return HString.format("%s (%s)", new Object[] { this.name, this.region }); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/LanguageManager.edit.java b/patches/minecraft/net/minecraft/client/resources/LanguageManager.edit.java new file mode 100644 index 0000000..9a137a6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/LanguageManager.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 7 : 14 @ 10 : 16 + +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ import com.google.common.collect.Sets; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 17 @ 19 : 21 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/Locale.edit.java b/patches/minecraft/net/minecraft/client/resources/Locale.edit.java new file mode 100644 index 0000000..555588b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/Locale.edit.java @@ -0,0 +1,73 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 4 : 5 @ 7 + ++ import java.util.HashSet; + +> INSERT 8 : 9 @ 10 + ++ import java.util.Set; + +> CHANGE 10 : 19 @ 11 : 13 + +~ +~ import com.google.common.base.Charsets; +~ import com.google.common.base.Splitter; +~ import com.google.common.collect.Iterables; +~ import com.google.common.collect.Maps; +~ +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.HString; +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; + +> DELETE 20 @ 14 : 16 + +> INSERT 27 : 29 @ 23 + ++ private static final Set hasShownMissing = new HashSet(); ++ + +> CHANGE 33 : 34 @ 27 : 28 + +~ String s1 = HString.format("lang/%s.lang", new Object[] { s }); + +> CHANGE 37 : 45 @ 31 : 32 + +~ List res = resourceManager.getAllResources(new ResourceLocation(s2, s1)); +~ if (res.size() > 0) { +~ this.loadLocaleData(res); +~ } else { +~ if (s2.equalsIgnoreCase("minecraft") && hasShownMissing.add(s)) { +~ EagRuntime.showPopup("ERROR: language \"" + s + "\" is not available on this site!"); +~ } +~ } + +> CHANGE 46 : 49 @ 33 : 34 + +~ if (s2.equalsIgnoreCase("minecraft") && hasShownMissing.add(s)) { +~ EagRuntime.showPopup("ERROR: language \"" + s + "\" is not available on this site!"); +~ } + +> CHANGE 99 : 101 @ 84 : 85 + +~ String s2 = pattern.matcher(astring[1]).replaceAll("%s"); // TODO: originally "%$1s" but must be +~ // "%s" to work with TeaVM (why?) + +> INSERT 102 : 105 @ 86 + ++ if (s1.startsWith("eaglercraft.")) { ++ this.properties.put(s1.substring(12), s2); ++ } + +> CHANGE 120 : 121 @ 101 : 102 + +~ return HString.format(s, parameters); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/ResourceIndex.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourceIndex.edit.java new file mode 100644 index 0000000..c358698 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/ResourceIndex.edit.java @@ -0,0 +1,48 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 18 + +> CHANGE 3 : 5 @ 19 : 21 + +~ // private static final Logger logger = LogManager.getLogger(); +~ // private final Map resourceMap = Maps.newHashMap(); + +> CHANGE 6 : 30 @ 22 : 27 + +~ // public ResourceIndex(File parFile, String parString1) { +~ /* +~ * if (parString1 != null) { File file1 = new File(parFile, "objects"); File +~ * file2 = new File(parFile, "indexes/" + parString1 + ".json"); BufferedReader +~ * bufferedreader = null; +~ * +~ * try { bufferedreader = Files.newReader(file2, Charsets.UTF_8); JsonObject +~ * jsonobject = (new JsonParser()).parse(bufferedreader).getAsJsonObject(); +~ * JsonObject jsonobject1 = JsonUtils.getJsonObject(jsonobject, "objects", +~ * (JsonObject) null); if (jsonobject1 != null) { for (Entry entry : +~ * jsonobject1.entrySet()) { JsonObject jsonobject2 = (JsonObject) +~ * entry.getValue(); String s = (String) entry.getKey(); String[] astring = +~ * s.split("/", 2); String s1 = astring.length == 1 ? astring[0] : astring[0] + +~ * ":" + astring[1]; String s2 = JsonUtils.getString(jsonobject2, "hash"); File +~ * file3 = new File(file1, s2.substring(0, 2) + "/" + s2); +~ * this.resourceMap.put(s1, file3); } } } catch (JsonParseException var20) { +~ * logger.error("Unable to parse resource index file: " + file2); } catch +~ * (FileNotFoundException var21) { +~ * logger.error("Can\'t find the resource index file: " + file2); } finally { +~ * IOUtils.closeQuietly(bufferedreader); } +~ * +~ * } +~ */ +~ // } + +> CHANGE 31 : 34 @ 28 : 57 + +~ // public Map getResourceMap() { +~ // return this.resourceMap; +~ // } + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackFileNotFoundException.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackFileNotFoundException.edit.java new file mode 100644 index 0000000..f8baeb4 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackFileNotFoundException.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 5 : 7 @ 5 + ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ + +> CHANGE 9 : 10 @ 7 : 8 + +~ super(HString.format("\'%s\' in ResourcePack \'%s\'", new Object[] { parString1, parFile })); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntry.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntry.edit.java new file mode 100644 index 0000000..83dc8ad --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntry.edit.java @@ -0,0 +1,70 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 8 @ 3 + ++ ++ import net.lax1dude.eaglercraft.v1_8.Keyboard; ++ import net.lax1dude.eaglercraft.v1_8.vfs.SYS; ++ import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; + +> DELETE 14 @ 9 : 11 + +> INSERT 142 : 171 @ 139 + ++ private void proceedWithBs(int l, boolean deleteInstead) { ++ if (!deleteInstead && l != 1) { ++ String s1 = I18n.format("resourcePack.incompatible.confirm.title", new Object[0]); ++ String s = I18n.format("resourcePack.incompatible.confirm." + (l > 1 ? "new" : "old"), new Object[0]); ++ this.mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback() { ++ public void confirmClicked(boolean flag, int var2) { ++ List list2 = ResourcePackListEntry.this.resourcePacksGUI ++ .getListContaining(ResourcePackListEntry.this); ++ ResourcePackListEntry.this.mc.displayGuiScreen(ResourcePackListEntry.this.resourcePacksGUI); ++ if (flag) { ++ list2.remove(ResourcePackListEntry.this); ++ ResourcePackListEntry.this.resourcePacksGUI.getSelectedResourcePacks().add(0, ++ ResourcePackListEntry.this); ++ } ++ ++ } ++ }, s1, s, 0).withOpaqueBackground()); ++ } else { ++ this.mc.displayGuiScreen(this.resourcePacksGUI); ++ this.resourcePacksGUI.getListContaining(this).remove(this); ++ if (deleteInstead) { ++ this.mc.loadingScreen.eaglerShow(I18n.format("resourcePack.load.deleting"), this.func_148312_b()); ++ SYS.deleteResourcePack(this.func_148312_b()); ++ } else { ++ this.resourcePacksGUI.getSelectedResourcePacks().add(0, this); ++ } ++ } ++ } ++ + +> CHANGE 176 : 180 @ 144 : 148 + +~ if (Keyboard.isKeyDown(KeyboardConstants.KEY_LSHIFT) +~ || Keyboard.isKeyDown(KeyboardConstants.KEY_RSHIFT)) { +~ proceedWithBs(l, false); +~ } else { + +> CHANGE 182 : 183 @ 150 : 158 + +~ proceedWithBs(l, flag); + +> CHANGE 185 : 189 @ 160 : 164 + +~ }, I18n.format("resourcePack.prompt.title", this.func_148312_b()), +~ I18n.format("resourcePack.prompt.text", new Object[0]), +~ I18n.format("resourcePack.prompt.delete", new Object[0]), +~ I18n.format("resourcePack.prompt.add", new Object[0]), 0).withOpaqueBackground()); + +> DELETE 190 @ 165 : 166 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryDefault.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryDefault.edit.java new file mode 100644 index 0000000..2585010 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryDefault.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 8 @ 4 + ++ ++ import org.json.JSONException; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 11 @ 7 : 9 + +> DELETE 14 @ 12 : 14 + +> CHANGE 46 : 47 @ 46 : 47 + +~ } catch (JSONException jsonparseexception) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryFound.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryFound.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackListEntryFound.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/ResourcePackRepository.edit.java b/patches/minecraft/net/minecraft/client/resources/ResourcePackRepository.edit.java new file mode 100644 index 0000000..8c48f49 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/ResourcePackRepository.edit.java @@ -0,0 +1,167 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 11 + +> DELETE 3 @ 12 : 14 + +> DELETE 4 @ 15 : 18 + +> CHANGE 6 : 18 @ 20 : 22 + +~ import java.util.function.Consumer; +~ +~ import com.google.common.collect.ImmutableList; +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; +~ import net.lax1dude.eaglercraft.v1_8.vfs.FolderResourcePack; +~ import net.lax1dude.eaglercraft.v1_8.vfs.SYS; +~ import net.lax1dude.eaglercraft.v1_8.futures.ListenableFuture; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +~ import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +> DELETE 19 @ 23 : 24 + +> DELETE 21 @ 26 : 29 + +> DELETE 25 @ 33 : 34 + +> DELETE 26 @ 35 : 42 + +> DELETE 29 @ 45 : 53 + +> DELETE 30 @ 54 : 55 + +> DELETE 32 @ 57 : 58 + +> CHANGE 36 : 37 @ 62 : 64 + +~ public ResourcePackRepository(IResourcePack rprDefaultResourcePackIn, IMetadataSerializer rprMetadataSerializerIn, + +> DELETE 38 @ 65 : 67 + +> DELETE 40 @ 69 : 70 + +> DELETE 63 @ 93 : 111 + +> CHANGE 64 : 66 @ 112 : 113 + +~ if (SYS.VFS == null) +~ return; + +> CHANGE 67 : 70 @ 114 : 115 + +~ List list = Lists.newArrayList(); +~ +~ for (String file1 : SYS.getResourcePackNames()) { + +> INSERT 71 : 72 @ 116 + ++ + +> CHANGE 75 : 76 @ 119 : 120 + +~ list.add(resourcepackrepository$entry); + +> CHANGE 77 : 81 @ 121 : 122 + +~ logger.error("Failed to call \"updateResourcePack\" for resource pack \"{}\"", +~ resourcepackrepository$entry.resourcePackFile); +~ logger.error(var6); +~ list.remove(resourcepackrepository$entry); + +> INSERT 84 : 85 @ 125 + ++ + +> CHANGE 86 : 87 @ 126 : 127 + +~ list.add(this.repositoryEntriesAll.get(i)); + +> CHANGE 91 : 92 @ 131 : 132 + +~ this.repositoryEntriesAll.removeAll(list); + +> CHANGE 97 : 98 @ 137 : 138 + +~ this.repositoryEntriesAll = list; + +> CHANGE 113 : 120 @ 153 : 185 + +~ public void downloadResourcePack(String s1, String s2, Consumer cb) { +~ SYS.loadRemoteResourcePack(s1, s2, res -> { +~ if (res != null) { +~ ResourcePackRepository.this.resourcePackInstance = new FolderResourcePack(res, "srp/"); +~ Minecraft.getMinecraft().scheduleResourcesRefresh(); +~ cb.accept(true); +~ return; + +> CHANGE 121 : 128 @ 186 : 214 + +~ cb.accept(false); +~ }, runnable -> { +~ Minecraft.getMinecraft().addScheduledTask(runnable); +~ }, () -> { +~ Minecraft.getMinecraft().loadingScreen.eaglerShow(I18n.format("resourcePack.load.loading"), +~ "Server resource pack"); +~ }); + +> DELETE 130 @ 216 : 236 + +> CHANGE 135 : 138 @ 241 : 255 + +~ if (this.resourcePackInstance != null) { +~ this.resourcePackInstance = null; +~ Minecraft.getMinecraft().scheduleResourcesRefresh(); + +> DELETE 139 @ 256 : 257 + +> CHANGE 142 : 143 @ 260 : 261 + +~ private final String resourcePackFile; + +> CHANGE 145 : 146 @ 263 : 264 + +~ private ImageData texturePackIcon; + +> INSERT 147 : 148 @ 265 + ++ private TextureManager iconTextureManager; + +> CHANGE 149 : 150 @ 266 : 267 + +~ private Entry(String resourcePackFileIn) { + +> CHANGE 154 : 157 @ 271 : 274 + +~ if (SYS.VFS == null) +~ return; +~ this.reResourcePack = (IResourcePack) new FolderResourcePack(this.resourcePackFile, "resourcepacks/"); + +> CHANGE 163 : 165 @ 280 : 281 + +~ logger.error("Failed to load resource pack icon for \"{}\"!", resourcePackFile); +~ logger.error(var2); + +> INSERT 176 : 177 @ 292 + ++ this.iconTextureManager = textureManagerIn; + +> INSERT 185 : 189 @ 300 + ++ if (this.locationTexturePackIcon != null) { ++ this.iconTextureManager.deleteTexture(this.locationTexturePackIcon); ++ this.locationTexturePackIcon = null; ++ } + +> CHANGE 224 : 225 @ 335 : 339 + +~ return this.resourcePackFile; + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/SimpleReloadableResourceManager.edit.java b/patches/minecraft/net/minecraft/client/resources/SimpleReloadableResourceManager.edit.java new file mode 100644 index 0000000..d47c309 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/SimpleReloadableResourceManager.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 8 @ 2 + ++ import java.io.FileNotFoundException; ++ import java.io.IOException; ++ import java.util.List; ++ import java.util.Map; ++ import java.util.Set; ++ + +> CHANGE 14 : 17 @ 8 : 19 + +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 19 @ 21 : 23 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/SimpleResource.edit.java b/patches/minecraft/net/minecraft/client/resources/SimpleResource.edit.java new file mode 100644 index 0000000..ae15f9a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/SimpleResource.edit.java @@ -0,0 +1,45 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 6 + +~ import java.io.IOException; + +> CHANGE 4 : 5 @ 7 : 8 + +~ import java.nio.charset.StandardCharsets; + +> CHANGE 6 : 13 @ 9 : 10 + +~ +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import com.google.common.collect.Maps; +~ +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; + +> DELETE 16 @ 13 : 14 + +> CHANGE 25 : 26 @ 23 : 24 + +~ private JSONObject mcmetaJson; + +> DELETE 55 @ 53 : 54 + +> CHANGE 57 : 61 @ 56 : 58 + +~ this.mcmetaJson = new JSONObject( +~ IOUtils.inputStreamToString(this.mcmetaInputStream, StandardCharsets.UTF_8)); +~ } catch (IOException e) { +~ throw new JSONException(e); + +> CHANGE 62 : 63 @ 59 : 60 + +~ IOUtils.closeQuietly(this.mcmetaInputStream); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSection.edit.java new file mode 100644 index 0000000..73e05ad --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSection.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 5 @ 6 : 8 + +> INSERT 6 : 8 @ 9 + ++ import com.google.common.collect.Sets; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.edit.java new file mode 100644 index 0000000..85d604f --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/AnimationMetadataSectionSerializer.edit.java @@ -0,0 +1,104 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 12 + +> CHANGE 3 : 4 @ 13 : 17 + +~ + +> INSERT 5 : 8 @ 18 + ++ import org.json.JSONArray; ++ import org.json.JSONException; ++ import org.json.JSONObject; + +> INSERT 9 : 13 @ 19 + ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeSerializer; ++ + +> CHANGE 14 : 16 @ 20 : 23 + +~ implements JSONTypeSerializer { +~ public AnimationMetadataSection deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 17 : 18 @ 24 : 26 + +~ int i = jsonobject.optInt("frametime", 1); + +> CHANGE 24 : 25 @ 32 : 33 + +~ JSONArray jsonarray = jsonobject.getJSONArray("frames"); + +> CHANGE 26 : 28 @ 34 : 37 + +~ for (int j = 0; j < jsonarray.length(); ++j) { +~ AnimationFrame animationframe = this.parseAnimationFrame(j, jsonarray.get(j)); + +> CHANGE 33 : 34 @ 42 : 44 + +~ throw new JSONException("Invalid animation->frames: expected array, was " + jsonobject.get("frames"), + +> CHANGE 38 : 40 @ 48 : 50 + +~ int k = jsonobject.optInt("width", -1); +~ int l = jsonobject.optInt("height", -1); + +> CHANGE 48 : 49 @ 58 : 59 + +~ boolean flag = jsonobject.optBoolean("interpolate", false); + +> CHANGE 52 : 58 @ 62 : 68 + +~ private AnimationFrame parseAnimationFrame(int parInt1, Object parJsonElement) { +~ if (parJsonElement instanceof Number) { +~ return new AnimationFrame(((Number) parJsonElement).intValue()); +~ } else if (parJsonElement instanceof JSONObject) { +~ JSONObject jsonobject = (JSONObject) parJsonElement; +~ int i = jsonobject.optInt("time", -1); + +> CHANGE 62 : 63 @ 72 : 73 + +~ int j = jsonobject.getInt(getSectionName()); + +> CHANGE 70 : 73 @ 80 : 84 + +~ public JSONObject serialize(AnimationMetadataSection animationmetadatasection) { +~ JSONObject jsonobject = new JSONObject(); +~ jsonobject.put("frametime", Integer.valueOf(animationmetadatasection.getFrameTime())); + +> CHANGE 74 : 75 @ 85 : 86 + +~ jsonobject.put("width", Integer.valueOf(animationmetadatasection.getFrameWidth())); + +> CHANGE 78 : 79 @ 89 : 90 + +~ jsonobject.put("height", Integer.valueOf(animationmetadatasection.getFrameHeight())); + +> CHANGE 82 : 83 @ 93 : 94 + +~ JSONArray jsonarray = new JSONArray(); + +> CHANGE 86 : 90 @ 97 : 101 + +~ JSONObject jsonobject1 = new JSONObject(); +~ jsonobject1.put("index", Integer.valueOf(animationmetadatasection.getFrameIndex(i))); +~ jsonobject1.put("time", Integer.valueOf(animationmetadatasection.getFrameTimeSingle(i))); +~ jsonarray.put(jsonobject1); + +> CHANGE 91 : 92 @ 102 : 103 + +~ jsonarray.put(Integer.valueOf(animationmetadatasection.getFrameIndex(i))); + +> CHANGE 95 : 96 @ 106 : 107 + +~ jsonobject.put("frames", jsonarray); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/BaseMetadataSectionSerializer.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSection.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSection.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSectionSerializer.edit.java new file mode 100644 index 0000000..ce9d82d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/FontMetadataSectionSerializer.edit.java @@ -0,0 +1,63 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 10 + +> INSERT 3 : 5 @ 11 + ++ import org.json.JSONException; ++ import org.json.JSONObject; + +> CHANGE 7 : 8 @ 13 : 16 + +~ public FontMetadataSection deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 15 : 17 @ 23 : 25 + +~ if (!(jsonobject.get("characters") instanceof JSONObject)) { +~ throw new JSONException( + +> CHANGE 20 : 21 @ 28 : 29 + +~ JSONObject jsonobject1 = jsonobject.getJSONObject("characters"); + +> CHANGE 22 : 24 @ 30 : 32 + +~ if (!(jsonobject1.get("default") instanceof JSONObject)) { +~ throw new JSONException( + +> CHANGE 27 : 29 @ 35 : 37 + +~ JSONObject jsonobject2 = jsonobject1.getJSONObject("default"); +~ f = jsonobject2.optFloat("width", f); + +> CHANGE 30 : 31 @ 38 : 39 + +~ f1 = jsonobject2.optFloat("spacing", f1); + +> CHANGE 32 : 33 @ 40 : 41 + +~ f2 = jsonobject2.optFloat("left", f1); + +> CHANGE 37 : 38 @ 45 : 46 + +~ JSONObject jsonobject3 = jsonobject1.optJSONObject(Integer.toString(i)); + +> CHANGE 41 : 43 @ 49 : 52 + +~ if (jsonobject3 != null) { +~ f3 = jsonobject3.optFloat("width", f); + +> CHANGE 44 : 45 @ 53 : 54 + +~ f4 = jsonobject3.optFloat("spacing", f1); + +> CHANGE 46 : 47 @ 55 : 56 + +~ f5 = jsonobject3.optFloat("left", f2); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/IMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/IMetadataSectionSerializer.edit.java new file mode 100644 index 0000000..4645fb9 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/IMetadataSectionSerializer.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import org.json.JSONObject; + +> CHANGE 4 : 7 @ 5 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeDeserializer; +~ +~ public interface IMetadataSectionSerializer extends JSONTypeDeserializer { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/IMetadataSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/IMetadataSerializer.edit.java new file mode 100644 index 0000000..5898778 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/IMetadataSerializer.edit.java @@ -0,0 +1,34 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 10 + +~ import org.json.JSONObject; +~ +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; + +> DELETE 10 @ 15 : 17 + +> DELETE 11 @ 18 : 24 + +> DELETE 15 @ 28 : 30 + +> CHANGE 17 : 18 @ 32 : 33 + +~ public T parseMetadataSection(String parString1, JSONObject parJsonObject) { + +> CHANGE 22 : 23 @ 37 : 38 + +~ } else if (parJsonObject.optJSONObject(parString1) == null) { + +> CHANGE 31 : 32 @ 46 : 47 + +~ return (T) ((IMetadataSection) JSONTypeProvider.deserialize(parJsonObject.getJSONObject(parString1), + +> DELETE 37 @ 52 : 60 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSection.edit.java new file mode 100644 index 0000000..681a62a --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSection.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.edit.java new file mode 100644 index 0000000..ff69f15 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/LanguageMetadataSectionSerializer.edit.java @@ -0,0 +1,45 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 8 + +> CHANGE 3 : 9 @ 9 : 10 + +~ +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import com.google.common.collect.Sets; +~ + +> DELETE 10 @ 11 : 14 + +> CHANGE 12 : 13 @ 16 : 19 + +~ public LanguageMetadataSection deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 15 : 20 @ 21 : 27 + +~ for (String s : jsonobject.keySet()) { +~ JSONObject jsonobject1 = jsonobject.getJSONObject(s); +~ String s1 = jsonobject1.getString("region"); +~ String s2 = jsonobject1.getString("name"); +~ boolean flag = jsonobject1.optBoolean("bidirectional", false); + +> CHANGE 21 : 22 @ 28 : 29 + +~ throw new JSONException("Invalid language->\'" + s + "\'->region: empty value"); + +> CHANGE 25 : 26 @ 32 : 33 + +~ throw new JSONException("Invalid language->\'" + s + "\'->name: empty value"); + +> CHANGE 29 : 30 @ 36 : 37 + +~ throw new JSONException("Duplicate language->\'" + s + "\' defined"); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSection.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSection.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSectionSerializer.edit.java new file mode 100644 index 0000000..79ee409 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/PackMetadataSectionSerializer.edit.java @@ -0,0 +1,41 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 7 @ 2 : 11 + +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeSerializer; +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; + +> DELETE 8 @ 12 : 13 + +> CHANGE 10 : 14 @ 15 : 21 + +~ implements JSONTypeSerializer { +~ public PackMetadataSection deserialize(JSONObject jsonobject) throws JSONException { +~ IChatComponent ichatcomponent = JSONTypeProvider.deserialize(jsonobject.get("description"), +~ IChatComponent.class); + +> CHANGE 15 : 16 @ 22 : 23 + +~ throw new JSONException("Invalid/missing description!"); + +> CHANGE 17 : 18 @ 24 : 25 + +~ int i = jsonobject.getInt("pack_format"); + +> CHANGE 22 : 27 @ 29 : 34 + +~ public JSONObject serialize(PackMetadataSection packmetadatasection) { +~ JSONObject jsonobject = new JSONObject(); +~ jsonobject.put("pack_format", packmetadatasection.getPackFormat()); +~ jsonobject.put("description", +~ (JSONObject) JSONTypeProvider.serialize(packmetadatasection.getPackDescription())); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSection.edit.java b/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSection.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSection.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.edit.java b/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.edit.java new file mode 100644 index 0000000..0d54ab8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/data/TextureMetadataSectionSerializer.edit.java @@ -0,0 +1,54 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 9 + +> DELETE 3 @ 10 : 13 + +> INSERT 4 : 10 @ 14 + ++ import org.json.JSONArray; ++ import org.json.JSONException; ++ import org.json.JSONObject; ++ ++ import com.google.common.collect.Lists; ++ + +> CHANGE 11 : 14 @ 15 : 20 + +~ public TextureMetadataSection deserialize(JSONObject jsonobject) throws JSONException { +~ boolean flag = jsonobject.optBoolean("blur", false); +~ boolean flag1 = jsonobject.optBoolean("clamp", false); + +> CHANGE 17 : 18 @ 23 : 24 + +~ JSONArray jsonarray = jsonobject.getJSONArray("mipmaps"); + +> CHANGE 19 : 22 @ 25 : 28 + +~ for (int i = 0; i < jsonarray.length(); ++i) { +~ Object jsonelement = jsonarray.get(i); +~ if (jsonelement instanceof Number) { + +> CHANGE 23 : 24 @ 29 : 30 + +~ arraylist.add(((Number) jsonelement).intValue()); + +> CHANGE 25 : 26 @ 31 : 32 + +~ throw new JSONException( + +> CHANGE 29 : 31 @ 35 : 37 + +~ } else if (jsonelement instanceof JSONObject) { +~ throw new JSONException( + +> CHANGE 35 : 36 @ 41 : 43 + +~ throw new JSONException("Invalid texture->mipmaps: expected array, was " + jsonobject.get("mipmaps"), + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/BuiltInModel.edit.java b/patches/minecraft/net/minecraft/client/resources/model/BuiltInModel.edit.java new file mode 100644 index 0000000..3cf97e0 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/BuiltInModel.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 5 @ 3 + ++ ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> DELETE 7 @ 5 : 7 + +> CHANGE 36 : 37 @ 36 : 37 + +~ public EaglerTextureAtlasSprite getParticleTexture() { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.edit.java b/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.edit.java new file mode 100644 index 0000000..b1798c1 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/IBakedModel.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 5 @ 3 + ++ ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> DELETE 7 @ 5 : 6 + +> CHANGE 20 : 21 @ 19 : 20 + +~ EaglerTextureAtlasSprite getParticleTexture(); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java new file mode 100644 index 0000000..4da85d8 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelBakery.edit.java @@ -0,0 +1,136 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 8 + +> INSERT 8 : 9 @ 14 + ++ import java.nio.charset.StandardCharsets; + +> DELETE 17 @ 22 : 23 + +> INSERT 18 : 30 @ 24 + ++ import java.util.Set; ++ ++ import com.google.common.base.Charsets; ++ import com.google.common.base.Joiner; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ import com.google.common.collect.Sets; ++ ++ import net.lax1dude.eaglercraft.v1_8.IOUtils; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> DELETE 39 @ 33 : 34 + +> DELETE 42 @ 37 : 43 + +> DELETE 49 @ 50 : 53 + +> CHANGE 69 : 70 @ 73 : 74 + +~ private final Map sprites = Maps.newHashMap(); + +> INSERT 130 : 131 @ 134 + ++ LOGGER.warn(var6); + +> CHANGE 133 : 135 @ 136 : 137 + +~ LOGGER.warn("Unable to load definition " + modelresourcelocation); +~ LOGGER.warn(exception); + +> CHANGE 174 : 175 @ 176 : 177 + +~ modelblockdefinition = new ModelBlockDefinition((ArrayList) arraylist); + +> CHANGE 197 : 199 @ 199 : 200 + +~ + modelresourcelocation + "\'"); +~ LOGGER.warn(exception); + +> CHANGE 217 : 218 @ 218 : 219 + +~ String str; + +> CHANGE 220 : 222 @ 221 : 223 + +~ str = (String) BUILT_IN_MODELS.get(s1); +~ if (str == null) { + +> DELETE 224 @ 225 : 227 + +> CHANGE 226 : 229 @ 229 : 230 + +~ try (InputStream is = iresource.getInputStream()) { +~ str = IOUtils.inputStreamToString(is, StandardCharsets.UTF_8); +~ } + +> CHANGE 231 : 233 @ 232 : 240 + +~ ModelBlock modelblock = ModelBlock.deserialize(str); +~ modelblock.name = parResourceLocation.toString(); + +> CHANGE 234 : 235 @ 241 : 242 + +~ return modelblock; + +> CHANGE 256 : 258 @ 263 : 264 + +~ + Item.itemRegistry.getNameForObject(item) + "\'"); +~ LOGGER.warn(exception); + +> CHANGE 446 : 447 @ 452 : 453 + +~ for (ModelResourceLocation modelresourcelocation : (List) arraylist) { + +> CHANGE 466 : 467 @ 472 : 473 + +~ EaglerTextureAtlasSprite textureatlassprite = (EaglerTextureAtlasSprite) this.sprites + +> CHANGE 474 : 475 @ 480 : 481 + +~ EaglerTextureAtlasSprite textureatlassprite1 = (EaglerTextureAtlasSprite) this.sprites + +> CHANGE 491 : 492 @ 497 : 498 + +~ EaglerTextureAtlasSprite parTextureAtlasSprite, EnumFacing parEnumFacing, ModelRotation parModelRotation, + +> CHANGE 509 : 510 @ 515 : 516 + +~ List arraydeque = Lists.newLinkedList(); + +> CHANGE 521 : 522 @ 527 : 528 + +~ ResourceLocation resourcelocation2 = (ResourceLocation) arraydeque.remove(0); + +> CHANGE 536 : 538 @ 542 : 543 + +~ + "; unable to load model: \'" + resourcelocation2 + "\'"); +~ LOGGER.warn(exception); + +> CHANGE 588 : 590 @ 593 : 595 + +~ for (ResourceLocation resourcelocation : (Set) set) { +~ EaglerTextureAtlasSprite textureatlassprite = texturemap.registerSprite(resourcelocation); + +> CHANGE 611 : 612 @ 616 : 617 + +~ EaglerTextureAtlasSprite.setLocationNameCompass(resourcelocation2.toString()); + +> CHANGE 614 : 615 @ 619 : 620 + +~ EaglerTextureAtlasSprite.setLocationNameClock(resourcelocation2.toString()); + +> CHANGE 667 : 668 @ 672 : 673 + +~ for (EaglerTextureAtlasSprite textureatlassprite : this.sprites.values()) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java new file mode 100644 index 0000000..c2a52f6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelManager.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelResourceLocation.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelResourceLocation.edit.java new file mode 100644 index 0000000..b8cfb7d --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelResourceLocation.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 6 @ 5 + ++ import net.minecraft.util.ResourceLocation; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.edit.java b/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.edit.java new file mode 100644 index 0000000..3e350ab --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/ModelRotation.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 8 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.vector.Matrix4f; ++ import net.lax1dude.eaglercraft.v1_8.vector.Vector3f; + +> DELETE 10 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/SimpleBakedModel.edit.java b/patches/minecraft/net/minecraft/client/resources/model/SimpleBakedModel.edit.java new file mode 100644 index 0000000..dc34805 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/SimpleBakedModel.edit.java @@ -0,0 +1,52 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 7 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> DELETE 11 @ 8 : 10 + +> CHANGE 18 : 19 @ 17 : 18 + +~ protected final EaglerTextureAtlasSprite texture; + +> CHANGE 22 : 23 @ 21 : 22 + +~ EaglerTextureAtlasSprite parTextureAtlasSprite, ItemCameraTransforms parItemCameraTransforms) { + +> CHANGE 51 : 52 @ 50 : 51 + +~ public EaglerTextureAtlasSprite getParticleTexture() { + +> CHANGE 63 : 64 @ 62 : 63 + +~ private EaglerTextureAtlasSprite builderTexture; + +> CHANGE 71 : 72 @ 70 : 71 + +~ public Builder(IBakedModel parIBakedModel, EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> CHANGE 83 : 84 @ 82 : 83 + +~ private void addFaceBreakingFours(IBakedModel parIBakedModel, EaglerTextureAtlasSprite parTextureAtlasSprite, + +> CHANGE 91 : 93 @ 90 : 91 + +~ private void addGeneralBreakingFours(IBakedModel parIBakedModel, +~ EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> CHANGE 122 : 123 @ 120 : 121 + +~ public SimpleBakedModel.Builder setTexture(EaglerTextureAtlasSprite parTextureAtlasSprite) { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/resources/model/WeightedBakedModel.edit.java b/patches/minecraft/net/minecraft/client/resources/model/WeightedBakedModel.edit.java new file mode 100644 index 0000000..e74ddb6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/resources/model/WeightedBakedModel.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 4 : 9 @ 6 + ++ ++ import com.google.common.collect.ComparisonChain; ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerTextureAtlasSprite; + +> DELETE 11 @ 8 : 10 + +> CHANGE 45 : 46 @ 44 : 45 + +~ public EaglerTextureAtlasSprite getParticleTexture() { + +> EOF diff --git a/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java b/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java new file mode 100644 index 0000000..173dbb6 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/settings/GameSettings.edit.java @@ -0,0 +1,359 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> CHANGE 3 : 6 @ 8 : 11 + +~ import java.io.ByteArrayOutputStream; +~ import java.io.InputStreamReader; +~ import java.io.OutputStreamWriter; + +> DELETE 7 @ 12 : 14 + +> INSERT 10 : 28 @ 17 + ++ ++ import org.json.JSONArray; ++ ++ import com.google.common.collect.ImmutableSet; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ import com.google.common.collect.Sets; ++ ++ import net.lax1dude.eaglercraft.v1_8.ArrayUtils; ++ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ++ import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; ++ import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ import net.lax1dude.eaglercraft.v1_8.Keyboard; ++ import net.lax1dude.eaglercraft.v1_8.Mouse; ++ import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 33 @ 22 : 24 + +> DELETE 38 @ 29 : 35 + +> DELETE 41 @ 38 : 43 + +> DELETE 42 @ 44 : 52 + +> CHANGE 65 : 68 @ 75 : 78 + +~ public int clouds = 1; +~ public boolean fancyGraphics = false; +~ public int ambientOcclusion = 0; + +> DELETE 76 @ 86 : 87 + +> DELETE 77 @ 88 : 89 + +> CHANGE 113 : 114 @ 125 : 126 + +~ public KeyBinding keyBindSprint = new KeyBinding("key.sprint", KeyboardConstants.KEY_R, "key.categories.movement"); + +> CHANGE 124 : 130 @ 136 : 143 + +~ public KeyBinding keyBindSmoothCamera = new KeyBinding("key.smoothCamera", KeyboardConstants.KEY_M, +~ "key.categories.misc"); +~ public KeyBinding keyBindZoomCamera = new KeyBinding("key.zoomCamera", KeyboardConstants.KEY_C, +~ "key.categories.misc"); +~ public KeyBinding keyBindFunction = new KeyBinding("key.function", KeyboardConstants.KEY_F, "key.categories.misc"); +~ public KeyBinding keyBindClose = new KeyBinding("key.close", KeyboardConstants.KEY_GRAVE, "key.categories.misc"); + +> DELETE 142 @ 155 : 156 + +> CHANGE 154 : 155 @ 168 : 169 + +~ public int guiScale = 3; + +> INSERT 158 : 166 @ 172 + ++ public boolean hudFps = true; ++ public boolean hudCoords = true; ++ public boolean hudPlayer = true; ++ public boolean hudWorld = false; ++ public boolean hudStats = false; ++ public boolean hud24h = false; ++ public boolean chunkFix = true; ++ public boolean fog = true; + +> CHANGE 167 : 168 @ 173 : 174 + +~ public GameSettings(Minecraft mcIn) { + +> CHANGE 172 : 174 @ 178 : 181 + +~ this.keyBindTogglePerspective, this.keyBindSmoothCamera, this.keyBindZoomCamera, this.keyBindFunction, +~ this.keyBindClose }, this.keyBindsHotbar); + +> CHANGE 177 : 179 @ 184 : 185 + +~ this.gammaSetting = 1.0F; +~ this.language = EagRuntime.getConfiguration().getDefaultLocale(); + +> CHANGE 181 : 182 @ 187 : 193 + +~ GameSettings.Options.RENDER_DISTANCE.setValueMax(18.0F); + +> CHANGE 183 : 184 @ 194 : 195 + +~ this.renderDistanceChunks = 4; + +> DELETE 187 @ 198 : 213 + +> CHANGE 190 : 191 @ 216 : 217 + +~ : HString.format("%c", new Object[] { Character.valueOf((char) (parInt1 - 256)) }) + +> DELETE 274 @ 300 : 301 + +> DELETE 278 @ 305 : 306 + +> INSERT 323 : 325 @ 351 + ++ this.mc.loadingScreen.eaglerShow(I18n.format("resourcePack.load.refreshing"), ++ I18n.format("resourcePack.load.pleaseWait")); + +> DELETE 383 @ 409 : 426 + +> INSERT 396 : 428 @ 439 + ++ if (parOptions == GameSettings.Options.HUD_FPS) { ++ this.hudFps = !this.hudFps; ++ } ++ ++ if (parOptions == GameSettings.Options.HUD_COORDS) { ++ this.hudCoords = !this.hudCoords; ++ } ++ ++ if (parOptions == GameSettings.Options.HUD_PLAYER) { ++ this.hudPlayer = !this.hudPlayer; ++ } ++ ++ if (parOptions == GameSettings.Options.HUD_STATS) { ++ this.hudStats = !this.hudStats; ++ } ++ ++ if (parOptions == GameSettings.Options.HUD_WORLD) { ++ this.hudWorld = !this.hudWorld; ++ } ++ ++ if (parOptions == GameSettings.Options.HUD_24H) { ++ this.hud24h = !this.hud24h; ++ } ++ ++ if (parOptions == GameSettings.Options.CHUNK_FIX) { ++ this.chunkFix = !this.chunkFix; ++ } ++ ++ if (parOptions == GameSettings.Options.FOG) { ++ this.fog = !this.fog; ++ } ++ + +> DELETE 482 @ 493 : 499 + +> INSERT 494 : 510 @ 511 + ++ case HUD_COORDS: ++ return this.hudCoords; ++ case HUD_FPS: ++ return this.hudFps; ++ case HUD_PLAYER: ++ return this.hudPlayer; ++ case HUD_STATS: ++ return this.hudStats; ++ case HUD_WORLD: ++ return this.hudWorld; ++ case HUD_24H: ++ return this.hud24h; ++ case CHUNK_FIX: ++ return this.chunkFix; ++ case FOG: ++ return this.fog; + +> CHANGE 553 : 556 @ 554 : 558 + +~ : (parOptions == GameSettings.Options.CHAT_SCALE +~ ? s + (int) (f * 90.0F + 10.0F) + "%" +~ : (parOptions == GameSettings.Options.CHAT_HEIGHT_UNFOCUSED + +> CHANGE 559 : 560 @ 561 : 562 + +~ : (parOptions == GameSettings.Options.CHAT_HEIGHT_FOCUSED + +> CHANGE 561 : 562 @ 563 : 564 + +~ .calculateChatboxHeight( + +> CHANGE 564 : 583 @ 566 : 600 + +~ : (parOptions == GameSettings.Options.CHAT_WIDTH +~ ? s + GuiNewChat +~ .calculateChatboxWidth( +~ f) +~ + "px" +~ : (parOptions == GameSettings.Options.RENDER_DISTANCE +~ ? s + (int) f1 +~ + (f1 == 1.0F +~ ? " chunk" +~ : " chunks") +~ : (parOptions == GameSettings.Options.MIPMAP_LEVELS +~ ? (f == 0.0F +~ ? s + I18n +~ .format("options.off", +~ new Object[0]) +~ : s + (int) (f +~ * 100.0F) +~ + "%") +~ : "yee")))))))))))); + +> CHANGE 618 : 620 @ 635 : 636 + +~ byte[] options = EagRuntime.getStorage("g"); +~ if (options == null) { + +> CHANGE 623 : 625 @ 639 : 640 + +~ BufferedReader bufferedreader = new BufferedReader( +~ new InputStreamReader(EaglerZLIB.newGZIPInputStream(new EaglerInputStream(options)))); + +> CHANGE 708 : 714 @ 723 : 724 + +~ this.resourcePacks.clear(); +~ for (Object o : (new JSONArray(s.substring(s.indexOf(58) + 1))).toList()) { +~ if (o instanceof String) { +~ this.resourcePacks.add((String) o); +~ } +~ } + +> CHANGE 720 : 726 @ 730 : 731 + +~ this.field_183018_l.clear(); +~ for (Object o : (new JSONArray(s.substring(s.indexOf(58) + 1))).toList()) { +~ if (o instanceof String) { +~ this.field_183018_l.add((String) o); +~ } +~ } + +> DELETE 764 @ 769 : 773 + +> DELETE 768 @ 777 : 781 + +> INSERT 884 : 916 @ 897 + ++ if (astring[0].equals("hudFps")) { ++ this.hudFps = astring[1].equals("true"); ++ } ++ ++ if (astring[0].equals("hudWorld")) { ++ this.hudWorld = astring[1].equals("true"); ++ } ++ ++ if (astring[0].equals("hudCoords")) { ++ this.hudCoords = astring[1].equals("true"); ++ } ++ ++ if (astring[0].equals("hudPlayer")) { ++ this.hudPlayer = astring[1].equals("true"); ++ } ++ ++ if (astring[0].equals("hudStats")) { ++ this.hudStats = astring[1].equals("true"); ++ } ++ ++ if (astring[0].equals("hud24h")) { ++ this.hud24h = astring[1].equals("true"); ++ } ++ ++ if (astring[0].equals("chunkFix")) { ++ this.chunkFix = astring[1].equals("true"); ++ } ++ ++ if (astring[0].equals("fog")) { ++ this.fog = astring[1].equals("true"); ++ } ++ + +> INSERT 922 : 924 @ 903 + ++ Keyboard.setFunctionKeyModifier(keyBindFunction.getKeyCode()); ++ + +> DELETE 941 @ 920 : 921 + +> CHANGE 953 : 955 @ 933 : 934 + +~ ByteArrayOutputStream bao = new ByteArrayOutputStream(); +~ PrintWriter printwriter = new PrintWriter(new OutputStreamWriter(EaglerZLIB.newGZIPOutputStream(bao))); + +> CHANGE 981 : 983 @ 960 : 962 + +~ printwriter.println("resourcePacks:" + toJSONArray(this.resourcePacks)); +~ printwriter.println("incompatibleResourcePacks:" + toJSONArray(this.field_183018_l)); + +> DELETE 991 @ 970 : 971 + +> DELETE 992 @ 972 : 973 + +> INSERT 1021 : 1029 @ 1002 + ++ printwriter.println("hudFps:" + this.hudFps); ++ printwriter.println("hudWorld:" + this.hudWorld); ++ printwriter.println("hudCoords:" + this.hudCoords); ++ printwriter.println("hudPlayer:" + this.hudPlayer); ++ printwriter.println("hudStats:" + this.hudStats); ++ printwriter.println("hud24h:" + this.hud24h); ++ printwriter.println("chunkFix:" + this.chunkFix); ++ printwriter.println("fog:" + this.fog); + +> INSERT 1034 : 1036 @ 1007 + ++ Keyboard.setFunctionKeyModifier(keyBindFunction.getKeyCode()); ++ + +> INSERT 1047 : 1049 @ 1018 + ++ ++ EagRuntime.setStorage("g", bao.toByteArray()); + +> CHANGE 1059 : 1060 @ 1028 : 1029 + +~ : (parSoundCategory == SoundCategory.VOICE ? 0.0F : 1.0F); + +> INSERT 1113 : 1121 @ 1082 + ++ private String toJSONArray(List e) { ++ JSONArray arr = new JSONArray(); ++ for (String s : e) { ++ arr.put(s); ++ } ++ return arr.toString(); ++ } ++ + +> CHANGE 1125 : 1126 @ 1086 : 1087 + +~ RENDER_DISTANCE("options.renderDistance", true, false, 1.0F, 16.0F, 1.0F), + +> CHANGE 1134 : 1136 @ 1095 : 1099 + +~ TOUCHSCREEN("options.touchscreen", false, true), CHAT_SCALE("options.chat.scale", true, false), +~ CHAT_WIDTH("options.chat.width", true, false), CHAT_HEIGHT_FOCUSED("options.chat.height.focused", true, false), + +> CHANGE 1150 : 1155 @ 1113 : 1114 + +~ ENTITY_SHADOWS("options.entityShadows", false, true), HUD_FPS("options.hud.fps", false, true), +~ HUD_COORDS("options.hud.coords", false, true), HUD_STATS("options.hud.stats", false, true), +~ HUD_WORLD("options.hud.world", false, true), HUD_PLAYER("options.hud.player", false, true), +~ HUD_24H("options.hud.24h", false, true), CHUNK_FIX("options.chunkFix", false, true), +~ FOG("options.fog", false, true); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/settings/KeyBinding.edit.java b/patches/minecraft/net/minecraft/client/settings/KeyBinding.edit.java new file mode 100644 index 0000000..d182d08 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/settings/KeyBinding.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 4 : 8 @ 6 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Sets; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/client/stream/IStream.edit.java b/patches/minecraft/net/minecraft/client/stream/IStream.edit.java new file mode 100644 index 0000000..8083f55 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/stream/IStream.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 8 + +> DELETE 15 @ 21 : 25 + +> DELETE 29 @ 39 : 41 + +> DELETE 31 @ 43 : 45 + +> DELETE 39 @ 53 : 55 + +> DELETE 43 @ 59 : 61 + +> EOF diff --git a/patches/minecraft/net/minecraft/client/util/JsonBlendingMode.edit.java b/patches/minecraft/net/minecraft/client/util/JsonBlendingMode.edit.java new file mode 100644 index 0000000..8ba60e3 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/util/JsonBlendingMode.edit.java @@ -0,0 +1,51 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 6 + +~ import org.json.JSONObject; + +> INSERT 4 : 7 @ 7 + ++ import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU; ++ import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; ++ + +> CHANGE 52 : 53 @ 52 : 53 + +~ EaglercraftGPU.glBlendEquation(this.field_148112_f); + +> CHANGE 95 : 96 @ 95 : 96 + +~ public static JsonBlendingMode func_148110_a(JSONObject parJsonObject) { + +> CHANGE 106 : 108 @ 106 : 108 + +~ if (parJsonObject.get("func") instanceof String) { +~ i = func_148108_a(parJsonObject.getString("func")); + +> CHANGE 113 : 115 @ 113 : 115 + +~ if (parJsonObject.get("srcrgb") instanceof String) { +~ j = func_148107_b(parJsonObject.getString("srcrgb")); + +> CHANGE 120 : 122 @ 120 : 122 + +~ if (parJsonObject.get("dstrgb") instanceof String) { +~ k = func_148107_b(parJsonObject.getString("dstrgb")); + +> CHANGE 127 : 129 @ 127 : 129 + +~ if (parJsonObject.get("srcalpha") instanceof String) { +~ l = func_148107_b(parJsonObject.getString("srcalpha")); + +> CHANGE 136 : 138 @ 136 : 138 + +~ if (parJsonObject.get("dstalpha") instanceof String) { +~ i1 = func_148107_b(parJsonObject.getString("dstalpha")); + +> EOF diff --git a/patches/minecraft/net/minecraft/client/util/JsonException.edit.java b/patches/minecraft/net/minecraft/client/util/JsonException.edit.java new file mode 100644 index 0000000..f1fb721 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/util/JsonException.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 5 : 6 @ 6 + ++ + +> INSERT 8 : 10 @ 8 + ++ import com.google.common.collect.Lists; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/command/EntityNotFoundException.edit.java b/patches/minecraft/net/minecraft/command/EntityNotFoundException.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/command/EntityNotFoundException.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/command/ICommandSender.edit.java b/patches/minecraft/net/minecraft/command/ICommandSender.edit.java new file mode 100644 index 0000000..641b33f --- /dev/null +++ b/patches/minecraft/net/minecraft/command/ICommandSender.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 27 @ 28 : 29 + +> EOF diff --git a/patches/minecraft/net/minecraft/command/PlayerNotFoundException.edit.java b/patches/minecraft/net/minecraft/command/PlayerNotFoundException.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/command/PlayerNotFoundException.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/command/PlayerSelector.edit.java b/patches/minecraft/net/minecraft/command/PlayerSelector.edit.java new file mode 100644 index 0000000..b5eaa59 --- /dev/null +++ b/patches/minecraft/net/minecraft/command/PlayerSelector.edit.java @@ -0,0 +1,120 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 8 + +> DELETE 8 @ 14 : 15 + +> INSERT 9 : 10 @ 16 + ++ import java.util.Set; + +> CHANGE 12 : 21 @ 18 : 20 + +~ +~ import com.google.common.base.Predicate; +~ import com.google.common.base.Predicates; +~ import com.google.common.collect.ComparisonChain; +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ import com.google.common.collect.Sets; +~ +~ import net.minecraft.client.Minecraft; + +> DELETE 25 @ 24 : 25 + +> DELETE 29 @ 29 : 30 + +> DELETE 46 @ 47 : 51 + +> CHANGE 59 : 60 @ 64 : 65 + +~ for (Entity entity : (List) list) { + +> CHANGE 63 : 64 @ 68 : 69 + +~ return IChatComponent.join(arraylist); + +> CHANGE 80 : 81 @ 85 : 86 + +~ for (World world : (List) list) { + +> CHANGE 107 : 111 @ 112 : 113 + +~ Minecraft mc = Minecraft.getMinecraft(); +~ if (mc.theWorld != null) { +~ arraylist.add(mc.thePlayer); +~ } + +> CHANGE 133 : 135 @ 135 : 137 + +~ String ss = func_179651_b(parMap, "type"); +~ final boolean flag = ss != null && ss.startsWith("!"); + +> CHANGE 136 : 137 @ 138 : 139 + +~ ss = ss.substring(1); + +> INSERT 138 : 139 @ 140 + ++ final String s = ss; + +> CHANGE 168 : 169 @ 169 : 176 + +~ return false; + +> CHANGE 182 : 183 @ 189 : 195 + +~ return false; + +> CHANGE 192 : 194 @ 204 : 206 + +~ String ss = func_179651_b(parMap, "team"); +~ final boolean flag = ss != null && ss.startsWith("!"); + +> CHANGE 195 : 196 @ 207 : 208 + +~ ss = ss.substring(1); + +> INSERT 197 : 198 @ 209 + ++ final String s = ss; + +> CHANGE 219 : 220 @ 230 : 231 + +~ final Map map = func_96560_a(parMap); + +> CHANGE 223 : 224 @ 234 : 235 + +~ Scoreboard scoreboard = Minecraft.getMinecraft().theWorld.getScoreboard(); + +> CHANGE 238 : 239 @ 249 : 251 + +~ String s1 = entity instanceof EntityPlayer ? entity.getName() : entity.getUniqueID().toString(); + +> CHANGE 264 : 266 @ 276 : 278 + +~ String ss = func_179651_b(parMap, "name"); +~ final boolean flag = ss != null && ss.startsWith("!"); + +> CHANGE 267 : 268 @ 279 : 280 + +~ ss = ss.substring(1); + +> INSERT 269 : 270 @ 281 + ++ final String s = ss; + +> CHANGE 410 : 411 @ 421 : 422 + +~ parList = (List) Lists.newArrayList(new Entity[] { entity }); + +> INSERT 544 : 545 @ 555 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/command/server/CommandBlockLogic.edit.java b/patches/minecraft/net/minecraft/command/server/CommandBlockLogic.edit.java new file mode 100644 index 0000000..2a8ff94 --- /dev/null +++ b/patches/minecraft/net/minecraft/command/server/CommandBlockLogic.edit.java @@ -0,0 +1,47 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 5 @ 4 : 11 + +~ +~ import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; + +> DELETE 7 @ 13 : 14 + +> DELETE 9 @ 16 : 17 + +> CHANGE 11 : 12 @ 19 : 20 + +~ public abstract class CommandBlockLogic { + +> DELETE 18 @ 26 : 27 + +> DELETE 35 @ 44 : 46 + +> DELETE 51 @ 62 : 64 + +> DELETE 67 @ 80 : 83 + +> DELETE 68 @ 84 : 110 + +> DELETE 83 @ 125 : 130 + +> CHANGE 87 : 88 @ 134 : 137 + +~ return true; + +> DELETE 90 @ 139 : 143 + +> CHANGE 112 : 113 @ 165 : 169 + +~ playerIn.openEditCommandBlock(this); + +> DELETE 116 @ 172 : 176 + +> EOF diff --git a/patches/minecraft/net/minecraft/crash/CrashReport.edit.java b/patches/minecraft/net/minecraft/crash/CrashReport.edit.java new file mode 100644 index 0000000..e37dece --- /dev/null +++ b/patches/minecraft/net/minecraft/crash/CrashReport.edit.java @@ -0,0 +1,103 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> DELETE 4 @ 7 : 9 + +> CHANGE 8 : 17 @ 13 : 14 + +~ +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.ArrayUtils; +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.IOUtils; +~ import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformType; +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 18 @ 15 : 20 + +> DELETE 25 @ 27 : 28 + +> CHANGE 26 : 27 @ 29 : 30 + +~ private String[] stacktrace; + +> INSERT 31 : 32 @ 34 + ++ this.stacktrace = EagRuntime.getStackTraceElements(causeThrowable); + +> CHANGE 58 : 69 @ 60 : 88 + +~ if (EagRuntime.getPlatformType() != EnumPlatformType.JAVASCRIPT) { +~ this.theReportCategory.addCrashSectionCallable("Memory", new Callable() { +~ public String call() { +~ long i = EagRuntime.maxMemory(); +~ long j = EagRuntime.totalMemory(); +~ long k = EagRuntime.freeMemory(); +~ long l = i / 1024L / 1024L; +~ long i1 = j / 1024L / 1024L; +~ long j1 = k / 1024L / 1024L; +~ return k + " bytes (" + j1 + " MB) / " + j + " bytes (" + i1 + " MB) up to " + i + " bytes (" + l +~ + " MB)"; + +> CHANGE 70 : 72 @ 89 : 98 + +~ }); +~ } + +> CHANGE 84 : 85 @ 110 : 111 + +~ this.stacktrace = (String[]) ArrayUtils + +> CHANGE 92 : 93 @ 118 : 119 + +~ for (String stacktraceelement : this.stacktrace) { + +> CHANGE 109 : 110 @ 135 : 146 + +~ StringBuilder stackTrace = new StringBuilder(); + +> CHANGE 111 : 118 @ 147 : 148 + +~ if ((this.cause.getMessage() == null || this.cause.getMessage().length() == 0) +~ && ((this.cause instanceof NullPointerException) || (this.cause instanceof StackOverflowError) +~ || (this.cause instanceof OutOfMemoryError))) { +~ stackTrace.append(this.cause.getClass().getName()).append(": "); +~ stackTrace.append(this.description).append('\n'); +~ } else { +~ stackTrace.append(this.cause.toString()).append('\n'); + +> CHANGE 120 : 123 @ 150 : 151 + +~ EagRuntime.getStackTrace(this.cause, (s) -> { +~ stackTrace.append("\tat ").append(s).append('\n'); +~ }); + +> CHANGE 124 : 125 @ 152 : 163 + +~ return stackTrace.toString(); + +> DELETE 152 @ 190 : 215 + +> CHANGE 164 : 167 @ 227 : 230 + +~ String[] astacktraceelement = EagRuntime.getStackTraceElements(cause); +~ String stacktraceelement = null; +~ String stacktraceelement1 = null; + +> CHANGE 188 : 189 @ 251 : 252 + +~ this.stacktrace = new String[j]; + +> CHANGE 200 : 201 @ 263 : 282 + +~ return "eagler"; + +> EOF diff --git a/patches/minecraft/net/minecraft/crash/CrashReportCategory.edit.java b/patches/minecraft/net/minecraft/crash/CrashReportCategory.edit.java new file mode 100644 index 0000000..61a23b5 --- /dev/null +++ b/patches/minecraft/net/minecraft/crash/CrashReportCategory.edit.java @@ -0,0 +1,91 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 9 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ++ import net.lax1dude.eaglercraft.v1_8.HString; + +> DELETE 11 @ 7 : 8 + +> CHANGE 17 : 18 @ 14 : 15 + +~ private String[] stackTrace = new String[0]; + +> CHANGE 25 : 26 @ 22 : 23 + +~ return HString.format("%.2f,%.2f,%.2f - %s", new Object[] { Double.valueOf(x), Double.valueOf(y), + +> CHANGE 36 : 37 @ 33 : 34 + +~ stringbuilder.append(HString.format("World: (%d,%d,%d)", + +> CHANGE 54 : 55 @ 51 : 52 + +~ stringbuilder.append(HString.format("Chunk: (at %d,%d,%d in %d,%d; contains blocks %d,0,%d to %d,255,%d)", + +> CHANGE 76 : 77 @ 73 : 74 + +~ HString.format("Region: (%d,%d; contains chunks %d,%d to %d,%d, blocks %d,0,%d to %d,255,%d)", + +> CHANGE 105 : 107 @ 102 : 104 + +~ String[] astacktraceelement = EagRuntime.getStackTraceElements(new Exception()); +~ if (astacktraceelement.length - 3 - size <= 0) { + +> CHANGE 109 : 110 @ 106 : 107 + +~ this.stackTrace = new String[astacktraceelement.length - 3 - size]; + +> CHANGE 115 : 116 @ 112 : 113 + +~ public boolean firstTwoElementsOfStackTraceMatch(String s1, String s2) { + +> CHANGE 117 : 119 @ 114 : 119 + +~ String stacktraceelement = this.stackTrace[0]; +~ if (s1.equals(stacktraceelement)) { + +> CHANGE 136 : 137 @ 136 : 137 + +~ String[] astacktraceelement = new String[this.stackTrace.length - amount]; + +> CHANGE 155 : 156 @ 155 : 156 + +~ for (String stacktraceelement : this.stackTrace) { + +> CHANGE 157 : 158 @ 157 : 158 + +~ builder.append(stacktraceelement); + +> CHANGE 163 : 164 @ 163 : 164 + +~ public String[] getStackTrace() { + +> CHANGE 173 : 175 @ 173 : 175 + +~ return HString.format("ID #%d (%s // %s)", new Object[] { Integer.valueOf(i), +~ blockIn.getUnlocalizedName(), blockIn.getClass().getName() }); + +> CHANGE 185 : 186 @ 185 : 186 + +~ String s = HString.format("%4s", new Object[] { Integer.toBinaryString(blockData) }).replace(" ", + +> CHANGE 187 : 188 @ 187 : 188 + +~ return HString.format("%1$d / 0x%1$X / 0b%2$s", new Object[] { Integer.valueOf(blockData), s }); + +> CHANGE 221 : 222 @ 221 : 222 + +~ this.value = "~~ERROR~~ " + throwable.getClass().getName() + ": " + throwable.getMessage(); + +> EOF diff --git a/patches/minecraft/net/minecraft/creativetab/CreativeTabs.edit.java b/patches/minecraft/net/minecraft/creativetab/CreativeTabs.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/creativetab/CreativeTabs.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.edit.java b/patches/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.edit.java new file mode 100644 index 0000000..4890f7c --- /dev/null +++ b/patches/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.edit.java b/patches/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.edit.java new file mode 100644 index 0000000..4890f7c --- /dev/null +++ b/patches/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/dispenser/IBehaviorDispenseItem.edit.java b/patches/minecraft/net/minecraft/dispenser/IBehaviorDispenseItem.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/dispenser/IBehaviorDispenseItem.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/dispenser/IBlockSource.edit.java b/patches/minecraft/net/minecraft/dispenser/IBlockSource.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/dispenser/IBlockSource.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/dispenser/ILocatableSource.edit.java b/patches/minecraft/net/minecraft/dispenser/ILocatableSource.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/dispenser/ILocatableSource.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/dispenser/ILocation.edit.java b/patches/minecraft/net/minecraft/dispenser/ILocation.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/dispenser/ILocation.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/dispenser/PositionImpl.edit.java b/patches/minecraft/net/minecraft/dispenser/PositionImpl.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/dispenser/PositionImpl.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/Enchantment.edit.java b/patches/minecraft/net/minecraft/enchantment/Enchantment.edit.java new file mode 100644 index 0000000..4e01412 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/Enchantment.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 5 : 9 @ 7 : 25 + +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowDamage.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowDamage.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowDamage.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowFire.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowFire.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowFire.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowInfinite.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowInfinite.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowInfinite.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowKnockback.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowKnockback.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentArrowKnockback.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentDamage.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentDamage.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentDamage.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentData.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentData.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentData.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentDigging.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentDigging.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentDigging.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentDurability.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentDurability.edit.java new file mode 100644 index 0000000..3e3e443 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentDurability.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 30 : 31 @ 31 : 32 + +~ public static boolean negateDamage(ItemStack parItemStack, int parInt1, EaglercraftRandom parRandom) { + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentFireAspect.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentFireAspect.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentFireAspect.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentFishingSpeed.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentFishingSpeed.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentFishingSpeed.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentHelper.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentHelper.edit.java new file mode 100644 index 0000000..74b1e38 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentHelper.edit.java @@ -0,0 +1,44 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 8 : 13 @ 10 : 13 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ + +> CHANGE 26 : 27 @ 26 : 27 + +~ private static final EaglercraftRandom enchantmentRand = new EaglercraftRandom(); + +> CHANGE 238 : 240 @ 238 : 239 + +~ public static int calcItemStackEnchantability(EaglercraftRandom parRandom, int parInt1, int parInt2, +~ ItemStack parItemStack) { + +> CHANGE 254 : 255 @ 253 : 254 + +~ public static ItemStack addRandomEnchantment(EaglercraftRandom parRandom, ItemStack parItemStack, int parInt1) { + +> CHANGE 262 : 263 @ 261 : 262 + +~ for (EnchantmentData enchantmentdata : (List) list) { + +> CHANGE 274 : 276 @ 273 : 274 + +~ public static List buildEnchantmentList(EaglercraftRandom randomIn, ItemStack itemStackIn, +~ int parInt1) { + +> CHANGE 306 : 307 @ 304 : 305 + +~ for (EnchantmentData enchantmentdata1 : (List) arraylist) { + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentKnockback.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentKnockback.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentKnockback.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentLootBonus.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentLootBonus.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentLootBonus.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentOxygen.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentOxygen.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentOxygen.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentProtection.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentProtection.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentProtection.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentThorns.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentThorns.edit.java new file mode 100644 index 0000000..19bb772 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentThorns.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> CHANGE 34 : 35 @ 36 : 37 + +~ EaglercraftRandom random = entitylivingbase.getRNG(); + +> CHANGE 52 : 53 @ 54 : 55 + +~ public static boolean func_92094_a(int parInt1, EaglercraftRandom parRandom) { + +> CHANGE 56 : 57 @ 58 : 59 + +~ public static int func_92095_b(int parInt1, EaglercraftRandom parRandom) { + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentUntouching.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentUntouching.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentUntouching.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWalker.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWalker.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWalker.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWorker.edit.java b/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWorker.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/enchantment/EnchantmentWaterWorker.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/DataWatcher.edit.java b/patches/minecraft/net/minecraft/entity/DataWatcher.edit.java new file mode 100644 index 0000000..0e94b6e --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/DataWatcher.edit.java @@ -0,0 +1,53 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 6 : 12 @ 8 : 10 + +~ +~ import org.apache.commons.lang3.ObjectUtils; +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ + +> DELETE 14 @ 12 : 13 + +> DELETE 19 @ 18 : 19 + +> DELETE 26 @ 26 : 27 + +> DELETE 42 @ 43 : 44 + +> DELETE 43 @ 45 : 46 + +> DELETE 50 @ 53 : 54 + +> DELETE 51 @ 55 : 56 + +> DELETE 79 @ 84 : 86 + +> DELETE 88 @ 95 : 97 + +> DELETE 129 @ 138 : 140 + +> DELETE 139 @ 150 : 152 + +> DELETE 146 @ 159 : 161 + +> DELETE 149 @ 164 : 166 + +> DELETE 154 @ 171 : 172 + +> DELETE 163 @ 181 : 182 + +> DELETE 256 @ 275 : 276 + +> DELETE 266 @ 286 : 287 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/Entity.edit.java b/patches/minecraft/net/minecraft/entity/Entity.edit.java new file mode 100644 index 0000000..f72515e --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/Entity.edit.java @@ -0,0 +1,149 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 6 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ import net.lax1dude.eaglercraft.v1_8.HString; + +> INSERT 7 : 8 @ 6 + ++ + +> DELETE 15 @ 13 : 16 + +> DELETE 19 @ 20 : 23 + +> DELETE 22 @ 26 : 27 + +> DELETE 30 @ 35 : 36 + +> DELETE 44 @ 50 : 51 + +> CHANGE 45 : 46 @ 52 : 53 + +~ public abstract class Entity { + +> CHANGE 90 : 91 @ 97 : 98 + +~ protected EaglercraftRandom rand; + +> CHANGE 118 : 119 @ 125 : 127 + +~ protected EaglercraftUUID entityUniqueID; + +> CHANGE 139 : 140 @ 147 : 148 + +~ this.rand = new EaglercraftRandom(); + +> DELETE 143 @ 151 : 152 + +> DELETE 202 @ 211 : 214 + +> DELETE 247 @ 259 : 274 + +> DELETE 248 @ 275 : 297 + +> CHANGE 250 : 251 @ 299 : 311 + +~ this.fire = 0; + +> DELETE 252 @ 312 : 316 + +> DELETE 261 @ 325 : 329 + +> CHANGE 377 : 378 @ 445 : 446 + +~ for (AxisAlignedBB axisalignedbb1 : (List) list1) { + +> CHANGE 384 : 385 @ 452 : 453 + +~ for (AxisAlignedBB axisalignedbb2 : (List) list1) { + +> CHANGE 390 : 391 @ 458 : 459 + +~ for (AxisAlignedBB axisalignedbb13 : (List) list1) { + +> CHANGE 408 : 409 @ 476 : 477 + +~ for (AxisAlignedBB axisalignedbb6 : (List) list) { + +> CHANGE 415 : 416 @ 483 : 484 + +~ for (AxisAlignedBB axisalignedbb7 : (List) list) { + +> CHANGE 422 : 423 @ 490 : 491 + +~ for (AxisAlignedBB axisalignedbb8 : (List) list) { + +> CHANGE 430 : 431 @ 498 : 499 + +~ for (AxisAlignedBB axisalignedbb9 : (List) list) { + +> CHANGE 437 : 438 @ 505 : 506 + +~ for (AxisAlignedBB axisalignedbb10 : (List) list) { + +> CHANGE 444 : 445 @ 512 : 513 + +~ for (AxisAlignedBB axisalignedbb11 : (List) list) { + +> CHANGE 463 : 464 @ 531 : 532 + +~ for (AxisAlignedBB axisalignedbb12 : (List) list) { + +> DELETE 1059 @ 1127 : 1128 + +> CHANGE 1114 : 1116 @ 1183 : 1184 + +~ this.entityUniqueID = new EaglercraftUUID(tagCompund.getLong("UUIDMost"), +~ tagCompund.getLong("UUIDLeast")); + +> CHANGE 1117 : 1118 @ 1185 : 1186 + +~ this.entityUniqueID = EaglercraftUUID.fromString(tagCompund.getString("UUID")); + +> DELETE 1127 @ 1195 : 1196 + +> CHANGE 1343 : 1344 @ 1412 : 1413 + +~ for (AxisAlignedBB axisalignedbb : (List) list) { + +> DELETE 1367 @ 1436 : 1457 + +> CHANGE 1395 : 1396 @ 1485 : 1486 + +~ boolean flag = this.worldObj != null; + +> CHANGE 1579 : 1580 @ 1669 : 1670 + +~ return HString.format("%s[\'%s\'/%d, l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", + +> DELETE 1605 @ 1695 : 1729 + +> CHANGE 1635 : 1636 @ 1759 : 1760 + +~ return EntityList.getEntityString(Entity.this) + " (" + Entity.this.getClass().getName() + ")"; + +> CHANGE 1644 : 1645 @ 1768 : 1769 + +~ category.addCrashSection("Entity\'s Exact location", HString.format("%.2f, %.2f, %.2f", + +> CHANGE 1649 : 1650 @ 1773 : 1774 + +~ category.addCrashSection("Entity\'s Momentum", HString.format("%.2f, %.2f, %.2f", new Object[] { + +> CHANGE 1667 : 1668 @ 1791 : 1792 + +~ public EaglercraftUUID getUniqueID() { + +> DELETE 1730 @ 1854 : 1858 + +> DELETE 1781 @ 1909 : 1921 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityAgeable.edit.java b/patches/minecraft/net/minecraft/entity/EntityAgeable.edit.java new file mode 100644 index 0000000..223ffa2 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityAgeable.edit.java @@ -0,0 +1,34 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 26 @ 28 : 51 + +> CHANGE 38 : 39 @ 63 : 64 + +~ return this.dataWatcher.getWatchableObjectByte(12); + +> CHANGE 91 : 98 @ 116 : 127 + +~ if (this.field_175503_c > 0) { +~ if (this.field_175503_c % 4 == 0) { +~ this.worldObj.spawnParticle(EnumParticleTypes.VILLAGER_HAPPY, +~ this.posX + (double) (this.rand.nextFloat() * this.width * 2.0F) - (double) this.width, +~ this.posY + 0.5D + (double) (this.rand.nextFloat() * this.height), +~ this.posZ + (double) (this.rand.nextFloat() * this.width * 2.0F) - (double) this.width, 0.0D, +~ 0.0D, 0.0D, new int[0]); + +> CHANGE 100 : 101 @ 129 : 142 + +~ --this.field_175503_c; + +> INSERT 103 : 104 @ 144 + ++ this.setScaleForAge(this.isChild()); + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityBodyHelper.edit.java b/patches/minecraft/net/minecraft/entity/EntityBodyHelper.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityBodyHelper.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityCreature.edit.java b/patches/minecraft/net/minecraft/entity/EntityCreature.edit.java new file mode 100644 index 0000000..4509d05 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityCreature.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> DELETE 6 @ 9 : 10 + +> CHANGE 10 : 12 @ 14 : 15 + +~ public static final EaglercraftUUID FLEEING_SPEED_MODIFIER_UUID = EaglercraftUUID +~ .fromString("E199AD21-BA8A-4C53-8D13-6182D5C69D3A"); + +> DELETE 16 @ 19 : 20 + +> CHANGE 32 : 33 @ 36 : 37 + +~ return false; + +> DELETE 81 @ 85 : 90 + +> DELETE 85 @ 94 : 98 + +> DELETE 99 @ 112 : 117 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityFlying.edit.java b/patches/minecraft/net/minecraft/entity/EntityFlying.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityFlying.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityHanging.edit.java b/patches/minecraft/net/minecraft/entity/EntityHanging.edit.java new file mode 100644 index 0000000..018dc4a --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityHanging.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import org.apache.commons.lang3.Validate; ++ + +> DELETE 6 @ 4 : 5 + +> DELETE 13 @ 12 : 13 + +> CHANGE 81 : 82 @ 81 : 89 + +~ this.tickCounter1++; + +> DELETE 128 @ 135 : 149 + +> DELETE 129 @ 150 : 154 + +> DELETE 133 @ 158 : 162 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityLeashKnot.edit.java b/patches/minecraft/net/minecraft/entity/EntityLeashKnot.edit.java new file mode 100644 index 0000000..750ccbc --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityLeashKnot.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> DELETE 4 @ 7 : 9 + +> DELETE 63 @ 68 : 97 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityList.edit.java b/patches/minecraft/net/minecraft/entity/EntityList.edit.java new file mode 100644 index 0000000..c02170d --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityList.edit.java @@ -0,0 +1,165 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 6 : 12 @ 8 : 12 + +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 78 @ 78 : 80 + +> INSERT 82 : 84 @ 84 + ++ private static final Map> stringToConstructorMapping = Maps ++ .newHashMap(); + +> INSERT 86 : 87 @ 86 + ++ private static final Map> idToConstructorMapping = Maps.newHashMap(); + +> CHANGE 91 : 93 @ 90 : 91 + +~ private static void addMapping(Class entityClass, +~ EntityConstructor entityConstructor, String entityName, int id) { + +> INSERT 103 : 104 @ 101 + ++ stringToConstructorMapping.put(entityName, entityConstructor); + +> INSERT 106 : 107 @ 103 + ++ idToConstructorMapping.put(Integer.valueOf(id), entityConstructor); + +> CHANGE 112 : 114 @ 108 : 109 + +~ private static void addMapping(Class entityClass, +~ EntityConstructor entityConstructor, String entityName, int entityID, int baseColor, + +> CHANGE 115 : 116 @ 110 : 111 + +~ addMapping(entityClass, entityConstructor, entityName, entityID); + +> CHANGE 123 : 126 @ 118 : 122 + +~ EntityConstructor constructor = stringToConstructorMapping.get(entityName); +~ if (constructor != null) { +~ entity = constructor.createEntity(worldIn); + +> CHANGE 128 : 129 @ 124 : 125 + +~ logger.error("Could not create entity", exception); + +> CHANGE 142 : 145 @ 138 : 142 + +~ EntityConstructor constructor = stringToConstructorMapping.get(nbt.getString("id")); +~ if (constructor != null) { +~ entity = constructor.createEntity(worldIn); + +> CHANGE 147 : 148 @ 144 : 145 + +~ logger.error("Could not create entity", exception); + +> CHANGE 163 : 166 @ 160 : 164 + +~ EntityConstructor constructor = getConstructorFromID(entityID); +~ if (constructor != null) { +~ entity = constructor.createEntity(worldIn); + +> CHANGE 168 : 169 @ 166 : 167 + +~ logger.error("Could not create entity", exception); + +> INSERT 187 : 191 @ 185 + ++ public static EntityConstructor getConstructorFromID(int entityID) { ++ return idToConstructorMapping.get(Integer.valueOf(entityID)); ++ } ++ + +> CHANGE 208 : 209 @ 202 : 203 + +~ Set set = stringToClassMapping.keySet(); + +> CHANGE 238 : 307 @ 232 : 294 + +~ addMapping(EntityItem.class, (w) -> new EntityItem(w), "Item", 1); +~ addMapping(EntityXPOrb.class, (w) -> new EntityXPOrb(w), "XPOrb", 2); +~ addMapping(EntityEgg.class, (w) -> new EntityEgg(w), "ThrownEgg", 7); +~ addMapping(EntityLeashKnot.class, (w) -> new EntityLeashKnot(w), "LeashKnot", 8); +~ addMapping(EntityPainting.class, (w) -> new EntityPainting(w), "Painting", 9); +~ addMapping(EntityArrow.class, (w) -> new EntityArrow(w), "Arrow", 10); +~ addMapping(EntitySnowball.class, (w) -> new EntitySnowball(w), "Snowball", 11); +~ addMapping(EntityLargeFireball.class, (w) -> new EntityLargeFireball(w), "Fireball", 12); +~ addMapping(EntitySmallFireball.class, (w) -> new EntitySmallFireball(w), "SmallFireball", 13); +~ addMapping(EntityEnderPearl.class, (w) -> new EntityEnderPearl(w), "ThrownEnderpearl", 14); +~ addMapping(EntityEnderEye.class, (w) -> new EntityEnderEye(w), "EyeOfEnderSignal", 15); +~ addMapping(EntityPotion.class, (w) -> new EntityPotion(w), "ThrownPotion", 16); +~ addMapping(EntityExpBottle.class, (w) -> new EntityExpBottle(w), "ThrownExpBottle", 17); +~ addMapping(EntityItemFrame.class, (w) -> new EntityItem(w), "ItemFrame", 18); +~ addMapping(EntityWitherSkull.class, (w) -> new EntityWitherSkull(w), "WitherSkull", 19); +~ addMapping(EntityTNTPrimed.class, (w) -> new EntityTNTPrimed(w), "PrimedTnt", 20); +~ addMapping(EntityFallingBlock.class, (w) -> new EntityFallingBlock(w), "FallingSand", 21); +~ addMapping(EntityFireworkRocket.class, (w) -> new EntityFireworkRocket(w), "FireworksRocketEntity", 22); +~ addMapping(EntityArmorStand.class, (w) -> new EntityArmorStand(w), "ArmorStand", 30); +~ addMapping(EntityBoat.class, (w) -> new EntityBoat(w), "Boat", 41); +~ addMapping(EntityMinecartEmpty.class, (w) -> new EntityMinecartEmpty(w), +~ EntityMinecart.EnumMinecartType.RIDEABLE.getName(), 42); +~ addMapping(EntityMinecartChest.class, (w) -> new EntityMinecartChest(w), +~ EntityMinecart.EnumMinecartType.CHEST.getName(), 43); +~ addMapping(EntityMinecartFurnace.class, (w) -> new EntityMinecartFurnace(w), +~ EntityMinecart.EnumMinecartType.FURNACE.getName(), 44); +~ addMapping(EntityMinecartTNT.class, (w) -> new EntityMinecartTNT(w), +~ EntityMinecart.EnumMinecartType.TNT.getName(), 45); +~ addMapping(EntityMinecartHopper.class, (w) -> new EntityMinecartHopper(w), +~ EntityMinecart.EnumMinecartType.HOPPER.getName(), 46); +~ addMapping(EntityMinecartMobSpawner.class, (w) -> new EntityMinecartMobSpawner(w), +~ EntityMinecart.EnumMinecartType.SPAWNER.getName(), 47); +~ addMapping(EntityMinecartCommandBlock.class, (w) -> new EntityMinecartCommandBlock(w), +~ EntityMinecart.EnumMinecartType.COMMAND_BLOCK.getName(), 40); +~ addMapping(EntityLiving.class, null, "Mob", 48); +~ addMapping(EntityMob.class, null, "Monster", 49); +~ addMapping(EntityCreeper.class, (w) -> new EntityCreeper(w), "Creeper", 50, 894731, 0); +~ addMapping(EntitySkeleton.class, (w) -> new EntitySkeleton(w), "Skeleton", 51, 12698049, 4802889); +~ addMapping(EntitySpider.class, (w) -> new EntitySpider(w), "Spider", 52, 3419431, 11013646); +~ addMapping(EntityGiantZombie.class, (w) -> new EntityGiantZombie(w), "Giant", 53); +~ addMapping(EntityZombie.class, (w) -> new EntityZombie(w), "Zombie", 54, '\uafaf', 7969893); +~ addMapping(EntitySlime.class, (w) -> new EntitySlime(w), "Slime", 55, 5349438, 8306542); +~ addMapping(EntityGhast.class, (w) -> new EntityGhast(w), "Ghast", 56, 16382457, 12369084); +~ addMapping(EntityPigZombie.class, (w) -> new EntityPigZombie(w), "PigZombie", 57, 15373203, 5009705); +~ addMapping(EntityEnderman.class, (w) -> new EntityEnderman(w), "Enderman", 58, 1447446, 0); +~ addMapping(EntityCaveSpider.class, (w) -> new EntityCaveSpider(w), "CaveSpider", 59, 803406, 11013646); +~ addMapping(EntitySilverfish.class, (w) -> new EntitySilverfish(w), "Silverfish", 60, 7237230, 3158064); +~ addMapping(EntityBlaze.class, (w) -> new EntityBlaze(w), "Blaze", 61, 16167425, 16775294); +~ addMapping(EntityMagmaCube.class, (w) -> new EntityMagmaCube(w), "LavaSlime", 62, 3407872, 16579584); +~ addMapping(EntityDragon.class, (w) -> new EntityDragon(w), "EnderDragon", 63); +~ addMapping(EntityWither.class, (w) -> new EntityWither(w), "WitherBoss", 64); +~ addMapping(EntityBat.class, (w) -> new EntityBat(w), "Bat", 65, 4996656, 986895); +~ addMapping(EntityWitch.class, (w) -> new EntityWitch(w), "Witch", 66, 3407872, 5349438); +~ addMapping(EntityEndermite.class, (w) -> new EntityEndermite(w), "Endermite", 67, 1447446, 7237230); +~ addMapping(EntityGuardian.class, (w) -> new EntityGuardian(w), "Guardian", 68, 5931634, 15826224); +~ addMapping(EntityPig.class, (w) -> new EntityPig(w), "Pig", 90, 15771042, 14377823); +~ addMapping(EntitySheep.class, (w) -> new EntitySheep(w), "Sheep", 91, 15198183, 16758197); +~ addMapping(EntityCow.class, (w) -> new EntityCow(w), "Cow", 92, 4470310, 10592673); +~ addMapping(EntityChicken.class, (w) -> new EntityChicken(w), "Chicken", 93, 10592673, 16711680); +~ addMapping(EntitySquid.class, (w) -> new EntitySquid(w), "Squid", 94, 2243405, 7375001); +~ addMapping(EntityWolf.class, (w) -> new EntityWolf(w), "Wolf", 95, 14144467, 13545366); +~ addMapping(EntityMooshroom.class, (w) -> new EntityMooshroom(w), "MushroomCow", 96, 10489616, 12040119); +~ addMapping(EntitySnowman.class, (w) -> new EntitySnowman(w), "SnowMan", 97); +~ addMapping(EntityOcelot.class, (w) -> new EntityOcelot(w), "Ozelot", 98, 15720061, 5653556); +~ addMapping(EntityIronGolem.class, (w) -> new EntityIronGolem(w), "VillagerGolem", 99); +~ addMapping(EntityHorse.class, (w) -> new EntityHorse(w), "EntityHorse", 100, 12623485, 15656192); +~ addMapping(EntityRabbit.class, (w) -> new EntityRabbit(w), "Rabbit", 101, 10051392, 7555121); +~ addMapping(EntityVillager.class, (w) -> new EntityVillager(w), "Villager", 120, 5651507, 12422002); +~ addMapping(EntityEnderCrystal.class, (w) -> new EntityEnderCrystal(w), "EnderCrystal", 200); + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityLiving.edit.java b/patches/minecraft/net/minecraft/entity/EntityLiving.edit.java new file mode 100644 index 0000000..9a9375d --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityLiving.edit.java @@ -0,0 +1,61 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> DELETE 5 @ 4 : 16 + +> DELETE 22 @ 33 : 36 + +> DELETE 29 @ 43 : 44 + +> DELETE 33 @ 48 : 51 + +> DELETE 34 @ 52 : 55 + +> DELETE 35 @ 56 : 57 + +> DELETE 45 @ 67 : 73 + +> DELETE 46 @ 74 : 81 + +> DELETE 53 @ 88 : 112 + +> CHANGE 114 : 124 @ 173 : 188 + +~ for (int i = 0; i < 20; ++i) { +~ double d0 = this.rand.nextGaussian() * 0.02D; +~ double d1 = this.rand.nextGaussian() * 0.02D; +~ double d2 = this.rand.nextGaussian() * 0.02D; +~ double d3 = 10.0D; +~ this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_NORMAL, +~ this.posX + (double) (this.rand.nextFloat() * this.width * 2.0F) - (double) this.width - d0 * d3, +~ this.posY + (double) (this.rand.nextFloat() * this.height) - d1 * d3, +~ this.posZ + (double) (this.rand.nextFloat() * this.width * 2.0F) - (double) this.width - d2 * d3, +~ d0, d1, d2, new int[0]); + +> DELETE 125 @ 189 : 190 + +> DELETE 136 @ 201 : 209 + +> DELETE 249 @ 322 : 338 + +> DELETE 344 @ 433 : 461 + +> DELETE 672 @ 789 : 797 + +> DELETE 691 @ 816 : 821 + +> CHANGE 696 : 698 @ 826 : 827 + +~ EaglercraftUUID uuid = new EaglercraftUUID(this.leashNBTTag.getLong("UUIDMost"), +~ this.leashNBTTag.getLong("UUIDLeast")); + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java b/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java new file mode 100644 index 0000000..7f44b2a --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityLivingBase.edit.java @@ -0,0 +1,111 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 6 : 13 @ 9 : 11 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import com.google.common.base.Predicate; +~ import com.google.common.base.Predicates; +~ import com.google.common.collect.Maps; +~ + +> DELETE 15 @ 13 : 14 + +> DELETE 16 @ 15 : 19 + +> DELETE 21 @ 24 : 27 + +> DELETE 22 @ 28 : 30 + +> DELETE 31 @ 39 : 42 + +> DELETE 44 @ 55 : 56 + +> CHANGE 46 : 48 @ 58 : 59 + +~ private static final EaglercraftUUID sprintingSpeedBoostModifierUUID = EaglercraftUUID +~ .fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); + +> DELETE 141 @ 152 : 169 + +> CHANGE 166 : 167 @ 194 : 197 + +~ this.extinguish(); + +> DELETE 189 @ 219 : 222 + +> CHANGE 203 : 204 @ 236 : 237 + +~ if (this.hurtResistantTime > 0) { + +> DELETE 245 @ 278 : 281 + +> DELETE 246 @ 282 : 290 + +> CHANGE 279 : 280 @ 323 : 324 + +~ public EaglercraftRandom getRNG() { + +> DELETE 354 @ 398 : 401 + +> CHANGE 391 : 392 @ 438 : 444 + +~ if (potioneffect.onUpdate(this) && potioneffect.getDuration() % 600 == 0) { + +> CHANGE 396 : 397 @ 448 : 452 + +~ this.potionsNeedUpdate = false; + +> DELETE 398 @ 453 : 456 + +> DELETE 445 @ 503 : 504 + +> DELETE 446 @ 505 : 514 + +> DELETE 508 @ 576 : 581 + +> DELETE 512 @ 585 : 592 + +> DELETE 516 @ 596 : 601 + +> CHANGE 535 : 536 @ 620 : 713 + +~ return false; + +> DELETE 569 @ 746 : 751 + +> DELETE 570 @ 752 : 761 + +> DELETE 760 @ 951 : 955 + +> DELETE 819 @ 1014 : 1015 + +> CHANGE 973 : 976 @ 1169 : 1174 + +~ if (!this.worldObj.isBlockLoaded(new BlockPos((int) this.posX, 0, (int) this.posZ)) +~ || !this.worldObj.getChunkFromBlockCoords(new BlockPos((int) this.posX, 0, (int) this.posZ)) +~ .isLoaded()) { + +> DELETE 1063 @ 1261 : 1297 + +> DELETE 1221 @ 1455 : 1459 + +> DELETE 1250 @ 1488 : 1492 + +> DELETE 1281 @ 1523 : 1529 + +> DELETE 1282 @ 1530 : 1541 + +> CHANGE 1313 : 1314 @ 1572 : 1573 + +~ return false; + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.edit.java b/patches/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.edit.java new file mode 100644 index 0000000..9d1c038 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/EntitySpawnPlacementRegistry.edit.java b/patches/minecraft/net/minecraft/entity/EntitySpawnPlacementRegistry.edit.java new file mode 100644 index 0000000..c34ac5b --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/EntitySpawnPlacementRegistry.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 5 + +~ +~ import com.google.common.collect.Maps; +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/IEntityOwnable.edit.java b/patches/minecraft/net/minecraft/entity/IEntityOwnable.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/IEntityOwnable.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/IRangedAttackMob.edit.java b/patches/minecraft/net/minecraft/entity/IRangedAttackMob.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/IRangedAttackMob.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/NpcMerchant.edit.java b/patches/minecraft/net/minecraft/entity/NpcMerchant.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/NpcMerchant.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/SharedMonsterAttributes.edit.java b/patches/minecraft/net/minecraft/entity/SharedMonsterAttributes.edit.java new file mode 100644 index 0000000..bbf5a6d --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/SharedMonsterAttributes.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 7 @ 3 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 14 @ 11 : 13 + +> CHANGE 47 : 48 @ 46 : 47 + +~ for (AttributeModifier attributemodifier : (Collection) collection) { + +> CHANGE 105 : 107 @ 104 : 105 + +~ EaglercraftUUID uuid = new EaglercraftUUID(parNBTTagCompound.getLong("UUIDMost"), +~ parNBTTagCompound.getLong("UUIDLeast")); + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/AttributeModifier.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/AttributeModifier.edit.java new file mode 100644 index 0000000..382b1d0 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/AttributeModifier.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> INSERT 6 : 9 @ 7 + ++ import net.lax1dude.eaglercraft.v1_8.ThreadLocalRandom; ++ import net.minecraft.util.MathHelper; ++ + +> CHANGE 13 : 14 @ 11 : 12 + +~ private final EaglercraftUUID id; + +> CHANGE 20 : 21 @ 18 : 19 + +~ public AttributeModifier(EaglercraftUUID idIn, String nameIn, double amountIn, int operationIn) { + +> CHANGE 30 : 31 @ 28 : 29 + +~ public EaglercraftUUID getID() { + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttribute.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttribute.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttribute.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttributeMap.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttributeMap.edit.java new file mode 100644 index 0000000..ae743c7 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/BaseAttributeMap.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 5 : 10 @ 8 : 11 + +~ +~ import com.google.common.collect.HashMultimap; +~ import com.google.common.collect.Maps; +~ import com.google.common.collect.Multimap; +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/IAttributeInstance.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/IAttributeInstance.edit.java new file mode 100644 index 0000000..a2764ec --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/IAttributeInstance.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +> CHANGE 18 : 19 @ 20 : 21 + +~ AttributeModifier getModifier(EaglercraftUUID var1); + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.edit.java new file mode 100644 index 0000000..c043dcb --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/ModifiableAttributeInstance.edit.java @@ -0,0 +1,41 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 4 : 5 @ 7 + ++ import java.util.List; + +> CHANGE 7 : 8 @ 9 : 14 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +> INSERT 9 : 13 @ 15 + ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ import com.google.common.collect.Sets; ++ + +> CHANGE 18 : 19 @ 20 : 21 + +~ private final Map mapByUUID = Maps.newHashMap(); + +> CHANGE 63 : 64 @ 65 : 66 + +~ public AttributeModifier getModifier(EaglercraftUUID uuid) { + +> CHANGE 75 : 76 @ 77 : 78 + +~ Set object = (Set) this.mapByName.get(attributemodifier.getName()); + +> CHANGE 114 : 115 @ 116 : 117 + +~ for (AttributeModifier attributemodifier : (List) Lists.newArrayList(collection)) { + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/ai/attributes/RangedAttribute.edit.java b/patches/minecraft/net/minecraft/entity/ai/attributes/RangedAttribute.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/ai/attributes/RangedAttribute.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/boss/BossStatus.edit.java b/patches/minecraft/net/minecraft/entity/boss/BossStatus.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/boss/BossStatus.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/boss/EntityDragon.edit.java b/patches/minecraft/net/minecraft/entity/boss/EntityDragon.edit.java new file mode 100644 index 0000000..4d70747 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/boss/EntityDragon.edit.java @@ -0,0 +1,72 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 3 : 4 @ 6 : 9 + +~ + +> DELETE 6 @ 11 : 12 + +> DELETE 8 @ 14 : 16 + +> DELETE 9 @ 17 : 18 + +> DELETE 11 @ 20 : 23 + +> DELETE 13 @ 25 : 26 + +> DELETE 15 @ 28 : 30 + +> CHANGE 86 : 91 @ 101 : 108 + +~ float f = MathHelper.cos(this.animTime * 3.1415927F * 2.0F); +~ float f1 = MathHelper.cos(this.prevAnimTime * 3.1415927F * 2.0F); +~ if (f1 <= -0.3F && f >= -0.3F && !this.isSilent()) { +~ this.worldObj.playSound(this.posX, this.posY, this.posZ, "mob.enderdragon.wings", 5.0F, +~ 0.8F + this.rand.nextFloat() * 0.3F, false); + +> CHANGE 129 : 142 @ 146 : 240 + +~ if (this.newPosRotationIncrements > 0) { +~ double d10 = this.posX + (this.newPosX - this.posX) / (double) this.newPosRotationIncrements; +~ double d0 = this.posY + (this.newPosY - this.posY) / (double) this.newPosRotationIncrements; +~ double d1 = this.posZ + (this.newPosZ - this.posZ) / (double) this.newPosRotationIncrements; +~ double d2 = MathHelper.wrapAngleTo180_double(this.newRotationYaw - (double) this.rotationYaw); +~ this.rotationYaw = (float) ((double) this.rotationYaw +~ + d2 / (double) this.newPosRotationIncrements); +~ this.rotationPitch = (float) ((double) this.rotationPitch +~ + (this.newRotationPitch - (double) this.rotationPitch) +~ / (double) this.newPosRotationIncrements); +~ --this.newPosRotationIncrements; +~ this.setPosition(d10, d0, d1); +~ this.setRotation(this.rotationYaw, this.rotationPitch); + +> DELETE 171 @ 269 : 279 + +> DELETE 208 @ 316 : 321 + +> DELETE 215 @ 328 : 333 + +> CHANGE 228 : 229 @ 346 : 347 + +~ for (EntityEnderCrystal entityendercrystal1 : (List) list) { + +> DELETE 241 @ 359 : 421 + +> DELETE 245 @ 425 : 466 + +> DELETE 290 @ 511 : 529 + +> DELETE 292 @ 531 : 534 + +> DELETE 293 @ 535 : 547 + +> DELETE 295 @ 549 : 592 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/boss/EntityWither.edit.java b/patches/minecraft/net/minecraft/entity/boss/EntityWither.edit.java new file mode 100644 index 0000000..47be141 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/boss/EntityWither.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import java.util.List; ++ + +> CHANGE 6 : 7 @ 4 : 5 + +~ + +> DELETE 10 @ 8 : 9 + +> DELETE 14 @ 13 : 21 + +> DELETE 22 @ 29 : 30 + +> DELETE 23 @ 31 : 32 + +> DELETE 51 @ 60 : 69 + +> DELETE 86 @ 104 : 111 + +> DELETE 87 @ 112 : 126 + +> DELETE 398 @ 437 : 444 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/effect/EntityLightningBolt.edit.java b/patches/minecraft/net/minecraft/entity/effect/EntityLightningBolt.edit.java new file mode 100644 index 0000000..1d018e2 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/effect/EntityLightningBolt.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> DELETE 3 @ 8 : 11 + +> DELETE 16 @ 24 : 43 + +> DELETE 35 @ 62 : 69 + +> CHANGE 39 : 40 @ 73 : 85 + +~ this.worldObj.setLastLightningBolt(2); + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityArmorStand.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityArmorStand.edit.java new file mode 100644 index 0000000..352401e --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityArmorStand.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> DELETE 7 @ 7 : 8 + +> DELETE 8 @ 9 : 14 + +> DELETE 12 @ 18 : 19 + +> DELETE 13 @ 20 : 21 + +> DELETE 16 @ 24 : 25 + +> DELETE 264 @ 273 : 336 + +> DELETE 269 @ 341 : 368 + +> CHANGE 270 : 271 @ 369 : 424 + +~ return false; + +> DELETE 283 @ 436 : 476 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityBoat.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityBoat.edit.java new file mode 100644 index 0000000..57611e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityBoat.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 8 @ 9 : 11 + +> DELETE 11 @ 14 : 16 + +> DELETE 73 @ 78 : 111 + +> CHANGE 182 : 183 @ 220 : 221 + +~ if (this.isBoatEmpty) { + +> CHANGE 276 : 277 @ 314 : 328 + +~ if (!(this.isCollidedHorizontally && d9 > 0.2975D)) { + +> DELETE 301 @ 352 : 369 + +> CHANGE 320 : 321 @ 388 : 398 + +~ return true; + +> DELETE 327 @ 404 : 417 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityEnderCrystal.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityEnderCrystal.edit.java new file mode 100644 index 0000000..7af5040 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityEnderCrystal.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> DELETE 61 @ 62 : 79 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityEnderEye.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityEnderEye.edit.java new file mode 100644 index 0000000..bebc520 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityEnderEye.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> DELETE 108 @ 111 : 121 + +> DELETE 109 @ 122 : 131 + +> DELETE 124 @ 146 : 160 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityEnderPearl.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityEnderPearl.edit.java new file mode 100644 index 0000000..7964db9 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityEnderPearl.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 5 + +> DELETE 4 @ 6 : 7 + +> DELETE 42 @ 45 : 74 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityExpBottle.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityExpBottle.edit.java new file mode 100644 index 0000000..f4b55b3 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityExpBottle.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> DELETE 4 @ 5 : 6 + +> DELETE 33 @ 35 : 38 + +> DELETE 34 @ 39 : 48 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityFallingBlock.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityFallingBlock.edit.java new file mode 100644 index 0000000..a290505 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityFallingBlock.edit.java @@ -0,0 +1,35 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 7 @ 4 + ++ import java.util.List; ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 9 @ 6 : 8 + +> DELETE 14 @ 13 : 15 + +> DELETE 15 @ 16 : 17 + +> DELETE 17 @ 19 : 20 + +> DELETE 72 @ 75 : 78 + +> DELETE 80 @ 86 : 101 + +> DELETE 81 @ 102 : 135 + +> CHANGE 94 : 95 @ 148 : 149 + +~ for (Entity entity : (List) arraylist) { + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityFireworkRocket.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityFireworkRocket.edit.java new file mode 100644 index 0000000..26d65fa --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityFireworkRocket.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 99 : 100 @ 99 : 100 + +~ if (this.fireworkAge % 2 < 2) { + +> DELETE 105 @ 105 : 110 + +> CHANGE 108 : 109 @ 113 : 114 + +~ if (b0 == 17) { + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityItem.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityItem.edit.java new file mode 100644 index 0000000..6bd3d95 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityItem.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 9 @ 7 : 8 + +> DELETE 11 @ 10 : 11 + +> DELETE 16 @ 16 : 18 + +> DELETE 84 @ 86 : 90 + +> DELETE 105 @ 111 : 114 + +> DELETE 241 @ 250 : 259 + +> DELETE 242 @ 260 : 295 + +> DELETE 253 @ 306 : 314 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityItemFrame.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityItemFrame.edit.java new file mode 100644 index 0000000..eda1ae5 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityItemFrame.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 41 @ 41 : 46 + +> DELETE 167 @ 172 : 185 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecart.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecart.edit.java new file mode 100644 index 0000000..8604f5c --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecart.edit.java @@ -0,0 +1,50 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ + +> DELETE 14 @ 12 : 19 + +> DELETE 18 @ 23 : 24 + +> DELETE 29 @ 35 : 36 + +> CHANGE 114 : 115 @ 121 : 148 + +~ return true; + +> CHANGE 157 : 168 @ 190 : 245 + +~ if (this.turnProgress > 0) { +~ double d4 = this.posX + (this.minecartX - this.posX) / (double) this.turnProgress; +~ double d5 = this.posY + (this.minecartY - this.posY) / (double) this.turnProgress; +~ double d6 = this.posZ + (this.minecartZ - this.posZ) / (double) this.turnProgress; +~ double d1 = MathHelper.wrapAngleTo180_double(this.minecartYaw - (double) this.rotationYaw); +~ this.rotationYaw = (float) ((double) this.rotationYaw + d1 / (double) this.turnProgress); +~ this.rotationPitch = (float) ((double) this.rotationPitch +~ + (this.minecartPitch - (double) this.rotationPitch) / (double) this.turnProgress); +~ --this.turnProgress; +~ this.setPosition(d4, d5, d6); +~ this.setRotation(this.rotationYaw, this.rotationPitch); + +> CHANGE 169 : 170 @ 246 : 286 + +~ this.setPosition(this.posX, this.posY, this.posZ); + +> DELETE 171 @ 287 : 304 + +> DELETE 522 @ 655 : 665 + +> DELETE 523 @ 666 : 733 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartChest.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartChest.edit.java new file mode 100644 index 0000000..a095108 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartChest.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartContainer.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartContainer.edit.java new file mode 100644 index 0000000..4b31f10 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartContainer.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 144 @ 145 : 149 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartEmpty.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartEmpty.edit.java new file mode 100644 index 0000000..3f565a4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartEmpty.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> DELETE 22 @ 23 : 27 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartFurnace.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartFurnace.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartFurnace.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartHopper.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartHopper.edit.java new file mode 100644 index 0000000..008836c --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartHopper.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 7 + +> DELETE 49 @ 51 : 55 + +> DELETE 84 @ 90 : 111 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityMinecartTNT.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityMinecartTNT.edit.java new file mode 100644 index 0000000..5f3124b --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityMinecartTNT.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> DELETE 80 @ 81 : 86 + +> DELETE 81 @ 87 : 92 + +> DELETE 110 @ 121 : 128 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityPainting.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityPainting.edit.java new file mode 100644 index 0000000..a620c08 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityPainting.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityTNTPrimed.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityTNTPrimed.edit.java new file mode 100644 index 0000000..3a572e2 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityTNTPrimed.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 61 @ 61 : 64 + +> DELETE 69 @ 72 : 77 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/item/EntityXPOrb.edit.java b/patches/minecraft/net/minecraft/entity/item/EntityXPOrb.edit.java new file mode 100644 index 0000000..236f66c --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/item/EntityXPOrb.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 160 @ 160 : 174 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityBlaze.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityBlaze.edit.java new file mode 100644 index 0000000..e5ca398 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityBlaze.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 14 + +> DELETE 6 @ 16 : 17 + +> DELETE 8 @ 19 : 20 + +> DELETE 18 @ 30 : 37 + +> CHANGE 57 : 61 @ 76 : 81 + +~ if (this.rand.nextInt(24) == 0 && !this.isSilent()) { +~ this.worldObj.playSound(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "fire.fire", +~ 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F, false); +~ } + +> CHANGE 62 : 67 @ 82 : 89 + +~ for (int i = 0; i < 2; ++i) { +~ this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, +~ this.posX + (this.rand.nextDouble() - 0.5D) * (double) this.width, +~ this.posY + this.rand.nextDouble() * (double) this.height, +~ this.posZ + (this.rand.nextDouble() - 0.5D) * (double) this.width, 0.0D, 0.0D, 0.0D, new int[0]); + +> DELETE 134 @ 156 : 234 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityCaveSpider.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityCaveSpider.edit.java new file mode 100644 index 0000000..585f95e --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityCaveSpider.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityCreeper.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityCreeper.edit.java new file mode 100644 index 0000000..0a08400 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityCreeper.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 13 + +> DELETE 5 @ 14 : 17 + +> DELETE 22 @ 34 : 43 + +> DELETE 96 @ 117 : 118 + +> DELETE 162 @ 184 : 189 + +> DELETE 167 @ 194 : 205 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityEnderman.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityEnderman.edit.java new file mode 100644 index 0000000..80763c4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityEnderman.edit.java @@ -0,0 +1,62 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> CHANGE 3 : 7 @ 8 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import com.google.common.collect.Sets; +~ + +> DELETE 8 @ 10 : 11 + +> DELETE 12 @ 15 : 23 + +> DELETE 13 @ 24 : 27 + +> DELETE 14 @ 28 : 29 + +> DELETE 17 @ 32 : 33 + +> DELETE 23 @ 39 : 40 + +> CHANGE 27 : 29 @ 44 : 45 + +~ private static final EaglercraftUUID attackingSpeedBoostModifierUUID = EaglercraftUUID +~ .fromString("020E0DFB-87AE-4653-9556-831010E291A0"); + +> DELETE 38 @ 54 : 69 + +> DELETE 76 @ 107 : 123 + +> CHANGE 81 : 88 @ 128 : 137 + +~ for (int i = 0; i < 2; ++i) { +~ this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, +~ this.posX + (this.rand.nextDouble() - 0.5D) * (double) this.width, +~ this.posY + this.rand.nextDouble() * (double) this.height - 0.25D, +~ this.posZ + (this.rand.nextDouble() - 0.5D) * (double) this.width, +~ (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), +~ (this.rand.nextDouble() - 0.5D) * 2.0D, new int[0]); + +> DELETE 89 @ 138 : 139 + +> DELETE 232 @ 282 : 285 + +> CHANGE 234 : 235 @ 287 : 293 + +~ this.isAggressive = true; + +> CHANGE 267 : 268 @ 325 : 326 + +~ public static void bootstrap() { + +> DELETE 284 @ 342 : 493 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityEndermite.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityEndermite.edit.java new file mode 100644 index 0000000..164676b --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityEndermite.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 13 + +> DELETE 20 @ 28 : 35 + +> CHANGE 84 : 91 @ 99 : 116 + +~ for (int i = 0; i < 2; ++i) { +~ this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, +~ this.posX + (this.rand.nextDouble() - 0.5D) * (double) this.width, +~ this.posY + this.rand.nextDouble() * (double) this.height, +~ this.posZ + (this.rand.nextDouble() - 0.5D) * (double) this.width, +~ (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), +~ (this.rand.nextDouble() - 0.5D) * 2.0D, new int[0]); + +> DELETE 92 @ 117 : 118 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityGhast.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityGhast.edit.java new file mode 100644 index 0000000..25ba236 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityGhast.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 5 + +> DELETE 4 @ 6 : 10 + +> DELETE 5 @ 11 : 12 + +> DELETE 9 @ 16 : 18 + +> DELETE 10 @ 19 : 21 + +> DELETE 21 @ 32 : 37 + +> DELETE 35 @ 51 : 59 + +> DELETE 120 @ 144 : 311 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityGiantZombie.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityGiantZombie.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityGiantZombie.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityGuardian.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityGuardian.edit.java new file mode 100644 index 0000000..77195d4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityGuardian.edit.java @@ -0,0 +1,126 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 6 @ 7 : 19 + +> DELETE 12 @ 25 : 30 + +> DELETE 15 @ 33 : 34 + +> DELETE 18 @ 37 : 38 + +> DELETE 29 @ 49 : 50 + +> DELETE 34 @ 55 : 67 + +> DELETE 55 @ 88 : 92 + +> DELETE 79 @ 116 : 120 + +> DELETE 95 @ 136 : 137 + +> DELETE 104 @ 146 : 150 + +> CHANGE 111 : 112 @ 157 : 158 + +~ } else { + +> DELETE 123 @ 169 : 171 + +> CHANGE 173 : 178 @ 221 : 239 + +~ this.field_175484_c = this.field_175482_b; +~ if (!this.isInWater()) { +~ this.field_175483_bk = 2.0F; +~ if (this.motionY > 0.0D && this.field_175480_bp && !this.isSilent()) { +~ this.worldObj.playSound(this.posX, this.posY, this.posZ, "mob.guardian.flop", 1.0F, 1.0F, false); + +> CHANGE 180 : 185 @ 241 : 247 + +~ this.field_175480_bp = this.motionY < 0.0D +~ && this.worldObj.isBlockNormalCube((new BlockPos(this)).down(), false); +~ } else if (this.func_175472_n()) { +~ if (this.field_175483_bk < 0.5F) { +~ this.field_175483_bk = 4.0F; + +> CHANGE 186 : 187 @ 248 : 249 + +~ this.field_175483_bk += (0.5F - this.field_175483_bk) * 0.1F; + +> INSERT 188 : 191 @ 250 + ++ } else { ++ this.field_175483_bk += (0.125F - this.field_175483_bk) * 0.2F; ++ } + +> CHANGE 192 : 201 @ 251 : 253 + +~ this.field_175482_b += this.field_175483_bk; +~ this.field_175486_bm = this.field_175485_bl; +~ if (!this.isInWater()) { +~ this.field_175485_bl = this.rand.nextFloat(); +~ } else if (this.func_175472_n()) { +~ this.field_175485_bl += (0.0F - this.field_175485_bl) * 0.25F; +~ } else { +~ this.field_175485_bl += (1.0F - this.field_175485_bl) * 0.06F; +~ } + +> CHANGE 202 : 211 @ 254 : 261 + +~ if (this.func_175472_n() && this.isInWater()) { +~ Vec3 vec3 = this.getLook(0.0F); +~ +~ for (int i = 0; i < 2; ++i) { +~ this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, +~ this.posX + (this.rand.nextDouble() - 0.5D) * (double) this.width - vec3.xCoord * 1.5D, +~ this.posY + this.rand.nextDouble() * (double) this.height - vec3.yCoord * 1.5D, +~ this.posZ + (this.rand.nextDouble() - 0.5D) * (double) this.width - vec3.zCoord * 1.5D, 0.0D, +~ 0.0D, 0.0D, new int[0]); + +> INSERT 212 : 213 @ 262 + ++ } + +> CHANGE 214 : 218 @ 263 : 267 + +~ if (this.hasTargetedEntity()) { +~ if (this.field_175479_bo < this.func_175464_ck()) { +~ ++this.field_175479_bo; +~ } + +> CHANGE 219 : 231 @ 268 : 282 + +~ EntityLivingBase entitylivingbase = this.getTargetedEntity(); +~ if (entitylivingbase != null) { +~ double d5 = (double) this.func_175477_p(0.0F); +~ double d0 = entitylivingbase.posX - this.posX; +~ double d1 = entitylivingbase.posY + (double) (entitylivingbase.height * 0.5F) +~ - (this.posY + (double) this.getEyeHeight()); +~ double d2 = entitylivingbase.posZ - this.posZ; +~ double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2); +~ d0 = d0 / d3; +~ d1 = d1 / d3; +~ d2 = d2 / d3; +~ double d4 = this.rand.nextDouble(); + +> CHANGE 232 : 237 @ 283 : 289 + +~ while (d4 < d3) { +~ d4 += 1.8D - d5 + this.rand.nextDouble() * (1.7D - d5); +~ this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX + d0 * d4, +~ this.posY + d1 * d4 + (double) this.getEyeHeight(), this.posZ + d2 * d4, 0.0D, 0.0D, 0.0D, +~ new int[0]); + +> DELETE 271 @ 323 : 356 + +> DELETE 319 @ 404 : 405 + +> DELETE 346 @ 432 : 568 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityIronGolem.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityIronGolem.edit.java new file mode 100644 index 0000000..2e9d84c --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityIronGolem.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 7 @ 8 : 10 + +> DELETE 9 @ 12 : 26 + +> DELETE 14 @ 31 : 32 + +> DELETE 18 @ 36 : 37 + +> DELETE 22 @ 41 : 42 + +> DELETE 28 @ 48 : 61 + +> DELETE 35 @ 68 : 83 + +> DELETE 122 @ 170 : 174 + +> DELETE 176 @ 228 : 275 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityMagmaCube.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityMagmaCube.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityMagmaCube.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityMob.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityMob.edit.java new file mode 100644 index 0000000..9d1fcec --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityMob.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 8 + +> DELETE 30 @ 31 : 39 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityPigZombie.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityPigZombie.edit.java new file mode 100644 index 0000000..cd66313 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityPigZombie.edit.java @@ -0,0 +1,35 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> DELETE 5 @ 4 : 5 + +> DELETE 8 @ 8 : 10 + +> DELETE 10 @ 12 : 13 + +> CHANGE 20 : 21 @ 23 : 24 + +~ private static final EaglercraftUUID ATTACK_SPEED_BOOST_MODIFIER_UUID = EaglercraftUUID + +> CHANGE 26 : 27 @ 29 : 30 + +~ private EaglercraftUUID angerTargetUUID; + +> DELETE 41 @ 44 : 49 + +> CHANGE 105 : 106 @ 113 : 114 + +~ this.angerTargetUUID = EaglercraftUUID.fromString(s); + +> DELETE 188 @ 196 : 219 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySilverfish.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySilverfish.edit.java new file mode 100644 index 0000000..26f4bf6 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySilverfish.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 6 + +> DELETE 5 @ 8 : 15 + +> DELETE 9 @ 19 : 22 + +> DELETE 12 @ 25 : 26 + +> DELETE 16 @ 30 : 36 + +> DELETE 49 @ 69 : 81 + +> DELETE 84 @ 116 : 225 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySkeleton.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySkeleton.edit.java new file mode 100644 index 0000000..e9218ae --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySkeleton.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 14 @ 13 : 28 + +> DELETE 28 @ 42 : 43 + +> DELETE 30 @ 45 : 48 + +> DELETE 33 @ 51 : 65 + +> DELETE 78 @ 110 : 124 + +> CHANGE 79 : 80 @ 125 : 135 + +~ if (this.getSkeletonType() == 1) { + +> DELETE 156 @ 211 : 221 + +> DELETE 171 @ 236 : 245 + +> DELETE 227 @ 301 : 309 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySlime.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySlime.edit.java new file mode 100644 index 0000000..7f89bd0 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySlime.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 13 + +> DELETE 11 @ 17 : 18 + +> DELETE 30 @ 37 : 44 + +> DELETE 78 @ 92 : 97 + +> DELETE 137 @ 156 : 183 + +> DELETE 246 @ 292 : 450 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySnowman.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySnowman.edit.java new file mode 100644 index 0000000..21ef55d --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySnowman.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 5 @ 7 : 15 + +> DELETE 6 @ 16 : 17 + +> DELETE 8 @ 19 : 22 + +> DELETE 15 @ 29 : 36 + +> DELETE 23 @ 44 : 75 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntitySpider.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntitySpider.edit.java new file mode 100644 index 0000000..3d66f4c --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntitySpider.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 5 @ 4 : 6 + +> DELETE 8 @ 9 : 21 + +> DELETE 10 @ 23 : 25 + +> DELETE 21 @ 36 : 46 + +> DELETE 27 @ 52 : 56 + +> DELETE 32 @ 61 : 69 + +> DELETE 129 @ 166 : 197 + +> CHANGE 132 : 133 @ 200 : 201 + +~ public void func_111104_a(EaglercraftRandom rand) { + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityWitch.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityWitch.edit.java new file mode 100644 index 0000000..6102d0f --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityWitch.edit.java @@ -0,0 +1,30 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> DELETE 7 @ 8 : 15 + +> DELETE 8 @ 16 : 19 + +> DELETE 11 @ 22 : 23 + +> DELETE 12 @ 24 : 25 + +> CHANGE 18 : 20 @ 31 : 32 + +~ private static final EaglercraftUUID MODIFIER_UUID = EaglercraftUUID +~ .fromString("5CD17E52-A79A-43D3-A529-90FDE04B181E"); + +> DELETE 29 @ 41 : 48 + +> DELETE 62 @ 81 : 138 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/EntityZombie.edit.java b/patches/minecraft/net/minecraft/entity/monster/EntityZombie.edit.java new file mode 100644 index 0000000..df8a0c1 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/EntityZombie.edit.java @@ -0,0 +1,48 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 4 : 6 @ 4 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> DELETE 13 @ 12 : 22 + +> DELETE 15 @ 24 : 25 + +> DELETE 16 @ 26 : 30 + +> DELETE 24 @ 38 : 39 + +> CHANGE 37 : 39 @ 52 : 53 + +~ private static final EaglercraftUUID babySpeedBoostUUID = EaglercraftUUID +~ .fromString("B9766B59-9566-4402-BC1F-2EE2A276D836"); + +> DELETE 41 @ 55 : 56 + +> DELETE 48 @ 63 : 71 + +> DELETE 51 @ 74 : 84 + +> DELETE 80 @ 113 : 125 + +> DELETE 94 @ 139 : 147 + +> DELETE 105 @ 158 : 190 + +> DELETE 158 @ 243 : 255 + +> DELETE 248 @ 345 : 347 + +> DELETE 334 @ 433 : 434 + +> DELETE 358 @ 458 : 459 + +> DELETE 375 @ 476 : 480 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/monster/IMob.edit.java b/patches/minecraft/net/minecraft/entity/monster/IMob.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/monster/IMob.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityAmbientCreature.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityAmbientCreature.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityAmbientCreature.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityAnimal.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityAnimal.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityAnimal.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityBat.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityBat.edit.java new file mode 100644 index 0000000..75f2f1f --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityBat.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 7 @ 6 : 7 + +> DELETE 10 @ 10 : 11 + +> DELETE 151 @ 152 : 164 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityChicken.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityChicken.edit.java new file mode 100644 index 0000000..4eef49b --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityChicken.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 15 + +> DELETE 28 @ 37 : 45 + +> DELETE 56 @ 73 : 79 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityCow.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityCow.edit.java new file mode 100644 index 0000000..16ac9ed --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityCow.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 14 + +> DELETE 9 @ 18 : 19 + +> DELETE 16 @ 26 : 35 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityHorse.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityHorse.edit.java new file mode 100644 index 0000000..1880ec5 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityHorse.edit.java @@ -0,0 +1,56 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 11 @ 10 : 18 + +> DELETE 14 @ 21 : 22 + +> DELETE 24 @ 32 : 33 + +> DELETE 25 @ 34 : 35 + +> DELETE 84 @ 94 : 103 + +> CHANGE 296 : 297 @ 315 : 319 + +~ + +> DELETE 358 @ 380 : 386 + +> DELETE 573 @ 601 : 606 + +> DELETE 719 @ 752 : 755 + +> DELETE 753 @ 789 : 797 + +> DELETE 759 @ 803 : 829 + +> CHANGE 763 : 764 @ 833 : 834 + +~ if (this.dataWatcher.hasObjectChanged()) { + +> DELETE 773 @ 843 : 848 + +> DELETE 829 @ 904 : 908 + +> DELETE 854 @ 933 : 937 + +> DELETE 872 @ 955 : 962 + +> DELETE 873 @ 963 : 964 + +> DELETE 921 @ 1012 : 1017 + +> CHANGE 991 : 992 @ 1087 : 1095 + +~ String s = nbttagcompound.getString("OwnerUUID"); + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityMooshroom.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityMooshroom.edit.java new file mode 100644 index 0000000..1825dd4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityMooshroom.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> DELETE 37 @ 39 : 47 + +> DELETE 38 @ 48 : 59 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityOcelot.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityOcelot.edit.java new file mode 100644 index 0000000..a77d90f --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityOcelot.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 8 @ 9 : 24 + +> DELETE 13 @ 29 : 30 + +> DELETE 20 @ 37 : 39 + +> DELETE 24 @ 43 : 55 + +> DELETE 31 @ 62 : 82 + +> DELETE 84 @ 135 : 136 + +> DELETE 91 @ 143 : 179 + +> DELETE 158 @ 246 : 258 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityPig.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityPig.edit.java new file mode 100644 index 0000000..cd47428 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityPig.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 14 + +> DELETE 6 @ 15 : 17 + +> DELETE 11 @ 22 : 23 + +> DELETE 16 @ 28 : 29 + +> DELETE 20 @ 33 : 44 + +> DELETE 64 @ 88 : 100 + +> DELETE 99 @ 135 : 144 + +> DELETE 100 @ 145 : 148 + +> DELETE 117 @ 165 : 169 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityRabbit.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityRabbit.edit.java new file mode 100644 index 0000000..ee55914 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityRabbit.edit.java @@ -0,0 +1,42 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> DELETE 5 @ 7 : 8 + +> DELETE 7 @ 10 : 25 + +> DELETE 13 @ 31 : 34 + +> DELETE 15 @ 36 : 39 + +> DELETE 19 @ 43 : 44 + +> DELETE 31 @ 56 : 72 + +> DELETE 33 @ 74 : 79 + +> DELETE 42 @ 88 : 93 + +> DELETE 49 @ 100 : 101 + +> DELETE 71 @ 123 : 178 + +> DELETE 74 @ 181 : 203 + +> DELETE 77 @ 206 : 210 + +> DELETE 183 @ 316 : 327 + +> DELETE 206 @ 350 : 354 + +> DELETE 230 @ 378 : 491 + +> DELETE 263 @ 524 : 571 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntitySheep.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntitySheep.edit.java new file mode 100644 index 0000000..56fab26 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntitySheep.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 7 @ 4 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Maps; +~ + +> DELETE 11 @ 9 : 20 + +> DELETE 21 @ 30 : 31 + +> DELETE 34 @ 44 : 45 + +> DELETE 42 @ 53 : 63 + +> DELETE 46 @ 67 : 72 + +> CHANGE 47 : 48 @ 73 : 77 + +~ this.sheepTimer = Math.max(0, this.sheepTimer - 1); + +> DELETE 112 @ 141 : 155 + +> CHANGE 170 : 171 @ 213 : 214 + +~ public static EnumDyeColor getRandomSheepColor(EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntitySquid.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntitySquid.edit.java new file mode 100644 index 0000000..1028cd7 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntitySquid.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 6 + +> DELETE 32 @ 34 : 35 + +> CHANGE 89 : 90 @ 92 : 102 + +~ this.squidRotation = 6.2831855F; + +> DELETE 108 @ 120 : 126 + +> DELETE 117 @ 135 : 142 + +> DELETE 149 @ 174 : 199 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityTameable.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityTameable.edit.java new file mode 100644 index 0000000..a5fdcd5 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityTameable.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> DELETE 6 @ 5 : 8 + +> DELETE 8 @ 10 : 12 + +> DELETE 12 @ 16 : 17 + +> CHANGE 37 : 38 @ 42 : 50 + +~ String s = nbttagcompound.getString("OwnerUUID"); + +> DELETE 42 @ 54 : 56 + +> CHANGE 117 : 118 @ 131 : 132 + +~ EaglercraftUUID uuid = EaglercraftUUID.fromString(this.getOwnerId()); + +> DELETE 128 @ 142 : 146 + +> DELETE 157 @ 175 : 184 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityVillager.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityVillager.edit.java new file mode 100644 index 0000000..4e10f31 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityVillager.edit.java @@ -0,0 +1,249 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 7 @ 6 : 7 + +> DELETE 8 @ 8 : 10 + +> DELETE 12 @ 14 : 30 + +> DELETE 15 @ 33 : 36 + +> DELETE 24 @ 45 : 48 + +> DELETE 28 @ 52 : 53 + +> DELETE 34 @ 59 : 60 + +> DELETE 41 @ 67 : 68 + +> CHANGE 53 : 54 @ 80 : 232 + +~ private static EntityVillager.ITradeList[][][][] DEFAULT_TRADE_LIST_MAP = null; + +> INSERT 55 : 220 @ 233 + ++ public static void bootstrap() { ++ DEFAULT_TRADE_LIST_MAP = new EntityVillager.ITradeList[][][][] { ++ { { { new EntityVillager.EmeraldForItems(Items.wheat, new EntityVillager.PriceInfo(18, 22)), ++ new EntityVillager.EmeraldForItems(Items.potato, new EntityVillager.PriceInfo(15, 19)), ++ new EntityVillager.EmeraldForItems(Items.carrot, ++ new EntityVillager.PriceInfo(15, 19)), ++ new EntityVillager.ListItemForEmeralds(Items.bread, new EntityVillager.PriceInfo(-4, -2)) }, ++ { new EntityVillager.EmeraldForItems(Item.getItemFromBlock(Blocks.pumpkin), ++ new EntityVillager.PriceInfo(8, 13)), ++ new EntityVillager.ListItemForEmeralds(Items.pumpkin_pie, ++ new EntityVillager.PriceInfo(-3, -2)) }, ++ { new EntityVillager.EmeraldForItems(Item.getItemFromBlock(Blocks.melon_block), ++ new EntityVillager.PriceInfo(7, 12)), ++ new EntityVillager.ListItemForEmeralds(Items.apple, ++ new EntityVillager.PriceInfo(-5, -7)) }, ++ { new EntityVillager.ListItemForEmeralds(Items.cookie, new EntityVillager.PriceInfo(-6, -10)), ++ new EntityVillager.ListItemForEmeralds(Items.cake, ++ new EntityVillager.PriceInfo(1, 1)) } }, ++ { { new EntityVillager.EmeraldForItems(Items.string, new EntityVillager.PriceInfo(15, 20)), ++ new EntityVillager.EmeraldForItems(Items.coal, new EntityVillager.PriceInfo(16, 24)), ++ new EntityVillager.ItemAndEmeraldToItem( ++ Items.fish, new EntityVillager.PriceInfo(6, 6), Items.cooked_fish, ++ new EntityVillager.PriceInfo(6, 6)) }, ++ { new EntityVillager.ListEnchantedItemForEmeralds(Items.fishing_rod, ++ new EntityVillager.PriceInfo(7, 8)) } }, ++ { { new EntityVillager.EmeraldForItems(Item.getItemFromBlock(Blocks.wool), ++ new EntityVillager.PriceInfo(16, 22)), ++ new EntityVillager.ListItemForEmeralds(Items.shears, ++ new EntityVillager.PriceInfo(3, 4)) }, ++ { new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 0), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 1), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 2), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 3), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 4), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 5), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 6), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 7), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 8), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 9), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 10), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 11), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 12), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 13), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 14), ++ new EntityVillager.PriceInfo(1, 2)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Item.getItemFromBlock(Blocks.wool), 1, 15), ++ new EntityVillager.PriceInfo(1, 2)) } }, ++ { { new EntityVillager.EmeraldForItems(Items.string, new EntityVillager.PriceInfo(15, 20)), ++ new EntityVillager.ListItemForEmeralds(Items.arrow, ++ new EntityVillager.PriceInfo(-12, -8)) }, ++ { new EntityVillager.ListItemForEmeralds(Items.bow, new EntityVillager.PriceInfo(2, 3)), ++ new EntityVillager.ItemAndEmeraldToItem(Item.getItemFromBlock(Blocks.gravel), ++ new EntityVillager.PriceInfo(10, 10), Items.flint, ++ new EntityVillager.PriceInfo(6, 10)) } } }, ++ { { { new EntityVillager.EmeraldForItems(Items.paper, new EntityVillager.PriceInfo(24, 36)), ++ new EntityVillager.ListEnchantedBookForEmeralds() }, ++ { new EntityVillager.EmeraldForItems(Items.book, new EntityVillager.PriceInfo(8, 10)), ++ new EntityVillager.ListItemForEmeralds(Items.compass, ++ new EntityVillager.PriceInfo(10, 12)), ++ new EntityVillager.ListItemForEmeralds(Item.getItemFromBlock(Blocks.bookshelf), ++ new EntityVillager.PriceInfo(3, 4)) }, ++ { new EntityVillager.EmeraldForItems(Items.written_book, new EntityVillager.PriceInfo(2, 2)), ++ new EntityVillager.ListItemForEmeralds(Items.clock, ++ new EntityVillager.PriceInfo(10, 12)), ++ new EntityVillager.ListItemForEmeralds(Item.getItemFromBlock(Blocks.glass), ++ new EntityVillager.PriceInfo(-5, -3)) }, ++ { new EntityVillager.ListEnchantedBookForEmeralds() }, ++ { new EntityVillager.ListEnchantedBookForEmeralds() }, ++ { new EntityVillager.ListItemForEmeralds(Items.name_tag, ++ new EntityVillager.PriceInfo(20, 22)) } } }, ++ { { { new EntityVillager.EmeraldForItems(Items.rotten_flesh, new EntityVillager.PriceInfo(36, 40)), ++ new EntityVillager.EmeraldForItems(Items.gold_ingot, new EntityVillager.PriceInfo(8, 10)) }, ++ { new EntityVillager.ListItemForEmeralds(Items.redstone, new EntityVillager.PriceInfo(-4, -1)), ++ new EntityVillager.ListItemForEmeralds( ++ new ItemStack(Items.dye, 1, EnumDyeColor.BLUE.getDyeDamage()), ++ new EntityVillager.PriceInfo(-2, -1)) }, ++ { new EntityVillager.ListItemForEmeralds(Items.ender_eye, new EntityVillager.PriceInfo(7, 11)), ++ new EntityVillager.ListItemForEmeralds(Item.getItemFromBlock(Blocks.glowstone), ++ new EntityVillager.PriceInfo(-3, -1)) }, ++ { new EntityVillager.ListItemForEmeralds(Items.experience_bottle, ++ new EntityVillager.PriceInfo(3, 11)) } } }, ++ { { { new EntityVillager.EmeraldForItems(Items.coal, new EntityVillager.PriceInfo(16, 24)), ++ new EntityVillager.ListItemForEmeralds(Items.iron_helmet, new EntityVillager.PriceInfo(4, 6)) }, ++ { new EntityVillager.EmeraldForItems(Items.iron_ingot, new EntityVillager.PriceInfo(7, 9)), ++ new EntityVillager.ListItemForEmeralds(Items.iron_chestplate, ++ new EntityVillager.PriceInfo(10, 14)) }, ++ { new EntityVillager.EmeraldForItems(Items.diamond, new EntityVillager.PriceInfo(3, 4)), ++ new EntityVillager.ListEnchantedItemForEmeralds(Items.diamond_chestplate, ++ new EntityVillager.PriceInfo(16, 19)) }, ++ { new EntityVillager.ListItemForEmeralds(Items.chainmail_boots, ++ new EntityVillager.PriceInfo(5, 7)), ++ new EntityVillager.ListItemForEmeralds(Items.chainmail_leggings, ++ new EntityVillager.PriceInfo(9, 11)), ++ new EntityVillager.ListItemForEmeralds(Items.chainmail_helmet, ++ new EntityVillager.PriceInfo(5, 7)), ++ new EntityVillager.ListItemForEmeralds(Items.chainmail_chestplate, ++ new EntityVillager.PriceInfo(11, 15)) } }, ++ { { new EntityVillager.EmeraldForItems(Items.coal, new EntityVillager.PriceInfo(16, 24)), ++ new EntityVillager.ListItemForEmeralds(Items.iron_axe, ++ new EntityVillager.PriceInfo(6, 8)) }, ++ { new EntityVillager.EmeraldForItems(Items.iron_ingot, ++ new EntityVillager.PriceInfo(7, 9)), ++ new EntityVillager.ListEnchantedItemForEmeralds(Items.iron_sword, ++ new EntityVillager.PriceInfo(9, 10)) }, ++ { new EntityVillager.EmeraldForItems(Items.diamond, new EntityVillager.PriceInfo(3, 4)), ++ new EntityVillager.ListEnchantedItemForEmeralds(Items.diamond_sword, ++ new EntityVillager.PriceInfo(12, 15)), ++ new EntityVillager.ListEnchantedItemForEmeralds(Items.diamond_axe, ++ new EntityVillager.PriceInfo(9, 12)) } }, ++ { { new EntityVillager.EmeraldForItems(Items.coal, new EntityVillager.PriceInfo(16, 24)), ++ new EntityVillager.ListEnchantedItemForEmeralds(Items.iron_shovel, ++ new EntityVillager.PriceInfo(5, 7)) }, ++ { new EntityVillager.EmeraldForItems(Items.iron_ingot, ++ new EntityVillager.PriceInfo(7, 9)), ++ new EntityVillager.ListEnchantedItemForEmeralds(Items.iron_pickaxe, ++ new EntityVillager.PriceInfo(9, 11)) }, ++ { new EntityVillager.EmeraldForItems(Items.diamond, new EntityVillager.PriceInfo(3, 4)), ++ new EntityVillager.ListEnchantedItemForEmeralds(Items.diamond_pickaxe, ++ new EntityVillager.PriceInfo(12, 15)) } } }, ++ { { { new EntityVillager.EmeraldForItems(Items.porkchop, new EntityVillager.PriceInfo(14, 18)), ++ new EntityVillager.EmeraldForItems(Items.chicken, new EntityVillager.PriceInfo(14, 18)) }, ++ { new EntityVillager.EmeraldForItems(Items.coal, new EntityVillager.PriceInfo(16, 24)), ++ new EntityVillager.ListItemForEmeralds(Items.cooked_porkchop, ++ new EntityVillager.PriceInfo(-7, -5)), ++ new EntityVillager.ListItemForEmeralds(Items.cooked_chicken, ++ new EntityVillager.PriceInfo(-8, -6)) } }, ++ { { new EntityVillager.EmeraldForItems(Items.leather, new EntityVillager.PriceInfo(9, 12)), ++ new EntityVillager.ListItemForEmeralds(Items.leather_leggings, ++ new EntityVillager.PriceInfo(2, 4)) }, ++ { new EntityVillager.ListEnchantedItemForEmeralds(Items.leather_chestplate, ++ new EntityVillager.PriceInfo(7, 12)) }, ++ { new EntityVillager.ListItemForEmeralds(Items.saddle, ++ new EntityVillager.PriceInfo(8, 10)) } } } }; ++ } ++ + +> DELETE 229 @ 242 : 258 + +> DELETE 232 @ 261 : 281 + +> DELETE 237 @ 286 : 329 + +> DELETE 241 @ 333 : 338 + +> DELETE 298 @ 395 : 396 + +> DELETE 340 @ 438 : 477 + +> DELETE 413 @ 550 : 558 + +> DELETE 553 @ 698 : 699 + +> DELETE 572 @ 718 : 728 + +> DELETE 573 @ 729 : 732 + +> CHANGE 667 : 668 @ 826 : 827 + +~ public void modifyMerchantRecipeList(MerchantRecipeList recipeList, EaglercraftRandom random) { + +> CHANGE 678 : 679 @ 837 : 838 + +~ void modifyMerchantRecipeList(MerchantRecipeList var1, EaglercraftRandom var2); + +> CHANGE 695 : 696 @ 854 : 855 + +~ public void modifyMerchantRecipeList(MerchantRecipeList merchantrecipelist, EaglercraftRandom random) { + +> CHANGE 714 : 715 @ 873 : 874 + +~ public void modifyMerchantRecipeList(MerchantRecipeList merchantrecipelist, EaglercraftRandom random) { + +> CHANGE 738 : 739 @ 897 : 898 + +~ public void modifyMerchantRecipeList(MerchantRecipeList merchantrecipelist, EaglercraftRandom random) { + +> CHANGE 765 : 766 @ 924 : 925 + +~ public void modifyMerchantRecipeList(MerchantRecipeList merchantrecipelist, EaglercraftRandom random) { + +> CHANGE 790 : 791 @ 949 : 950 + +~ public int getPrice(EaglercraftRandom rand) { + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityWaterMob.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityWaterMob.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityWaterMob.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/passive/EntityWolf.edit.java b/patches/minecraft/net/minecraft/entity/passive/EntityWolf.edit.java new file mode 100644 index 0000000..2527ba2 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/passive/EntityWolf.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 7 @ 8 : 22 + +> DELETE 9 @ 24 : 30 + +> DELETE 17 @ 38 : 39 + +> DELETE 34 @ 56 : 77 + +> DELETE 114 @ 157 : 172 + +> DELETE 198 @ 256 : 257 + +> DELETE 259 @ 318 : 325 + +> DELETE 268 @ 334 : 350 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/player/EntityPlayer.edit.java b/patches/minecraft/net/minecraft/entity/player/EntityPlayer.edit.java new file mode 100644 index 0000000..924f471 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/player/EntityPlayer.edit.java @@ -0,0 +1,105 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 4 : 10 @ 7 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import com.google.common.base.Charsets; +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> INSERT 15 : 16 @ 13 + ++ import net.minecraft.command.ICommandSender; + +> DELETE 31 @ 28 : 29 + +> DELETE 34 @ 32 : 36 + +> DELETE 50 @ 52 : 53 + +> DELETE 57 @ 60 : 61 + +> CHANGE 77 : 78 @ 81 : 82 + +~ public abstract class EntityPlayer extends EntityLivingBase implements ICommandSender { + +> CHANGE 120 : 121 @ 124 : 125 + +~ this.inventoryContainer = new ContainerPlayer(this.inventory, false, this); + +> DELETE 170 @ 174 : 178 + +> CHANGE 189 : 190 @ 197 : 200 + +~ --this.itemInUseCount; + +> DELETE 204 @ 214 : 222 + +> DELETE 212 @ 230 : 234 + +> DELETE 255 @ 277 : 285 + +> CHANGE 355 : 368 @ 385 : 404 + +~ double d0 = this.posX; +~ double d1 = this.posY; +~ double d2 = this.posZ; +~ float f = this.rotationYaw; +~ float f1 = this.rotationPitch; +~ super.updateRidden(); +~ this.prevCameraYaw = this.cameraYaw; +~ this.cameraYaw = 0.0F; +~ this.addMountedMovementStat(this.posX - d0, this.posY - d1, this.posZ - d2); +~ if (this.ridingEntity instanceof EntityPig) { +~ this.rotationPitch = f1; +~ this.rotationYaw = f; +~ this.renderYawOffset = ((EntityPig) this.ridingEntity).renderYawOffset; + +> DELETE 404 @ 440 : 443 + +> CHANGE 512 : 513 @ 551 : 552 + +~ for (ScoreObjective scoreobjective : (Collection) collection) { + +> DELETE 724 @ 763 : 767 + +> DELETE 925 @ 968 : 977 + +> DELETE 1007 @ 1059 : 1063 + +> DELETE 1008 @ 1064 : 1089 + +> DELETE 1044 @ 1125 : 1128 + +> DELETE 1083 @ 1167 : 1170 + +> DELETE 1375 @ 1462 : 1466 + +> DELETE 1376 @ 1467 : 1468 + +> DELETE 1394 @ 1486 : 1490 + +> CHANGE 1550 : 1552 @ 1646 : 1648 + +~ public static EaglercraftUUID getUUID(GameProfile profile) { +~ EaglercraftUUID uuid = profile.getId(); + +> CHANGE 1559 : 1561 @ 1655 : 1657 + +~ public static EaglercraftUUID getOfflineUUID(String username) { +~ return EaglercraftUUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8)); + +> CHANGE 1579 : 1580 @ 1675 : 1676 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/player/InventoryPlayer.edit.java b/patches/minecraft/net/minecraft/entity/player/InventoryPlayer.edit.java new file mode 100644 index 0000000..3b27105 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/player/InventoryPlayer.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 7 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityArrow.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityArrow.edit.java new file mode 100644 index 0000000..5db4a8a --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityArrow.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 13 @ 12 : 15 + +> DELETE 14 @ 16 : 17 + +> DELETE 264 @ 267 : 271 + +> DELETE 281 @ 288 : 295 + +> DELETE 422 @ 436 : 455 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityEgg.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityEgg.edit.java new file mode 100644 index 0000000..891090c --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityEgg.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> DELETE 29 @ 31 : 45 + +> DELETE 37 @ 53 : 57 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityFireball.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityFireball.edit.java new file mode 100644 index 0000000..c1101e7 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityFireball.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> CHANGE 78 : 79 @ 77 : 79 + +~ if (this.worldObj.isBlockLoaded(new BlockPos(this))) { + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityFishHook.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityFishHook.edit.java new file mode 100644 index 0000000..02cbc51 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityFishHook.edit.java @@ -0,0 +1,36 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> DELETE 7 @ 6 : 7 + +> DELETE 8 @ 8 : 10 + +> DELETE 15 @ 17 : 18 + +> DELETE 18 @ 21 : 22 + +> DELETE 22 @ 26 : 27 + +> DELETE 24 @ 29 : 30 + +> DELETE 189 @ 195 : 204 + +> DELETE 190 @ 205 : 218 + +> DELETE 308 @ 336 : 437 + +> CHANGE 348 : 349 @ 477 : 516 + +~ return 0; + +> DELETE 351 @ 518 : 543 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.edit.java new file mode 100644 index 0000000..a4ec278 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 5 + +> DELETE 4 @ 6 : 7 + +> DELETE 24 @ 27 : 40 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityPotion.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityPotion.edit.java new file mode 100644 index 0000000..4f7edf5 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityPotion.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 5 + +> DELETE 6 @ 8 : 12 + +> DELETE 63 @ 69 : 82 + +> DELETE 64 @ 83 : 105 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.edit.java new file mode 100644 index 0000000..d586366 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 7 + +> DELETE 25 @ 29 : 44 + +> DELETE 26 @ 45 : 56 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntitySnowball.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntitySnowball.edit.java new file mode 100644 index 0000000..ee3d4de --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntitySnowball.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> DELETE 38 @ 39 : 43 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityThrowable.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityThrowable.edit.java new file mode 100644 index 0000000..a848c74 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityThrowable.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 9 @ 11 : 12 + +> DELETE 16 @ 19 : 20 + +> DELETE 158 @ 162 : 191 + +> DELETE 259 @ 292 : 302 + +> EOF diff --git a/patches/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.edit.java b/patches/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.edit.java new file mode 100644 index 0000000..45540d0 --- /dev/null +++ b/patches/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 9 + +> DELETE 9 @ 12 : 13 + +> DELETE 47 @ 51 : 65 + +> DELETE 48 @ 66 : 86 + +> EOF diff --git a/patches/minecraft/net/minecraft/event/ClickEvent.edit.java b/patches/minecraft/net/minecraft/event/ClickEvent.edit.java new file mode 100644 index 0000000..68d9884 --- /dev/null +++ b/patches/minecraft/net/minecraft/event/ClickEvent.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 6 @ 5 + ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/event/HoverEvent.edit.java b/patches/minecraft/net/minecraft/event/HoverEvent.edit.java new file mode 100644 index 0000000..3827cac --- /dev/null +++ b/patches/minecraft/net/minecraft/event/HoverEvent.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/init/Blocks.edit.java b/patches/minecraft/net/minecraft/init/Blocks.edit.java new file mode 100644 index 0000000..1c3a5a3 --- /dev/null +++ b/patches/minecraft/net/minecraft/init/Blocks.edit.java @@ -0,0 +1,215 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 34 @ 34 : 35 + +> CHANGE 37 : 235 @ 38 : 236 + +~ public static Block air; +~ public static Block stone; +~ public static BlockGrass grass; +~ public static Block dirt; +~ public static Block cobblestone; +~ public static Block planks; +~ public static Block sapling; +~ public static Block bedrock; +~ public static BlockDynamicLiquid flowing_water; +~ public static BlockStaticLiquid water; +~ public static BlockDynamicLiquid flowing_lava; +~ public static BlockStaticLiquid lava; +~ public static BlockSand sand; +~ public static Block gravel; +~ public static Block gold_ore; +~ public static Block iron_ore; +~ public static Block coal_ore; +~ public static Block log; +~ public static Block log2; +~ public static BlockLeaves leaves; +~ public static BlockLeaves leaves2; +~ public static Block sponge; +~ public static Block glass; +~ public static Block lapis_ore; +~ public static Block lapis_block; +~ public static Block dispenser; +~ public static Block sandstone; +~ public static Block noteblock; +~ public static Block bed; +~ public static Block golden_rail; +~ public static Block detector_rail; +~ public static BlockPistonBase sticky_piston; +~ public static Block web; +~ public static BlockTallGrass tallgrass; +~ public static BlockDeadBush deadbush; +~ public static BlockPistonBase piston; +~ public static BlockPistonExtension piston_head; +~ public static Block wool; +~ public static BlockPistonMoving piston_extension; +~ public static BlockFlower yellow_flower; +~ public static BlockFlower red_flower; +~ public static BlockBush brown_mushroom; +~ public static BlockBush red_mushroom; +~ public static Block gold_block; +~ public static Block iron_block; +~ public static BlockSlab double_stone_slab; +~ public static BlockSlab stone_slab; +~ public static Block brick_block; +~ public static Block tnt; +~ public static Block bookshelf; +~ public static Block mossy_cobblestone; +~ public static Block obsidian; +~ public static Block torch; +~ public static BlockFire fire; +~ public static Block mob_spawner; +~ public static Block oak_stairs; +~ public static BlockChest chest; +~ public static BlockRedstoneWire redstone_wire; +~ public static Block diamond_ore; +~ public static Block diamond_block; +~ public static Block crafting_table; +~ public static Block wheat; +~ public static Block farmland; +~ public static Block furnace; +~ public static Block lit_furnace; +~ public static Block standing_sign; +~ public static Block oak_door; +~ public static Block spruce_door; +~ public static Block birch_door; +~ public static Block jungle_door; +~ public static Block acacia_door; +~ public static Block dark_oak_door; +~ public static Block ladder; +~ public static Block rail; +~ public static Block stone_stairs; +~ public static Block wall_sign; +~ public static Block lever; +~ public static Block stone_pressure_plate; +~ public static Block iron_door; +~ public static Block wooden_pressure_plate; +~ public static Block redstone_ore; +~ public static Block lit_redstone_ore; +~ public static Block unlit_redstone_torch; +~ public static Block redstone_torch; +~ public static Block stone_button; +~ public static Block snow_layer; +~ public static Block ice; +~ public static Block snow; +~ public static BlockCactus cactus; +~ public static Block clay; +~ public static BlockReed reeds; +~ public static Block jukebox; +~ public static Block oak_fence; +~ public static Block spruce_fence; +~ public static Block birch_fence; +~ public static Block jungle_fence; +~ public static Block dark_oak_fence; +~ public static Block acacia_fence; +~ public static Block pumpkin; +~ public static Block netherrack; +~ public static Block soul_sand; +~ public static Block glowstone; +~ public static BlockPortal portal; +~ public static Block lit_pumpkin; +~ public static Block cake; +~ public static BlockRedstoneRepeater unpowered_repeater; +~ public static BlockRedstoneRepeater powered_repeater; +~ public static Block trapdoor; +~ public static Block monster_egg; +~ public static Block stonebrick; +~ public static Block brown_mushroom_block; +~ public static Block red_mushroom_block; +~ public static Block iron_bars; +~ public static Block glass_pane; +~ public static Block melon_block; +~ public static Block pumpkin_stem; +~ public static Block melon_stem; +~ public static Block vine; +~ public static Block oak_fence_gate; +~ public static Block spruce_fence_gate; +~ public static Block birch_fence_gate; +~ public static Block jungle_fence_gate; +~ public static Block dark_oak_fence_gate; +~ public static Block acacia_fence_gate; +~ public static Block brick_stairs; +~ public static Block stone_brick_stairs; +~ public static BlockMycelium mycelium; +~ public static Block waterlily; +~ public static Block nether_brick; +~ public static Block nether_brick_fence; +~ public static Block nether_brick_stairs; +~ public static Block nether_wart; +~ public static Block enchanting_table; +~ public static Block brewing_stand; +~ public static BlockCauldron cauldron; +~ public static Block end_portal; +~ public static Block end_portal_frame; +~ public static Block end_stone; +~ public static Block dragon_egg; +~ public static Block redstone_lamp; +~ public static Block lit_redstone_lamp; +~ public static BlockSlab double_wooden_slab; +~ public static BlockSlab wooden_slab; +~ public static Block cocoa; +~ public static Block sandstone_stairs; +~ public static Block emerald_ore; +~ public static Block ender_chest; +~ public static BlockTripWireHook tripwire_hook; +~ public static Block tripwire; +~ public static Block emerald_block; +~ public static Block spruce_stairs; +~ public static Block birch_stairs; +~ public static Block jungle_stairs; +~ public static Block command_block; +~ public static BlockBeacon beacon; +~ public static Block cobblestone_wall; +~ public static Block flower_pot; +~ public static Block carrots; +~ public static Block potatoes; +~ public static Block wooden_button; +~ public static BlockSkull skull; +~ public static Block anvil; +~ public static Block trapped_chest; +~ public static Block light_weighted_pressure_plate; +~ public static Block heavy_weighted_pressure_plate; +~ public static BlockRedstoneComparator unpowered_comparator; +~ public static BlockRedstoneComparator powered_comparator; +~ public static BlockDaylightDetector daylight_detector; +~ public static BlockDaylightDetector daylight_detector_inverted; +~ public static Block redstone_block; +~ public static Block quartz_ore; +~ public static BlockHopper hopper; +~ public static Block quartz_block; +~ public static Block quartz_stairs; +~ public static Block activator_rail; +~ public static Block dropper; +~ public static Block stained_hardened_clay; +~ public static Block barrier; +~ public static Block iron_trapdoor; +~ public static Block hay_block; +~ public static Block carpet; +~ public static Block hardened_clay; +~ public static Block coal_block; +~ public static Block packed_ice; +~ public static Block acacia_stairs; +~ public static Block dark_oak_stairs; +~ public static Block slime_block; +~ public static BlockDoublePlant double_plant; +~ public static BlockStainedGlass stained_glass; +~ public static BlockStainedGlassPane stained_glass_pane; +~ public static Block prismarine; +~ public static Block sea_lantern; +~ public static Block standing_banner; +~ public static Block wall_banner; +~ public static Block red_sandstone; +~ public static Block red_sandstone_stairs; +~ public static BlockSlab double_stone_slab2; +~ public static BlockSlab stone_slab2; + +> CHANGE 240 : 241 @ 241 : 242 + +~ static void doBootstrap() { + +> EOF diff --git a/patches/minecraft/net/minecraft/init/Bootstrap.edit.java b/patches/minecraft/net/minecraft/init/Bootstrap.edit.java new file mode 100644 index 0000000..384107b --- /dev/null +++ b/patches/minecraft/net/minecraft/init/Bootstrap.edit.java @@ -0,0 +1,84 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 7 @ 4 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> INSERT 29 : 31 @ 28 + ++ import net.minecraft.entity.monster.EntityEnderman; ++ import net.minecraft.entity.passive.EntityVillager; + +> DELETE 36 @ 33 : 35 + +> INSERT 38 : 39 @ 37 + ++ import net.minecraft.item.ItemAxe; + +> INSERT 42 : 43 @ 40 + ++ import net.minecraft.item.ItemPickaxe; + +> INSERT 44 : 45 @ 41 + ++ import net.minecraft.item.ItemSpade; + +> DELETE 46 @ 42 : 44 + +> DELETE 47 @ 45 : 46 + +> DELETE 48 @ 47 : 48 + +> DELETE 51 @ 51 : 52 + +> CHANGE 52 : 53 @ 53 : 55 + +~ import net.minecraft.world.biome.BiomeGenBase; + +> CHANGE 155 : 156 @ 157 : 158 + +~ EaglercraftRandom random = world.rand; + +> CHANGE 294 : 295 @ 296 : 301 + +~ if (!ItemDye.applyBonemeal(itemstack, world, blockpos)) { + +> CHANGE 335 : 336 @ 341 : 375 + +~ if (!(world.isAirBlock(blockpos) && blockskull.canDispenserPlace(world, blockpos, itemstack))) { + +> DELETE 361 @ 400 : 404 + +> INSERT 388 : 390 @ 431 + ++ Blocks.doBootstrap(); ++ BiomeGenBase.bootstrap(); + +> INSERT 391 : 395 @ 432 + ++ EntityEnderman.bootstrap(); ++ ItemAxe.bootstrap(); ++ ItemPickaxe.bootstrap(); ++ ItemSpade.bootstrap(); + +> INSERT 396 : 398 @ 433 + ++ Items.doBootstrap(); ++ EntityVillager.bootstrap(); + +> CHANGE 404 : 406 @ 439 : 441 + +~ System.setErr(new LoggingPrintStream("STDERR", true, System.err)); +~ System.setOut(new LoggingPrintStream("STDOUT", false, SYSOUT)); + +> EOF diff --git a/patches/minecraft/net/minecraft/init/Items.edit.java b/patches/minecraft/net/minecraft/init/Items.edit.java new file mode 100644 index 0000000..b579438 --- /dev/null +++ b/patches/minecraft/net/minecraft/init/Items.edit.java @@ -0,0 +1,204 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 15 : 202 @ 16 : 203 + +~ public static Item iron_shovel; +~ public static Item iron_pickaxe; +~ public static Item iron_axe; +~ public static Item flint_and_steel; +~ public static Item apple; +~ public static ItemBow bow; +~ public static Item arrow; +~ public static Item coal; +~ public static Item diamond; +~ public static Item iron_ingot; +~ public static Item gold_ingot; +~ public static Item iron_sword; +~ public static Item wooden_sword; +~ public static Item wooden_shovel; +~ public static Item wooden_pickaxe; +~ public static Item wooden_axe; +~ public static Item stone_sword; +~ public static Item stone_shovel; +~ public static Item stone_pickaxe; +~ public static Item stone_axe; +~ public static Item diamond_sword; +~ public static Item diamond_shovel; +~ public static Item diamond_pickaxe; +~ public static Item diamond_axe; +~ public static Item stick; +~ public static Item bowl; +~ public static Item mushroom_stew; +~ public static Item golden_sword; +~ public static Item golden_shovel; +~ public static Item golden_pickaxe; +~ public static Item golden_axe; +~ public static Item string; +~ public static Item feather; +~ public static Item gunpowder; +~ public static Item wooden_hoe; +~ public static Item stone_hoe; +~ public static Item iron_hoe; +~ public static Item diamond_hoe; +~ public static Item golden_hoe; +~ public static Item wheat_seeds; +~ public static Item wheat; +~ public static Item bread; +~ public static ItemArmor leather_helmet; +~ public static ItemArmor leather_chestplate; +~ public static ItemArmor leather_leggings; +~ public static ItemArmor leather_boots; +~ public static ItemArmor chainmail_helmet; +~ public static ItemArmor chainmail_chestplate; +~ public static ItemArmor chainmail_leggings; +~ public static ItemArmor chainmail_boots; +~ public static ItemArmor iron_helmet; +~ public static ItemArmor iron_chestplate; +~ public static ItemArmor iron_leggings; +~ public static ItemArmor iron_boots; +~ public static ItemArmor diamond_helmet; +~ public static ItemArmor diamond_chestplate; +~ public static ItemArmor diamond_leggings; +~ public static ItemArmor diamond_boots; +~ public static ItemArmor golden_helmet; +~ public static ItemArmor golden_chestplate; +~ public static ItemArmor golden_leggings; +~ public static ItemArmor golden_boots; +~ public static Item flint; +~ public static Item porkchop; +~ public static Item cooked_porkchop; +~ public static Item painting; +~ public static Item golden_apple; +~ public static Item sign; +~ public static Item oak_door; +~ public static Item spruce_door; +~ public static Item birch_door; +~ public static Item jungle_door; +~ public static Item acacia_door; +~ public static Item dark_oak_door; +~ public static Item bucket; +~ public static Item water_bucket; +~ public static Item lava_bucket; +~ public static Item minecart; +~ public static Item saddle; +~ public static Item iron_door; +~ public static Item redstone; +~ public static Item snowball; +~ public static Item boat; +~ public static Item leather; +~ public static Item milk_bucket; +~ public static Item brick; +~ public static Item clay_ball; +~ public static Item reeds; +~ public static Item paper; +~ public static Item book; +~ public static Item slime_ball; +~ public static Item chest_minecart; +~ public static Item furnace_minecart; +~ public static Item egg; +~ public static Item compass; +~ public static ItemFishingRod fishing_rod; +~ public static Item clock; +~ public static Item glowstone_dust; +~ public static Item fish; +~ public static Item cooked_fish; +~ public static Item dye; +~ public static Item bone; +~ public static Item sugar; +~ public static Item cake; +~ public static Item bed; +~ public static Item repeater; +~ public static Item cookie; +~ public static ItemMap filled_map; +~ public static ItemShears shears; +~ public static Item melon; +~ public static Item pumpkin_seeds; +~ public static Item melon_seeds; +~ public static Item beef; +~ public static Item cooked_beef; +~ public static Item chicken; +~ public static Item cooked_chicken; +~ public static Item mutton; +~ public static Item cooked_mutton; +~ public static Item rabbit; +~ public static Item cooked_rabbit; +~ public static Item rabbit_stew; +~ public static Item rabbit_foot; +~ public static Item rabbit_hide; +~ public static Item rotten_flesh; +~ public static Item ender_pearl; +~ public static Item blaze_rod; +~ public static Item ghast_tear; +~ public static Item gold_nugget; +~ public static Item nether_wart; +~ public static ItemPotion potionitem; +~ public static Item glass_bottle; +~ public static Item spider_eye; +~ public static Item fermented_spider_eye; +~ public static Item blaze_powder; +~ public static Item magma_cream; +~ public static Item brewing_stand; +~ public static Item cauldron; +~ public static Item ender_eye; +~ public static Item speckled_melon; +~ public static Item spawn_egg; +~ public static Item experience_bottle; +~ public static Item fire_charge; +~ public static Item writable_book; +~ public static Item written_book; +~ public static Item emerald; +~ public static Item item_frame; +~ public static Item flower_pot; +~ public static Item carrot; +~ public static Item potato; +~ public static Item baked_potato; +~ public static Item poisonous_potato; +~ public static ItemEmptyMap map; +~ public static Item golden_carrot; +~ public static Item skull; +~ public static Item carrot_on_a_stick; +~ public static Item nether_star; +~ public static Item pumpkin_pie; +~ public static Item fireworks; +~ public static Item firework_charge; +~ public static ItemEnchantedBook enchanted_book; +~ public static Item comparator; +~ public static Item netherbrick; +~ public static Item quartz; +~ public static Item tnt_minecart; +~ public static Item hopper_minecart; +~ public static ItemArmorStand armor_stand; +~ public static Item iron_horse_armor; +~ public static Item golden_horse_armor; +~ public static Item diamond_horse_armor; +~ public static Item lead; +~ public static Item name_tag; +~ public static Item command_block_minecart; +~ public static Item record_13; +~ public static Item record_cat; +~ public static Item record_blocks; +~ public static Item record_chirp; +~ public static Item record_far; +~ public static Item record_mall; +~ public static Item record_mellohi; +~ public static Item record_stal; +~ public static Item record_strad; +~ public static Item record_ward; +~ public static Item record_11; +~ public static Item record_wait; +~ public static Item prismarine_shard; +~ public static Item prismarine_crystals; +~ public static Item banner; + +> CHANGE 207 : 208 @ 208 : 209 + +~ static void doBootstrap() { + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/AnimalChest.edit.java b/patches/minecraft/net/minecraft/inventory/AnimalChest.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/AnimalChest.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/Container.edit.java b/patches/minecraft/net/minecraft/inventory/Container.edit.java new file mode 100644 index 0000000..d55f20a --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/Container.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 5 : 9 @ 7 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Sets; ++ + +> DELETE 11 @ 9 : 12 + +> CHANGE 30 : 31 @ 31 : 32 + +~ this.inventoryItemStacks.add((ItemStack) null); + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerBeacon.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerBeacon.edit.java new file mode 100644 index 0000000..535c289 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerBeacon.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 8 + +> DELETE 41 @ 45 : 56 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerBrewingStand.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerBrewingStand.edit.java new file mode 100644 index 0000000..932b3c9 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerBrewingStand.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerChest.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerChest.edit.java new file mode 100644 index 0000000..4890f7c --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerChest.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerDispenser.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerDispenser.edit.java new file mode 100644 index 0000000..4890f7c --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerDispenser.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerEnchantment.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerEnchantment.edit.java new file mode 100644 index 0000000..efb23ca --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerEnchantment.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 11 @ 10 : 15 + +> DELETE 13 @ 17 : 18 + +> CHANGE 20 : 21 @ 25 : 26 + +~ private EaglercraftRandom rand; + +> CHANGE 40 : 41 @ 45 : 46 + +~ this.rand = new EaglercraftRandom(); + +> CHANGE 117 : 118 @ 122 : 191 + +~ if (!(itemstack != null && itemstack.isItemEnchantable())) { + +> DELETE 136 @ 209 : 241 + +> DELETE 157 @ 262 : 275 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerFurnace.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerFurnace.edit.java new file mode 100644 index 0000000..ba107aa --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerFurnace.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerHopper.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerHopper.edit.java new file mode 100644 index 0000000..8f13c7e --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerHopper.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerHorseInventory.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerHorseInventory.edit.java new file mode 100644 index 0000000..f9719d3 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerHorseInventory.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerMerchant.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerMerchant.edit.java new file mode 100644 index 0000000..29f6c11 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerMerchant.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 11 + +> DELETE 106 @ 112 : 124 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerPlayer.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerPlayer.edit.java new file mode 100644 index 0000000..af0f665 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerPlayer.edit.java @@ -0,0 +1,27 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 12 + +> CHANGE 29 : 31 @ 35 : 36 + +~ for (int k = 0; k < 4; ++k) { +~ final int k2 = k; + +> CHANGE 40 : 41 @ 45 : 46 + +~ ? ((ItemArmor) itemstack.getItem()).armorType == k2 + +> CHANGE 42 : 43 @ 47 : 48 + +~ && itemstack.getItem() != Items.skull ? false : k2 == 0)); + +> CHANGE 46 : 47 @ 51 : 52 + +~ return ItemArmor.EMPTY_SLOT_NAMES[k2]; + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerRepair.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerRepair.edit.java new file mode 100644 index 0000000..6860ace --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerRepair.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 4 : 9 @ 4 : 6 + +~ +~ import org.apache.commons.lang3.StringUtils; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 15 @ 12 : 18 + +> DELETE 18 @ 21 : 24 + +> DELETE 78 @ 84 : 100 + +> DELETE 297 @ 319 : 332 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ContainerWorkbench.edit.java b/patches/minecraft/net/minecraft/inventory/ContainerWorkbench.edit.java new file mode 100644 index 0000000..321d5d8 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ContainerWorkbench.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 11 + +> DELETE 46 @ 52 : 65 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ICrafting.edit.java b/patches/minecraft/net/minecraft/inventory/ICrafting.edit.java new file mode 100644 index 0000000..8c7e8e5 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ICrafting.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 5 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/IInvBasic.edit.java b/patches/minecraft/net/minecraft/inventory/IInvBasic.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/IInvBasic.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/ISidedInventory.edit.java b/patches/minecraft/net/minecraft/inventory/ISidedInventory.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/ISidedInventory.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/InventoryBasic.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryBasic.edit.java new file mode 100644 index 0000000..8a96b84 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/InventoryBasic.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 7 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/InventoryCraftResult.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryCraftResult.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/InventoryCraftResult.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/InventoryCrafting.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryCrafting.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/InventoryCrafting.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/InventoryEnderChest.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryEnderChest.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/InventoryEnderChest.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/InventoryHelper.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryHelper.edit.java new file mode 100644 index 0000000..10b8729 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/InventoryHelper.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 6 @ 5 : 6 + +> CHANGE 12 : 13 @ 12 : 13 + +~ private static final EaglercraftRandom RANDOM = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/InventoryLargeChest.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryLargeChest.edit.java new file mode 100644 index 0000000..8f13c7e --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/InventoryLargeChest.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/InventoryMerchant.edit.java b/patches/minecraft/net/minecraft/inventory/InventoryMerchant.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/InventoryMerchant.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/Slot.edit.java b/patches/minecraft/net/minecraft/inventory/Slot.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/Slot.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/SlotCrafting.edit.java b/patches/minecraft/net/minecraft/inventory/SlotCrafting.edit.java new file mode 100644 index 0000000..f9719d3 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/SlotCrafting.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/SlotFurnaceFuel.edit.java b/patches/minecraft/net/minecraft/inventory/SlotFurnaceFuel.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/SlotFurnaceFuel.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/SlotFurnaceOutput.edit.java b/patches/minecraft/net/minecraft/inventory/SlotFurnaceOutput.edit.java new file mode 100644 index 0000000..e087e7e --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/SlotFurnaceOutput.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 4 @ 5 : 7 + +> DELETE 5 @ 8 : 9 + +> DELETE 6 @ 10 : 11 + +> DELETE 40 @ 45 : 56 + +> DELETE 41 @ 57 : 68 + +> EOF diff --git a/patches/minecraft/net/minecraft/inventory/SlotMerchantResult.edit.java b/patches/minecraft/net/minecraft/inventory/SlotMerchantResult.edit.java new file mode 100644 index 0000000..a095108 --- /dev/null +++ b/patches/minecraft/net/minecraft/inventory/SlotMerchantResult.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/Item.edit.java b/patches/minecraft/net/minecraft/item/Item.edit.java new file mode 100644 index 0000000..f2f6183 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/Item.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 7 @ 2 + ++ import java.util.List; ++ import java.util.Map; ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; ++ + +> CHANGE 11 : 12 @ 6 : 10 + +~ + +> DELETE 35 @ 33 : 101 + +> CHANGE 51 : 53 @ 117 : 118 + +~ protected static final EaglercraftUUID itemModifierUUID = EaglercraftUUID +~ .fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF"); + +> CHANGE 54 : 55 @ 119 : 120 + +~ protected static EaglercraftRandom itemRand = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemAnvilBlock.edit.java b/patches/minecraft/net/minecraft/item/ItemAnvilBlock.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemAnvilBlock.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemAppleGold.edit.java b/patches/minecraft/net/minecraft/item/ItemAppleGold.edit.java new file mode 100644 index 0000000..8b23bd0 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemAppleGold.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 6 @ 5 : 11 + +> CHANGE 23 : 24 @ 28 : 39 + +~ if (itemstack.getMetadata() == 0) { + +> DELETE 26 @ 41 : 42 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemArmor.edit.java b/patches/minecraft/net/minecraft/item/ItemArmor.edit.java new file mode 100644 index 0000000..75669ca --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemArmor.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.base.Predicates; ++ + +> DELETE 15 @ 13 : 15 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemArmorStand.edit.java b/patches/minecraft/net/minecraft/item/ItemArmorStand.edit.java new file mode 100644 index 0000000..954a411 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemArmorStand.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> DELETE 6 @ 6 : 7 + +> DELETE 7 @ 8 : 11 + +> DELETE 10 @ 14 : 16 + +> DELETE 43 @ 49 : 70 + +> DELETE 50 @ 77 : 89 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemAxe.edit.java b/patches/minecraft/net/minecraft/item/ItemAxe.edit.java new file mode 100644 index 0000000..6b6767c --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemAxe.edit.java @@ -0,0 +1,30 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Sets; ++ + +> DELETE 9 @ 7 : 10 + +> CHANGE 11 : 12 @ 12 : 15 + +~ private static Set EFFECTIVE_ON; + +> INSERT 13 : 18 @ 16 + ++ public static void bootstrap() { ++ EFFECTIVE_ON = Sets.newHashSet(new Block[] { Blocks.planks, Blocks.bookshelf, Blocks.log, Blocks.log2, ++ Blocks.chest, Blocks.pumpkin, Blocks.lit_pumpkin, Blocks.melon_block, Blocks.ladder }); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemBanner.edit.java b/patches/minecraft/net/minecraft/item/ItemBanner.edit.java new file mode 100644 index 0000000..5eb0a2e --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemBanner.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 5 + +~ + +> DELETE 7 @ 8 : 12 + +> DELETE 9 @ 14 : 15 + +> DELETE 12 @ 18 : 19 + +> DELETE 36 @ 43 : 45 + +> DELETE 37 @ 46 : 62 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemBed.edit.java b/patches/minecraft/net/minecraft/item/ItemBed.edit.java new file mode 100644 index 0000000..c85a606 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemBed.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> DELETE 4 @ 7 : 10 + +> DELETE 6 @ 12 : 13 + +> CHANGE 15 : 16 @ 22 : 62 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemBlock.edit.java b/patches/minecraft/net/minecraft/item/ItemBlock.edit.java new file mode 100644 index 0000000..cee888c --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemBlock.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 10 @ 9 : 14 + +> CHANGE 63 : 64 @ 67 : 98 + +~ return false; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemBoat.edit.java b/patches/minecraft/net/minecraft/item/ItemBoat.edit.java new file mode 100644 index 0000000..61ce530 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemBoat.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 9 @ 8 : 10 + +> DELETE 82 @ 83 : 87 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemBook.edit.java b/patches/minecraft/net/minecraft/item/ItemBook.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemBook.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemBow.edit.java b/patches/minecraft/net/minecraft/item/ItemBow.edit.java new file mode 100644 index 0000000..ce85984 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemBow.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 8 @ 8 : 11 + +> DELETE 64 @ 67 : 70 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemBucket.edit.java b/patches/minecraft/net/minecraft/item/ItemBucket.edit.java new file mode 100644 index 0000000..ed1bae4 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemBucket.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 10 @ 10 : 12 + +> DELETE 115 @ 117 : 121 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemBucketMilk.edit.java b/patches/minecraft/net/minecraft/item/ItemBucketMilk.edit.java new file mode 100644 index 0000000..e478b02 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemBucketMilk.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 8 + +> DELETE 18 @ 21 : 26 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemCarrotOnAStick.edit.java b/patches/minecraft/net/minecraft/item/ItemCarrotOnAStick.edit.java new file mode 100644 index 0000000..caa38d2 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemCarrotOnAStick.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> DELETE 4 @ 5 : 9 + +> DELETE 22 @ 27 : 42 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemCloth.edit.java b/patches/minecraft/net/minecraft/item/ItemCloth.edit.java new file mode 100644 index 0000000..4890f7c --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemCloth.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemCoal.edit.java b/patches/minecraft/net/minecraft/item/ItemCoal.edit.java new file mode 100644 index 0000000..3efecf1 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemCoal.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemColored.edit.java b/patches/minecraft/net/minecraft/item/ItemColored.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemColored.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemDoor.edit.java b/patches/minecraft/net/minecraft/item/ItemDoor.edit.java new file mode 100644 index 0000000..bbee998 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemDoor.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemDoublePlant.edit.java b/patches/minecraft/net/minecraft/item/ItemDoublePlant.edit.java new file mode 100644 index 0000000..66c85a1 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemDoublePlant.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 6 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemDye.edit.java b/patches/minecraft/net/minecraft/item/ItemDye.edit.java new file mode 100644 index 0000000..fb91e05 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemDye.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 14 @ 13 : 16 + +> DELETE 42 @ 44 : 48 + +> CHANGE 78 : 79 @ 84 : 93 + +~ if (igrowable.canGrow(worldIn, target, iblockstate, true)) { + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemEditableBook.edit.java b/patches/minecraft/net/minecraft/item/ItemEditableBook.edit.java new file mode 100644 index 0000000..eb2154b --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemEditableBook.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 9 + +> DELETE 8 @ 12 : 13 + +> DELETE 65 @ 70 : 74 + +> DELETE 93 @ 102 : 109 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemEgg.edit.java b/patches/minecraft/net/minecraft/item/ItemEgg.edit.java new file mode 100644 index 0000000..b5d7155 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemEgg.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 7 + +> DELETE 19 @ 22 : 25 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemEmptyMap.edit.java b/patches/minecraft/net/minecraft/item/ItemEmptyMap.edit.java new file mode 100644 index 0000000..f9719d3 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemEmptyMap.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemEnchantedBook.edit.java b/patches/minecraft/net/minecraft/item/ItemEnchantedBook.edit.java new file mode 100644 index 0000000..3d63f06 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemEnchantedBook.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 10 @ 9 : 12 + +> CHANGE 92 : 93 @ 94 : 95 + +~ public WeightedRandomChestContent getRandom(EaglercraftRandom rand) { + +> CHANGE 96 : 97 @ 98 : 99 + +~ public WeightedRandomChestContent getRandom(EaglercraftRandom rand, int minChance, int maxChance, int weight) { + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemEnderEye.edit.java b/patches/minecraft/net/minecraft/item/ItemEnderEye.edit.java new file mode 100644 index 0000000..081feb5 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemEnderEye.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> DELETE 7 @ 8 : 11 + +> DELETE 9 @ 13 : 15 + +> CHANGE 22 : 23 @ 28 : 111 + +~ return true; + +> DELETE 28 @ 116 : 144 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemEnderPearl.edit.java b/patches/minecraft/net/minecraft/item/ItemEnderPearl.edit.java new file mode 100644 index 0000000..51b81c1 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemEnderPearl.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> DELETE 4 @ 5 : 7 + +> DELETE 19 @ 22 : 25 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemExpBottle.edit.java b/patches/minecraft/net/minecraft/item/ItemExpBottle.edit.java new file mode 100644 index 0000000..079eab8 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemExpBottle.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> DELETE 4 @ 5 : 7 + +> DELETE 22 @ 25 : 28 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemFireball.edit.java b/patches/minecraft/net/minecraft/item/ItemFireball.edit.java new file mode 100644 index 0000000..2aba31d --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemFireball.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 4 @ 5 : 8 + +> CHANGE 15 : 16 @ 19 : 40 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemFirework.edit.java b/patches/minecraft/net/minecraft/item/ItemFirework.edit.java new file mode 100644 index 0000000..880bac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemFirework.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 4 : 7 @ 5 : 6 + +~ +~ import com.google.common.collect.Lists; +~ + +> DELETE 8 @ 7 : 10 + +> DELETE 10 @ 12 : 14 + +> DELETE 11 @ 15 : 16 + +> DELETE 13 @ 18 : 28 + +> DELETE 14 @ 29 : 35 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemFireworkCharge.edit.java b/patches/minecraft/net/minecraft/item/ItemFireworkCharge.edit.java new file mode 100644 index 0000000..d87ed20 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemFireworkCharge.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemFishFood.edit.java b/patches/minecraft/net/minecraft/item/ItemFishFood.edit.java new file mode 100644 index 0000000..6a7724d --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemFishFood.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Maps; ++ + +> DELETE 9 @ 7 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemFishingRod.edit.java b/patches/minecraft/net/minecraft/item/ItemFishingRod.edit.java new file mode 100644 index 0000000..679079d --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemFishingRod.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 7 + +> DELETE 29 @ 32 : 36 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemFlintAndSteel.edit.java b/patches/minecraft/net/minecraft/item/ItemFlintAndSteel.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemFlintAndSteel.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemFood.edit.java b/patches/minecraft/net/minecraft/item/ItemFood.edit.java new file mode 100644 index 0000000..15f8626 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemFood.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 8 + +> DELETE 40 @ 44 : 49 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemGlassBottle.edit.java b/patches/minecraft/net/minecraft/item/ItemGlassBottle.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemGlassBottle.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemHangingEntity.edit.java b/patches/minecraft/net/minecraft/item/ItemHangingEntity.edit.java new file mode 100644 index 0000000..68096ad --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemHangingEntity.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 9 + +> DELETE 32 @ 34 : 38 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemHoe.edit.java b/patches/minecraft/net/minecraft/item/ItemHoe.edit.java new file mode 100644 index 0000000..eda18e3 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemHoe.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 9 @ 9 : 11 + +> CHANGE 57 : 58 @ 59 : 66 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemLead.edit.java b/patches/minecraft/net/minecraft/item/ItemLead.edit.java new file mode 100644 index 0000000..e21b058 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemLead.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 8 @ 8 : 10 + +> CHANGE 22 : 23 @ 24 : 30 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemLeaves.edit.java b/patches/minecraft/net/minecraft/item/ItemLeaves.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemLeaves.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemLilyPad.edit.java b/patches/minecraft/net/minecraft/item/ItemLilyPad.edit.java new file mode 100644 index 0000000..1187e49 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemLilyPad.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 8 @ 8 : 11 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemMap.edit.java b/patches/minecraft/net/minecraft/item/ItemMap.edit.java new file mode 100644 index 0000000..28fb95b --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemMap.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import java.util.List; ++ + +> CHANGE 7 : 8 @ 5 : 6 + +~ + +> DELETE 17 @ 15 : 17 + +> DELETE 43 @ 43 : 55 + +> DELETE 180 @ 192 : 207 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemMapBase.edit.java b/patches/minecraft/net/minecraft/item/ItemMapBase.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemMapBase.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemMinecart.edit.java b/patches/minecraft/net/minecraft/item/ItemMinecart.edit.java new file mode 100644 index 0000000..00de504 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemMinecart.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 12 @ 12 : 14 + +> DELETE 87 @ 89 : 109 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemMonsterPlacer.edit.java b/patches/minecraft/net/minecraft/item/ItemMonsterPlacer.edit.java new file mode 100644 index 0000000..6492304 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemMonsterPlacer.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 6 + +~ + +> DELETE 11 @ 13 : 20 + +> DELETE 14 @ 23 : 24 + +> CHANGE 43 : 44 @ 53 : 94 + +~ return true; + +> DELETE 46 @ 96 : 137 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemMultiTexture.edit.java b/patches/minecraft/net/minecraft/item/ItemMultiTexture.edit.java new file mode 100644 index 0000000..3efecf1 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemMultiTexture.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemNameTag.edit.java b/patches/minecraft/net/minecraft/item/ItemNameTag.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemNameTag.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemPickaxe.edit.java b/patches/minecraft/net/minecraft/item/ItemPickaxe.edit.java new file mode 100644 index 0000000..cc7c7ae --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemPickaxe.edit.java @@ -0,0 +1,34 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Sets; ++ + +> DELETE 9 @ 7 : 10 + +> CHANGE 11 : 12 @ 12 : 18 + +~ private static Set EFFECTIVE_ON; + +> INSERT 13 : 22 @ 19 + ++ public static void bootstrap() { ++ EFFECTIVE_ON = Sets.newHashSet(new Block[] { Blocks.activator_rail, Blocks.coal_ore, Blocks.cobblestone, ++ Blocks.detector_rail, Blocks.diamond_block, Blocks.diamond_ore, Blocks.double_stone_slab, ++ Blocks.golden_rail, Blocks.gold_block, Blocks.gold_ore, Blocks.ice, Blocks.iron_block, Blocks.iron_ore, ++ Blocks.lapis_block, Blocks.lapis_ore, Blocks.lit_redstone_ore, Blocks.mossy_cobblestone, ++ Blocks.netherrack, Blocks.packed_ice, Blocks.rail, Blocks.redstone_ore, Blocks.sandstone, ++ Blocks.red_sandstone, Blocks.stone, Blocks.stone_slab }); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemPiston.edit.java b/patches/minecraft/net/minecraft/item/ItemPiston.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemPiston.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemPotion.edit.java b/patches/minecraft/net/minecraft/item/ItemPotion.edit.java new file mode 100644 index 0000000..cb8ef1c --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemPotion.edit.java @@ -0,0 +1,43 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 7 : 13 @ 10 + ++ import java.util.Set; ++ ++ import com.google.common.collect.HashMultimap; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> DELETE 17 @ 14 : 15 + +> DELETE 18 @ 16 : 19 + +> DELETE 79 @ 80 : 89 + +> DELETE 106 @ 116 : 119 + +> CHANGE 130 : 131 @ 143 : 144 + +~ for (PotionEffect potioneffect : (List) list) { + +> CHANGE 168 : 169 @ 181 : 182 + +~ for (PotionEffect potioneffect : (List) list1) { + +> CHANGE 173 : 174 @ 186 : 187 + +~ for (Entry entry : (Set) map.entrySet()) { + +> CHANGE 207 : 208 @ 220 : 221 + +~ for (Entry entry1 : (Set) hashmultimap.entries()) { + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemRecord.edit.java b/patches/minecraft/net/minecraft/item/ItemRecord.edit.java new file mode 100644 index 0000000..007d1e3 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemRecord.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Maps; ++ + +> DELETE 12 @ 10 : 14 + +> CHANGE 33 : 34 @ 35 : 44 + +~ return true; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemRedstone.edit.java b/patches/minecraft/net/minecraft/item/ItemRedstone.edit.java new file mode 100644 index 0000000..bbee998 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemRedstone.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemReed.edit.java b/patches/minecraft/net/minecraft/item/ItemReed.edit.java new file mode 100644 index 0000000..1187e49 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemReed.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 8 @ 8 : 11 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSaddle.edit.java b/patches/minecraft/net/minecraft/item/ItemSaddle.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSaddle.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSeedFood.edit.java b/patches/minecraft/net/minecraft/item/ItemSeedFood.edit.java new file mode 100644 index 0000000..a095108 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSeedFood.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSeeds.edit.java b/patches/minecraft/net/minecraft/item/ItemSeeds.edit.java new file mode 100644 index 0000000..75f4d7b --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSeeds.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemShears.edit.java b/patches/minecraft/net/minecraft/item/ItemShears.edit.java new file mode 100644 index 0000000..bbee998 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemShears.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSign.edit.java b/patches/minecraft/net/minecraft/item/ItemSign.edit.java new file mode 100644 index 0000000..7fdb9fb --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSign.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 5 @ 7 : 12 + +> DELETE 7 @ 14 : 15 + +> DELETE 27 @ 35 : 37 + +> DELETE 28 @ 38 : 55 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSimpleFoiled.edit.java b/patches/minecraft/net/minecraft/item/ItemSimpleFoiled.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSimpleFoiled.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSkull.edit.java b/patches/minecraft/net/minecraft/item/ItemSkull.edit.java new file mode 100644 index 0000000..75608e9 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSkull.edit.java @@ -0,0 +1,30 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> DELETE 7 @ 6 : 7 + +> DELETE 11 @ 11 : 13 + +> DELETE 13 @ 15 : 16 + +> DELETE 16 @ 19 : 20 + +> DELETE 49 @ 53 : 89 + +> CHANGE 96 : 97 @ 136 : 137 + +~ GameProfile gameprofile = new GameProfile((EaglercraftUUID) null, nbt.getString("SkullOwner")); + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSlab.edit.java b/patches/minecraft/net/minecraft/item/ItemSlab.edit.java new file mode 100644 index 0000000..5e03c1f --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSlab.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 9 + +> CHANGE 95 : 96 @ 97 : 98 + +~ .withProperty((IProperty) this.singleSlab.getVariantProperty(), comparable); + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSnow.edit.java b/patches/minecraft/net/minecraft/item/ItemSnow.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSnow.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSnowball.edit.java b/patches/minecraft/net/minecraft/item/ItemSnowball.edit.java new file mode 100644 index 0000000..b5d7155 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSnowball.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 7 + +> DELETE 19 @ 22 : 25 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSoup.edit.java b/patches/minecraft/net/minecraft/item/ItemSoup.edit.java new file mode 100644 index 0000000..a095108 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSoup.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSpade.edit.java b/patches/minecraft/net/minecraft/item/ItemSpade.edit.java new file mode 100644 index 0000000..d2f0868 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSpade.edit.java @@ -0,0 +1,30 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Sets; ++ + +> DELETE 8 @ 6 : 8 + +> CHANGE 10 : 11 @ 10 : 13 + +~ private static Set EFFECTIVE_ON; + +> INSERT 12 : 17 @ 14 + ++ public static void bootstrap() { ++ EFFECTIVE_ON = Sets.newHashSet(new Block[] { Blocks.clay, Blocks.dirt, Blocks.farmland, Blocks.grass, ++ Blocks.gravel, Blocks.mycelium, Blocks.sand, Blocks.snow, Blocks.snow_layer, Blocks.soul_sand }); ++ } ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemStack.edit.java b/patches/minecraft/net/minecraft/item/ItemStack.edit.java new file mode 100644 index 0000000..128dc40 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemStack.edit.java @@ -0,0 +1,41 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> DELETE 5 @ 8 : 9 + +> INSERT 6 : 14 @ 10 + ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ import java.util.Set; ++ ++ import com.google.common.collect.HashMultimap; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Multimap; ++ + +> DELETE 27 @ 23 : 27 + +> CHANGE 212 : 213 @ 212 : 213 + +~ public boolean attemptDamageItem(int amount, EaglercraftRandom rand) { + +> CHANGE 462 : 463 @ 462 : 463 + +~ s = s + HString.format("#%04d/%d%s", + +> CHANGE 465 : 466 @ 465 : 466 + +~ s = s + HString.format("#%04d%s", new Object[] { Integer.valueOf(i), s1 }); + +> CHANGE 522 : 523 @ 522 : 523 + +~ for (Entry entry : (Set) multimap.entries()) { + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemSword.edit.java b/patches/minecraft/net/minecraft/item/ItemSword.edit.java new file mode 100644 index 0000000..a7a99d0 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemSword.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 12 @ 11 : 14 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemTool.edit.java b/patches/minecraft/net/minecraft/item/ItemTool.edit.java new file mode 100644 index 0000000..8f8780a --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemTool.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Multimap; ++ + +> DELETE 11 @ 9 : 11 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/ItemWritableBook.edit.java b/patches/minecraft/net/minecraft/item/ItemWritableBook.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/ItemWritableBook.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/CraftingManager.edit.java b/patches/minecraft/net/minecraft/item/crafting/CraftingManager.edit.java new file mode 100644 index 0000000..0c016d3 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/CraftingManager.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 7 : 11 @ 9 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> DELETE 23 @ 21 : 38 + +> CHANGE 26 : 27 @ 41 : 42 + +~ private static CraftingManager instance; + +> INSERT 30 : 33 @ 45 + ++ if (instance == null) { ++ instance = new CraftingManager(); ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipes.edit.java b/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipes.edit.java new file mode 100644 index 0000000..748bdf0 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/FurnaceRecipes.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Maps; ++ + +> CHANGE 17 : 18 @ 15 : 16 + +~ private static FurnaceRecipes smeltingBase; + +> INSERT 22 : 25 @ 20 + ++ if (smeltingBase == null) { ++ smeltingBase = new FurnaceRecipes(); ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.edit.java new file mode 100644 index 0000000..585f95e --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeBookCloning.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.edit.java new file mode 100644 index 0000000..9de9c4f --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeFireworks.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 10 @ 8 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.edit.java new file mode 100644 index 0000000..a2eaaa6 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipeRepairItem.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 9 @ 7 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesArmor.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesArmor.edit.java new file mode 100644 index 0000000..b7b408f --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesArmor.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.edit.java new file mode 100644 index 0000000..b55299d --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 12 @ 10 : 11 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.edit.java new file mode 100644 index 0000000..bbee998 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesBanners.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesCrafting.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesCrafting.edit.java new file mode 100644 index 0000000..45da252 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesCrafting.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 16 @ 16 : 17 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesDyes.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesDyes.edit.java new file mode 100644 index 0000000..b8cec50 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesDyes.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 9 @ 9 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesFood.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesFood.edit.java new file mode 100644 index 0000000..585f95e --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesFood.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesIngots.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesIngots.edit.java new file mode 100644 index 0000000..e92233f --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesIngots.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 8 + +> CHANGE 9 : 10 @ 10 : 18 + +~ private Object[][] recipeItems; + +> INSERT 12 : 21 @ 20 + ++ recipeItems = new Object[][] { { Blocks.gold_block, new ItemStack(Items.gold_ingot, 9) }, ++ { Blocks.iron_block, new ItemStack(Items.iron_ingot, 9) }, ++ { Blocks.diamond_block, new ItemStack(Items.diamond, 9) }, ++ { Blocks.emerald_block, new ItemStack(Items.emerald, 9) }, ++ { Blocks.lapis_block, new ItemStack(Items.dye, 9, EnumDyeColor.BLUE.getDyeDamage()) }, ++ { Blocks.redstone_block, new ItemStack(Items.redstone, 9) }, ++ { Blocks.coal_block, new ItemStack(Items.coal, 9, 0) }, ++ { Blocks.hay_block, new ItemStack(Items.wheat, 9) }, ++ { Blocks.slime_block, new ItemStack(Items.slime_ball, 9) } }; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.edit.java new file mode 100644 index 0000000..b7b408f --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesMapCloning.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesMapExtending.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesMapExtending.edit.java new file mode 100644 index 0000000..b7b408f --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesMapExtending.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesTools.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesTools.edit.java new file mode 100644 index 0000000..a5cbccb --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesTools.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> CHANGE 10 : 11 @ 11 : 18 + +~ private Object[][] recipeItems; + +> INSERT 13 : 21 @ 20 + ++ recipeItems = new Object[][] { ++ { Blocks.planks, Blocks.cobblestone, Items.iron_ingot, Items.diamond, Items.gold_ingot }, ++ { Items.wooden_pickaxe, Items.stone_pickaxe, Items.iron_pickaxe, Items.diamond_pickaxe, ++ Items.golden_pickaxe }, ++ { Items.wooden_shovel, Items.stone_shovel, Items.iron_shovel, Items.diamond_shovel, ++ Items.golden_shovel }, ++ { Items.wooden_axe, Items.stone_axe, Items.iron_axe, Items.diamond_axe, Items.golden_axe }, ++ { Items.wooden_hoe, Items.stone_hoe, Items.iron_hoe, Items.diamond_hoe, Items.golden_hoe } }; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/RecipesWeapons.edit.java b/patches/minecraft/net/minecraft/item/crafting/RecipesWeapons.edit.java new file mode 100644 index 0000000..a267371 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/RecipesWeapons.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> CHANGE 9 : 10 @ 10 : 13 + +~ private Object[][] recipeItems; + +> INSERT 12 : 15 @ 15 + ++ recipeItems = new Object[][] { ++ { Blocks.planks, Blocks.cobblestone, Items.iron_ingot, Items.diamond, Items.gold_ingot }, ++ { Items.wooden_sword, Items.stone_sword, Items.iron_sword, Items.diamond_sword, Items.golden_sword } }; + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.edit.java b/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/ShapedRecipes.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.edit.java b/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.edit.java new file mode 100644 index 0000000..5d19591 --- /dev/null +++ b/patches/minecraft/net/minecraft/item/crafting/ShapelessRecipes.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 9 @ 7 : 8 + +> CHANGE 46 : 47 @ 45 : 46 + +~ for (ItemStack itemstack1 : (List) arraylist) { + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/CompressedStreamTools.edit.java b/patches/minecraft/net/minecraft/nbt/CompressedStreamTools.edit.java new file mode 100644 index 0000000..02fe817 --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/CompressedStreamTools.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 8 @ 8 : 11 + +> CHANGE 11 : 13 @ 14 : 16 + +~ +~ import net.lax1dude.eaglercraft.v1_8.EaglerZLIB; + +> DELETE 15 @ 18 : 22 + +> CHANGE 19 : 21 @ 26 : 27 + +~ DataInputStream datainputstream = new DataInputStream( +~ new BufferedInputStream(EaglerZLIB.newGZIPInputStream(is))); + +> CHANGE 34 : 35 @ 40 : 41 + +~ new BufferedOutputStream(EaglerZLIB.newGZIPOutputStream(parOutputStream))); + +> DELETE 44 @ 50 : 96 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/JsonToNBT.edit.java b/patches/minecraft/net/minecraft/nbt/JsonToNBT.edit.java new file mode 100644 index 0000000..580766c --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/JsonToNBT.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import java.util.Stack; ++ import java.util.regex.Pattern; ++ + +> DELETE 8 @ 5 : 21 + +> INSERT 9 : 12 @ 22 + ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTBase.edit.java b/patches/minecraft/net/minecraft/nbt/NBTBase.edit.java new file mode 100644 index 0000000..da3d928 --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTBase.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 18 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagByte.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagByte.edit.java new file mode 100644 index 0000000..75f4d7b --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagByte.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagByteArray.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagByteArray.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagByteArray.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagCompound.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagCompound.edit.java new file mode 100644 index 0000000..f19faf7 --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagCompound.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 6 @ 7 : 8 + +> INSERT 7 : 8 @ 9 + ++ import java.util.Set; + +> INSERT 9 : 12 @ 10 + ++ ++ import com.google.common.collect.Maps; ++ + +> DELETE 14 @ 12 : 24 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagDouble.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagDouble.edit.java new file mode 100644 index 0000000..120f801 --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagDouble.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 5 : 6 @ 5 : 7 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagEnd.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagEnd.edit.java new file mode 100644 index 0000000..75f4d7b --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagEnd.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagFloat.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagFloat.edit.java new file mode 100644 index 0000000..120f801 --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagFloat.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 5 : 6 @ 5 : 7 + +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagInt.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagInt.edit.java new file mode 100644 index 0000000..75f4d7b --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagInt.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagIntArray.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagIntArray.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagIntArray.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagList.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagList.edit.java new file mode 100644 index 0000000..b2287ee --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagList.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 6 @ 7 : 16 + +> INSERT 7 : 12 @ 17 + ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagLong.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagLong.edit.java new file mode 100644 index 0000000..75f4d7b --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagLong.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagShort.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagShort.edit.java new file mode 100644 index 0000000..75f4d7b --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagShort.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTTagString.edit.java b/patches/minecraft/net/minecraft/nbt/NBTTagString.edit.java new file mode 100644 index 0000000..75f4d7b --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTTagString.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/nbt/NBTUtil.edit.java b/patches/minecraft/net/minecraft/nbt/NBTUtil.edit.java new file mode 100644 index 0000000..6f406af --- /dev/null +++ b/patches/minecraft/net/minecraft/nbt/NBTUtil.edit.java @@ -0,0 +1,60 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 9 @ 2 : 8 + +~ import com.google.common.collect.Multimap; +~ import com.google.common.collect.MultimapBuilder; +~ +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.Property; + +> CHANGE 26 : 27 @ 25 : 26 + +~ EaglercraftUUID uuid; + +> CHANGE 28 : 29 @ 27 : 28 + +~ uuid = EaglercraftUUID.fromString(s1); + +> CHANGE 33 : 34 @ 32 : 33 + +~ Multimap propertiesMap = MultimapBuilder.hashKeys().arrayListValues().build(); + +> DELETE 36 @ 35 : 36 + +> CHANGE 38 : 39 @ 38 : 40 + +~ for (int i = 0, l = nbttaglist.tagCount(); i < l; ++i) { + +> CHANGE 40 : 48 @ 41 : 47 + +~ String value = nbttagcompound1.getString("Value"); +~ if (!StringUtils.isNullOrEmpty(value)) { +~ String sig = nbttagcompound1.getString("Signature"); +~ if (!StringUtils.isNullOrEmpty(sig)) { +~ propertiesMap.put(s2, new Property(s2, value, sig)); +~ } else { +~ propertiesMap.put(s2, new Property(s2, value)); +~ } + +> CHANGE 53 : 54 @ 52 : 53 + +~ return new GameProfile(uuid, s, propertiesMap); + +> CHANGE 66 : 68 @ 65 : 66 + +~ Multimap propertiesMap = profile.getProperties(); +~ if (!propertiesMap.isEmpty()) { + +> DELETE 69 @ 67 : 68 + +> DELETE 84 @ 83 : 85 + +> EOF diff --git a/patches/minecraft/net/minecraft/network/EnumConnectionState.edit.java b/patches/minecraft/net/minecraft/network/EnumConnectionState.edit.java new file mode 100644 index 0000000..774384f --- /dev/null +++ b/patches/minecraft/net/minecraft/network/EnumConnectionState.edit.java @@ -0,0 +1,30 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import java.util.Collection; ++ import java.util.Map; ++ + +> CHANGE 8 : 10 @ 5 : 8 + +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; + +> DELETE 115 @ 113 : 114 + +> CHANGE 262 : 263 @ 261 : 262 + +~ this.directionMaps.put(direction, (BiMap>) object); + +> CHANGE 308 : 310 @ 307 : 308 + +~ for (Class oclass : (Collection) ((BiMap) enumconnectionstate.directionMaps +~ .get(enumpacketdirection)).values()) { + +> EOF diff --git a/patches/minecraft/net/minecraft/network/Packet.edit.java b/patches/minecraft/net/minecraft/network/Packet.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/Packet.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/network/PacketBuffer.edit.java b/patches/minecraft/net/minecraft/network/PacketBuffer.edit.java new file mode 100644 index 0000000..e1f7bb6 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/PacketBuffer.edit.java @@ -0,0 +1,143 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 10 + +> DELETE 8 @ 16 : 18 + +> CHANGE 9 : 18 @ 19 : 20 + +~ import java.nio.charset.StandardCharsets; +~ +~ import net.lax1dude.eaglercraft.v1_8.DecoderException; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ import net.lax1dude.eaglercraft.v1_8.EncoderException; +~ +~ import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; +~ import net.lax1dude.eaglercraft.v1_8.netty.ByteBufInputStream; +~ import net.lax1dude.eaglercraft.v1_8.netty.ByteBufOutputStream; + +> CHANGE 116 : 117 @ 118 : 119 + +~ public void writeUuid(EaglercraftUUID uuid) { + +> CHANGE 121 : 123 @ 123 : 125 + +~ public EaglercraftUUID readUuid() { +~ return new EaglercraftUUID(this.readLong(), this.readLong()); + +> CHANGE 205 : 206 @ 207 : 208 + +~ String s = new String(this.readBytes(i).array(), StandardCharsets.UTF_8); + +> CHANGE 216 : 217 @ 218 : 219 + +~ byte[] abyte = string.getBytes(StandardCharsets.UTF_8); + +> DELETE 238 @ 240 : 244 + +> CHANGE 391 : 396 @ 397 : 398 + +~ if (parByteBuf instanceof PacketBuffer) { +~ return this.buf.getBytes(parInt1, ((PacketBuffer) parByteBuf).buf); +~ } else { +~ return this.buf.getBytes(parInt1, parByteBuf); +~ } + +> CHANGE 399 : 404 @ 401 : 402 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.getBytes(i, ((PacketBuffer) bytebuf).buf, j); +~ } else { +~ return this.buf.getBytes(i, bytebuf, j); +~ } + +> CHANGE 407 : 412 @ 405 : 406 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.getBytes(i, ((PacketBuffer) bytebuf).buf, j, k); +~ } else { +~ return this.buf.getBytes(i, bytebuf, j, k); +~ } + +> DELETE 430 @ 424 : 428 + +> CHANGE 471 : 476 @ 469 : 470 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.setBytes(i, ((PacketBuffer) bytebuf).buf, j); +~ } else { +~ return this.buf.setBytes(i, bytebuf, j); +~ } + +> CHANGE 479 : 484 @ 473 : 474 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.setBytes(i, ((PacketBuffer) bytebuf).buf, j, k); +~ } else { +~ return this.buf.setBytes(i, bytebuf, j, k); +~ } + +> DELETE 502 @ 492 : 496 + +> CHANGE 567 : 572 @ 561 : 562 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.readBytes(((PacketBuffer) bytebuf).buf); +~ } else { +~ return this.buf.readBytes(bytebuf); +~ } + +> CHANGE 575 : 580 @ 565 : 566 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.readBytes(((PacketBuffer) bytebuf).buf, i); +~ } else { +~ return this.buf.readBytes(bytebuf, i); +~ } + +> CHANGE 583 : 588 @ 569 : 570 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.readBytes(((PacketBuffer) bytebuf).buf, i, j); +~ } else { +~ return this.buf.readBytes(bytebuf, i, j); +~ } + +> DELETE 606 @ 588 : 592 + +> CHANGE 647 : 652 @ 633 : 634 + +~ if (parByteBuf instanceof PacketBuffer) { +~ return this.buf.writeBytes(((PacketBuffer) parByteBuf).buf); +~ } else { +~ return this.buf.writeBytes(parByteBuf); +~ } + +> CHANGE 655 : 660 @ 637 : 638 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.writeBytes(((PacketBuffer) bytebuf).buf, i); +~ } else { +~ return this.buf.writeBytes(bytebuf, i); +~ } + +> CHANGE 663 : 668 @ 641 : 642 + +~ if (bytebuf instanceof PacketBuffer) { +~ return this.buf.writeBytes(((PacketBuffer) bytebuf).buf, i, j); +~ } else { +~ return this.buf.writeBytes(bytebuf, i, j); +~ } + +> DELETE 686 @ 660 : 664 + +> DELETE 706 @ 684 : 700 + +> DELETE 794 @ 788 : 807 + +> EOF diff --git a/patches/minecraft/net/minecraft/network/ServerStatusResponse.edit.java b/patches/minecraft/net/minecraft/network/ServerStatusResponse.edit.java new file mode 100644 index 0000000..81e0fa7 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/ServerStatusResponse.edit.java @@ -0,0 +1,129 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 10 @ 2 : 13 + +~ import org.json.JSONArray; +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeCodec; +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> DELETE 11 @ 14 : 15 + +> CHANGE 68 : 73 @ 72 : 79 + +~ implements JSONTypeCodec { +~ public ServerStatusResponse.MinecraftProtocolVersionIdentifier deserialize(JSONObject jsonobject) +~ throws JSONException { +~ return new ServerStatusResponse.MinecraftProtocolVersionIdentifier(jsonobject.getString("name"), +~ jsonobject.getInt("protocol")); + +> CHANGE 75 : 80 @ 81 : 87 + +~ public JSONObject serialize( +~ ServerStatusResponse.MinecraftProtocolVersionIdentifier serverstatusresponse$minecraftprotocolversionidentifier) { +~ JSONObject jsonobject = new JSONObject(); +~ jsonobject.put("name", serverstatusresponse$minecraftprotocolversionidentifier.getName()); +~ jsonobject.put("protocol", + +> CHANGE 112 : 114 @ 119 : 124 + +~ public static class Serializer implements JSONTypeCodec { +~ public ServerStatusResponse.PlayerCountData deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 115 : 120 @ 125 : 130 + +~ jsonobject.getInt("max"), jsonobject.getInt("online")); +~ JSONArray jsonarray = jsonobject.optJSONArray("sample"); +~ if (jsonarray != null) { +~ if (jsonarray.length() > 0) { +~ GameProfile[] agameprofile = new GameProfile[jsonarray.length()]; + +> CHANGE 122 : 126 @ 132 : 136 + +~ JSONObject jsonobject1 = jsonarray.getJSONObject(i); +~ String s = jsonobject1.getString("id"); +~ agameprofile[i] = new GameProfile(EaglercraftUUID.fromString(s), +~ jsonobject1.getString("name")); + +> CHANGE 135 : 140 @ 145 : 151 + +~ public JSONObject serialize(ServerStatusResponse.PlayerCountData serverstatusresponse$playercountdata) +~ throws JSONException { +~ JSONObject jsonobject = new JSONObject(); +~ jsonobject.put("max", Integer.valueOf(serverstatusresponse$playercountdata.getMaxPlayers())); +~ jsonobject.put("online", Integer.valueOf(serverstatusresponse$playercountdata.getOnlinePlayerCount())); + +> CHANGE 142 : 143 @ 153 : 154 + +~ JSONArray jsonarray = new JSONArray(); + +> CHANGE 145 : 150 @ 156 : 161 + +~ JSONObject jsonobject1 = new JSONObject(); +~ EaglercraftUUID uuid = serverstatusresponse$playercountdata.getPlayers()[i].getId(); +~ jsonobject1.put("id", uuid == null ? "" : uuid.toString()); +~ jsonobject1.put("name", serverstatusresponse$playercountdata.getPlayers()[i].getName()); +~ jsonarray.put(jsonobject1); + +> CHANGE 152 : 153 @ 163 : 164 + +~ jsonobject.put("sample", jsonarray); + +> CHANGE 160 : 162 @ 171 : 176 + +~ public static class Serializer implements JSONTypeCodec { +~ public ServerStatusResponse deserialize(JSONObject jsonobject) throws JSONException { + +> CHANGE 164 : 165 @ 178 : 179 + +~ serverstatusresponse.setServerDescription((IChatComponent) JSONTypeProvider + +> CHANGE 169 : 171 @ 183 : 186 + +~ serverstatusresponse.setPlayerCountData((ServerStatusResponse.PlayerCountData) JSONTypeProvider +~ .deserialize(jsonobject.get("players"), ServerStatusResponse.PlayerCountData.class)); + +> CHANGE 175 : 178 @ 190 : 193 + +~ (ServerStatusResponse.MinecraftProtocolVersionIdentifier) JSONTypeProvider.deserialize( +~ jsonobject.get("version"), +~ ServerStatusResponse.MinecraftProtocolVersionIdentifier.class)); + +> CHANGE 181 : 182 @ 196 : 197 + +~ serverstatusresponse.setFavicon(jsonobject.getString("favicon")); + +> CHANGE 187 : 189 @ 202 : 205 + +~ public JSONObject serialize(ServerStatusResponse serverstatusresponse) { +~ JSONObject jsonobject = new JSONObject(); + +> CHANGE 190 : 192 @ 206 : 208 + +~ jsonobject.put("description", +~ (Object) JSONTypeProvider.serialize(serverstatusresponse.getServerDescription())); + +> CHANGE 195 : 197 @ 211 : 213 + +~ jsonobject.put("players", +~ (Object) JSONTypeProvider.serialize(serverstatusresponse.getPlayerCountData())); + +> CHANGE 200 : 202 @ 216 : 218 + +~ jsonobject.put("version", +~ (Object) JSONTypeProvider.serialize(serverstatusresponse.getProtocolVersionInfo())); + +> CHANGE 205 : 206 @ 221 : 222 + +~ jsonobject.put("favicon", serverstatusresponse.getFavicon()); + +> EOF diff --git a/patches/minecraft/net/minecraft/network/handshake/client/C00Handshake.edit.java b/patches/minecraft/net/minecraft/network/handshake/client/C00Handshake.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/handshake/client/C00Handshake.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/login/client/C00PacketLoginStart.edit.java b/patches/minecraft/net/minecraft/network/login/client/C00PacketLoginStart.edit.java new file mode 100644 index 0000000..70353de --- /dev/null +++ b/patches/minecraft/net/minecraft/network/login/client/C00PacketLoginStart.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> CHANGE 21 : 22 @ 20 : 21 + +~ this.profile = new GameProfile((EaglercraftUUID) null, parPacketBuffer.readStringFromBuffer(16)); + +> EOF diff --git a/patches/minecraft/net/minecraft/network/login/client/C01PacketEncryptionResponse.edit.java b/patches/minecraft/net/minecraft/network/login/client/C01PacketEncryptionResponse.edit.java new file mode 100644 index 0000000..2897856 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/login/client/C01PacketEncryptionResponse.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 6 + +~ + +> DELETE 7 @ 9 : 10 + +> CHANGE 15 : 19 @ 18 : 22 + +~ // public C01PacketEncryptionResponse(SecretKey secretKey, PublicKey publicKey, byte[] verifyToken) { +~ // this.secretKeyEncrypted = CryptManager.encryptData(publicKey, secretKey.getEncoded()); +~ // this.verifyTokenEncrypted = CryptManager.encryptData(publicKey, verifyToken); +~ // } + +> CHANGE 34 : 37 @ 37 : 40 + +~ // public SecretKey getSecretKey(PrivateKey key) { +~ // return CryptManager.decryptSharedKey(key, this.secretKeyEncrypted); +~ // } + +> CHANGE 38 : 41 @ 41 : 44 + +~ // public byte[] getVerifyToken(PrivateKey key) { +~ // return key == null ? this.verifyTokenEncrypted : CryptManager.decryptData(key, this.verifyTokenEncrypted); +~ // } + +> EOF diff --git a/patches/minecraft/net/minecraft/network/login/server/S00PacketDisconnect.edit.java b/patches/minecraft/net/minecraft/network/login/server/S00PacketDisconnect.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/login/server/S00PacketDisconnect.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/login/server/S01PacketEncryptionRequest.edit.java b/patches/minecraft/net/minecraft/network/login/server/S01PacketEncryptionRequest.edit.java new file mode 100644 index 0000000..1e27e24 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/login/server/S01PacketEncryptionRequest.edit.java @@ -0,0 +1,44 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ + +> DELETE 7 @ 7 : 8 + +> CHANGE 10 : 11 @ 11 : 12 + +~ // private PublicKey publicKey; + +> CHANGE 16 : 21 @ 17 : 22 + +~ // public S01PacketEncryptionRequest(String serverId, PublicKey key, byte[] verifyToken) { +~ // this.hashedServerId = serverId; +~ // this.publicKey = key; +~ // this.verifyToken = verifyToken; +~ // } + +> CHANGE 24 : 27 @ 25 : 26 + +~ // this.publicKey = +~ // CryptManager.decodePublicKey(parPacketBuffer.readByteArray()); +~ parPacketBuffer.readByteArray(); // skip + +> CHANGE 31 : 34 @ 30 : 33 + +~ // parPacketBuffer.writeString(this.hashedServerId); +~ // parPacketBuffer.writeByteArray(this.publicKey.getEncoded()); +~ // parPacketBuffer.writeByteArray(this.verifyToken); + +> CHANGE 44 : 47 @ 43 : 46 + +~ // public PublicKey getPublicKey() { +~ // return this.publicKey; +~ // } + +> EOF diff --git a/patches/minecraft/net/minecraft/network/login/server/S02PacketLoginSuccess.edit.java b/patches/minecraft/net/minecraft/network/login/server/S02PacketLoginSuccess.edit.java new file mode 100644 index 0000000..3843f53 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/login/server/S02PacketLoginSuccess.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 6 @ 4 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> CHANGE 23 : 24 @ 22 : 23 + +~ EaglercraftUUID uuid = EaglercraftUUID.fromString(s); + +> CHANGE 28 : 29 @ 27 : 28 + +~ EaglercraftUUID uuid = this.profile.getId(); + +> EOF diff --git a/patches/minecraft/net/minecraft/network/login/server/S03PacketEnableCompression.edit.java b/patches/minecraft/net/minecraft/network/login/server/S03PacketEnableCompression.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/login/server/S03PacketEnableCompression.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C00PacketKeepAlive.edit.java b/patches/minecraft/net/minecraft/network/play/client/C00PacketKeepAlive.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C00PacketKeepAlive.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.edit.java b/patches/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C02PacketUseEntity.edit.java b/patches/minecraft/net/minecraft/network/play/client/C02PacketUseEntity.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C02PacketUseEntity.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C03PacketPlayer.edit.java b/patches/minecraft/net/minecraft/network/play/client/C03PacketPlayer.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C03PacketPlayer.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C07PacketPlayerDigging.edit.java b/patches/minecraft/net/minecraft/network/play/client/C07PacketPlayerDigging.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C07PacketPlayerDigging.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.edit.java b/patches/minecraft/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C08PacketPlayerBlockPlacement.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C09PacketHeldItemChange.edit.java b/patches/minecraft/net/minecraft/network/play/client/C09PacketHeldItemChange.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C09PacketHeldItemChange.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C0APacketAnimation.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0APacketAnimation.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C0APacketAnimation.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C0BPacketEntityAction.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0BPacketEntityAction.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C0BPacketEntityAction.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C0CPacketInput.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0CPacketInput.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C0CPacketInput.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C0EPacketClickWindow.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0EPacketClickWindow.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C0EPacketClickWindow.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C0FPacketConfirmTransaction.edit.java b/patches/minecraft/net/minecraft/network/play/client/C0FPacketConfirmTransaction.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C0FPacketConfirmTransaction.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.edit.java b/patches/minecraft/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C10PacketCreativeInventoryAction.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C11PacketEnchantItem.edit.java b/patches/minecraft/net/minecraft/network/play/client/C11PacketEnchantItem.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C11PacketEnchantItem.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C12PacketUpdateSign.edit.java b/patches/minecraft/net/minecraft/network/play/client/C12PacketUpdateSign.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C12PacketUpdateSign.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C13PacketPlayerAbilities.edit.java b/patches/minecraft/net/minecraft/network/play/client/C13PacketPlayerAbilities.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C13PacketPlayerAbilities.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C14PacketTabComplete.edit.java b/patches/minecraft/net/minecraft/network/play/client/C14PacketTabComplete.edit.java new file mode 100644 index 0000000..f31ed4b --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C14PacketTabComplete.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 6 @ 3 + ++ ++ import org.apache.commons.lang3.StringUtils; ++ + +> DELETE 10 @ 7 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C15PacketClientSettings.edit.java b/patches/minecraft/net/minecraft/network/play/client/C15PacketClientSettings.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C15PacketClientSettings.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C16PacketClientStatus.edit.java b/patches/minecraft/net/minecraft/network/play/client/C16PacketClientStatus.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C16PacketClientStatus.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.edit.java b/patches/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.edit.java new file mode 100644 index 0000000..c93b66d --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 5 @ 4 + ++ ++ import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C18PacketSpectate.edit.java b/patches/minecraft/net/minecraft/network/play/client/C18PacketSpectate.edit.java new file mode 100644 index 0000000..126e355 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C18PacketSpectate.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> DELETE 8 @ 8 : 9 + +> CHANGE 10 : 11 @ 11 : 12 + +~ private EaglercraftUUID id; + +> CHANGE 15 : 16 @ 16 : 17 + +~ public C18PacketSpectate(EaglercraftUUID id) { + +> DELETE 31 @ 32 : 35 + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/client/C19PacketResourcePackStatus.edit.java b/patches/minecraft/net/minecraft/network/play/client/C19PacketResourcePackStatus.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/client/C19PacketResourcePackStatus.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S00PacketKeepAlive.edit.java b/patches/minecraft/net/minecraft/network/play/server/S00PacketKeepAlive.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S00PacketKeepAlive.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S01PacketJoinGame.edit.java b/patches/minecraft/net/minecraft/network/play/server/S01PacketJoinGame.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S01PacketJoinGame.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S02PacketChat.edit.java b/patches/minecraft/net/minecraft/network/play/server/S02PacketChat.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S02PacketChat.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S03PacketTimeUpdate.edit.java b/patches/minecraft/net/minecraft/network/play/server/S03PacketTimeUpdate.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S03PacketTimeUpdate.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S04PacketEntityEquipment.edit.java b/patches/minecraft/net/minecraft/network/play/server/S04PacketEntityEquipment.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S04PacketEntityEquipment.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.edit.java b/patches/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S06PacketUpdateHealth.edit.java b/patches/minecraft/net/minecraft/network/play/server/S06PacketUpdateHealth.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S06PacketUpdateHealth.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S07PacketRespawn.edit.java b/patches/minecraft/net/minecraft/network/play/server/S07PacketRespawn.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S07PacketRespawn.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S08PacketPlayerPosLook.edit.java b/patches/minecraft/net/minecraft/network/play/server/S08PacketPlayerPosLook.edit.java new file mode 100644 index 0000000..d7402df --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S08PacketPlayerPosLook.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 5 : 6 @ 5 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S09PacketHeldItemChange.edit.java b/patches/minecraft/net/minecraft/network/play/server/S09PacketHeldItemChange.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S09PacketHeldItemChange.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S0APacketUseBed.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0APacketUseBed.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S0APacketUseBed.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S0BPacketAnimation.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0BPacketAnimation.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S0BPacketAnimation.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S0CPacketSpawnPlayer.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0CPacketSpawnPlayer.edit.java new file mode 100644 index 0000000..3dce0c2 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S0CPacketSpawnPlayer.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 4 : 6 @ 4 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ + +> CHANGE 17 : 18 @ 16 : 17 + +~ private EaglercraftUUID playerId; + +> CHANGE 83 : 84 @ 82 : 83 + +~ public EaglercraftUUID getPlayer() { + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S0DPacketCollectItem.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0DPacketCollectItem.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S0DPacketCollectItem.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S0EPacketSpawnObject.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0EPacketSpawnObject.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S0EPacketSpawnObject.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S0FPacketSpawnMob.edit.java b/patches/minecraft/net/minecraft/network/play/server/S0FPacketSpawnMob.edit.java new file mode 100644 index 0000000..78b3da1 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S0FPacketSpawnMob.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S10PacketSpawnPainting.edit.java b/patches/minecraft/net/minecraft/network/play/server/S10PacketSpawnPainting.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S10PacketSpawnPainting.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.edit.java b/patches/minecraft/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S11PacketSpawnExperienceOrb.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S12PacketEntityVelocity.edit.java b/patches/minecraft/net/minecraft/network/play/server/S12PacketEntityVelocity.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S12PacketEntityVelocity.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S13PacketDestroyEntities.edit.java b/patches/minecraft/net/minecraft/network/play/server/S13PacketDestroyEntities.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S13PacketDestroyEntities.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S14PacketEntity.edit.java b/patches/minecraft/net/minecraft/network/play/server/S14PacketEntity.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S14PacketEntity.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S18PacketEntityTeleport.edit.java b/patches/minecraft/net/minecraft/network/play/server/S18PacketEntityTeleport.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S18PacketEntityTeleport.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityHeadLook.edit.java b/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityHeadLook.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityHeadLook.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityStatus.edit.java b/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityStatus.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S19PacketEntityStatus.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S1BPacketEntityAttach.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1BPacketEntityAttach.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S1BPacketEntityAttach.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S1CPacketEntityMetadata.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1CPacketEntityMetadata.edit.java new file mode 100644 index 0000000..78b3da1 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S1CPacketEntityMetadata.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S1DPacketEntityEffect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1DPacketEntityEffect.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S1DPacketEntityEffect.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S1EPacketRemoveEntityEffect.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S1FPacketSetExperience.edit.java b/patches/minecraft/net/minecraft/network/play/server/S1FPacketSetExperience.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S1FPacketSetExperience.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S20PacketEntityProperties.edit.java b/patches/minecraft/net/minecraft/network/play/server/S20PacketEntityProperties.edit.java new file mode 100644 index 0000000..ddc7206 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S20PacketEntityProperties.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 6 : 10 @ 7 : 8 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import com.google.common.collect.Lists; +~ + +> CHANGE 45 : 46 @ 43 : 44 + +~ EaglercraftUUID uuid = parPacketBuffer.readUuid(); + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java b/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java new file mode 100644 index 0000000..141bb2e --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S21PacketChunkData.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ + +> CHANGE 81 : 82 @ 79 : 80 + +~ for (ExtendedBlockStorage extendedblockstorage1 : (ArrayList) arraylist) { + +> CHANGE 90 : 91 @ 88 : 89 + +~ for (ExtendedBlockStorage extendedblockstorage2 : (ArrayList) arraylist) { + +> CHANGE 96 : 97 @ 94 : 95 + +~ for (ExtendedBlockStorage extendedblockstorage3 : (ArrayList) arraylist) { + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S22PacketMultiBlockChange.edit.java b/patches/minecraft/net/minecraft/network/play/server/S22PacketMultiBlockChange.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S22PacketMultiBlockChange.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S23PacketBlockChange.edit.java b/patches/minecraft/net/minecraft/network/play/server/S23PacketBlockChange.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S23PacketBlockChange.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S24PacketBlockAction.edit.java b/patches/minecraft/net/minecraft/network/play/server/S24PacketBlockAction.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S24PacketBlockAction.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S25PacketBlockBreakAnim.edit.java b/patches/minecraft/net/minecraft/network/play/server/S25PacketBlockBreakAnim.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S25PacketBlockBreakAnim.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.edit.java b/patches/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.edit.java new file mode 100644 index 0000000..5370dcf --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> DELETE 8 @ 7 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S27PacketExplosion.edit.java b/patches/minecraft/net/minecraft/network/play/server/S27PacketExplosion.edit.java new file mode 100644 index 0000000..87f357f --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S27PacketExplosion.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S28PacketEffect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S28PacketEffect.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S28PacketEffect.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S29PacketSoundEffect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S29PacketSoundEffect.edit.java new file mode 100644 index 0000000..fdf5b51 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S29PacketSoundEffect.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 6 @ 3 + ++ ++ import org.apache.commons.lang3.Validate; ++ + +> DELETE 10 @ 7 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S2APacketParticles.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2APacketParticles.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S2APacketParticles.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S2BPacketChangeGameState.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2BPacketChangeGameState.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S2BPacketChangeGameState.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S2CPacketSpawnGlobalEntity.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S2DPacketOpenWindow.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2DPacketOpenWindow.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S2DPacketOpenWindow.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S2EPacketCloseWindow.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2EPacketCloseWindow.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S2EPacketCloseWindow.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S2FPacketSetSlot.edit.java b/patches/minecraft/net/minecraft/network/play/server/S2FPacketSetSlot.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S2FPacketSetSlot.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S30PacketWindowItems.edit.java b/patches/minecraft/net/minecraft/network/play/server/S30PacketWindowItems.edit.java new file mode 100644 index 0000000..78b3da1 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S30PacketWindowItems.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S31PacketWindowProperty.edit.java b/patches/minecraft/net/minecraft/network/play/server/S31PacketWindowProperty.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S31PacketWindowProperty.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S32PacketConfirmTransaction.edit.java b/patches/minecraft/net/minecraft/network/play/server/S32PacketConfirmTransaction.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S32PacketConfirmTransaction.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S33PacketUpdateSign.edit.java b/patches/minecraft/net/minecraft/network/play/server/S33PacketUpdateSign.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S33PacketUpdateSign.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S34PacketMaps.edit.java b/patches/minecraft/net/minecraft/network/play/server/S34PacketMaps.edit.java new file mode 100644 index 0000000..78b3da1 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S34PacketMaps.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S35PacketUpdateTileEntity.edit.java b/patches/minecraft/net/minecraft/network/play/server/S35PacketUpdateTileEntity.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S35PacketUpdateTileEntity.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S36PacketSignEditorOpen.edit.java b/patches/minecraft/net/minecraft/network/play/server/S36PacketSignEditorOpen.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S36PacketSignEditorOpen.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S37PacketStatistics.edit.java b/patches/minecraft/net/minecraft/network/play/server/S37PacketStatistics.edit.java new file mode 100644 index 0000000..bb99c36 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S37PacketStatistics.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 5 : 8 @ 6 + ++ ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S38PacketPlayerListItem.edit.java b/patches/minecraft/net/minecraft/network/play/server/S38PacketPlayerListItem.edit.java new file mode 100644 index 0000000..41be344 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S38PacketPlayerListItem.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 6 + +> CHANGE 4 : 10 @ 8 : 9 + +~ +~ import com.google.common.base.Objects; +~ import com.google.common.collect.Lists; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.Property; + +> DELETE 23 @ 22 : 44 + +> CHANGE 81 : 82 @ 102 : 155 + +~ // server only + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S39PacketPlayerAbilities.edit.java b/patches/minecraft/net/minecraft/network/play/server/S39PacketPlayerAbilities.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S39PacketPlayerAbilities.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S3APacketTabComplete.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3APacketTabComplete.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S3APacketTabComplete.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S3BPacketScoreboardObjective.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3BPacketScoreboardObjective.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S3BPacketScoreboardObjective.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S3CPacketUpdateScore.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3CPacketUpdateScore.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S3CPacketUpdateScore.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S3DPacketDisplayScoreboard.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S3EPacketTeams.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3EPacketTeams.edit.java new file mode 100644 index 0000000..87f357f --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S3EPacketTeams.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S3FPacketCustomPayload.edit.java b/patches/minecraft/net/minecraft/network/play/server/S3FPacketCustomPayload.edit.java new file mode 100644 index 0000000..c93b66d --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S3FPacketCustomPayload.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 5 @ 4 + ++ ++ import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S40PacketDisconnect.edit.java b/patches/minecraft/net/minecraft/network/play/server/S40PacketDisconnect.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S40PacketDisconnect.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S41PacketServerDifficulty.edit.java b/patches/minecraft/net/minecraft/network/play/server/S41PacketServerDifficulty.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S41PacketServerDifficulty.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S42PacketCombatEvent.edit.java b/patches/minecraft/net/minecraft/network/play/server/S42PacketCombatEvent.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S42PacketCombatEvent.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S43PacketCamera.edit.java b/patches/minecraft/net/minecraft/network/play/server/S43PacketCamera.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S43PacketCamera.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S44PacketWorldBorder.edit.java b/patches/minecraft/net/minecraft/network/play/server/S44PacketWorldBorder.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S44PacketWorldBorder.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S45PacketTitle.edit.java b/patches/minecraft/net/minecraft/network/play/server/S45PacketTitle.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S45PacketTitle.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S46PacketSetCompressionLevel.edit.java b/patches/minecraft/net/minecraft/network/play/server/S46PacketSetCompressionLevel.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S46PacketSetCompressionLevel.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.edit.java b/patches/minecraft/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S47PacketPlayerListHeaderFooter.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S48PacketResourcePackSend.edit.java b/patches/minecraft/net/minecraft/network/play/server/S48PacketResourcePackSend.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S48PacketResourcePackSend.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.edit.java b/patches/minecraft/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/play/server/S49PacketUpdateEntityNBT.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/status/client/C00PacketServerQuery.edit.java b/patches/minecraft/net/minecraft/network/status/client/C00PacketServerQuery.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/status/client/C00PacketServerQuery.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/status/client/C01PacketPing.edit.java b/patches/minecraft/net/minecraft/network/status/client/C01PacketPing.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/status/client/C01PacketPing.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/network/status/server/S00PacketServerInfo.edit.java b/patches/minecraft/net/minecraft/network/status/server/S00PacketServerInfo.edit.java new file mode 100644 index 0000000..52c61be --- /dev/null +++ b/patches/minecraft/net/minecraft/network/status/server/S00PacketServerInfo.edit.java @@ -0,0 +1,41 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 3 : 8 @ 5 + ++ ++ import org.json.JSONException; ++ import org.json.JSONObject; ++ ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; + +> DELETE 13 @ 10 : 11 + +> CHANGE 16 : 17 @ 14 : 23 + +~ + +> CHANGE 27 : 33 @ 33 : 35 + +~ try { +~ this.response = (ServerStatusResponse) JSONTypeProvider.deserialize( +~ new JSONObject(parPacketBuffer.readStringFromBuffer(32767)), ServerStatusResponse.class); +~ } catch (JSONException exc) { +~ throw new IOException("Invalid ServerStatusResponse JSON payload", exc); +~ } + +> CHANGE 36 : 41 @ 38 : 39 + +~ try { +~ parPacketBuffer.writeString(((JSONObject) JSONTypeProvider.serialize(this.response)).toString()); +~ } catch (JSONException exc) { +~ throw new IOException("Invalid ServerStatusResponse JSON payload", exc); +~ } + +> EOF diff --git a/patches/minecraft/net/minecraft/network/status/server/S01PacketPong.edit.java b/patches/minecraft/net/minecraft/network/status/server/S01PacketPong.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/status/server/S01PacketPong.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/potion/Potion.edit.java b/patches/minecraft/net/minecraft/potion/Potion.edit.java new file mode 100644 index 0000000..d3ffb81 --- /dev/null +++ b/patches/minecraft/net/minecraft/potion/Potion.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 6 + +> INSERT 4 : 9 @ 7 + ++ import java.util.Set; ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; ++ ++ import com.google.common.collect.Maps; ++ + +> DELETE 17 @ 15 : 20 + +> CHANGE 140 : 141 @ 143 : 146 + +~ // multiplayer only + +> CHANGE 242 : 244 @ 247 : 249 + +~ AttributeModifier attributemodifier = new AttributeModifier(EaglercraftUUID.fromString(parString1), +~ this.getName(), parDouble1, parInt1); + +> EOF diff --git a/patches/minecraft/net/minecraft/potion/PotionAbsorption.edit.java b/patches/minecraft/net/minecraft/potion/PotionAbsorption.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/potion/PotionAbsorption.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/potion/PotionAttackDamage.edit.java b/patches/minecraft/net/minecraft/potion/PotionAttackDamage.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/potion/PotionAttackDamage.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/potion/PotionEffect.edit.java b/patches/minecraft/net/minecraft/potion/PotionEffect.edit.java new file mode 100644 index 0000000..8fc4917 --- /dev/null +++ b/patches/minecraft/net/minecraft/potion/PotionEffect.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 6 @ 4 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/potion/PotionHealth.edit.java b/patches/minecraft/net/minecraft/potion/PotionHealth.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/potion/PotionHealth.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/potion/PotionHealthBoost.edit.java b/patches/minecraft/net/minecraft/potion/PotionHealthBoost.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/potion/PotionHealthBoost.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/potion/PotionHelper.edit.java b/patches/minecraft/net/minecraft/potion/PotionHelper.edit.java new file mode 100644 index 0000000..01df8f1 --- /dev/null +++ b/patches/minecraft/net/minecraft/potion/PotionHelper.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 6 : 10 @ 8 : 10 + +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/profiler/Profiler.edit.java b/patches/minecraft/net/minecraft/profiler/Profiler.edit.java new file mode 100644 index 0000000..6c6665f --- /dev/null +++ b/patches/minecraft/net/minecraft/profiler/Profiler.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 6 @ 8 : 10 + +> INSERT 7 : 13 @ 11 + ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/GoalColor.edit.java b/patches/minecraft/net/minecraft/scoreboard/GoalColor.edit.java new file mode 100644 index 0000000..681a62a --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/GoalColor.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/IScoreObjectiveCriteria.edit.java b/patches/minecraft/net/minecraft/scoreboard/IScoreObjectiveCriteria.edit.java new file mode 100644 index 0000000..4efef00 --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/IScoreObjectiveCriteria.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Maps; ++ + +> DELETE 8 @ 6 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/Score.edit.java b/patches/minecraft/net/minecraft/scoreboard/Score.edit.java new file mode 100644 index 0000000..bc4b50e --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/Score.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> DELETE 6 @ 5 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/ScoreDummyCriteria.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScoreDummyCriteria.edit.java new file mode 100644 index 0000000..681a62a --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/ScoreDummyCriteria.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/ScoreHealthCriteria.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScoreHealthCriteria.edit.java new file mode 100644 index 0000000..3efecf1 --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/ScoreHealthCriteria.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 5 @ 4 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/ScoreObjective.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScoreObjective.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/ScoreObjective.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/ScorePlayerTeam.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScorePlayerTeam.edit.java new file mode 100644 index 0000000..0de3756 --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/ScorePlayerTeam.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 4 : 7 @ 5 : 7 + +~ +~ import com.google.common.collect.Sets; +~ + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/Scoreboard.edit.java b/patches/minecraft/net/minecraft/scoreboard/Scoreboard.edit.java new file mode 100644 index 0000000..9ce5ef5 --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/Scoreboard.edit.java @@ -0,0 +1,31 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 7 : 11 @ 9 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> DELETE 13 @ 11 : 15 + +> CHANGE 40 : 41 @ 42 : 43 + +~ this.scoreObjectiveCriterias.put(criteria, (List) object); + +> CHANGE 73 : 74 @ 75 : 76 + +~ this.entitiesScoreObjectives.put(name, (Map) object); + +> CHANGE 135 : 136 @ 137 : 138 + +~ for (Map map : (Collection) collection) { + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/ScoreboardSaveData.edit.java b/patches/minecraft/net/minecraft/scoreboard/ScoreboardSaveData.edit.java new file mode 100644 index 0000000..705ba08 --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/ScoreboardSaveData.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 7 @ 5 : 11 + +> DELETE 9 @ 13 : 15 + +> EOF diff --git a/patches/minecraft/net/minecraft/scoreboard/Team.edit.java b/patches/minecraft/net/minecraft/scoreboard/Team.edit.java new file mode 100644 index 0000000..f8b8212 --- /dev/null +++ b/patches/minecraft/net/minecraft/scoreboard/Team.edit.java @@ -0,0 +1,15 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 5 : 7 @ 6 + ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/server/management/LowerStringMap.edit.java b/patches/minecraft/net/minecraft/server/management/LowerStringMap.edit.java new file mode 100644 index 0000000..959a4e0 --- /dev/null +++ b/patches/minecraft/net/minecraft/server/management/LowerStringMap.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 5 @ 6 : 7 + +> INSERT 6 : 8 @ 8 + ++ import com.google.common.collect.Maps; ++ + +> CHANGE 41 : 42 @ 41 : 42 + +~ this.put((String) entry.getKey(), (V) entry.getValue()); + +> EOF diff --git a/patches/minecraft/net/minecraft/stats/Achievement.edit.java b/patches/minecraft/net/minecraft/stats/Achievement.edit.java new file mode 100644 index 0000000..f9719d3 --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/Achievement.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/stats/AchievementList.edit.java b/patches/minecraft/net/minecraft/stats/AchievementList.edit.java new file mode 100644 index 0000000..c1fe8e2 --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/AchievementList.edit.java @@ -0,0 +1,122 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 9 @ 7 : 8 + +> CHANGE 16 : 51 @ 15 : 84 + +~ public static List achievementList; +~ public static Achievement openInventory; +~ public static Achievement mineWood; +~ public static Achievement buildWorkBench; +~ public static Achievement buildPickaxe; +~ public static Achievement buildFurnace; +~ public static Achievement acquireIron; +~ public static Achievement buildHoe; +~ public static Achievement makeBread; +~ public static Achievement bakeCake; +~ public static Achievement buildBetterPickaxe; +~ public static Achievement cookFish; +~ public static Achievement onARail; +~ public static Achievement buildSword; +~ public static Achievement killEnemy; +~ public static Achievement killCow; +~ public static Achievement flyPig; +~ public static Achievement snipeSkeleton; +~ public static Achievement diamonds; +~ public static Achievement diamondsToYou; +~ public static Achievement portal; +~ public static Achievement ghast; +~ public static Achievement blazeRod; +~ public static Achievement potion; +~ public static Achievement theEnd; +~ public static Achievement theEnd2; +~ public static Achievement enchantments; +~ public static Achievement overkill; +~ public static Achievement bookcase; +~ public static Achievement breedCow; +~ public static Achievement spawnWither; +~ public static Achievement killWither; +~ public static Achievement fullBeacon; +~ public static Achievement exploreAllBiomes; +~ public static Achievement overpowered; + +> INSERT 53 : 116 @ 86 + ++ achievementList = Lists.newArrayList(); ++ openInventory = (new Achievement("achievement.openInventory", "openInventory", 0, 0, Items.book, ++ (Achievement) null)).initIndependentStat().registerStat(); ++ mineWood = (new Achievement("achievement.mineWood", "mineWood", 2, 1, Blocks.log, openInventory)) ++ .registerStat(); ++ buildWorkBench = (new Achievement("achievement.buildWorkBench", "buildWorkBench", 4, -1, Blocks.crafting_table, ++ mineWood)).registerStat(); ++ buildPickaxe = (new Achievement("achievement.buildPickaxe", "buildPickaxe", 4, 2, Items.wooden_pickaxe, ++ buildWorkBench)).registerStat(); ++ buildFurnace = (new Achievement("achievement.buildFurnace", "buildFurnace", 3, 4, Blocks.furnace, buildPickaxe)) ++ .registerStat(); ++ acquireIron = (new Achievement("achievement.acquireIron", "acquireIron", 1, 4, Items.iron_ingot, buildFurnace)) ++ .registerStat(); ++ buildHoe = (new Achievement("achievement.buildHoe", "buildHoe", 2, -3, Items.wooden_hoe, buildWorkBench)) ++ .registerStat(); ++ makeBread = (new Achievement("achievement.makeBread", "makeBread", -1, -3, Items.bread, buildHoe)) ++ .registerStat(); ++ bakeCake = (new Achievement("achievement.bakeCake", "bakeCake", 0, -5, Items.cake, buildHoe)).registerStat(); ++ buildBetterPickaxe = (new Achievement("achievement.buildBetterPickaxe", "buildBetterPickaxe", 6, 2, ++ Items.stone_pickaxe, buildPickaxe)).registerStat(); ++ cookFish = (new Achievement("achievement.cookFish", "cookFish", 2, 6, Items.cooked_fish, buildFurnace)) ++ .registerStat(); ++ onARail = (new Achievement("achievement.onARail", "onARail", 2, 3, Blocks.rail, acquireIron)).setSpecial() ++ .registerStat(); ++ buildSword = (new Achievement("achievement.buildSword", "buildSword", 6, -1, Items.wooden_sword, ++ buildWorkBench)).registerStat(); ++ killEnemy = (new Achievement("achievement.killEnemy", "killEnemy", 8, -1, Items.bone, buildSword)) ++ .registerStat(); ++ killCow = (new Achievement("achievement.killCow", "killCow", 7, -3, Items.leather, buildSword)).registerStat(); ++ flyPig = (new Achievement("achievement.flyPig", "flyPig", 9, -3, Items.saddle, killCow)).setSpecial() ++ .registerStat(); ++ snipeSkeleton = (new Achievement("achievement.snipeSkeleton", "snipeSkeleton", 7, 0, Items.bow, killEnemy)) ++ .setSpecial().registerStat(); ++ diamonds = (new Achievement("achievement.diamonds", "diamonds", -1, 5, Blocks.diamond_ore, acquireIron)) ++ .registerStat(); ++ diamondsToYou = (new Achievement("achievement.diamondsToYou", "diamondsToYou", -1, 2, Items.diamond, diamonds)) ++ .registerStat(); ++ portal = (new Achievement("achievement.portal", "portal", -1, 7, Blocks.obsidian, diamonds)).registerStat(); ++ ghast = (new Achievement("achievement.ghast", "ghast", -4, 8, Items.ghast_tear, portal)).setSpecial() ++ .registerStat(); ++ blazeRod = (new Achievement("achievement.blazeRod", "blazeRod", 0, 9, Items.blaze_rod, portal)).registerStat(); ++ potion = (new Achievement("achievement.potion", "potion", 2, 8, Items.potionitem, blazeRod)).registerStat(); ++ theEnd = (new Achievement("achievement.theEnd", "theEnd", 3, 10, Items.ender_eye, blazeRod)).setSpecial() ++ .registerStat(); ++ theEnd2 = (new Achievement("achievement.theEnd2", "theEnd2", 4, 13, Blocks.dragon_egg, theEnd)).setSpecial() ++ .registerStat(); ++ enchantments = (new Achievement("achievement.enchantments", "enchantments", -4, 4, Blocks.enchanting_table, ++ diamonds)).registerStat(); ++ overkill = (new Achievement("achievement.overkill", "overkill", -4, 1, Items.diamond_sword, enchantments)) ++ .setSpecial().registerStat(); ++ bookcase = (new Achievement("achievement.bookcase", "bookcase", -3, 6, Blocks.bookshelf, enchantments)) ++ .registerStat(); ++ breedCow = (new Achievement("achievement.breedCow", "breedCow", 7, -5, Items.wheat, killCow)).registerStat(); ++ spawnWither = (new Achievement("achievement.spawnWither", "spawnWither", 7, 12, ++ new ItemStack(Items.skull, 1, 1), theEnd2)).registerStat(); ++ killWither = (new Achievement("achievement.killWither", "killWither", 7, 10, Items.nether_star, spawnWither)) ++ .registerStat(); ++ fullBeacon = (new Achievement("achievement.fullBeacon", "fullBeacon", 7, 8, Blocks.beacon, killWither)) ++ .setSpecial().registerStat(); ++ exploreAllBiomes = (new Achievement("achievement.exploreAllBiomes", "exploreAllBiomes", 4, 8, ++ Items.diamond_boots, theEnd)).func_150953_b(JsonSerializableSet.class).setSpecial().registerStat(); ++ overpowered = (new Achievement("achievement.overpowered", "overpowered", 6, 4, ++ new ItemStack(Items.golden_apple, 1, 1), buildBetterPickaxe)).setSpecial().registerStat(); + +> EOF diff --git a/patches/minecraft/net/minecraft/stats/ObjectiveStat.edit.java b/patches/minecraft/net/minecraft/stats/ObjectiveStat.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/ObjectiveStat.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/stats/StatBase.edit.java b/patches/minecraft/net/minecraft/stats/StatBase.edit.java new file mode 100644 index 0000000..b394ae5 --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/StatBase.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 5 : 6 @ 5 + ++ + +> DELETE 8 @ 7 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/stats/StatBasic.edit.java b/patches/minecraft/net/minecraft/stats/StatBasic.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/StatBasic.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/stats/StatCrafting.edit.java b/patches/minecraft/net/minecraft/stats/StatCrafting.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/StatCrafting.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/stats/StatFileWriter.edit.java b/patches/minecraft/net/minecraft/stats/StatFileWriter.edit.java new file mode 100644 index 0000000..866077c --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/StatFileWriter.edit.java @@ -0,0 +1,22 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Maps; ++ + +> DELETE 7 @ 5 : 7 + +> CHANGE 11 : 12 @ 11 : 12 + +~ protected final Map statsData = Maps.newHashMap(); + +> EOF diff --git a/patches/minecraft/net/minecraft/stats/StatList.edit.java b/patches/minecraft/net/minecraft/stats/StatList.edit.java new file mode 100644 index 0000000..3158ba0 --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/StatList.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> INSERT 5 : 10 @ 8 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ import com.google.common.collect.Sets; ++ + +> DELETE 19 @ 17 : 21 + +> CHANGE 165 : 166 @ 167 : 168 + +~ for (Item item : (HashSet) hashset) { + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.edit.java b/patches/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.edit.java new file mode 100644 index 0000000..9e9f970 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.edit.java @@ -0,0 +1,38 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 15 @ 13 : 14 + +> CHANGE 62 : 70 @ 61 : 131 + +~ double d3 = (double) ((float) blockpos.getX() + this.getSpawnerWorld().rand.nextFloat()); +~ double d4 = (double) ((float) blockpos.getY() + this.getSpawnerWorld().rand.nextFloat()); +~ double d5 = (double) ((float) blockpos.getZ() + this.getSpawnerWorld().rand.nextFloat()); +~ this.getSpawnerWorld().spawnParticle(EnumParticleTypes.SMOKE_NORMAL, d3, d4, d5, 0.0D, 0.0D, 0.0D, +~ new int[0]); +~ this.getSpawnerWorld().spawnParticle(EnumParticleTypes.FLAME, d3, d4, d5, 0.0D, 0.0D, 0.0D, new int[0]); +~ if (this.spawnDelay > 0) { +~ --this.spawnDelay; + +> INSERT 72 : 74 @ 133 + ++ this.prevMobRotation = this.mobRotation; ++ this.mobRotation = (this.mobRotation + (double) (1000.0F / ((float) this.spawnDelay + 200.0F))) % 360.0D; + +> CHANGE 230 : 231 @ 289 : 290 + +~ if (delay == 1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntity.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntity.edit.java new file mode 100644 index 0000000..e3646c6 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntity.edit.java @@ -0,0 +1,44 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 10 @ 5 + ++ ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 17 @ 12 : 32 + +> DELETE 19 @ 34 : 36 + +> CHANGE 77 : 78 @ 94 : 95 + +~ logger.error("Could not create TileEntity", exception); + +> CHANGE 162 : 163 @ 179 : 180 + +~ + TileEntity.this.getClass().getName(); + +> CHANGE 173 : 174 @ 190 : 191 + +~ return HString.format("ID #%d (%s // %s)", + +> CHANGE 175 : 176 @ 192 : 193 + +~ Block.getBlockById(i).getClass().getName() }); + +> CHANGE 188 : 190 @ 205 : 207 + +~ String s = HString.format("%4s", new Object[] { Integer.toBinaryString(i) }).replace(" ", "0"); +~ return HString.format("%1$d / 0x%1$X / 0b%2$s", new Object[] { Integer.valueOf(i), s }); + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityBanner.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityBanner.edit.java new file mode 100644 index 0000000..85c8d3e --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityBanner.edit.java @@ -0,0 +1,57 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 7 @ 4 + ++ import java.util.function.Supplier; ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 16 @ 13 : 14 + +> CHANGE 185 : 194 @ 183 : 190 + +~ BORDER("border", "bo", "###", "# #", "###"), +~ CURLY_BORDER("curly_border", "cbo", () -> new ItemStack(Blocks.vine)), +~ CREEPER("creeper", "cre", () -> new ItemStack(Items.skull, 1, 4)), +~ GRADIENT("gradient", "gra", "# #", " # ", " # "), GRADIENT_UP("gradient_up", "gru", " # ", " # ", "# #"), +~ BRICKS("bricks", "bri", () -> new ItemStack(Blocks.brick_block)), +~ SKULL("skull", "sku", () -> new ItemStack(Items.skull, 1, 1)), +~ FLOWER("flower", "flo", +~ () -> new ItemStack(Blocks.red_flower, 1, BlockFlower.EnumFlowerType.OXEYE_DAISY.getMeta())), +~ MOJANG("mojang", "moj", () -> new ItemStack(Items.golden_apple, 1, 1)); + +> INSERT 198 : 199 @ 194 + ++ private Supplier patternCraftingStackSupplier; + +> CHANGE 207 : 208 @ 202 : 203 + +~ private EnumBannerPattern(String name, String id, Supplier craftingItem) { + +> CHANGE 209 : 210 @ 204 : 205 + +~ this.patternCraftingStackSupplier = craftingItem; + +> CHANGE 232 : 233 @ 227 : 228 + +~ return this.patternCraftingStackSupplier != null || this.craftingLayers[0] != null; + +> CHANGE 236 : 237 @ 231 : 232 + +~ return this.patternCraftingStackSupplier != null; + +> INSERT 240 : 243 @ 235 + ++ if (patternCraftingStack == null) { ++ patternCraftingStack = patternCraftingStackSupplier.get(); ++ } + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityBeacon.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityBeacon.edit.java new file mode 100644 index 0000000..7771286 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityBeacon.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 4 : 7 @ 5 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 25 @ 23 : 27 + +> DELETE 54 @ 56 : 62 + +> DELETE 55 @ 63 : 82 + +> DELETE 137 @ 164 : 172 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.edit.java new file mode 100644 index 0000000..51b7d7d --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 4 @ 4 : 6 + +~ + +> DELETE 16 @ 18 : 19 + +> DELETE 62 @ 65 : 83 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityChest.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityChest.edit.java new file mode 100644 index 0000000..df8c044 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityChest.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 9 @ 9 : 10 + +> DELETE 12 @ 13 : 16 + +> DELETE 219 @ 223 : 226 + +> DELETE 220 @ 227 : 241 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.edit.java new file mode 100644 index 0000000..b406b61 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.netty.ByteBuf; + +> DELETE 8 @ 9 : 10 + +> DELETE 76 @ 78 : 81 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityComparator.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityComparator.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityComparator.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityDaylightDetector.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityDaylightDetector.edit.java new file mode 100644 index 0000000..7866049 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityDaylightDetector.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 6 @ 8 : 15 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityDispenser.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityDispenser.edit.java new file mode 100644 index 0000000..f5bc475 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityDispenser.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 12 @ 11 : 12 + +> CHANGE 14 : 15 @ 14 : 15 + +~ private static final EaglercraftRandom RNG = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityDropper.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityDropper.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityDropper.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityEnchantmentTable.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityEnchantmentTable.edit.java new file mode 100644 index 0000000..e39986b --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityEnchantmentTable.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 9 @ 8 : 9 + +> CHANGE 27 : 28 @ 27 : 28 + +~ private static EaglercraftRandom rand = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityEndPortal.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityEndPortal.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityEndPortal.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityEnderChest.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityEnderChest.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityEnderChest.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.edit.java new file mode 100644 index 0000000..585f95e --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityFurnace.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityFurnace.edit.java new file mode 100644 index 0000000..f37021a --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityFurnace.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> DELETE 22 @ 23 : 24 + +> DELETE 24 @ 26 : 27 + +> DELETE 160 @ 163 : 165 + +> DELETE 163 @ 168 : 210 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityHopper.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityHopper.edit.java new file mode 100644 index 0000000..a2050f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityHopper.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 6 @ 5 : 6 + +> DELETE 17 @ 17 : 21 + +> DELETE 153 @ 157 : 165 + +> CHANGE 156 : 157 @ 168 : 190 + +~ return false; + +> DELETE 159 @ 192 : 240 + +> DELETE 347 @ 428 : 435 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityLockable.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityLockable.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityLockable.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityMobSpawner.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityMobSpawner.edit.java new file mode 100644 index 0000000..b7a3ac6 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityMobSpawner.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityNote.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityNote.edit.java new file mode 100644 index 0000000..b7b408f --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityNote.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntityPiston.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntityPiston.edit.java new file mode 100644 index 0000000..b2fa269 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntityPiston.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 11 @ 9 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntitySign.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntitySign.edit.java new file mode 100644 index 0000000..76c8390 --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntitySign.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import org.json.JSONException; +~ + +> DELETE 5 @ 4 : 5 + +> DELETE 8 @ 8 : 9 + +> DELETE 11 @ 12 : 14 + +> DELETE 14 @ 17 : 18 + +> DELETE 24 @ 28 : 29 + +> DELETE 33 @ 38 : 39 + +> DELETE 75 @ 81 : 83 + +> CHANGE 89 : 90 @ 97 : 98 + +~ } catch (JSONException var8) { + +> DELETE 94 @ 102 : 103 + +> DELETE 127 @ 136 : 189 + +> DELETE 130 @ 192 : 195 + +> EOF diff --git a/patches/minecraft/net/minecraft/tileentity/TileEntitySkull.edit.java b/patches/minecraft/net/minecraft/tileentity/TileEntitySkull.edit.java new file mode 100644 index 0000000..8fe736e --- /dev/null +++ b/patches/minecraft/net/minecraft/tileentity/TileEntitySkull.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 5 @ 2 : 6 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; + +> DELETE 9 @ 10 : 12 + +> CHANGE 38 : 39 @ 41 : 42 + +~ this.playerProfile = new GameProfile((EaglercraftUUID) null, s); + +> CHANGE 73 : 74 @ 76 : 100 + +~ return input; + +> EOF diff --git a/patches/minecraft/net/minecraft/util/AxisAlignedBB.edit.java b/patches/minecraft/net/minecraft/util/AxisAlignedBB.edit.java new file mode 100644 index 0000000..9f1c788 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/AxisAlignedBB.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/BlockPos.edit.java b/patches/minecraft/net/minecraft/util/BlockPos.edit.java new file mode 100644 index 0000000..1ea19be --- /dev/null +++ b/patches/minecraft/net/minecraft/util/BlockPos.edit.java @@ -0,0 +1,44 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.AbstractIterator; ++ + +> DELETE 7 @ 5 : 9 + +> INSERT 110 : 127 @ 112 + ++ public BlockPos offsetFaster(EnumFacing facing, MutableBlockPos ret) { ++ ret.x = this.getX() + facing.getFrontOffsetX(); ++ ret.y = this.getY() + facing.getFrontOffsetY(); ++ ret.z = this.getZ() + facing.getFrontOffsetZ(); ++ return ret; ++ } ++ ++ /** ++ * only use with a regular "net.minecraft.util.BlockPos"! ++ */ ++ public BlockPos offsetEvenFaster(EnumFacing facing, MutableBlockPos ret) { ++ ret.x = this.x + facing.getFrontOffsetX(); ++ ret.y = this.y + facing.getFrontOffsetY(); ++ ret.z = this.z + facing.getFrontOffsetZ(); ++ return ret; ++ } ++ + +> DELETE 235 @ 220 : 223 + +> CHANGE 241 : 242 @ 229 : 233 + +~ super(x_, y_, z_); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/Cartesian.edit.java b/patches/minecraft/net/minecraft/util/Cartesian.edit.java new file mode 100644 index 0000000..b92fb19 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/Cartesian.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 6 + +> INSERT 10 : 15 @ 14 + ++ import com.google.common.base.Function; ++ import com.google.common.collect.Iterables; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.UnmodifiableIterator; ++ + +> CHANGE 47 : 48 @ 46 : 47 + +~ return (List) Arrays.asList((Object[]) aobject); + +> CHANGE 62 : 63 @ 61 : 62 + +~ ? Collections.singletonList((T[]) Cartesian.createArray(this.clazz, 0)).iterator() + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ChatComponentProcessor.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentProcessor.edit.java new file mode 100644 index 0000000..fdeaffb --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ChatComponentProcessor.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 9 @ 8 : 14 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ChatComponentScore.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentScore.edit.java new file mode 100644 index 0000000..8c2ce3c --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ChatComponentScore.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 10 + +> DELETE 25 @ 33 : 45 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ChatComponentSelector.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentSelector.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ChatComponentSelector.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ChatComponentStyle.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentStyle.edit.java new file mode 100644 index 0000000..4011e14 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ChatComponentStyle.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 5 @ 2 + ++ import java.util.Iterator; ++ import java.util.List; ++ + +> DELETE 8 @ 5 : 11 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ChatComponentText.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentText.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ChatComponentText.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ChatComponentTranslation.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentTranslation.edit.java new file mode 100644 index 0000000..4f56e89 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ChatComponentTranslation.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 8 @ 10 : 16 + +> INSERT 9 : 14 @ 17 + ++ import com.google.common.collect.Iterators; ++ import com.google.common.collect.Lists; ++ ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ + +> CHANGE 72 : 73 @ 75 : 76 + +~ HString.format(format.substring(j, k), new Object[0])); + +> CHANGE 98 : 99 @ 101 : 102 + +~ HString.format(format.substring(j), new Object[0])); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ChatComponentTranslationFormatException.edit.java b/patches/minecraft/net/minecraft/util/ChatComponentTranslationFormatException.edit.java new file mode 100644 index 0000000..385a6ce --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ChatComponentTranslationFormatException.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.HString; + +> CHANGE 6 : 7 @ 6 : 7 + +~ super(HString.format("Error parsing: %s: %s", new Object[] { component, message })); + +> CHANGE 10 : 11 @ 10 : 11 + +~ super(HString.format("Invalid index %d requested for %s", new Object[] { Integer.valueOf(index), component })); + +> CHANGE 14 : 15 @ 14 : 15 + +~ super(HString.format("Error while parsing: %s", new Object[] { component }), cause); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ChatStyle.edit.java b/patches/minecraft/net/minecraft/util/ChatStyle.edit.java new file mode 100644 index 0000000..66da729 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ChatStyle.edit.java @@ -0,0 +1,155 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 7 @ 2 : 11 + +~ import org.json.JSONException; +~ import org.json.JSONObject; +~ +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeCodec; +~ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; + +> DELETE 9 @ 13 : 15 + +> CHANGE 339 : 348 @ 345 : 357 + +~ public static class Serializer implements JSONTypeCodec { +~ public ChatStyle deserialize(JSONObject jsonobject) throws JSONException { +~ ChatStyle chatstyle = new ChatStyle(); +~ if (jsonobject == null) { +~ return null; +~ } else { +~ if (jsonobject.has("bold")) { +~ chatstyle.bold = jsonobject.getBoolean("bold"); +~ } + +> CHANGE 349 : 352 @ 358 : 361 + +~ if (jsonobject.has("italic")) { +~ chatstyle.italic = jsonobject.getBoolean("italic"); +~ } + +> CHANGE 353 : 356 @ 362 : 365 + +~ if (jsonobject.has("underlined")) { +~ chatstyle.underlined = jsonobject.getBoolean("underlined"); +~ } + +> CHANGE 357 : 360 @ 366 : 369 + +~ if (jsonobject.has("strikethrough")) { +~ chatstyle.strikethrough = jsonobject.getBoolean("strikethrough"); +~ } + +> CHANGE 361 : 364 @ 370 : 373 + +~ if (jsonobject.has("obfuscated")) { +~ chatstyle.obfuscated = jsonobject.getBoolean("obfuscated"); +~ } + +> CHANGE 365 : 368 @ 374 : 378 + +~ if (jsonobject.has("color")) { +~ chatstyle.color = EnumChatFormatting.getValueByName(jsonobject.getString("color")); +~ } + +> CHANGE 369 : 372 @ 379 : 382 + +~ if (jsonobject.has("insertion")) { +~ chatstyle.insertion = jsonobject.getString("insertion"); +~ } + +> CHANGE 373 : 383 @ 383 : 394 + +~ if (jsonobject.has("clickEvent")) { +~ JSONObject jsonobject1 = jsonobject.getJSONObject("clickEvent"); +~ if (jsonobject1 != null) { +~ String jsonprimitive = jsonobject1.optString("action"); +~ ClickEvent.Action clickevent$action = jsonprimitive == null ? null +~ : ClickEvent.Action.getValueByCanonicalName(jsonprimitive); +~ String jsonprimitive1 = jsonobject1.optString("value"); +~ if (clickevent$action != null && jsonprimitive1 != null +~ && clickevent$action.shouldAllowInChat()) { +~ chatstyle.chatClickEvent = new ClickEvent(clickevent$action, jsonprimitive1); + +> INSERT 385 : 386 @ 396 + ++ } + +> CHANGE 387 : 398 @ 397 : 409 + +~ if (jsonobject.has("hoverEvent")) { +~ JSONObject jsonobject2 = jsonobject.getJSONObject("hoverEvent"); +~ if (jsonobject2 != null) { +~ String jsonprimitive2 = jsonobject2.getString("action"); +~ HoverEvent.Action hoverevent$action = jsonprimitive2 == null ? null +~ : HoverEvent.Action.getValueByCanonicalName(jsonprimitive2); +~ IChatComponent ichatcomponent = JSONTypeProvider.deserializeNoCast(jsonobject2.get("value"), +~ IChatComponent.class); +~ if (hoverevent$action != null && ichatcomponent != null +~ && hoverevent$action.shouldAllowInChat()) { +~ chatstyle.chatHoverEvent = new HoverEvent(hoverevent$action, ichatcomponent); + +> DELETE 400 @ 411 : 413 + +> CHANGE 401 : 403 @ 414 : 416 + +~ +~ return chatstyle; + +> CHANGE 406 : 407 @ 419 : 421 + +~ public JSONObject serialize(ChatStyle chatstyle) { + +> CHANGE 410 : 411 @ 424 : 425 + +~ JSONObject jsonobject = new JSONObject(); + +> CHANGE 412 : 413 @ 426 : 427 + +~ jsonobject.put("bold", chatstyle.bold); + +> CHANGE 416 : 417 @ 430 : 431 + +~ jsonobject.put("italic", chatstyle.italic); + +> CHANGE 420 : 421 @ 434 : 435 + +~ jsonobject.put("underlined", chatstyle.underlined); + +> CHANGE 424 : 425 @ 438 : 439 + +~ jsonobject.put("strikethrough", chatstyle.strikethrough); + +> CHANGE 428 : 429 @ 442 : 443 + +~ jsonobject.put("obfuscated", chatstyle.obfuscated); + +> CHANGE 432 : 433 @ 446 : 447 + +~ jsonobject.put("color", (String) JSONTypeProvider.serialize(chatstyle.color)); + +> CHANGE 436 : 437 @ 450 : 451 + +~ jsonobject.put("insertion", chatstyle.insertion); + +> CHANGE 440 : 444 @ 454 : 458 + +~ JSONObject jsonobject1 = new JSONObject(); +~ jsonobject1.put("action", chatstyle.chatClickEvent.getAction().getCanonicalName()); +~ jsonobject1.put("value", chatstyle.chatClickEvent.getValue()); +~ jsonobject.put("clickEvent", jsonobject1); + +> CHANGE 447 : 452 @ 461 : 465 + +~ JSONObject jsonobject2 = new JSONObject(); +~ jsonobject2.put("action", chatstyle.chatHoverEvent.getAction().getCanonicalName()); +~ jsonobject2.put("value", +~ (JSONObject) JSONTypeProvider.serialize(chatstyle.chatHoverEvent.getValue())); +~ jsonobject.put("hoverEvent", jsonobject2); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ClassInheritanceMultiMap.edit.java b/patches/minecraft/net/minecraft/util/ClassInheritanceMultiMap.edit.java new file mode 100644 index 0000000..7bcabcd --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ClassInheritanceMultiMap.edit.java @@ -0,0 +1,26 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 6 + +> INSERT 8 : 13 @ 12 + ++ import com.google.common.collect.Iterators; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ import com.google.common.collect.Sets; ++ + +> CHANGE 36 : 37 @ 35 : 36 + +~ this.func_181743_a((T) object, clazz); + +> CHANGE 68 : 69 @ 67 : 68 + +~ this.map.put(parClass1, (List) Lists.newArrayList(new Object[] { parObject })); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/CombatEntry.edit.java b/patches/minecraft/net/minecraft/util/CombatEntry.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/CombatEntry.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/CombatTracker.edit.java b/patches/minecraft/net/minecraft/util/CombatTracker.edit.java new file mode 100644 index 0000000..4a1f518 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/CombatTracker.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 12 @ 10 : 15 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/DamageSource.edit.java b/patches/minecraft/net/minecraft/util/DamageSource.edit.java new file mode 100644 index 0000000..af421d9 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/DamageSource.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 7 @ 7 : 12 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/EnchantmentNameParts.edit.java b/patches/minecraft/net/minecraft/util/EnchantmentNameParts.edit.java new file mode 100644 index 0000000..c5be617 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/EnchantmentNameParts.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 6 : 7 @ 6 : 7 + +~ private EaglercraftRandom rand = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/EntityDamageSource.edit.java b/patches/minecraft/net/minecraft/util/EntityDamageSource.edit.java new file mode 100644 index 0000000..b93ea6b --- /dev/null +++ b/patches/minecraft/net/minecraft/util/EntityDamageSource.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/EntityDamageSourceIndirect.edit.java b/patches/minecraft/net/minecraft/util/EntityDamageSourceIndirect.edit.java new file mode 100644 index 0000000..932b3c9 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/EntityDamageSourceIndirect.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/EntitySelectors.edit.java b/patches/minecraft/net/minecraft/util/EntitySelectors.edit.java new file mode 100644 index 0000000..3105f68 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/EntitySelectors.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/util/EnumChatFormatting.edit.java b/patches/minecraft/net/minecraft/util/EnumChatFormatting.edit.java new file mode 100644 index 0000000..0dafbda --- /dev/null +++ b/patches/minecraft/net/minecraft/util/EnumChatFormatting.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 7 : 10 @ 9 + ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/util/EnumFacing.edit.java b/patches/minecraft/net/minecraft/util/EnumFacing.edit.java new file mode 100644 index 0000000..17a5949 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/EnumFacing.edit.java @@ -0,0 +1,25 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 6 @ 2 + ++ import java.util.Iterator; ++ import java.util.Map; ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ + +> DELETE 9 @ 5 : 11 + +> CHANGE 180 : 181 @ 182 : 183 + +~ public static EnumFacing random(EaglercraftRandom rand) { + +> CHANGE 321 : 322 @ 323 : 324 + +~ public EnumFacing random(EaglercraftRandom rand) { + +> EOF diff --git a/patches/minecraft/net/minecraft/util/EnumParticleTypes.edit.java b/patches/minecraft/net/minecraft/util/EnumParticleTypes.edit.java new file mode 100644 index 0000000..c1547f7 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/EnumParticleTypes.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 5 : 8 @ 7 + ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/util/FoodStats.edit.java b/patches/minecraft/net/minecraft/util/FoodStats.edit.java new file mode 100644 index 0000000..585f95e --- /dev/null +++ b/patches/minecraft/net/minecraft/util/FoodStats.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/IChatComponent.edit.java b/patches/minecraft/net/minecraft/util/IChatComponent.edit.java new file mode 100644 index 0000000..4fbe0eb --- /dev/null +++ b/patches/minecraft/net/minecraft/util/IChatComponent.edit.java @@ -0,0 +1,222 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 14 + +> DELETE 3 @ 15 : 23 + +> INSERT 4 : 11 @ 24 + ++ import org.json.JSONArray; ++ import org.json.JSONException; ++ import org.json.JSONObject; ++ ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeCodec; ++ import net.lax1dude.eaglercraft.v1_8.json.JSONTypeProvider; ++ + +> CHANGE 30 : 31 @ 43 : 45 + +~ public static class Serializer implements JSONTypeCodec { + +> CHANGE 32 : 38 @ 46 : 53 + +~ public IChatComponent deserialize(Object parJsonElement) throws JSONException { +~ if (parJsonElement instanceof String) { +~ return new ChatComponentText((String) parJsonElement); +~ } else if (!(parJsonElement instanceof JSONObject)) { +~ if (parJsonElement instanceof JSONArray) { +~ JSONArray jsonarray1 = (JSONArray) parJsonElement; + +> CHANGE 40 : 42 @ 55 : 58 + +~ for (Object jsonelement : jsonarray1) { +~ IChatComponent ichatcomponent1 = this.deserialize(jsonelement); + +> CHANGE 51 : 53 @ 67 : 69 + +~ throw new JSONException("Don\'t know how to turn " + parJsonElement.getClass().getSimpleName() +~ + " into a Component"); + +> CHANGE 55 : 56 @ 71 : 72 + +~ JSONObject jsonobject = (JSONObject) parJsonElement; + +> CHANGE 58 : 59 @ 74 : 75 + +~ object = new ChatComponentText(jsonobject.getString("text")); + +> CHANGE 60 : 61 @ 76 : 77 + +~ String s = jsonobject.getString("translate"); + +> CHANGE 62 : 64 @ 78 : 80 + +~ JSONArray jsonarray = jsonobject.getJSONArray("with"); +~ Object[] aobject = new Object[jsonarray.length()]; + +> CHANGE 66 : 67 @ 82 : 83 + +~ aobject[i] = this.deserialize(jsonarray.get(i)); + +> CHANGE 81 : 82 @ 97 : 98 + +~ JSONObject jsonobject1 = jsonobject.getJSONObject("score"); + +> CHANGE 83 : 84 @ 99 : 100 + +~ throw new JSONException("A score component needs a least a name and an objective"); + +> CHANGE 86 : 87 @ 102 : 104 + +~ object = new ChatComponentScore(jsonobject1.getString("name"), jsonobject1.getString("objective")); + +> CHANGE 88 : 89 @ 105 : 106 + +~ ((ChatComponentScore) object).setValue(jsonobject1.getString("value")); + +> CHANGE 92 : 93 @ 109 : 110 + +~ throw new JSONException( + +> CHANGE 96 : 97 @ 113 : 114 + +~ object = new ChatComponentSelector(jsonobject.getString("selector")); + +> CHANGE 100 : 103 @ 117 : 120 + +~ JSONArray jsonarray2 = jsonobject.getJSONArray("extra"); +~ if (jsonarray2.length() <= 0) { +~ throw new JSONException("Unexpected empty array of components"); + +> CHANGE 105 : 107 @ 122 : 125 + +~ for (int j = 0; j < jsonarray2.length(); ++j) { +~ ((IChatComponent) object).appendSibling(this.deserialize(jsonarray2.get(j))); + +> CHANGE 110 : 111 @ 128 : 130 + +~ ((IChatComponent) object).setChatStyle(JSONTypeProvider.deserialize(parJsonElement, ChatStyle.class)); + +> CHANGE 115 : 119 @ 134 : 142 + +~ private void serializeChatStyle(ChatStyle style, JSONObject object) { +~ JSONObject jsonelement = JSONTypeProvider.serialize(style); +~ for (String entry : jsonelement.keySet()) { +~ object.put(entry, jsonelement.get(entry)); + +> DELETE 120 @ 143 : 144 + +> CHANGE 122 : 123 @ 146 : 148 + +~ public Object serialize(IChatComponent ichatcomponent) { + +> CHANGE 125 : 126 @ 150 : 151 + +~ return ((ChatComponentText) ichatcomponent).getChatComponentText_TextValue(); + +> CHANGE 127 : 128 @ 152 : 153 + +~ JSONObject jsonobject = new JSONObject(); + +> CHANGE 129 : 130 @ 154 : 155 + +~ this.serializeChatStyle(ichatcomponent.getChatStyle(), jsonobject); + +> CHANGE 133 : 134 @ 158 : 159 + +~ JSONArray jsonarray = new JSONArray(); + +> CHANGE 136 : 137 @ 161 : 163 + +~ jsonarray.put(this.serialize(ichatcomponent1)); + +> CHANGE 139 : 140 @ 165 : 166 + +~ jsonobject.put("extra", jsonarray); + +> CHANGE 143 : 144 @ 169 : 171 + +~ jsonobject.put("text", ((ChatComponentText) ichatcomponent).getChatComponentText_TextValue()); + +> CHANGE 146 : 147 @ 173 : 174 + +~ jsonobject.put("translate", chatcomponenttranslation.getKey()); + +> CHANGE 149 : 150 @ 176 : 177 + +~ JSONArray jsonarray1 = new JSONArray(); + +> CHANGE 153 : 154 @ 180 : 182 + +~ jsonarray1.put(this.serialize((IChatComponent) object)); + +> CHANGE 155 : 156 @ 183 : 184 + +~ jsonarray1.put(String.valueOf(object)); + +> CHANGE 159 : 160 @ 187 : 188 + +~ jsonobject.put("with", jsonarray1); + +> CHANGE 163 : 168 @ 191 : 196 + +~ JSONObject jsonobject1 = new JSONObject(); +~ jsonobject1.put("name", chatcomponentscore.getName()); +~ jsonobject1.put("objective", chatcomponentscore.getObjective()); +~ jsonobject1.put("value", chatcomponentscore.getUnformattedTextForChat()); +~ jsonobject.put("score", jsonobject1); + +> CHANGE 175 : 176 @ 203 : 204 + +~ jsonobject.put("selector", chatcomponentselector.getSelector()); + +> INSERT 182 : 185 @ 210 + ++ /** ++ * So sorry for this implementation ++ */ + +> CHANGE 186 : 192 @ 211 : 212 + +~ if (component instanceof ChatComponentText) { +~ String escaped = new JSONObject().put("E", component.getUnformattedTextForChat()).toString(); +~ return escaped.substring(5, escaped.length() - 1); +~ } else { +~ return JSONTypeProvider.serialize(component).toString(); +~ } + +> CHANGE 195 : 196 @ 215 : 216 + +~ return (IChatComponent) JSONTypeProvider.deserialize(json, IChatComponent.class); + +> INSERT 197 : 198 @ 217 + ++ } + +> CHANGE 199 : 212 @ 218 : 224 + +~ public static IChatComponent join(List components) { +~ ChatComponentText chatcomponenttext = new ChatComponentText(""); +~ +~ for (int i = 0; i < components.size(); ++i) { +~ if (i > 0) { +~ if (i == components.size() - 1) { +~ chatcomponenttext.appendText(" and "); +~ } else if (i > 0) { +~ chatcomponenttext.appendText(", "); +~ } +~ } +~ +~ chatcomponenttext.appendSibling((IChatComponent) components.get(i)); + +> INSERT 213 : 215 @ 225 + ++ ++ return chatcomponenttext; + +> EOF diff --git a/patches/minecraft/net/minecraft/util/IJsonSerializable.edit.java b/patches/minecraft/net/minecraft/util/IJsonSerializable.edit.java new file mode 100644 index 0000000..cc8dcaa --- /dev/null +++ b/patches/minecraft/net/minecraft/util/IJsonSerializable.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> CHANGE 3 : 4 @ 5 : 6 + +~ void fromJson(Object var1); + +> CHANGE 5 : 6 @ 7 : 8 + +~ Object getSerializableElement(); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/IThreadListener.edit.java b/patches/minecraft/net/minecraft/util/IThreadListener.edit.java new file mode 100644 index 0000000..0527585 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/IThreadListener.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.futures.ListenableFuture; + +> DELETE 6 @ 6 : 8 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/IntHashMap.edit.java b/patches/minecraft/net/minecraft/util/IntHashMap.edit.java new file mode 100644 index 0000000..e88b633 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/IntHashMap.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 23 : 24 @ 23 : 24 + +~ return (V) inthashmap$entry.valueEntry; + +> EOF diff --git a/patches/minecraft/net/minecraft/util/JsonSerializableSet.edit.java b/patches/minecraft/net/minecraft/util/JsonSerializableSet.edit.java new file mode 100644 index 0000000..4bc4670 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/JsonSerializableSet.edit.java @@ -0,0 +1,34 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 6 @ 2 + ++ import java.util.Set; ++ ++ import org.json.JSONArray; ++ + +> DELETE 8 @ 4 : 9 + +> CHANGE 12 : 17 @ 13 : 17 + +~ public void fromJson(Object jsonelement) { +~ if (jsonelement instanceof JSONArray) { +~ JSONArray arr = (JSONArray) jsonelement; +~ for (int i = 0; i < arr.length(); ++i) { +~ underlyingSet.add(arr.getString(i)); + +> CHANGE 22 : 24 @ 22 : 24 + +~ public Object getSerializableElement() { +~ JSONArray jsonarray = new JSONArray(); + +> CHANGE 26 : 27 @ 26 : 27 + +~ jsonarray.put(s); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/LoggingPrintStream.edit.java b/patches/minecraft/net/minecraft/util/LoggingPrintStream.edit.java new file mode 100644 index 0000000..313fba6 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/LoggingPrintStream.edit.java @@ -0,0 +1,50 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 6 + +> INSERT 5 : 9 @ 7 + ++ import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ + +> DELETE 10 @ 8 : 9 + +> INSERT 11 : 13 @ 10 + ++ private final Logger logger; ++ private final boolean err; + +> CHANGE 14 : 15 @ 11 : 12 + +~ public LoggingPrintStream(String domainIn, boolean err, OutputStream outStream) { + +> INSERT 17 : 19 @ 14 + ++ this.logger = LogManager.getLogger(domainIn); ++ this.err = err; + +> CHANGE 30 : 44 @ 25 : 29 + +~ String callingClass = PlatformRuntime.getCallingClass(3); +~ if (callingClass == null) { +~ if (err) { +~ logger.error(string); +~ } else { +~ logger.info(string); +~ } +~ } else { +~ if (err) { +~ logger.error("@({}): {}", new Object[] { callingClass, string }); +~ } else { +~ logger.info("@({}): {}", new Object[] { callingClass, string }); +~ } +~ } + +> EOF diff --git a/patches/minecraft/net/minecraft/util/LongHashMap.edit.java b/patches/minecraft/net/minecraft/util/LongHashMap.edit.java new file mode 100644 index 0000000..dfb1c9b --- /dev/null +++ b/patches/minecraft/net/minecraft/util/LongHashMap.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 37 : 38 @ 37 : 38 + +~ return (V) longhashmap$entry.value; + +> EOF diff --git a/patches/minecraft/net/minecraft/util/MapPopulator.edit.java b/patches/minecraft/net/minecraft/util/MapPopulator.edit.java new file mode 100644 index 0000000..8a6353e --- /dev/null +++ b/patches/minecraft/net/minecraft/util/MapPopulator.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 6 : 8 @ 7 + ++ import com.google.common.collect.Maps; ++ + +> CHANGE 17 : 18 @ 16 : 17 + +~ map.put((K) object, (V) iterator.next()); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/MathHelper.edit.java b/patches/minecraft/net/minecraft/util/MathHelper.edit.java new file mode 100644 index 0000000..165eb6d --- /dev/null +++ b/patches/minecraft/net/minecraft/util/MathHelper.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +> CHANGE 103 : 104 @ 104 : 105 + +~ public static int getRandomIntegerInRange(EaglercraftRandom parRandom, int parInt1, int parInt2) { + +> CHANGE 107 : 108 @ 108 : 109 + +~ public static float randomFloatClamp(EaglercraftRandom parRandom, float parFloat1, float parFloat2) { + +> CHANGE 111 : 112 @ 112 : 113 + +~ public static double getRandomDoubleInRange(EaglercraftRandom parRandom, double parDouble1, double parDouble2) { + +> CHANGE 259 : 260 @ 260 : 261 + +~ public static EaglercraftUUID getRandomUuid(EaglercraftRandom rand) { + +> CHANGE 262 : 263 @ 263 : 264 + +~ return new EaglercraftUUID(i, j); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/Matrix4f.edit.java b/patches/minecraft/net/minecraft/util/Matrix4f.edit.java new file mode 100644 index 0000000..f2d71d9 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/Matrix4f.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ public class Matrix4f extends net.lax1dude.eaglercraft.v1_8.vector.Matrix4f { + +> EOF diff --git a/patches/minecraft/net/minecraft/util/MouseHelper.edit.java b/patches/minecraft/net/minecraft/util/MouseHelper.edit.java new file mode 100644 index 0000000..8245d25 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/MouseHelper.edit.java @@ -0,0 +1,13 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.Display; +~ import net.lax1dude.eaglercraft.v1_8.Mouse; + +> EOF diff --git a/patches/minecraft/net/minecraft/util/MovementInputFromOptions.edit.java b/patches/minecraft/net/minecraft/util/MovementInputFromOptions.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/MovementInputFromOptions.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/MovingObjectPosition.edit.java b/patches/minecraft/net/minecraft/util/MovingObjectPosition.edit.java new file mode 100644 index 0000000..4890f7c --- /dev/null +++ b/patches/minecraft/net/minecraft/util/MovingObjectPosition.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ObjectIntIdentityMap.edit.java b/patches/minecraft/net/minecraft/util/ObjectIntIdentityMap.edit.java new file mode 100644 index 0000000..0e4be8d --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ObjectIntIdentityMap.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> DELETE 5 @ 8 : 9 + +> INSERT 6 : 10 @ 10 + ++ import com.google.common.base.Predicates; ++ import com.google.common.collect.Iterators; ++ import com.google.common.collect.Lists; ++ + +> CHANGE 18 : 19 @ 18 : 19 + +~ this.objectList.add((T) null); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/RegistryDefaulted.edit.java b/patches/minecraft/net/minecraft/util/RegistryDefaulted.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/RegistryDefaulted.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/RegistryNamespaced.edit.java b/patches/minecraft/net/minecraft/util/RegistryNamespaced.edit.java new file mode 100644 index 0000000..16ee99c --- /dev/null +++ b/patches/minecraft/net/minecraft/util/RegistryNamespaced.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> DELETE 4 @ 6 : 9 + +> INSERT 5 : 8 @ 10 + ++ import com.google.common.collect.BiMap; ++ import com.google.common.collect.HashBiMap; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/util/RegistryNamespacedDefaultedByKey.edit.java b/patches/minecraft/net/minecraft/util/RegistryNamespacedDefaultedByKey.edit.java new file mode 100644 index 0000000..2eaecb0 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/RegistryNamespacedDefaultedByKey.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/RegistrySimple.edit.java b/patches/minecraft/net/minecraft/util/RegistrySimple.edit.java new file mode 100644 index 0000000..9ce76a3 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/RegistrySimple.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 6 : 7 @ 7 : 8 + +~ + +> DELETE 8 @ 9 : 11 + +> INSERT 9 : 14 @ 12 + ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ResourceLocation.edit.java b/patches/minecraft/net/minecraft/util/ResourceLocation.edit.java new file mode 100644 index 0000000..e3e4ec7 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ResourceLocation.edit.java @@ -0,0 +1,13 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 8 : 10 @ 8 + ++ public Object cachedPointer = null; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/util/ScreenShotHelper.edit.java b/patches/minecraft/net/minecraft/util/ScreenShotHelper.edit.java new file mode 100644 index 0000000..7526212 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/ScreenShotHelper.edit.java @@ -0,0 +1,21 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 21 + +~ import net.lax1dude.eaglercraft.v1_8.internal.PlatformApplication; + +> DELETE 5 @ 23 : 27 + +> CHANGE 6 : 8 @ 28 : 30 + +~ public static IChatComponent saveScreenshot() { +~ return new ChatComponentText("Saved Screenshot As: " + PlatformApplication.saveScreenshot()); + +> DELETE 9 @ 31 : 107 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/Session.edit.java b/patches/minecraft/net/minecraft/util/Session.edit.java new file mode 100644 index 0000000..369e252 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/Session.edit.java @@ -0,0 +1,52 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; +~ import net.lax1dude.eaglercraft.v1_8.mojang.authlib.GameProfile; +~ import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile; + +> CHANGE 8 : 9 @ 9 : 13 + +~ private GameProfile profile; + +> CHANGE 10 : 11 @ 14 : 20 + +~ private static final EaglercraftUUID offlineUUID; + +> CHANGE 12 : 14 @ 21 : 23 + +~ public Session() { +~ reset(); + +> CHANGE 16 : 18 @ 25 : 27 + +~ public GameProfile getProfile() { +~ return profile; + +> CHANGE 20 : 22 @ 29 : 31 + +~ public void update(String serverUsername, EaglercraftUUID uuid) { +~ profile = new GameProfile(uuid, serverUsername); + +> CHANGE 24 : 26 @ 33 : 35 + +~ public void reset() { +~ update(EaglerProfile.getName(), offlineUUID); + +> CHANGE 28 : 32 @ 37 : 44 + +~ static { +~ byte[] bytes = new byte[16]; +~ (new EaglercraftRandom()).nextBytes(bytes); +~ offlineUUID = new EaglercraftUUID(bytes); + +> DELETE 34 @ 46 : 71 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/StatCollector.edit.java b/patches/minecraft/net/minecraft/util/StatCollector.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/StatCollector.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/StringTranslate.edit.java b/patches/minecraft/net/minecraft/util/StringTranslate.edit.java new file mode 100644 index 0000000..12fabb4 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/StringTranslate.edit.java @@ -0,0 +1,58 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 6 + +> INSERT 5 : 6 @ 9 + ++ import java.util.regex.Matcher; + +> DELETE 7 @ 10 : 12 + +> INSERT 8 : 17 @ 13 + ++ import com.google.common.base.Charsets; ++ import com.google.common.base.Splitter; ++ import com.google.common.collect.Iterables; ++ import com.google.common.collect.Maps; ++ ++ import net.lax1dude.eaglercraft.v1_8.EagRuntime; ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ import net.lax1dude.eaglercraft.v1_8.IOUtils; ++ + +> CHANGE 25 : 27 @ 21 : 23 + +~ this.lastUpdateTimeInMilliseconds = System.currentTimeMillis(); +~ } + +> CHANGE 28 : 40 @ 24 : 32 + +~ public static void doCLINIT() { +~ InputStream inputstream = EagRuntime.getResourceStream("/assets/minecraft/lang/en_US.lang"); +~ for (String s : IOUtils.readLines(inputstream, Charsets.UTF_8)) { +~ if (!s.isEmpty() && s.charAt(0) != 35) { +~ String[] astring = (String[]) Iterables.toArray(equalSignSplitter.split(s), String.class); +~ if (astring != null && astring.length == 2) { +~ String s1 = astring[0]; +~ String s2 = numericVariablePattern.matcher(astring[1]).replaceAll("%s"); // TODO: originally "%$1s" +~ // but must be "%s" to +~ // work with TeaVM +~ // (why?) +~ instance.languageList.put(s1, s2); + +> DELETE 42 @ 34 : 38 + +> INSERT 44 : 45 @ 40 + ++ instance.lastUpdateTimeInMilliseconds = System.currentTimeMillis(); + +> CHANGE 65 : 66 @ 60 : 61 + +~ return HString.format(s, format); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/Timer.edit.java b/patches/minecraft/net/minecraft/util/Timer.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/Timer.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/TupleIntJsonSerializable.edit.java b/patches/minecraft/net/minecraft/util/TupleIntJsonSerializable.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/TupleIntJsonSerializable.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/Util.edit.java b/patches/minecraft/net/minecraft/util/Util.edit.java new file mode 100644 index 0000000..e0c1008 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/Util.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 6 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EagRuntime; +~ import net.lax1dude.eaglercraft.v1_8.futures.ExecutionException; +~ import net.lax1dude.eaglercraft.v1_8.futures.FutureTask; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> CHANGE 9 : 10 @ 8 : 15 + +~ return EagRuntime.getPlatformOS().getMinecraftEnum(); + +> EOF diff --git a/patches/minecraft/net/minecraft/util/Vec3.edit.java b/patches/minecraft/net/minecraft/util/Vec3.edit.java new file mode 100644 index 0000000..c7b24f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/Vec3.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/util/Vec3i.edit.java b/patches/minecraft/net/minecraft/util/Vec3i.edit.java new file mode 100644 index 0000000..31dc692 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/Vec3i.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> CHANGE 6 : 9 @ 7 : 10 + +~ public int x; +~ public int y; +~ public int z; + +> EOF diff --git a/patches/minecraft/net/minecraft/util/WeightedRandom.edit.java b/patches/minecraft/net/minecraft/util/WeightedRandom.edit.java new file mode 100644 index 0000000..b9b9653 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/WeightedRandom.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 4 @ 3 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 16 : 17 @ 16 : 17 + +~ public static T getRandomItem(EaglercraftRandom random, Collection collection, + +> CHANGE 37 : 38 @ 37 : 38 + +~ public static T getRandomItem(EaglercraftRandom random, Collection collection) { + +> EOF diff --git a/patches/minecraft/net/minecraft/util/WeightedRandomChestContent.edit.java b/patches/minecraft/net/minecraft/util/WeightedRandomChestContent.edit.java new file mode 100644 index 0000000..ecae70f --- /dev/null +++ b/patches/minecraft/net/minecraft/util/WeightedRandomChestContent.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 5 : 9 @ 6 : 7 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ + +> DELETE 13 @ 11 : 12 + +> CHANGE 34 : 36 @ 33 : 35 + +~ public static void generateChestContents(EaglercraftRandom random, List listIn, +~ IInventory inv, int max) { + +> CHANGE 56 : 57 @ 55 : 56 + +~ public static void generateDispenserContents(EaglercraftRandom random, List listIn, + +> EOF diff --git a/patches/minecraft/net/minecraft/util/WeightedRandomFishable.edit.java b/patches/minecraft/net/minecraft/util/WeightedRandomFishable.edit.java new file mode 100644 index 0000000..3d2a3a0 --- /dev/null +++ b/patches/minecraft/net/minecraft/util/WeightedRandomFishable.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 6 @ 5 : 6 + +> CHANGE 17 : 18 @ 17 : 18 + +~ public ItemStack getItemStack(EaglercraftRandom random) { + +> EOF diff --git a/patches/minecraft/net/minecraft/village/MerchantRecipeList.edit.java b/patches/minecraft/net/minecraft/village/MerchantRecipeList.edit.java new file mode 100644 index 0000000..0248129 --- /dev/null +++ b/patches/minecraft/net/minecraft/village/MerchantRecipeList.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> DELETE 10 @ 9 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/ChunkCache.edit.java b/patches/minecraft/net/minecraft/world/ChunkCache.edit.java new file mode 100644 index 0000000..67391be --- /dev/null +++ b/patches/minecraft/net/minecraft/world/ChunkCache.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 8 @ 8 : 12 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/DifficultyInstance.edit.java b/patches/minecraft/net/minecraft/world/DifficultyInstance.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/DifficultyInstance.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/Explosion.edit.java b/patches/minecraft/net/minecraft/world/Explosion.edit.java new file mode 100644 index 0000000..7c6047a --- /dev/null +++ b/patches/minecraft/net/minecraft/world/Explosion.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 5 : 11 @ 8 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ import com.google.common.collect.Sets; +~ + +> DELETE 26 @ 24 : 25 + +> CHANGE 30 : 31 @ 29 : 30 + +~ private final EaglercraftRandom explosionRNG; + +> CHANGE 53 : 54 @ 52 : 53 + +~ this.explosionRNG = new EaglercraftRandom(); + +> EOF diff --git a/patches/minecraft/net/minecraft/world/GameRules.edit.java b/patches/minecraft/net/minecraft/world/GameRules.edit.java new file mode 100644 index 0000000..78b3da1 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/GameRules.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 4 : 5 @ 4 + ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java b/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java new file mode 100644 index 0000000..585f95e --- /dev/null +++ b/patches/minecraft/net/minecraft/world/IBlockAccess.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 7 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/IInteractionObject.edit.java b/patches/minecraft/net/minecraft/world/IInteractionObject.edit.java new file mode 100644 index 0000000..b7b408f --- /dev/null +++ b/patches/minecraft/net/minecraft/world/IInteractionObject.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/ILockableContainer.edit.java b/patches/minecraft/net/minecraft/world/ILockableContainer.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/ILockableContainer.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/World.edit.java b/patches/minecraft/net/minecraft/world/World.edit.java new file mode 100644 index 0000000..7d79939 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/World.edit.java @@ -0,0 +1,115 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 7 : 8 @ 10 : 11 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 9 : 10 @ 12 : 13 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID; + +> INSERT 11 : 16 @ 14 + ++ ++ import com.google.common.base.Predicate; ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Sets; ++ + +> DELETE 34 @ 32 : 33 + +> DELETE 46 @ 45 : 60 + +> DELETE 47 @ 61 : 62 + +> DELETE 50 @ 65 : 66 + +> CHANGE 68 : 69 @ 84 : 85 + +~ protected int updateLCG = (new EaglercraftRandom()).nextInt(); + +> CHANGE 75 : 76 @ 91 : 92 + +~ public final EaglercraftRandom rand = new EaglercraftRandom(); + +> DELETE 83 @ 99 : 100 + +> DELETE 86 @ 103 : 104 + +> INSERT 96 : 99 @ 114 + ++ if (!client) { ++ throw new IllegalStateException("Singleplayer is unavailable because all of it's code was deleted"); ++ } + +> DELETE 107 @ 122 : 123 + +> CHANGE 119 : 120 @ 135 : 136 + +~ return chunk.getBiome(pos); + +> CHANGE 131 : 132 @ 147 : 148 + +~ return BiomeGenBase.plains; + +> DELETE 135 @ 151 : 155 + +> DELETE 189 @ 209 : 217 + +> DELETE 225 @ 253 : 255 + +> CHANGE 240 : 241 @ 270 : 271 + +~ if ((flags & 2) != 0 && ((flags & 4) == 0) && chunk.isPopulated()) { + +> DELETE 244 @ 274 : 281 + +> CHANGE 280 : 281 @ 317 : 321 + +~ this.notifyNeighborsOfStateChange(pos, blockType); + +> DELETE 348 @ 388 : 390 + +> DELETE 349 @ 391 : 411 + +> DELETE 1753 @ 1815 : 1873 + +> DELETE 1793 @ 1913 : 1937 + +> CHANGE 1801 : 1802 @ 1945 : 1946 + +~ public void forceBlockUpdateTick(Block blockType, BlockPos pos, EaglercraftRandom random) { + +> DELETE 2033 @ 2177 : 2181 + +> CHANGE 2061 : 2062 @ 2209 : 2210 + +~ if (entityType.isAssignableFrom(entity.getClass()) && filter.apply((T) entity)) { + +> CHANGE 2072 : 2074 @ 2220 : 2222 + +~ for (EntityPlayer entity : this.playerEntities) { +~ if (playerType.isAssignableFrom(entity.getClass()) && filter.apply((T) entity)) { + +> CHANGE 2306 : 2307 @ 2454 : 2455 + +~ public EntityPlayer getPlayerEntityByUUID(EaglercraftUUID uuid) { + +> CHANGE 2504 : 2505 @ 2652 : 2653 + +~ public EaglercraftRandom setRandomSeed(int parInt1, int parInt2, int parInt3) { + +> CHANGE 2557 : 2558 @ 2705 : 2706 + +~ this.theCalendar.setTimeInMillis(System.currentTimeMillis()); + +> DELETE 2625 @ 2773 : 2777 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/WorldProvider.edit.java b/patches/minecraft/net/minecraft/world/WorldProvider.edit.java new file mode 100644 index 0000000..8644282 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/WorldProvider.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 6 @ 6 : 14 + +> DELETE 7 @ 15 : 20 + +> DELETE 13 @ 26 : 27 + +> DELETE 38 @ 52 : 64 + +> DELETE 41 @ 67 : 79 + +> CHANGE 72 : 73 @ 110 : 111 + +~ float f2 = 0.0F; + +> DELETE 136 @ 174 : 178 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/WorldProviderEnd.edit.java b/patches/minecraft/net/minecraft/world/WorldProviderEnd.edit.java new file mode 100644 index 0000000..9b6d23d --- /dev/null +++ b/patches/minecraft/net/minecraft/world/WorldProviderEnd.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 10 + +> DELETE 8 @ 13 : 14 + +> DELETE 12 @ 18 : 22 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/WorldProviderHell.edit.java b/patches/minecraft/net/minecraft/world/WorldProviderHell.edit.java new file mode 100644 index 0000000..ad088f8 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/WorldProviderHell.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 6 + +> DELETE 4 @ 7 : 9 + +> DELETE 7 @ 12 : 13 + +> DELETE 26 @ 32 : 37 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/WorldProviderSurface.edit.java b/patches/minecraft/net/minecraft/world/WorldProviderSurface.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/WorldProviderSurface.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/WorldSettings.edit.java b/patches/minecraft/net/minecraft/world/WorldSettings.edit.java new file mode 100644 index 0000000..83d34e4 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/WorldSettings.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/WorldType.edit.java b/patches/minecraft/net/minecraft/world/WorldType.edit.java new file mode 100644 index 0000000..b8cec50 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/WorldType.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 9 @ 9 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeColorHelper.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeColorHelper.edit.java new file mode 100644 index 0000000..d8cf34b --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeColorHelper.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenBase.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenBase.edit.java new file mode 100644 index 0000000..dec2942 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenBase.edit.java @@ -0,0 +1,224 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 5 + +> CHANGE 5 : 6 @ 8 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> INSERT 7 : 14 @ 10 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ import com.google.common.collect.Sets; ++ ++ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; ++ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 16 @ 12 : 13 + +> DELETE 41 @ 38 : 57 + +> DELETE 43 @ 59 : 68 + +> CHANGE 62 : 107 @ 87 : 178 + +~ public static BiomeGenBase ocean; +~ public static BiomeGenBase plains; +~ public static BiomeGenBase desert; +~ public static BiomeGenBase extremeHills; +~ public static BiomeGenBase forest; +~ public static BiomeGenBase taiga; +~ public static BiomeGenBase swampland; +~ public static BiomeGenBase river; +~ public static BiomeGenBase hell; +~ public static BiomeGenBase sky; +~ public static BiomeGenBase frozenOcean; +~ public static BiomeGenBase frozenRiver; +~ public static BiomeGenBase icePlains; +~ public static BiomeGenBase iceMountains; +~ public static BiomeGenBase mushroomIsland; +~ public static BiomeGenBase mushroomIslandShore; +~ public static BiomeGenBase beach; +~ public static BiomeGenBase desertHills; +~ public static BiomeGenBase forestHills; +~ public static BiomeGenBase taigaHills; +~ public static BiomeGenBase extremeHillsEdge; +~ public static BiomeGenBase jungle; +~ public static BiomeGenBase jungleHills; +~ public static BiomeGenBase jungleEdge; +~ public static BiomeGenBase deepOcean; +~ public static BiomeGenBase stoneBeach; +~ public static BiomeGenBase coldBeach; +~ public static BiomeGenBase birchForest; +~ public static BiomeGenBase birchForestHills; +~ public static BiomeGenBase roofedForest; +~ public static BiomeGenBase coldTaiga; +~ public static BiomeGenBase coldTaigaHills; +~ public static BiomeGenBase megaTaiga; +~ public static BiomeGenBase megaTaigaHills; +~ public static BiomeGenBase extremeHillsPlus; +~ public static BiomeGenBase savanna; +~ public static BiomeGenBase savannaPlateau; +~ public static BiomeGenBase mesa; +~ public static BiomeGenBase mesaPlateau_F; +~ public static BiomeGenBase mesaPlateau; +~ public static BiomeGenBase field_180279_ad; +~ protected static final NoiseGeneratorPerlin temperatureNoise = new NoiseGeneratorPerlin( +~ new EaglercraftRandom(1234L), 1); +~ protected static final NoiseGeneratorPerlin GRASS_COLOR_NOISE = new NoiseGeneratorPerlin( +~ new EaglercraftRandom(2345L), 1); + +> DELETE 118 @ 189 : 190 + +> DELETE 125 @ 197 : 200 + +> DELETE 137 @ 212 : 215 + +> DELETE 139 @ 217 : 218 + +> DELETE 155 @ 234 : 238 + +> CHANGE 176 : 177 @ 259 : 268 + +~ public BlockFlower.EnumFlowerType pickRandomFlower(EaglercraftRandom rand, BlockPos pos) { + +> DELETE 271 @ 362 : 366 + +> CHANGE 287 : 289 @ 382 : 384 + +~ public void genTerrainBlocks(World worldIn, EaglercraftRandom rand, ChunkPrimer chunkPrimerIn, int parInt1, +~ int parInt2, double parDouble1) { + +> CHANGE 292 : 294 @ 387 : 389 + +~ public final void generateBiomeTerrain(World worldIn, EaglercraftRandom rand, ChunkPrimer chunkPrimerIn, +~ int parInt1, int parInt2, double parDouble1) { + +> CHANGE 395 : 508 @ 490 : 491 + +~ public static class Height { +~ public float rootHeight; +~ public float variation; +~ +~ public Height(float rootHeightIn, float variationIn) { +~ this.rootHeight = rootHeightIn; +~ this.variation = variationIn; +~ } +~ +~ public BiomeGenBase.Height attenuate() { +~ return new BiomeGenBase.Height(this.rootHeight * 0.8F, this.variation * 0.6F); +~ } +~ } +~ +~ public static class SpawnListEntry extends WeightedRandom.Item { +~ public Class entityClass; +~ public int minGroupCount; +~ public int maxGroupCount; +~ +~ public SpawnListEntry(Class entityclassIn, int weight, int groupCountMin, +~ int groupCountMax) { +~ super(weight); +~ this.entityClass = entityclassIn; +~ this.minGroupCount = groupCountMin; +~ this.maxGroupCount = groupCountMax; +~ } +~ +~ public String toString() { +~ return this.entityClass.getSimpleName() + "*(" + this.minGroupCount + "-" + this.maxGroupCount + "):" +~ + this.itemWeight; +~ } +~ } +~ +~ public static enum TempCategory { +~ OCEAN, COLD, MEDIUM, WARM; +~ } +~ +~ public static void bootstrap() { +~ ocean = (new BiomeGenOcean(0)).setColor(112).setBiomeName("Ocean").setHeight(height_Oceans); +~ plains = (new BiomeGenPlains(1)).setColor(9286496).setBiomeName("Plains"); +~ desert = (new BiomeGenDesert(2)).setColor(16421912).setBiomeName("Desert").setDisableRain() +~ .setTemperatureRainfall(2.0F, 0.0F).setHeight(height_LowPlains); +~ extremeHills = (new BiomeGenHills(3, false)).setColor(6316128).setBiomeName("Extreme Hills") +~ .setHeight(height_MidHills).setTemperatureRainfall(0.2F, 0.3F); +~ forest = (new BiomeGenForest(4, 0)).setColor(353825).setBiomeName("Forest"); +~ taiga = (new BiomeGenTaiga(5, 0)).setColor(747097).setBiomeName("Taiga").setFillerBlockMetadata(5159473) +~ .setTemperatureRainfall(0.25F, 0.8F).setHeight(height_MidPlains); +~ swampland = (new BiomeGenSwamp(6)).setColor(522674).setBiomeName("Swampland").setFillerBlockMetadata(9154376) +~ .setHeight(height_PartiallySubmerged).setTemperatureRainfall(0.8F, 0.9F); +~ river = (new BiomeGenRiver(7)).setColor(255).setBiomeName("River").setHeight(height_ShallowWaters); +~ hell = (new BiomeGenHell(8)).setColor(16711680).setBiomeName("Hell").setDisableRain() +~ .setTemperatureRainfall(2.0F, 0.0F); +~ sky = (new BiomeGenEnd(9)).setColor(8421631).setBiomeName("The End").setDisableRain(); +~ frozenOcean = (new BiomeGenOcean(10)).setColor(9474208).setBiomeName("FrozenOcean").setEnableSnow() +~ .setHeight(height_Oceans).setTemperatureRainfall(0.0F, 0.5F); +~ frozenRiver = (new BiomeGenRiver(11)).setColor(10526975).setBiomeName("FrozenRiver").setEnableSnow() +~ .setHeight(height_ShallowWaters).setTemperatureRainfall(0.0F, 0.5F); +~ icePlains = (new BiomeGenSnow(12, false)).setColor(16777215).setBiomeName("Ice Plains").setEnableSnow() +~ .setTemperatureRainfall(0.0F, 0.5F).setHeight(height_LowPlains); +~ iceMountains = (new BiomeGenSnow(13, false)).setColor(10526880).setBiomeName("Ice Mountains").setEnableSnow() +~ .setHeight(height_LowHills).setTemperatureRainfall(0.0F, 0.5F); +~ mushroomIsland = (new BiomeGenMushroomIsland(14)).setColor(16711935).setBiomeName("MushroomIsland") +~ .setTemperatureRainfall(0.9F, 1.0F).setHeight(height_LowIslands); +~ mushroomIslandShore = (new BiomeGenMushroomIsland(15)).setColor(10486015).setBiomeName("MushroomIslandShore") +~ .setTemperatureRainfall(0.9F, 1.0F).setHeight(height_Shores); +~ beach = (new BiomeGenBeach(16)).setColor(16440917).setBiomeName("Beach").setTemperatureRainfall(0.8F, 0.4F) +~ .setHeight(height_Shores); +~ desertHills = (new BiomeGenDesert(17)).setColor(13786898).setBiomeName("DesertHills").setDisableRain() +~ .setTemperatureRainfall(2.0F, 0.0F).setHeight(height_LowHills); +~ forestHills = (new BiomeGenForest(18, 0)).setColor(2250012).setBiomeName("ForestHills") +~ .setHeight(height_LowHills); +~ taigaHills = (new BiomeGenTaiga(19, 0)).setColor(1456435).setBiomeName("TaigaHills") +~ .setFillerBlockMetadata(5159473).setTemperatureRainfall(0.25F, 0.8F).setHeight(height_LowHills); +~ extremeHillsEdge = (new BiomeGenHills(20, true)).setColor(7501978).setBiomeName("Extreme Hills Edge") +~ .setHeight(height_MidHills.attenuate()).setTemperatureRainfall(0.2F, 0.3F); +~ jungle = (new BiomeGenJungle(21, false)).setColor(5470985).setBiomeName("Jungle") +~ .setFillerBlockMetadata(5470985).setTemperatureRainfall(0.95F, 0.9F); +~ jungleHills = (new BiomeGenJungle(22, false)).setColor(2900485).setBiomeName("JungleHills") +~ .setFillerBlockMetadata(5470985).setTemperatureRainfall(0.95F, 0.9F).setHeight(height_LowHills); +~ jungleEdge = (new BiomeGenJungle(23, true)).setColor(6458135).setBiomeName("JungleEdge") +~ .setFillerBlockMetadata(5470985).setTemperatureRainfall(0.95F, 0.8F); +~ deepOcean = (new BiomeGenOcean(24)).setColor(48).setBiomeName("Deep Ocean").setHeight(height_DeepOceans); +~ stoneBeach = (new BiomeGenStoneBeach(25)).setColor(10658436).setBiomeName("Stone Beach") +~ .setTemperatureRainfall(0.2F, 0.3F).setHeight(height_RockyWaters); +~ coldBeach = (new BiomeGenBeach(26)).setColor(16445632).setBiomeName("Cold Beach") +~ .setTemperatureRainfall(0.05F, 0.3F).setHeight(height_Shores).setEnableSnow(); +~ birchForest = (new BiomeGenForest(27, 2)).setBiomeName("Birch Forest").setColor(3175492); +~ birchForestHills = (new BiomeGenForest(28, 2)).setBiomeName("Birch Forest Hills").setColor(2055986) +~ .setHeight(height_LowHills); +~ roofedForest = (new BiomeGenForest(29, 3)).setColor(4215066).setBiomeName("Roofed Forest"); +~ coldTaiga = (new BiomeGenTaiga(30, 0)).setColor(3233098).setBiomeName("Cold Taiga") +~ .setFillerBlockMetadata(5159473).setEnableSnow().setTemperatureRainfall(-0.5F, 0.4F) +~ .setHeight(height_MidPlains).func_150563_c(16777215); +~ coldTaigaHills = (new BiomeGenTaiga(31, 0)).setColor(2375478).setBiomeName("Cold Taiga Hills") +~ .setFillerBlockMetadata(5159473).setEnableSnow().setTemperatureRainfall(-0.5F, 0.4F) +~ .setHeight(height_LowHills).func_150563_c(16777215); +~ megaTaiga = (new BiomeGenTaiga(32, 1)).setColor(5858897).setBiomeName("Mega Taiga") +~ .setFillerBlockMetadata(5159473).setTemperatureRainfall(0.3F, 0.8F).setHeight(height_MidPlains); +~ megaTaigaHills = (new BiomeGenTaiga(33, 1)).setColor(4542270).setBiomeName("Mega Taiga Hills") +~ .setFillerBlockMetadata(5159473).setTemperatureRainfall(0.3F, 0.8F).setHeight(height_LowHills); +~ extremeHillsPlus = (new BiomeGenHills(34, true)).setColor(5271632).setBiomeName("Extreme Hills+") +~ .setHeight(height_MidHills).setTemperatureRainfall(0.2F, 0.3F); +~ savanna = (new BiomeGenSavanna(35)).setColor(12431967).setBiomeName("Savanna") +~ .setTemperatureRainfall(1.2F, 0.0F).setDisableRain().setHeight(height_LowPlains); +~ savannaPlateau = (new BiomeGenSavanna(36)).setColor(10984804).setBiomeName("Savanna Plateau") +~ .setTemperatureRainfall(1.0F, 0.0F).setDisableRain().setHeight(height_HighPlateaus); +~ mesa = (new BiomeGenMesa(37, false, false)).setColor(14238997).setBiomeName("Mesa"); +~ mesaPlateau_F = (new BiomeGenMesa(38, false, true)).setColor(11573093).setBiomeName("Mesa Plateau F") +~ .setHeight(height_HighPlateaus); +~ mesaPlateau = (new BiomeGenMesa(39, false, false)).setColor(13274213).setBiomeName("Mesa Plateau") +~ .setHeight(height_HighPlateaus); +~ field_180279_ad = ocean; +~ + +> DELETE 549 @ 532 : 535 + +> DELETE 551 @ 537 : 573 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenBeach.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenBeach.edit.java new file mode 100644 index 0000000..958ec24 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenBeach.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> DELETE 10 @ 11 : 15 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenDesert.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenDesert.edit.java new file mode 100644 index 0000000..d87afb3 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenDesert.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 8 + +> DELETE 10 @ 15 : 19 + +> DELETE 12 @ 21 : 32 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenEnd.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenEnd.edit.java new file mode 100644 index 0000000..832e4c3 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenEnd.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 6 + +> DELETE 15 @ 17 : 18 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenForest.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenForest.edit.java new file mode 100644 index 0000000..87f1181 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenForest.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 8 @ 8 : 15 + +> DELETE 11 @ 18 : 21 + +> DELETE 15 @ 25 : 32 + +> DELETE 28 @ 45 : 49 + +> CHANGE 44 : 45 @ 65 : 71 + +~ public BlockFlower.EnumFlowerType pickRandomFlower(EaglercraftRandom random, BlockPos blockpos) { + +> DELETE 59 @ 85 : 135 + +> CHANGE 74 : 76 @ 150 : 160 + +~ && this.biomeID != BiomeGenBase.birchForestHills.biomeID ? new BiomeGenMutated(i, this) +~ : new BiomeGenMutated(i, this); + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenHell.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenHell.edit.java new file mode 100644 index 0000000..b7b408f --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenHell.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 5 @ 5 : 6 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenHills.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenHills.edit.java new file mode 100644 index 0000000..ad00b67 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenHills.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 5 @ 5 : 6 + +> DELETE 6 @ 7 : 8 + +> DELETE 7 @ 9 : 13 + +> DELETE 9 @ 15 : 19 + +> DELETE 18 @ 28 : 29 + +> CHANGE 23 : 25 @ 34 : 62 + +~ public void genTerrainBlocks(World world, EaglercraftRandom random, ChunkPrimer chunkprimer, int i, int j, +~ double d0) { + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenJungle.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenJungle.edit.java new file mode 100644 index 0000000..0063007 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenJungle.edit.java @@ -0,0 +1,33 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 6 @ 7 : 8 + +> DELETE 10 @ 12 : 23 + +> CHANGE 13 : 16 @ 26 : 34 + +~ private final IBlockState field_181620_aE; +~ private final IBlockState field_181621_aF; +~ private final IBlockState field_181622_aG; + +> INSERT 19 : 25 @ 37 + ++ field_181620_aE = Blocks.log.getDefaultState().withProperty(BlockOldLog.VARIANT, BlockPlanks.EnumType.JUNGLE); ++ field_181621_aF = Blocks.leaves.getDefaultState() ++ .withProperty(BlockOldLeaf.VARIANT, BlockPlanks.EnumType.JUNGLE) ++ .withProperty(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false)); ++ field_181622_aG = Blocks.leaves.getDefaultState().withProperty(BlockOldLeaf.VARIANT, BlockPlanks.EnumType.OAK) ++ .withProperty(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false)); + +> DELETE 26 @ 38 : 46 + +> DELETE 33 @ 53 : 83 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenMesa.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenMesa.edit.java new file mode 100644 index 0000000..81b668f --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenMesa.edit.java @@ -0,0 +1,36 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 3 : 5 @ 3 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 14 @ 13 : 14 + +> DELETE 16 @ 16 : 17 + +> DELETE 35 @ 36 : 41 + +> DELETE 36 @ 42 : 46 + +> DELETE 38 @ 48 : 52 + +> CHANGE 46 : 48 @ 60 : 65 + +~ public void genTerrainBlocks(World world, EaglercraftRandom random, ChunkPrimer chunkprimer, int i, int j, +~ double d0) { + +> CHANGE 53 : 54 @ 70 : 71 + +~ EaglercraftRandom random1 = new EaglercraftRandom(this.field_150622_aD); + +> CHANGE 165 : 166 @ 182 : 183 + +~ EaglercraftRandom random = new EaglercraftRandom(parLong1); + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenMushroomIsland.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenMushroomIsland.edit.java new file mode 100644 index 0000000..a4a9920 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenMushroomIsland.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 4 @ 4 : 5 + +> DELETE 8 @ 9 : 14 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenMutated.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenMutated.edit.java new file mode 100644 index 0000000..c6e6cc1 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenMutated.edit.java @@ -0,0 +1,28 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 2 : 4 @ 2 + ++ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; ++ + +> CHANGE 5 : 6 @ 3 : 4 + +~ + +> DELETE 8 @ 6 : 7 + +> DELETE 9 @ 8 : 9 + +> CHANGE 38 : 40 @ 38 : 43 + +~ public void genTerrainBlocks(World world, EaglercraftRandom random, ChunkPrimer chunkprimer, int i, int j, +~ double d0) { + +> DELETE 47 @ 50 : 54 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenOcean.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenOcean.edit.java new file mode 100644 index 0000000..3eeb55d --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenOcean.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 5 @ 4 : 5 + +> CHANGE 17 : 19 @ 17 : 18 + +~ public void genTerrainBlocks(World world, EaglercraftRandom random, ChunkPrimer chunkprimer, int i, int j, +~ double d0) { + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenPlains.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenPlains.edit.java new file mode 100644 index 0000000..d1cad27 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenPlains.edit.java @@ -0,0 +1,23 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 7 @ 7 : 9 + +> DELETE 16 @ 18 : 21 + +> CHANGE 18 : 19 @ 23 : 24 + +~ public BlockFlower.EnumFlowerType pickRandomFlower(EaglercraftRandom random, BlockPos blockpos) { + +> DELETE 43 @ 48 : 81 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenRiver.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenRiver.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenRiver.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenSavanna.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenSavanna.edit.java new file mode 100644 index 0000000..3c8d50c --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenSavanna.edit.java @@ -0,0 +1,38 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 5 @ 4 : 5 + +> DELETE 7 @ 7 : 8 + +> DELETE 8 @ 9 : 11 + +> DELETE 9 @ 12 : 14 + +> DELETE 11 @ 16 : 17 + +> DELETE 15 @ 21 : 24 + +> DELETE 17 @ 26 : 30 + +> DELETE 25 @ 38 : 51 + +> DELETE 28 @ 54 : 57 + +> CHANGE 30 : 32 @ 59 : 60 + +~ public void genTerrainBlocks(World world, EaglercraftRandom random, ChunkPrimer chunkprimer, int i, int j, +~ double d0) { + +> DELETE 44 @ 72 : 76 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenSnow.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenSnow.edit.java new file mode 100644 index 0000000..3032131 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenSnow.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 3 @ 4 : 11 + +> DELETE 6 @ 14 : 16 + +> DELETE 17 @ 27 : 49 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenStoneBeach.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenStoneBeach.edit.java new file mode 100644 index 0000000..958ec24 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenStoneBeach.edit.java @@ -0,0 +1,12 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 4 + +> DELETE 10 @ 11 : 15 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenSwamp.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenSwamp.edit.java new file mode 100644 index 0000000..bca3dad --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenSwamp.edit.java @@ -0,0 +1,29 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 5 @ 4 : 5 + +> DELETE 6 @ 6 : 7 + +> DELETE 7 @ 8 : 12 + +> DELETE 11 @ 16 : 26 + +> DELETE 15 @ 30 : 34 + +> CHANGE 25 : 26 @ 44 : 45 + +~ public BlockFlower.EnumFlowerType pickRandomFlower(EaglercraftRandom var1, BlockPos var2) { + +> DELETE 28 @ 47 : 69 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/biome/BiomeGenTaiga.edit.java b/patches/minecraft/net/minecraft/world/biome/BiomeGenTaiga.edit.java new file mode 100644 index 0000000..e9ad701 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/biome/BiomeGenTaiga.edit.java @@ -0,0 +1,32 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 5 @ 4 : 6 + +> DELETE 7 @ 8 : 9 + +> DELETE 8 @ 10 : 11 + +> DELETE 9 @ 12 : 19 + +> DELETE 11 @ 21 : 26 + +> DELETE 17 @ 32 : 42 + +> CHANGE 19 : 21 @ 44 : 57 + +~ public void genTerrainBlocks(World world, EaglercraftRandom random, ChunkPrimer chunkprimer, int i, int j, +~ double d0) { + +> DELETE 22 @ 58 : 82 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/border/IBorderListener.edit.java b/patches/minecraft/net/minecraft/world/border/IBorderListener.edit.java new file mode 100644 index 0000000..3d04761 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/border/IBorderListener.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/border/WorldBorder.edit.java b/patches/minecraft/net/minecraft/world/border/WorldBorder.edit.java new file mode 100644 index 0000000..b90b9a1 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/border/WorldBorder.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> INSERT 3 : 6 @ 4 + ++ ++ import com.google.common.collect.Lists; ++ + +> DELETE 10 @ 8 : 10 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java b/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java new file mode 100644 index 0000000..1b2cd7c --- /dev/null +++ b/patches/minecraft/net/minecraft/world/chunk/Chunk.edit.java @@ -0,0 +1,128 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import java.util.ArrayList; + +> CHANGE 6 : 7 @ 8 : 9 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 8 : 14 @ 10 : 11 + +~ +~ import com.google.common.base.Predicate; +~ import com.google.common.collect.Maps; +~ +~ import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +~ import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +> DELETE 32 @ 29 : 30 + +> DELETE 33 @ 31 : 35 + +> DELETE 34 @ 36 : 39 + +> CHANGE 58 : 59 @ 63 : 64 + +~ private List tileEntityPosQueue; + +> CHANGE 67 : 68 @ 72 : 73 + +~ this.tileEntityPosQueue = new ArrayList(); + +> CHANGE 410 : 419 @ 415 : 419 + +~ try { +~ if (pos.getY() >= 0 && pos.getY() >> 4 < this.storageArrays.length) { +~ ExtendedBlockStorage extendedblockstorage = this.storageArrays[pos.getY() >> 4]; +~ if (extendedblockstorage != null) { +~ int j = pos.getX() & 15; +~ int k = pos.getY() & 15; +~ int i = pos.getZ() & 15; +~ return extendedblockstorage.get(j, k, i); +~ } + +> CHANGE 421 : 428 @ 421 : 436 + +~ return Blocks.air.getDefaultState(); +~ } catch (Throwable throwable) { +~ CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Getting block state"); +~ CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being got"); +~ crashreportcategory.addCrashSectionCallable("Location", new Callable() { +~ public String call() throws Exception { +~ return CrashReportCategory.getCoordinateInfo(pos); + +> INSERT 429 : 433 @ 437 + ++ }); ++ throw new ReportedException(crashreport); ++ } ++ } + +> CHANGE 434 : 447 @ 438 : 448 + +~ /** +~ * only use with a regular "net.minecraft.util.BlockPos"! +~ */ +~ public IBlockState getBlockStateFaster(final BlockPos pos) { +~ try { +~ if (pos.y >= 0 && pos.y >> 4 < this.storageArrays.length) { +~ ExtendedBlockStorage extendedblockstorage = this.storageArrays[pos.getY() >> 4]; +~ if (extendedblockstorage != null) { +~ int j = pos.x & 15; +~ int k = pos.y & 15; +~ int i = pos.z & 15; +~ return extendedblockstorage.get(j, k, i); +~ } + +> INSERT 448 : 459 @ 449 + ++ ++ return Blocks.air.getDefaultState(); ++ } catch (Throwable throwable) { ++ CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Getting block state"); ++ CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being got"); ++ crashreportcategory.addCrashSectionCallable("Location", new Callable() { ++ public String call() throws Exception { ++ return CrashReportCategory.getCoordinateInfo(pos); ++ } ++ }); ++ throw new ReportedException(crashreport); + +> CHANGE 505 : 506 @ 495 : 498 + +~ if (block1 instanceof ITileEntityProvider) { + +> DELETE 539 @ 531 : 535 + +> CHANGE 790 : 792 @ 786 : 788 + +~ && (predicate == null || predicate.apply((T) entity))) { +~ list.add((T) entity); + +> CHANGE 810 : 812 @ 806 : 808 + +~ public EaglercraftRandom getRandomWithSeed(long i) { +~ return new EaglercraftRandom(this.worldObj.getSeed() + (long) (this.xPosition * this.xPosition * 4987142) + +> CHANGE 895 : 896 @ 891 : 892 + +~ this.recheckGaps(true); + +> CHANGE 904 : 905 @ 900 : 901 + +~ BlockPos blockpos = (BlockPos) this.tileEntityPosQueue.remove(0); + +> CHANGE 1014 : 1015 @ 1010 : 1011 + +~ public BiomeGenBase getBiome(BlockPos pos) { + +> DELETE 1018 @ 1014 : 1020 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/chunk/EmptyChunk.edit.java b/patches/minecraft/net/minecraft/world/chunk/EmptyChunk.edit.java new file mode 100644 index 0000000..a7d90c4 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/chunk/EmptyChunk.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> CHANGE 3 : 7 @ 4 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ +~ import com.google.common.base.Predicate; +~ + +> DELETE 15 @ 13 : 14 + +> CHANGE 105 : 107 @ 104 : 106 + +~ public EaglercraftRandom getRandomWithSeed(long seed) { +~ return new EaglercraftRandom(this.getWorld().getSeed() + (long) (this.xPosition * this.xPosition * 4987142) + +> EOF diff --git a/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java b/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java new file mode 100644 index 0000000..ffa970d --- /dev/null +++ b/patches/minecraft/net/minecraft/world/chunk/IChunkProvider.edit.java @@ -0,0 +1,14 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 9 @ 8 : 9 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/gen/FlatGeneratorInfo.edit.java b/patches/minecraft/net/minecraft/world/gen/FlatGeneratorInfo.edit.java new file mode 100644 index 0000000..bb55c2d --- /dev/null +++ b/patches/minecraft/net/minecraft/world/gen/FlatGeneratorInfo.edit.java @@ -0,0 +1,24 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 7 : 12 @ 9 + ++ import java.util.Set; ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> DELETE 16 @ 13 : 14 + +> CHANGE 78 : 79 @ 76 : 77 + +~ for (Entry entry1 : (Set) map.entrySet()) { + +> EOF diff --git a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorImproved.edit.java b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorImproved.edit.java new file mode 100644 index 0000000..4e8c0f6 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorImproved.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 4 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 21 : 22 @ 22 : 23 + +~ this(new EaglercraftRandom()); + +> CHANGE 24 : 25 @ 25 : 26 + +~ public NoiseGeneratorImproved(EaglercraftRandom parRandom) { + +> EOF diff --git a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorOctaves.edit.java b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorOctaves.edit.java new file mode 100644 index 0000000..c260d5c --- /dev/null +++ b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorOctaves.edit.java @@ -0,0 +1,19 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 4 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; +~ + +> DELETE 5 @ 4 : 6 + +> CHANGE 10 : 11 @ 11 : 12 + +~ public NoiseGeneratorOctaves(EaglercraftRandom parRandom, int parInt1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorPerlin.edit.java b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorPerlin.edit.java new file mode 100644 index 0000000..1847500 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorPerlin.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 5 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 8 : 9 @ 10 : 11 + +~ public NoiseGeneratorPerlin(EaglercraftRandom parRandom, int parInt1) { + +> EOF diff --git a/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorSimplex.edit.java b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorSimplex.edit.java new file mode 100644 index 0000000..481c5a0 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/gen/NoiseGeneratorSimplex.edit.java @@ -0,0 +1,20 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 2 : 3 @ 2 : 3 + +~ import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; + +> CHANGE 17 : 18 @ 17 : 18 + +~ this(new EaglercraftRandom()); + +> CHANGE 20 : 21 @ 20 : 21 + +~ public NoiseGeneratorSimplex(EaglercraftRandom parRandom) { + +> EOF diff --git a/patches/minecraft/net/minecraft/world/storage/ISaveHandler.edit.java b/patches/minecraft/net/minecraft/world/storage/ISaveHandler.edit.java new file mode 100644 index 0000000..fe5b5aa --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/ISaveHandler.edit.java @@ -0,0 +1,16 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 4 @ 5 : 9 + +> DELETE 10 @ 15 : 17 + +> DELETE 18 @ 25 : 29 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/storage/MapData.edit.java b/patches/minecraft/net/minecraft/world/storage/MapData.edit.java new file mode 100644 index 0000000..62986a0 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/MapData.edit.java @@ -0,0 +1,17 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 4 + +> INSERT 4 : 8 @ 6 + ++ ++ import com.google.common.collect.Lists; ++ import com.google.common.collect.Maps; ++ + +> EOF diff --git a/patches/minecraft/net/minecraft/world/storage/MapStorage.edit.java b/patches/minecraft/net/minecraft/world/storage/MapStorage.edit.java new file mode 100644 index 0000000..58b959c --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/MapStorage.edit.java @@ -0,0 +1,37 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 10 + +> CHANGE 4 : 8 @ 12 : 16 + +~ +~ import com.google.common.collect.Lists; +~ import com.google.common.collect.Maps; +~ + +> DELETE 9 @ 17 : 18 + +> CHANGE 22 : 23 @ 31 : 63 + +~ return (WorldSavedData) this.loadedDataMap.get(s); + +> CHANGE 36 : 37 @ 76 : 81 + +~ ((WorldSavedData) this.loadedDataList.get(i)).setDirty(false); + +> DELETE 41 @ 85 : 105 + +> CHANGE 42 : 43 @ 106 : 131 + +~ this.idCounts.clear(); + +> CHANGE 54 : 55 @ 142 : 165 + +~ return oshort.shortValue(); + +> EOF diff --git a/patches/minecraft/net/minecraft/world/storage/SaveDataMemoryStorage.edit.java b/patches/minecraft/net/minecraft/world/storage/SaveDataMemoryStorage.edit.java new file mode 100644 index 0000000..008e178 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/SaveDataMemoryStorage.edit.java @@ -0,0 +1,10 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 3 @ 3 : 5 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/storage/SaveHandlerMP.edit.java b/patches/minecraft/net/minecraft/world/storage/SaveHandlerMP.edit.java new file mode 100644 index 0000000..55d1cf7 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/SaveHandlerMP.edit.java @@ -0,0 +1,18 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> DELETE 2 @ 2 : 3 + +> DELETE 4 @ 5 : 10 + +> DELETE 13 @ 19 : 23 + +> DELETE 26 @ 36 : 40 + +> DELETE 30 @ 44 : 47 + +> EOF diff --git a/patches/minecraft/net/minecraft/world/storage/WorldInfo.edit.java b/patches/minecraft/net/minecraft/world/storage/WorldInfo.edit.java new file mode 100644 index 0000000..5527b98 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/storage/WorldInfo.edit.java @@ -0,0 +1,44 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> INSERT 3 : 4 @ 3 + ++ + +> DELETE 6 @ 5 : 6 + +> INSERT 12 : 14 @ 12 + ++ import net.lax1dude.eaglercraft.v1_8.HString; ++ + +> CHANGE 251 : 252 @ 249 : 250 + +~ // nbt.setLong("LastPlayed", MinecraftServer.getCurrentTimeMillis()); + +> CHANGE 553 : 554 @ 551 : 552 + +~ return HString.format("ID %02d - %s, ver %d. Features enabled: %b", + +> CHANGE 573 : 575 @ 571 : 573 + +~ return HString.format("%d game time, %d day time", new Object[] { +~ Long.valueOf(WorldInfo.this.totalTime), Long.valueOf(WorldInfo.this.worldTime) }); + +> CHANGE 598 : 599 @ 596 : 597 + +~ return HString.format("0x%05X - %s", new Object[] { Integer.valueOf(WorldInfo.this.saveVersion), s }); + +> CHANGE 603 : 604 @ 601 : 602 + +~ return HString.format("Rain time: %d (now: %b), thunder time: %d (now: %b)", + +> CHANGE 611 : 612 @ 609 : 610 + +~ return HString.format("Game mode: %s (ID %d). Hardcore: %b. Cheats: %b", new Object[] { + +> EOF diff --git a/patches/minecraft/output_license.txt b/patches/minecraft/output_license.txt new file mode 100644 index 0000000..2834f64 --- /dev/null +++ b/patches/minecraft/output_license.txt @@ -0,0 +1,15 @@ +This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. + +Minecraft 1.8.8 bytecode is (c) 2015 Mojang AB. "Do not distribute!" +Mod Coder Pack v9.18 deobfuscation configs are (c) Copyright by the MCP Team + +EaglercraftX 1.8 patch files are (c) 2022 LAX1DUDE. All Rights Reserved. + +WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES +NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED +TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE +SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + +NOT FOR COMMERCIAL OR MALICIOUS USE + +(please read the 'LICENSE' file this repo's root directory for more info) \ No newline at end of file diff --git a/patches/resources/assets/minecraft/lang/en_US.edit.lang b/patches/resources/assets/minecraft/lang/en_US.edit.lang new file mode 100644 index 0000000..8265bb0 --- /dev/null +++ b/patches/resources/assets/minecraft/lang/en_US.edit.lang @@ -0,0 +1,91 @@ + +# Eagler Context Redacted Diff +# Copyright (c) 2022 lax1dude. All rights reserved. + +# Version: 1.0 +# Author: lax1dude + +> CHANGE 17 : 21 @ 17 : 24 + +~ eaglercraft.recording.unsupported=Recording Unsupported! +~ eaglercraft.recording.stop=Stop Recording +~ eaglercraft.recording.start=Record Screen... +~ eaglercraft.soundCategory.voice=Voice + +> INSERT 22 : 87 @ 25 + ++ eaglercraft.resourcePack.prompt.title=What do you want to do with '%s'? ++ eaglercraft.resourcePack.prompt.text=Tip: Hold Shift to skip this screen when selecting a resource pack! ++ eaglercraft.resourcePack.prompt.delete=Delete this resource pack ++ eaglercraft.resourcePack.prompt.add=Select this resource pack ++ eaglercraft.resourcePack.load.refreshing=Refreshing Resources... ++ eaglercraft.resourcePack.load.pleaseWait=Please Wait. ++ eaglercraft.resourcePack.load.loading=Loading resource pack... ++ eaglercraft.resourcePack.load.deleting=Deleting resource pack... ++ ++ eaglercraft.gui.exitKey=Use '%s' to close this screen! ++ eaglercraft.gui.exitKeyRetarded=Use Backtick (`) to close this screen! ++ ++ eaglercraft.menu.forkOnGitlab=Fork on Gitlab ++ eaglercraft.menu.editProfile=Edit Profile ++ ++ eaglercraft.editProfile.title=Edit Profile ++ eaglercraft.editProfile.username=Username ++ eaglercraft.editProfile.playerSkin=Player Skin ++ eaglercraft.editProfile.addSkin=Add Skin ++ eaglercraft.editProfile.clearSkin=Clear List ++ ++ eaglercraft.addServer.SSLWarn1=you are on an https: page! ++ eaglercraft.addServer.SSLWarn2=html5 will only allow wss:// ++ ++ eaglercraft.chat.exit=Exit Chat ++ ++ eaglercraft.handshakeApprove.plaintext.title=Protocol Warning ++ ++ eaglercraft.handshakeApprove.plaintext.body.0=§eThis server's 3rd party login system is ++ eaglercraft.handshakeApprove.plaintext.body.1=§eusing insecure plain-text password login ++ ++ eaglercraft.handshakeApprove.plaintext.body.3=This means your password can be stolen ++ eaglercraft.handshakeApprove.plaintext.body.4=by who is running this server, and also ++ eaglercraft.handshakeApprove.plaintext.body.5=any proxy you use to connect to it with ++ ++ eaglercraft.handshakeApprove.plaintext.body.7=§7Would you like to continue? ++ ++ eaglercraft.handshakeApprove.unsupportedAuth.title=Protocol Unsupported ++ ++ eaglercraft.handshakeApprove.unsupportedAuth.body.0=§cThis server's login system is using a ++ eaglercraft.handshakeApprove.unsupportedAuth.body.1=§cprotocol not supported by this client ++ ++ eaglercraft.handshakeApprove.unsupportedAuth.body.3=Please make sure you are using the ++ eaglercraft.handshakeApprove.unsupportedAuth.body.4=latest version of EaglercraftX or ++ eaglercraft.handshakeApprove.unsupportedAuth.body.5=another fork made for this server ++ ++ eaglercraft.options.hud.fps=Show FPS ++ eaglercraft.options.hud.coords=Show XYZ ++ eaglercraft.options.hud.stats=Show Stats HUD ++ eaglercraft.options.hud.world=Show World HUD ++ eaglercraft.options.hud.player=Show Player ++ eaglercraft.options.hud.note=Check 'Video Settings' for the option to hide XYZ ++ eaglercraft.options.hud.24h=24h Day ++ eaglercraft.options.chunkFix=Chunk Lag Fix ++ eaglercraft.options.fog=Fog ++ ++ eaglercraft.key.function=Function ++ eaglercraft.key.zoomCamera=Zoom Camera ++ eaglercraft.key.close=Close Screen ++ ++ eaglercraft.disconnect.tooManyRequests=Too Many Requests! ++ ++ eaglercraft.auth.required=Authentication Required ++ eaglercraft.auth.continue=Join Server ++ + +> CHANGE 555 : 556 @ 493 : 494 + +~ resourcePack.openFolder=Open resource pack + +> CHANGE 559 : 560 @ 497 : 498 + +~ resourcePack.folderInfo=(Select resource pack files here) + +> EOF diff --git a/patches/resources/delete.txt b/patches/resources/delete.txt new file mode 100644 index 0000000..f273a80 --- /dev/null +++ b/patches/resources/delete.txt @@ -0,0 +1,88 @@ +# 87 files to delete: +assets/minecraft/shaders/post/ntsc.json +assets/minecraft/shaders/program/outline_combine.json +assets/minecraft/shaders/post/art.json +assets/minecraft/shaders/program/sobel.fsh +assets/minecraft/shaders/program/fxaa.vsh +assets/minecraft/shaders/program/outline_watercolor.fsh +assets/minecraft/shaders/program/notch.fsh +assets/minecraft/shaders/program/blur.fsh +assets/minecraft/shaders/program/phosphor.json +assets/minecraft/shaders/program/blobs.fsh +assets/minecraft/shaders/post/bits.json +assets/minecraft/shaders/post/desaturate.json +assets/minecraft/shaders/post/fxaa.json +assets/minecraft/shaders/program/outline_soft.fsh +assets/minecraft/shaders/program/overlay.json +assets/minecraft/shaders/post/blur.json +assets/minecraft/shaders/program/spiderclip.fsh +assets/minecraft/shaders/program/sobel.vsh +assets/minecraft/shaders/program/fxaa.fsh +assets/minecraft/shaders/program/wobble.fsh +assets/minecraft/shaders/post/sobel.json +assets/minecraft/shaders/post/creeper.json +assets/minecraft/shaders/post/bumpy.json +assets/minecraft/shaders/program/blobs.json +assets/minecraft/shaders/program/blit.fsh +assets/minecraft/shaders/post/notch.json +assets/minecraft/shaders/program/phosphor.fsh +assets/minecraft/shaders/program/blobs.vsh +assets/minecraft/shaders/program/blit.json +assets/minecraft/shaders/program/bits.json +assets/minecraft/shaders/program/outline_watercolor.json +assets/minecraft/shaders/program/bumpy.vsh +assets/minecraft/shaders/post/blobs.json +assets/minecraft/shaders/program/outline_combine.fsh +assets/minecraft/shaders/program/outline.fsh +assets/minecraft/shaders/program/flip.json +assets/minecraft/shaders/program/blobs2.json +assets/minecraft/shaders/program/color_convolve.json +assets/minecraft/shaders/program/blit.vsh +assets/minecraft/shaders/program/bumpy.fsh +assets/minecraft/shaders/post/outline.json +assets/minecraft/shaders/post/entity_outline.json +assets/minecraft/shaders/program/bits.fsh +assets/minecraft/shaders/post/pencil.json +assets/minecraft/shaders/program/invert.vsh +assets/minecraft/shaders/program/blobs2.fsh +assets/minecraft/shaders/program/rotscale.vsh +assets/minecraft/shaders/program/outline.json +assets/minecraft/shaders/post/invert.json +assets/minecraft/shaders/post/deconverge.json +assets/minecraft/shaders/program/antialias.fsh +assets/minecraft/shaders/program/spider.json +assets/minecraft/shaders/program/entity_outline.json +assets/minecraft/shaders/program/invert.fsh +assets/minecraft/shaders/program/downscale.vsh +assets/minecraft/shaders/program/scan_pincushion.fsh +assets/minecraft/shaders/program/overlay.fsh +assets/minecraft/shaders/program/deconverge.json +assets/minecraft/shaders/program/ntsc_encode.fsh +assets/minecraft/shaders/program/flip.vsh +assets/minecraft/shaders/program/notch.json +assets/minecraft/shaders/program/deconverge.fsh +assets/minecraft/shaders/post/color_convolve.json +assets/minecraft/shaders/post/wobble.json +assets/minecraft/shaders/program/ntsc_encode.json +assets/minecraft/shaders/program/entity_sobel.fsh +assets/minecraft/shaders/program/downscale.fsh +assets/minecraft/shaders/program/sobel.json +assets/minecraft/shaders/post/green.json +assets/minecraft/shaders/program/antialias.json +assets/minecraft/shaders/post/blobs2.json +assets/minecraft/shaders/post/antialias.json +assets/minecraft/shaders/program/downscale.json +assets/minecraft/shaders/program/wobble.json +assets/minecraft/shaders/program/fxaa.json +assets/minecraft/shaders/program/color_convolve.fsh +assets/minecraft/shaders/program/bumpy.json +assets/minecraft/shaders/program/outline_soft.json +assets/minecraft/shaders/program/scan_pincushion.json +assets/minecraft/shaders/post/flip.json +assets/minecraft/shaders/program/ntsc_decode.fsh +assets/minecraft/shaders/post/spider.json +assets/minecraft/shaders/program/ntsc_decode.json +assets/minecraft/shaders/program/invert.json +assets/minecraft/shaders/post/phosphor.json +assets/minecraft/shaders/post/scan_pincushion.json +assets/minecraft/shaders/program/blur.json diff --git a/run_chmod.sh b/run_chmod.sh new file mode 100644 index 0000000..af19843 --- /dev/null +++ b/run_chmod.sh @@ -0,0 +1,11 @@ +#!/bin/sh +chmod +x CompileLatestClient.sh +chmod +x build_help.sh +chmod +x build_init.sh +chmod +x build_make_workspace.sh +chmod +x build_make_pullrequest.sh +chmod +x build_test_pullrequest.sh +chmod +x build_make_unpatched.sh +chmod +x build_merge_pullrequest.sh +chmod +x build_merge_direct.sh +chmod +x build_clean_tmp.sh \ No newline at end of file diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java new file mode 100644 index 0000000..37ce82c --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java @@ -0,0 +1,152 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +class OpenGLObjects { + + static class BufferGL implements IBufferGL { + + final int ptr; + + BufferGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteBuffers(this); + } + + } + + static class BufferArrayGL implements IBufferArrayGL { + + final int ptr; + + BufferArrayGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteVertexArrays(this); + } + + } + + static class TextureGL implements ITextureGL { + + final int ptr; + + TextureGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteTextures(this); + } + + } + + static class ProgramGL implements IProgramGL { + + final int ptr; + + ProgramGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteProgram(this); + } + + } + + static class UniformGL implements IUniformGL { + + final int ptr; + + UniformGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + } + + } + + static class ShaderGL implements IShaderGL { + + final int ptr; + + ShaderGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteShader(this); + } + + } + + static class FramebufferGL implements IFramebufferGL { + + final int ptr; + + FramebufferGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteFramebuffer(this); + } + + } + + static class RenderbufferGL implements IRenderbufferGL { + + final int ptr; + + RenderbufferGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteRenderbuffer(this); + } + + } + + static class QueryGL implements IQueryGL { + + final int ptr; + + QueryGL(int ptr) { + this.ptr = ptr; + } + + @Override + public void free() { + PlatformOpenGL._wglDeleteQueries(this); + } + + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java new file mode 100644 index 0000000..98f79bf --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java @@ -0,0 +1,195 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import static org.lwjgl.glfw.GLFW.*; + +import java.awt.Component; +import java.awt.Desktop; +import java.awt.EventQueue; +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.Dialog.ModalExclusionType; +import java.awt.Dialog.ModalityType; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; + +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlatformApplication { + + private static long win = 0l; + + static void initHooks(long glfwWindow) { + win = glfwWindow; + } + + public static void openLink(String url) { + try { + Desktop.getDesktop().browse(new URI(url)); + } catch (Throwable var5) { + var5.printStackTrace(); + } + } + + public static void setClipboard(String text) { + glfwSetClipboardString(win, text); + } + + public static String getClipboard() { + return glfwGetClipboardString(win); + } + + public static void setLocalStorage(String name, byte[] data) { + try(FileOutputStream f = new FileOutputStream(new File("_eagstorage."+name+".dat"))) { + f.write(data); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static byte[] getLocalStorage(String data) { + File f = new File("_eagstorage."+data+".dat"); + if(!f.isFile()) { + return null; + } + byte[] b = new byte[(int)f.length()]; + try(FileInputStream s = new FileInputStream(f)) { + s.read(b); + return b; + } catch (IOException e) { + return null; + } + } + + public static String saveScreenshot() { + return "nothing"; + } + + public static void showPopup(String msg) { + JOptionPane pane = new JOptionPane(msg, JOptionPane.WARNING_MESSAGE, JOptionPane.DEFAULT_OPTION, null, + new Object[] { "OK" }, "OK"); + pane.setInitialValue("OK"); + JDialog dialog = pane.createDialog("EaglercraftX Runtime"); + pane.selectInitialValue(); + dialog.setIconImage(Toolkit.getDefaultToolkit().getImage("icon32.png")); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setAlwaysOnTop(true); + dialog.setModal(true); + dialog.setLocationByPlatform(true); + dialog.setModalExclusionType(ModalExclusionType.TOOLKIT_EXCLUDE); + dialog.setModalityType(ModalityType.TOOLKIT_MODAL); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + private static volatile boolean fileChooserOpen = false; + private static volatile boolean fileChooserHasResult = false; + private static volatile FileChooserResult fileChooserResultObject = null; + + public static void displayFileChooser(final String mime, final String ext) { + if(!fileChooserOpen) { + fileChooserOpen = true; + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + runDisplayFileChooser(mime, ext); + } + }); + } + } + + private static void runDisplayFileChooser(String mime, String ext) { + try { + JFileChooser fc = new FileChooserAlwaysOnTop((new File(".")).getAbsoluteFile()); + fc.setDialogTitle("select a file"); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + fc.setFileFilter(new FileFilterExt(ext)); + if(fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { + File f = fc.getSelectedFile(); + if(f != null) { + String name = f.getName(); + byte[] bytes = new byte[(int)f.length()]; + try(FileInputStream is = new FileInputStream(f)) { + is.read(bytes); + } + fileChooserResultObject = new FileChooserResult(name, bytes); + }else { + fileChooserResultObject = null; + } + } + }catch(Throwable t) { + fileChooserResultObject = null; + } + fileChooserOpen = false; + fileChooserHasResult = true; + } + + private static class FileChooserAlwaysOnTop extends JFileChooser { + + private FileChooserAlwaysOnTop(File file) { + super(file); + } + + protected JDialog createDialog(Component parent) throws HeadlessException { + JDialog dialog = super.createDialog(parent); + dialog.setAlwaysOnTop(true); + return dialog; + } + + } + + private static class FileFilterExt extends FileFilter { + + private final String extension; + + private FileFilterExt(String ext) { + extension = ext; + } + + @Override + public boolean accept(File f) { + return f.isDirectory() || f.getName().endsWith("." + extension); + } + + @Override + public String getDescription() { + return extension + " files"; + } + + } + + public static boolean fileChooserHasResult() { + return fileChooserHasResult; + } + + public static FileChooserResult getFileChooserResult() { + fileChooserHasResult = false; + FileChooserResult res = fileChooserResultObject; + fileChooserResultObject = null; + return res; + } + + public static void openCreditsPopup(String text) { + + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java new file mode 100644 index 0000000..23706af --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java @@ -0,0 +1,82 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.imageio.ImageIO; + +import net.lax1dude.eaglercraft.v1_8.EaglerInputStream; +import net.lax1dude.eaglercraft.v1_8.opengl.ImageData; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlatformAssets { + + static URL getDesktopResourceURL(String path) { + File f = new File("resources", path); + if(f.isFile()) { + try { + return f.toURI().toURL(); + } catch (MalformedURLException e) { + return null; + } + }else { + return null; + } + } + + public static final byte[] getResourceBytes(String path) { + File loadFile = new File("resources", path); + byte[] ret = new byte[(int) loadFile.length()]; + try(FileInputStream is = new FileInputStream(loadFile)) { + is.read(ret); + is.close(); + return ret; + }catch(IOException ex) { + return null; + } + } + + public static final ImageData loadImageFile(InputStream data) { + try { + BufferedImage img = ImageIO.read(data); + int w = img.getWidth(); + int h = img.getHeight(); + boolean a = img.getColorModel().hasAlpha(); + int[] pixels = new int[w * h]; + img.getRGB(0, 0, w, h, pixels, 0, w); + for(int i = 0; i < pixels.length; ++i) { + int j = pixels[i]; + if(!a) { + j = j | 0xFF000000; + } + pixels[i] = (j & 0xFF00FF00) | ((j & 0x00FF0000) >> 16) | + ((j & 0x000000FF) << 16); + } + return new ImageData(w, h, pixels, a); + }catch(IOException ex) { + return null; + } + } + + public static final ImageData loadImageFile(byte[] data) { + return loadImageFile(new EaglerInputStream(data)); + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java new file mode 100644 index 0000000..d20ea8a --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java @@ -0,0 +1,219 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.net.URL; + +import net.lax1dude.eaglercraft.v1_8.internal.paulscode.lwjgl3.LibraryLWJGLOpenAL; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import net.minecraft.util.MathHelper; +import paulscode.sound.SoundSystem; +import paulscode.sound.SoundSystemConfig; +import paulscode.sound.SoundSystemLogger; +import paulscode.sound.codecs.CodecJOrbis; +import paulscode.sound.codecs.CodecWav; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlatformAudio { + + protected static class PaulscodeAudioResource implements IAudioResource { + + protected final URL resourceLoc; + + protected PaulscodeAudioResource(URL resourceLoc) { + this.resourceLoc = resourceLoc; + } + + } + + protected static class PaulscodeAudioHandle implements IAudioHandle { + + protected final String sourceName; + protected long stall; + + protected PaulscodeAudioHandle(String sourceName) { + this.sourceName = sourceName; + this.stall = System.currentTimeMillis(); + } + + @Override + public void pause(boolean setPaused) { + if(setPaused) { + if(sndSystem.playing(sourceName)) { + sndSystem.pause(sourceName); + } + }else { + if(!sndSystem.playing(sourceName)) { + sndSystem.play(sourceName); + } + } + } + + @Override + public void restart() { + this.stall = System.currentTimeMillis(); + sndSystem.rewind(sourceName); + sndSystem.play(sourceName); + } + + @Override + public void move(float x, float y, float z) { + sndSystem.setPosition(sourceName, x, y, z); + } + + @Override + public void pitch(float f) { + sndSystem.setPitch(sourceName, f); + } + + @Override + public void gain(float f) { + sndSystem.setVolume(sourceName, f); + } + + @Override + public void end() { + sndSystem.stop(sourceName); + } + + @Override + public boolean shouldFree() { + return !sndSystem.playing(sourceName) && System.currentTimeMillis() - this.stall > 250l; //TODO: I hate this hack + } + + } + + public static IAudioResource loadAudioData(String filename, boolean holdInCache) { + URL ret = PlatformAssets.getDesktopResourceURL(filename); + if(ret != null) { + return new PaulscodeAudioResource(ret); + }else { + return null; + } + } + + public static void clearAudioCache() { + // browser only + } + + private static final Logger logger = LogManager.getLogger("EaglercraftPlatformAudio"); + private static SoundSystem sndSystem = null; + + static void platformInitialize() { + logger.info("Eaglercraft still uses Paul Lamb's SoundSystem but with LWJGL3"); + logger.info(" \"Author: Paul Lamb, www.paulscode.com\""); + try { + SoundSystemConfig.addLibrary(LibraryLWJGLOpenAL.class); + SoundSystemConfig.setCodec("ogg", CodecJOrbis.class); + SoundSystemConfig.setCodec("wav", CodecWav.class); + SoundSystemConfig.setLogger(new SoundSystemLogger() { + public void message(String parString1, int parInt1) { + if (!parString1.isEmpty()) { + logger.info(parString1); + } + } + public void importantMessage(String parString1, int parInt1) { + if (!parString1.isEmpty()) { + logger.warn(parString1); + } + } + public void errorMessage(String parString1, String parString2, int parInt1) { + if (!parString2.isEmpty()) { + logger.error("Error in class \"{}\"!", parString1); + logger.error(parString2); + } + } + }); + sndSystem = new SoundSystem(); + }catch(Throwable t) { + logger.error("Could not initialize Paulscode SoundSystem! Is this system's OpenAL installed correctly?"); + logger.error(t); + sndSystem = null; + } + } + + static void platformShutdown() { + if(sndSystem != null) { + sndSystem.cleanup(); + sndSystem = null; + } + } + + public static boolean available() { + return sndSystem != null; + } + + private static int sourceCounter = 0; + + public static IAudioHandle beginPlayback(IAudioResource track, float x, float y, float z, + float volume, float pitch) { + if(sndSystem == null) { + return null; + } + + float f1 = 16.0F; + if (volume > 1.0F) { + f1 *= volume; + } + + String srcName = "src" + ++sourceCounter; + sndSystem.newSource(false, srcName, ((PaulscodeAudioResource)track).resourceLoc, + ((PaulscodeAudioResource)track).resourceLoc.getPath(), false, x, y, z, 2, f1); + sndSystem.setTemporary(srcName, true); + sndSystem.setPitch(srcName, pitch); + sndSystem.setVolume(srcName, volume); + sndSystem.play(srcName); + + return new PaulscodeAudioHandle(srcName); + } + + public static IAudioHandle beginPlaybackStatic(IAudioResource track, float volume, float pitch) { + if(sndSystem == null) { + return null; + } + + String srcName = "src" + ++sourceCounter; + sndSystem.newSource(false, srcName, ((PaulscodeAudioResource)track).resourceLoc, + ((PaulscodeAudioResource)track).resourceLoc.getPath(), false, 0.0f, 0.0f, 0.0f, 0, 0.0f); + sndSystem.setTemporary(srcName, true); + sndSystem.setPitch(srcName, pitch); + sndSystem.setVolume(srcName, volume); + sndSystem.play(srcName); + + return new PaulscodeAudioHandle(srcName); + } + + public static void setListener(float x, float y, float z, float pitchDegrees, float yawDegrees) { + if(sndSystem == null) { + return; + } + float f2 = MathHelper.cos((yawDegrees + 90.0F) * 0.017453292F); + float f3 = MathHelper.sin((yawDegrees + 90.0F) * 0.017453292F); + float f4 = MathHelper.cos(-pitchDegrees * 0.017453292F); + float f5 = MathHelper.sin(-pitchDegrees * 0.017453292F); + float f6 = MathHelper.cos((-pitchDegrees + 90.0F) * 0.017453292F); + float f7 = MathHelper.sin((-pitchDegrees + 90.0F) * 0.017453292F); + float f8 = f2 * f4; + float f9 = f3 * f4; + float f10 = f2 * f6; + float f11 = f3 * f6; + sndSystem.setListenerPosition(x, y, z); + sndSystem.setListenerOrientation(f8, f5, f9, f10, f7, f11); + } + + public static void setMicVol(float vol) { + // nope + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java new file mode 100644 index 0000000..8fdc01b --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformBufferFunctions.java @@ -0,0 +1,22 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; + +public class PlatformBufferFunctions { + + public static void put(ByteBuffer newBuffer, ByteBuffer flip) { + int len = flip.remaining(); + for(int i = 0; i < len; ++i) { + newBuffer.put(flip.get()); + } + } + + public static void put(IntBuffer intBuffer, int index, int[] data) { + int p = intBuffer.position(); + intBuffer.position(index); + intBuffer.put(data); + intBuffer.position(p); + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java new file mode 100644 index 0000000..c8b44f7 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java @@ -0,0 +1,374 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import static org.lwjgl.glfw.GLFW.*; + +import java.util.LinkedList; +import java.util.List; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; + +import net.lax1dude.eaglercraft.v1_8.EagUtils; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlatformInput { + + private static long win = 0l; + + private static boolean windowFocused = true; + private static boolean windowResized = true; + + private static boolean windowCursorEntered = true; + private static boolean windowMouseGrabbed = false; + private static int cursorX = 0; + private static int cursorY = 0; + private static int cursorDX = 0; + private static int cursorDY = 0; + private static int DWheel = 0; + + private static int windowWidth = 640; + private static int windowHeight = 480; + + private static final List keyboardEventList = new LinkedList(); + private static KeyboardEvent currentKeyboardEvent = null; + + private static final char[] keyboardReleaseEventChars = new char[256]; + + private static boolean enableRepeatEvents = false; + private static int functionKeyModifier = GLFW_KEY_F; + + private static final List keyboardCharList = new LinkedList(); + + private static class KeyboardEvent { + + protected final int key; + protected final boolean pressed; + protected final boolean repeating; + protected char resolvedCharacter = '\0'; + + protected KeyboardEvent(int key, boolean pressed, boolean repeating) { + this.key = key; + this.pressed = pressed; + this.repeating = repeating; + } + + } + + private static final List mouseEventList = new LinkedList(); + private static MouseEvent currentMouseEvent = null; + + private static class MouseEvent { + + protected final int button; + protected final boolean pressed; + protected final int posX; + protected final int posY; + protected final float wheel; + + protected MouseEvent(int button, boolean pressed, int posX, int posY, float wheel) { + this.button = button; + this.pressed = pressed; + this.posX = posX; + this.posY = posY; + this.wheel = wheel; + } + + } + + static void initHooks(long glfwWindow) { + win = glfwWindow; + + glfwSetErrorCallback((arg0, arg1) -> { + String errorString = ""; + if(arg1 != 0l) { + try(MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer pbuffer = stack.mallocPointer(1); + pbuffer.put(0, arg1); + errorString = pbuffer.getStringUTF8(0); + } + } + PlatformRuntime.logger.error("GLFW Error #{}: {}", arg0, errorString); + }); + + if(!glfwRawMouseMotionSupported()) { + throw new UnsupportedOperationException("Raw mouse movement (cursor lock) is not supported!"); + } + + int[] v1 = new int[1], v2 = new int[1]; + glfwGetFramebufferSize(glfwWindow, v1, v2); + + windowWidth = v1[0]; + windowHeight = v2[0]; + + glfwSetFramebufferSizeCallback(glfwWindow, (window, width, height) -> { + windowWidth = width; + windowHeight = height; + windowResized = true; + }); + + glfwSetWindowFocusCallback(glfwWindow, (window, focused) -> { + windowFocused = focused; + }); + + glfwSetKeyCallback(glfwWindow, (window, key, scancode, action, mods) -> { + if(glfwGetKey(glfwWindow, functionKeyModifier) == GLFW_PRESS) { + if(key >= GLFW_KEY_1 && key <= GLFW_KEY_9) { + key = key - GLFW_KEY_1 + GLFW_KEY_F1; + } + } + key = KeyboardConstants.getEaglerKeyFromGLFW(key); + keyboardEventList.add(new KeyboardEvent(key, action != GLFW_RELEASE, action == GLFW_REPEAT)); + if(keyboardEventList.size() > 64) { + keyboardEventList.remove(0); + } + }); + + glfwSetCharCallback(glfwWindow, (window, character) -> { + keyboardCharList.add(Character.valueOf((char)character)); + if(keyboardCharList.size() > 64) { + keyboardCharList.remove(0); + } + }); + + glfwSetCursorPosCallback(glfwWindow, (window, posX, posY) -> { + posY = windowHeight - posY; + if(windowMouseGrabbed) { + cursorDX -= (cursorX - (int)posX); + cursorDY -= (cursorY - (int)posY); + cursorX = (int)posX; + cursorY = (int)posY; + }else { + cursorX = (int)posX; + cursorY = (int)posY; + mouseEventList.add(new MouseEvent(-1, false, cursorX, cursorY, 0.0f)); + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + } + }); + + glfwSetMouseButtonCallback(glfwWindow, (window, button, action, mods) -> { + mouseEventList.add(new MouseEvent(button, action != GLFW_RELEASE, cursorX, cursorY, 0.0f)); + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + }); + + glfwSetCursorEnterCallback(glfwWindow, (window, enter) -> { + windowCursorEntered = enter; + }); + + glfwSetScrollCallback(glfwWindow, (window, scrollX, scrollY) -> { + DWheel += (int)scrollY; + mouseEventList.add(new MouseEvent(-1, false, cursorX, cursorY, (float)scrollY)); + if(mouseEventList.size() > 64) { + mouseEventList.remove(0); + } + }); + + } + + public static int getWindowWidth() { + return windowWidth; + } + + public static int getWindowHeight() { + return windowHeight; + } + + public static boolean getWindowFocused() { + return windowFocused; + } + + public static boolean isCloseRequested() { + return glfwWindowShouldClose(win); + } + + public static void update() { + glfwPollEvents(); + glfwSwapBuffers(win); + } + + public static boolean wasResized() { + boolean b = windowResized; + windowResized = false; + return b; + } + + public static boolean keyboardNext() { + if(keyboardEventList.size() > 0) { + currentKeyboardEvent = keyboardEventList.remove(0); + if(currentKeyboardEvent.resolvedCharacter == '\0' && KeyboardConstants + .getKeyCharFromEagler(currentKeyboardEvent.key) != '\0') { + if(currentKeyboardEvent.pressed && keyboardCharList.size() > 0) { + currentKeyboardEvent.resolvedCharacter = keyboardCharList.remove(0); + keyboardReleaseEventChars[currentKeyboardEvent.key] = + currentKeyboardEvent.resolvedCharacter; + }else if(!currentKeyboardEvent.pressed) { + currentKeyboardEvent.resolvedCharacter = + keyboardReleaseEventChars[currentKeyboardEvent.key]; + keyboardReleaseEventChars[currentKeyboardEvent.key] = '\0'; + } + } + if(currentKeyboardEvent.repeating && !enableRepeatEvents) { + return keyboardNext(); + }else { + return true; + } + }else { + if(keyboardCharList.size() > 0) { + currentKeyboardEvent = new KeyboardEvent(KeyboardConstants.KEY_SPACE, true, false); + currentKeyboardEvent.resolvedCharacter = keyboardCharList.remove(0); + KeyboardEvent releaseEvent = new KeyboardEvent(KeyboardConstants.KEY_SPACE, false, false); + releaseEvent.resolvedCharacter = currentKeyboardEvent.resolvedCharacter; + keyboardEventList.add(releaseEvent); + return true; + }else { + return false; + } + } + } + + public static boolean keyboardGetEventKeyState() { + return currentKeyboardEvent.pressed; + } + + public static int keyboardGetEventKey() { + return currentKeyboardEvent.key; + } + + public static char keyboardGetEventCharacter() { + return currentKeyboardEvent.resolvedCharacter; + } + + public static boolean keyboardIsKeyDown(int key) { + if(glfwGetKey(win, functionKeyModifier) == GLFW_PRESS) { + if(key >= GLFW_KEY_1 && key <= GLFW_KEY_9) { + return false; + } + if(key >= GLFW_KEY_F1 && key <= GLFW_KEY_F9) { + key = key - GLFW_KEY_F1 + GLFW_KEY_1; + } + } + return glfwGetKey(win, KeyboardConstants.getGLFWKeyFromEagler(key)) == GLFW_PRESS; + } + + public static boolean keyboardIsRepeatEvent() { + return currentKeyboardEvent.repeating; + } + + public static void keyboardEnableRepeatEvents(boolean b) { + enableRepeatEvents = b; + } + + public static boolean mouseNext() { + if(mouseEventList.size() > 0) { + currentMouseEvent = mouseEventList.remove(0); + return true; + }else { + return false; + } + } + + public static boolean mouseGetEventButtonState() { + return currentMouseEvent.pressed; + } + + public static int mouseGetEventButton() { + return currentMouseEvent.button; + } + + public static int mouseGetEventX() { + return currentMouseEvent.posX; + } + + public static int mouseGetEventY() { + return currentMouseEvent.posY; + } + + public static int mouseGetEventDWheel() { + return (int)currentMouseEvent.wheel; + } + + public static int mouseGetX() { + return cursorX; + } + + public static int mouseGetY() { + return cursorY; + } + + public static boolean mouseIsButtonDown(int i) { + return glfwGetMouseButton(win, i) == GLFW_PRESS; + } + + public static int mouseGetDWheel() { + int i = DWheel; + DWheel = 0; + return i; + } + + public static void mouseSetGrabbed(boolean grab) { + if(grab != windowMouseGrabbed) { + cursorX = windowWidth / 2; + cursorY = windowHeight / 2; + glfwSetCursorPos(win, cursorX, cursorY); + windowMouseGrabbed = grab; + cursorDX = 0; + cursorDY = 0; + glfwSetInputMode(win, GLFW_CURSOR, grab ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL); + glfwSetInputMode(win, GLFW_RAW_MOUSE_MOTION, grab ? GLFW_TRUE : GLFW_FALSE); + } + } + + public static boolean isPointerLocked() { + return windowMouseGrabbed; + } + + public static boolean isMouseGrabbed() { + return windowMouseGrabbed; + } + + public static int mouseGetDX() { + int i = cursorDX; + cursorDX = 0; + return i; + } + + public static int mouseGetDY() { + int i = cursorDY; + cursorDY = 0; + return i; + } + + public static void mouseSetCursorPosition(int x, int y) { + cursorX = x; + cursorY = y; + glfwSetCursorPos(win, x, y); + } + + public static boolean mouseIsInsideWindow() { + return windowCursorEntered; + } + + public static boolean contextLost() { + return glfwGetWindowAttrib(win, GLFW_ICONIFIED) == GLFW_TRUE; + } + + public static void setFunctionKeyModifier(int key) { + functionKeyModifier = KeyboardConstants.getGLFWKeyFromEagler(key); + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java new file mode 100644 index 0000000..e6e7cbc --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java @@ -0,0 +1,136 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.LinkedList; +import java.util.List; + +import org.java_websocket.enums.ReadyState; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlatformNetworking { + + static final Logger networkLogger = LogManager.getLogger("PlatformNetworking"); + + private static WebSocketPlayClient wsPlayClient = null; + static EnumEaglerConnectionState playConnectState = EnumEaglerConnectionState.CLOSED; + static EnumServerRateLimit serverRateLimit = null; + + static String currentURI = null; + + public static EnumEaglerConnectionState playConnectionState() { + return ((wsPlayClient == null || wsPlayClient.isClosed()) && playConnectState == EnumEaglerConnectionState.CONNECTING) ? EnumEaglerConnectionState.FAILED : + ((wsPlayClient != null && wsPlayClient.getReadyState() == ReadyState.NOT_YET_CONNECTED) ? EnumEaglerConnectionState.CONNECTING : + (((wsPlayClient == null || wsPlayClient.isClosed()) && playConnectState != EnumEaglerConnectionState.FAILED) ? EnumEaglerConnectionState.CLOSED : playConnectState)); + } + + public static void startPlayConnection(String destination) { + if(!playConnectionState().isClosed()) { + networkLogger.warn("Tried connecting to a server while already connected to a different server!"); + playDisconnect(); + } + + currentURI = destination; + + synchronized(playPackets) { + playPackets.clear(); + } + + playConnectState = EnumEaglerConnectionState.CONNECTING; + networkLogger.info("Connecting to server: {}", destination); + + URI u; + + try { + u = new URI(destination); + }catch(URISyntaxException ex) { + networkLogger.error("Invalid server URI: {}", destination); + playConnectState = EnumEaglerConnectionState.FAILED; + return; + } + + wsPlayClient = new WebSocketPlayClient(u); + wsPlayClient.connect(); + } + + public static void playDisconnect() { + if(!playConnectionState().isClosed() && wsPlayClient != null) { + try { + wsPlayClient.closeBlocking(); + } catch (InterruptedException e) { + // :( + } + playConnectState = EnumEaglerConnectionState.CLOSED; + } + } + + private static final List playPackets = new LinkedList(); + + public static byte[] readPlayPacket() { + synchronized(playPackets) { + return playPackets.size() > 0 ? playPackets.remove(0) : null; + } + } + + public static int countAvailableReadData() { + int total = 0; + synchronized(playPackets) { + for(int i = 0, l = playPackets.size(); i < l; ++i) { + total += playPackets.get(i).length; + } + } + return total; + } + + static void recievedPlayPacket(byte[] arg0) { + synchronized(playPackets) { + playPackets.add(arg0); + } + } + + public static void writePlayPacket(byte[] pkt) { + if(wsPlayClient == null || wsPlayClient.isClosed()) { + networkLogger.error("Tried to send {} byte play packet while the socket was closed!", pkt.length); + }else { + wsPlayClient.send(pkt); + } + } + + public static IServerQuery sendServerQuery(String uri, String accept) { + URI u; + + try { + u = new URI(uri); + }catch(URISyntaxException ex) { + networkLogger.error("Invalid server URI: {}", uri); + playConnectState = EnumEaglerConnectionState.FAILED; + return null; + } + + return new WebSocketServerQuery(accept, u); + } + + public static EnumServerRateLimit getRateLimit() { + return serverRateLimit == null ? EnumServerRateLimit.OK : serverRateLimit; + } + + public static String getCurrentURI() { + return currentURI; + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java new file mode 100644 index 0000000..6c19480 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java @@ -0,0 +1,456 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerLWJGLAllocator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; + +import static org.lwjgl.opengles.GLES30.*; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlatformOpenGL { + + public static final void _wglEnable(int glEnum) { + glEnable(glEnum); + } + + public static final void _wglDisable(int glEnum) { + glDisable(glEnum); + } + + public static final void _wglClearColor(float r, float g, float b, float a) { + glClearColor(r, g, b, a); + } + + public static final void _wglClearDepth(float f) { + glClearDepthf(f); + } + + public static final void _wglClear(int bits) { + glClear(bits); + } + + public static final void _wglDepthFunc(int glEnum) { + glDepthFunc(glEnum); + } + + public static final void _wglDepthMask(boolean mask) { + glDepthMask(mask); + } + + public static final void _wglCullFace(int glEnum) { + glCullFace(glEnum); + } + + public static final void _wglViewport(int x, int y, int w, int h) { + glViewport(x, y, w, h); + } + + public static final void _wglBlendFunc(int src, int dst) { + glBlendFunc(src, dst); + } + + public static final void _wglBlendFuncSeparate(int srcColor, int dstColor, int srcAlpha, int dstAlpha) { + glBlendFuncSeparate(srcColor, dstColor, srcAlpha, dstAlpha); + } + + public static final void _wglBlendEquation(int glEnum) { + glBlendEquation(glEnum); + } + + public static final void _wglColorMask(boolean r, boolean g, boolean b, boolean a) { + glColorMask(r, g, b, a); + } + + public static final void _wglDrawBuffers(int buffer) { + glDrawBuffers(buffer); + } + + public static final void _wglDrawBuffers(int[] buffers) { + glDrawBuffers(buffers); + } + + public static final void _wglReadBuffer(int buffer) { + glReadBuffer(buffer); + } + + public static final void _wglPolygonOffset(float f1, float f2) { + glPolygonOffset(f1, f2); + } + + public static final void _wglLineWidth(float width) { + glLineWidth(width); + } + + public static final IBufferGL _wglGenBuffers() { + return new OpenGLObjects.BufferGL(glGenBuffers()); + } + + public static final ITextureGL _wglGenTextures() { + return new OpenGLObjects.TextureGL(glGenTextures()); + } + + public static final IBufferArrayGL _wglGenVertexArrays() { + return new OpenGLObjects.BufferArrayGL(glGenVertexArrays()); + } + + public static final IProgramGL _wglCreateProgram() { + return new OpenGLObjects.ProgramGL(glCreateProgram()); + } + + public static final IShaderGL _wglCreateShader(int type) { + return new OpenGLObjects.ShaderGL(glCreateShader(type)); + } + + public static final IFramebufferGL _wglCreateFramebuffer() { + return new OpenGLObjects.FramebufferGL(glGenFramebuffers()); + } + + public static final IRenderbufferGL _wglCreateRenderbuffer() { + return new OpenGLObjects.RenderbufferGL(glGenRenderbuffers()); + } + + public static final IQueryGL _wglGenQueries() { + return new OpenGLObjects.QueryGL(glGenQueries()); + } + + public static final void _wglDeleteBuffers(IBufferGL obj) { + glDeleteBuffers(((OpenGLObjects.BufferGL) obj).ptr); + } + + public static final void _wglDeleteTextures(ITextureGL obj) { + glDeleteTextures(((OpenGLObjects.TextureGL) obj).ptr); + } + + public static final void _wglDeleteVertexArrays(IBufferArrayGL obj) { + glDeleteVertexArrays(((OpenGLObjects.BufferArrayGL) obj).ptr); + } + + public static final void _wglDeleteProgram(IProgramGL obj) { + glDeleteProgram(((OpenGLObjects.ProgramGL) obj).ptr); + } + + public static final void _wglDeleteShader(IShaderGL obj) { + glDeleteShader(((OpenGLObjects.ShaderGL) obj).ptr); + } + + public static final void _wglDeleteFramebuffer(IFramebufferGL obj) { + glDeleteFramebuffers(((OpenGLObjects.FramebufferGL) obj).ptr); + } + + public static final void _wglDeleteRenderbuffer(IRenderbufferGL obj) { + glDeleteRenderbuffers(((OpenGLObjects.RenderbufferGL) obj).ptr); + } + + public static final void _wglDeleteQueries(IQueryGL obj) { + glDeleteQueries(((OpenGLObjects.QueryGL) obj).ptr); + } + + public static final void _wglBindBuffer(int target, IBufferGL obj) { + glBindBuffer(target, obj == null ? 0 : ((OpenGLObjects.BufferGL) obj).ptr); + } + + public static final void _wglBufferData(int target, ByteBuffer data, int usage) { + nglBufferData(target, data == null ? 0 : data.remaining(), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); + } + + public static final void _wglBufferData(int target, IntBuffer data, int usage) { + nglBufferData(target, data == null ? 0 : (data.remaining() << 2), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); + } + + public static final void _wglBufferData(int target, FloatBuffer data, int usage) { + nglBufferData(target, data == null ? 0 : (data.remaining() << 2), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data), usage); + } + + public static final void _wglBufferData(int target, int size, int usage) { + glBufferData(target, size, usage); + } + + public static final void _wglBufferSubData(int target, int offset, ByteBuffer data) { + nglBufferSubData(target, offset, data == null ? 0 : data.remaining(), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglBufferSubData(int target, int offset, IntBuffer data) { + nglBufferSubData(target, offset, data == null ? 0 : (data.remaining() << 2), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglBufferSubData(int target, int offset, FloatBuffer data) { + nglBufferSubData(target, offset, data == null ? 0 : (data.remaining() << 2), + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglBindVertexArray(IBufferArrayGL obj) { + glBindVertexArray(obj == null ? 0 : ((OpenGLObjects.BufferArrayGL) obj).ptr); + } + + public static final void _wglEnableVertexAttribArray(int index) { + glEnableVertexAttribArray(index); + } + + public static final void _wglDisableVertexAttribArray(int index) { + glDisableVertexAttribArray(index); + } + + public static final void _wglVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, + int offset) { + glVertexAttribPointer(index, size, type, normalized, stride, offset); + } + + public static final void _wglVertexAttribDivisor(int index, int divisor) { + glVertexAttribDivisor(index, divisor); + } + + public static final void _wglActiveTexture(int texture) { + glActiveTexture(texture); + } + + public static final void _wglBindTexture(int target, ITextureGL obj) { + glBindTexture(target, obj == null ? 0 : ((OpenGLObjects.TextureGL) obj).ptr); + } + + public static final void _wglTexParameterf(int target, int param, float value) { + glTexParameterf(target, param, value); + } + + public static final void _wglTexParameteri(int target, int param, int value) { + glTexParameteri(target, param, value); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, ByteBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, IntBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, + int border, int format, int type, FloatBuffer data) { + nglTexImage2D(target, level, internalFormat, width, height, border, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, + int format, int type, ByteBuffer data) { + glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, + int format, int type, IntBuffer data) { + glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, + int format, int type, FloatBuffer data) { + nglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, + data == null ? 0l : EaglerLWJGLAllocator.getAddress(data)); + } + + public static final void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, + int width, int height) { + glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } + + public static final void _wglPixelStorei(int pname, int value) { + glPixelStorei(pname, value); + } + + public static final void _wglGenerateMipmap(int target) { + glGenerateMipmap(target); + } + + public static final void _wglShaderSource(IShaderGL obj, String source) { + glShaderSource(((OpenGLObjects.ShaderGL) obj).ptr, source); + } + + public static final void _wglCompileShader(IShaderGL obj) { + glCompileShader(((OpenGLObjects.ShaderGL) obj).ptr); + } + + public static final int _wglGetShaderi(IShaderGL obj, int param) { + return glGetShaderi(((OpenGLObjects.ShaderGL) obj).ptr, param); + } + + public static final String _wglGetShaderInfoLog(IShaderGL obj) { + return glGetShaderInfoLog(((OpenGLObjects.ShaderGL) obj).ptr); + } + + public static final void _wglUseProgram(IProgramGL obj) { + glUseProgram(obj == null ? 0 : ((OpenGLObjects.ProgramGL) obj).ptr); + } + + public static final void _wglAttachShader(IProgramGL obj, IShaderGL shader) { + glAttachShader(((OpenGLObjects.ProgramGL) obj).ptr, ((OpenGLObjects.ShaderGL) shader).ptr); + } + + public static final void _wglDetachShader(IProgramGL obj, IShaderGL shader) { + glDetachShader(((OpenGLObjects.ProgramGL) obj).ptr, ((OpenGLObjects.ShaderGL) shader).ptr); + } + + public static final void _wglLinkProgram(IProgramGL obj) { + glLinkProgram(((OpenGLObjects.ProgramGL) obj).ptr); + } + + public static final int _wglGetProgrami(IProgramGL obj, int param) { + return glGetProgrami(((OpenGLObjects.ProgramGL) obj).ptr, param); + } + + public static final String _wglGetProgramInfoLog(IProgramGL obj) { + return glGetProgramInfoLog(((OpenGLObjects.ProgramGL) obj).ptr); + } + + public static final void _wglBindAttribLocation(IProgramGL obj, int index, String name) { + glBindAttribLocation(((OpenGLObjects.ProgramGL) obj).ptr, index, name); + } + + public static final int _wglGetAttribLocation(IProgramGL obj, String name) { + return glGetAttribLocation(((OpenGLObjects.ProgramGL) obj).ptr, name); + } + + public static final void _wglDrawArrays(int mode, int first, int count) { + glDrawArrays(mode, first, count); + } + + public static final void _wglDrawArraysInstanced(int mode, int first, int count, int instanced) { + glDrawArraysInstanced(mode, first, count, instanced); + } + + public static final void _wglDrawElements(int mode, int count, int type, int offset) { + glDrawElements(mode, count, type, offset); + } + + public static final void _wglDrawElementsInstanced(int mode, int count, int type, int offset, int instanced) { + glDrawElementsInstanced(mode, count, type, offset, instanced); + } + + public static final IUniformGL _wglGetUniformLocation(IProgramGL obj, String name) { + int loc = glGetUniformLocation(((OpenGLObjects.ProgramGL) obj).ptr, name); + return loc < 0 ? null : new OpenGLObjects.UniformGL(loc); + } + + public static final void _wglUniform1f(IUniformGL obj, float x) { + if (obj != null) + glUniform1f(((OpenGLObjects.UniformGL) obj).ptr, x); + } + + public static final void _wglUniform2f(IUniformGL obj, float x, float y) { + if (obj != null) + glUniform2f(((OpenGLObjects.UniformGL) obj).ptr, x, y); + } + + public static final void _wglUniform3f(IUniformGL obj, float x, float y, float z) { + if (obj != null) + glUniform3f(((OpenGLObjects.UniformGL) obj).ptr, x, y, z); + } + + public static final void _wglUniform4f(IUniformGL obj, float x, float y, float z, float w) { + if (obj != null) + glUniform4f(((OpenGLObjects.UniformGL) obj).ptr, x, y, z, w); + } + + public static final void _wglUniform1i(IUniformGL obj, int x) { + if (obj != null) + glUniform1i(((OpenGLObjects.UniformGL) obj).ptr, x); + } + + public static final void _wglUniform2i(IUniformGL obj, int x, int y) { + if (obj != null) + glUniform2i(((OpenGLObjects.UniformGL) obj).ptr, x, y); + } + + public static final void _wglUniform3i(IUniformGL obj, int x, int y, int z) { + if (obj != null) + glUniform3i(((OpenGLObjects.UniformGL) obj).ptr, x, y, z); + } + + public static final void _wglUniform4i(IUniformGL obj, int x, int y, int z, int w) { + if (obj != null) + glUniform4i(((OpenGLObjects.UniformGL) obj).ptr, x, y, z, w); + } + + public static final void _wglUniformMatrix2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 2, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglUniformMatrix3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix3fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 9, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglUniformMatrix4fv(IUniformGL obj, boolean transpose, FloatBuffer mat) { + if (obj != null) + nglUniformMatrix4fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 4, transpose, + EaglerLWJGLAllocator.getAddress(mat)); + } + + public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) { + glBindFramebuffer(target, framebuffer == null ? 0 : ((OpenGLObjects.FramebufferGL) framebuffer).ptr); + } + + public static final int _wglCheckFramebufferStatus(int target) { + return glCheckFramebufferStatus(target); + } + + public static final void _wglFramebufferTexture2D(int target, int attachment, int texTarget, ITextureGL texture, + int level) { + glFramebufferTexture2D(target, attachment, texTarget, ((OpenGLObjects.TextureGL) texture).ptr, level); + } + + public static final void _wglBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, + int dstX1, int dstY1, int bits, int filter) { + glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, bits, filter); + } + + public static final void _wglBindRenderbuffer(int target, IRenderbufferGL renderbuffer) { + glBindRenderbuffer(target, renderbuffer == null ? 0 : ((OpenGLObjects.RenderbufferGL) renderbuffer).ptr); + } + + public static final void _wglRenderbufferStorage(int target, int internalformat, int width, int height) { + glRenderbufferStorage(target, internalformat, width, height); + } + + public static final void _wglFramebufferRenderbuffer(int target, int attachment, int renderbufferTarget, + IRenderbufferGL renderbuffer) { + glFramebufferRenderbuffer(target, attachment, renderbufferTarget, + ((OpenGLObjects.RenderbufferGL) renderbuffer).ptr); + } + + public static final String _wglGetString(int param) { + return glGetString(param); + } + + public static final int _wglGetInteger(int param) { + return glGetInteger(param); + } + + public static final int _wglGetError() { + return glGetError(); + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java new file mode 100644 index 0000000..e7881a3 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java @@ -0,0 +1,448 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import static org.lwjgl.egl.EGL10.*; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.glfw.GLFWNativeEGL.*; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.function.Consumer; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.InflaterInputStream; + +import javax.imageio.ImageIO; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.egl.EGL; +import org.lwjgl.glfw.GLFWImage; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.opengles.GLDebugMessageKHRCallback; +import org.lwjgl.opengles.GLDebugMessageKHRCallbackI; +import org.lwjgl.opengles.GLES; +import org.lwjgl.opengles.GLES30; +import org.lwjgl.opengles.KHRDebug; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.jemalloc.JEmalloc; + +import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerLWJGLAllocator; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer; +import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopClientConfigAdapter; +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class PlatformRuntime { + + static final Logger logger = LogManager.getLogger("RuntimeLWJGL3"); + + private static String glVersion = "unknown"; + private static String glRenderer = "unknown"; + + private static EnumPlatformANGLE rendererANGLEPlatform = null; + + private static long windowHandle = 0l; + + public static void create() { + logger.info("Starting Desktop Runtime..."); + + if(requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT) { + logger.info("Setting ANGLE Platform: {}", requestedANGLEPlatform.name); + glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, requestedANGLEPlatform.eglEnum); + } + + glfwInit(); + logger.info("GLFW Version: {}", glfwGetVersionString()); + + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); + + + PointerBuffer buf = glfwGetMonitors(); + GLFWVidMode mon = glfwGetVideoMode(buf.get(0)); + + int windowWidth = mon.width() - 200; + int windowHeight = mon.height() - 250; + + int winX = (mon.width() - windowWidth) / 2; + int winY = (mon.height() - windowHeight - 20) / 2; + + windowHandle = glfwCreateWindow(windowWidth, windowHeight, "Eaglercraft Desktop Runtime", 0l, 0l); + + glfwSetWindowPos(windowHandle, winX, winY); + + int[] x2 = new int[1]; + int[] y2 = new int[1]; + int[] w2 = new int[1]; + int[] h2 = new int[1]; + glfwGetWindowFrameSize(windowHandle, x2, y2, w2, h2); + glfwSetWindowSize(windowHandle, windowWidth - x2[0] - w2[0], windowHeight - y2[0] - h2[0]); + + ImageIO.setUseCache(false); + BufferedImage[] windowIcons = null; + + try { + windowIcons = new BufferedImage[] { + ImageIO.read(new File("icon16.png")), + ImageIO.read(new File("icon32.png")) + }; + }catch(IOException t) { + logger.error("Could not load default window icons!"); + logger.error(t); + } + + if(windowIcons != null) { + try(MemoryStack st = MemoryStack.stackPush()) { + GLFWImage.Buffer windowIconsBuffer = GLFWImage.malloc(windowIcons.length, st); + + for(int i = 0; i < windowIcons.length; ++i) { + int w = windowIcons[i].getWidth(); + int h = windowIcons[i].getHeight(); + + int[] px = new int[w * h]; + windowIcons[i].getRGB(0, 0, w, h, px, 0, w); + + for(int j = 0; j < px.length; ++j) { + px[j] = (px[j] & 0xFF00FF00) | ((px[j] >> 16) & 0xFF) | ((px[j] & 0xFF) << 16); // swap R/B + } + + java.nio.ByteBuffer iconBuffer = st.malloc(w * h * 4); + iconBuffer.asIntBuffer().put(px); + iconBuffer.flip(); + + windowIconsBuffer.position(i); + windowIconsBuffer.width(w); + windowIconsBuffer.height(h); + windowIconsBuffer.pixels(iconBuffer); + } + + glfwSetWindowIcon(windowHandle, windowIconsBuffer); + } + } + + long glfw_eglHandle = glfwGetEGLDisplay(); + logger.info("EGL Version: {}", eglQueryString(glfw_eglHandle, EGL_VERSION)); + + int[] major = new int[] { 1 }; + int[] minor = new int[] { 4 }; + if(!eglInitialize(glfw_eglHandle, major, minor)) { + throw new RuntimeException("Could not initialize EGL"); + } + + EGL.createDisplayCapabilities(glfw_eglHandle, major[0], minor[0]); + glfwMakeContextCurrent(windowHandle); + GLES.createCapabilities(); + + logger.info("OpenGL Version: {}", (glVersion = GLES30.glGetString(GLES30.GL_VERSION))); + logger.info("OpenGL Renderer: {}", (glRenderer = GLES30.glGetString(GLES30.GL_RENDERER))); + + rendererANGLEPlatform = EnumPlatformANGLE.fromGLRendererString(glRenderer); + + if(requestedANGLEPlatform != EnumPlatformANGLE.DEFAULT + && rendererANGLEPlatform != requestedANGLEPlatform) { + logger.warn("Incorrect ANGLE Platform: {}", rendererANGLEPlatform.name); + } + + if(requestedANGLEPlatform == EnumPlatformANGLE.DEFAULT) { + logger.info("ANGLE Platform: {}", rendererANGLEPlatform.name); + } + + glfwSwapInterval(0); + + KHRDebug.glDebugMessageCallbackKHR(new GLDebugMessageKHRCallbackI() { + @Override + public void invoke(int source, int type, int id, int severity, int length, long message, long userParam) { + StringBuilder b = new StringBuilder(); + b.append("[KHR DEBUG #"); b.append(id); b.append("] "); + + switch(source) { + case KHRDebug.GL_DEBUG_SOURCE_API_KHR: b.append("[API - "); break; + case KHRDebug.GL_DEBUG_SOURCE_APPLICATION_KHR: b.append("[APPLICATION - "); break; + case KHRDebug.GL_DEBUG_SOURCE_SHADER_COMPILER_KHR: b.append("[SHADER COMPILER - "); break; + case KHRDebug.GL_DEBUG_SOURCE_THIRD_PARTY_KHR: b.append("[THIRD PARTY - "); break; + case KHRDebug.GL_DEBUG_SOURCE_OTHER_KHR: default: b.append("[OTHER - "); break; + } + + switch(type) { + case KHRDebug.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR: b.append("DEPRECATED BEHAVIOR] "); break; + case KHRDebug.GL_DEBUG_TYPE_ERROR_KHR: b.append("ERROR] "); break; + default: + case KHRDebug.GL_DEBUG_TYPE_OTHER_KHR: b.append("OTHER] "); break; + case KHRDebug.GL_DEBUG_TYPE_PERFORMANCE_KHR: b.append("PERFORMANCE] "); break; + case KHRDebug.GL_DEBUG_TYPE_PORTABILITY_KHR: b.append("PORTABILITY] "); break; + case KHRDebug.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR: b.append("UNDEFINED BEHAVIOR] "); break; + } + + switch(severity) { + default: + case KHRDebug.GL_DEBUG_SEVERITY_LOW_KHR: b.append("[LOW Severity] "); break; + case KHRDebug.GL_DEBUG_SEVERITY_MEDIUM_KHR: b.append("[MEDIUM Severity] "); break; + case KHRDebug.GL_DEBUG_SEVERITY_HIGH_KHR: b.append("[SEVERE] "); break; + } + + String message2 = GLDebugMessageKHRCallback.getMessage(length, message); + if(message2.contains("GPU stall due to ReadPixels")) return; + b.append(message2); + logger.error(b.toString()); + + StackTraceElement[] ex = new RuntimeException().getStackTrace(); + for(int i = 0; i < ex.length; ++i) { + logger.error(" at {}", ex[i]); + } + } + }, 0l); + + GLES30.glEnable(KHRDebug.GL_DEBUG_OUTPUT_KHR); + GLES30.glEnable(KHRDebug.GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); + + logger.info("Initializing Audio..."); + PlatformAudio.platformInitialize(); + + logger.info("Initializing Hooks..."); + PlatformInput.initHooks(windowHandle); + } + + public static void destroy() { + PlatformAudio.platformShutdown(); + GLES.destroy(); + EGL.destroy(); + glfwDestroyWindow(windowHandle); + glfwTerminate(); + } + + public static EnumPlatformType getPlatformType() { + return EnumPlatformType.DESKTOP; + } + + public static EnumPlatformAgent getPlatformAgent() { + return EnumPlatformAgent.DESKTOP; + } + + public static String getUserAgentString() { + return "Desktop/" + System.getProperty("os.name"); + } + + private static EnumPlatformOS currentPlatformOS = null; + + public static EnumPlatformOS getPlatformOS() { + if(currentPlatformOS == null) { + currentPlatformOS = EnumPlatformOS.getFromJVM(System.getProperty("os.name")); + } + return currentPlatformOS; + } + + private static EnumPlatformANGLE requestedANGLEPlatform = EnumPlatformANGLE.DEFAULT; + + public static void requestANGLE(EnumPlatformANGLE plaf) { + requestedANGLEPlatform = plaf; + } + + public static EnumPlatformANGLE getPlatformANGLE() { + return rendererANGLEPlatform; + } + + public static String getGLVersion() { + return glVersion; + } + + public static String getGLRenderer() { + return glRenderer; + } + public static ByteBuffer allocateByteBuffer(int length) { + return EaglerLWJGLAllocator.allocByteBuffer(length); + } + + public static IntBuffer allocateIntBuffer(int length) { + return EaglerLWJGLAllocator.allocIntBuffer(length); + } + + public static FloatBuffer allocateFloatBuffer(int length) { + return EaglerLWJGLAllocator.allocFloatBuffer(length); + } + + public static void freeByteBuffer(ByteBuffer byteBuffer) { + EaglerLWJGLAllocator.freeByteBuffer(byteBuffer); + } + + public static void freeIntBuffer(IntBuffer intBuffer) { + EaglerLWJGLAllocator.freeIntBuffer(intBuffer); + } + + public static void freeFloatBuffer(FloatBuffer floatBuffer) { + EaglerLWJGLAllocator.freeFloatBuffer(floatBuffer); + } + + public static class NativeNIO { + + public static java.nio.ByteBuffer allocateByteBuffer(int length) { + return MemoryUtil.memByteBuffer(JEmalloc.nje_malloc(length), length); + } + + public static java.nio.IntBuffer allocateIntBuffer(int length) { + return MemoryUtil.memIntBuffer(JEmalloc.nje_malloc(length << 2), length); + } + + public static java.nio.FloatBuffer allocateFloatBuffer(int length) { + return MemoryUtil.memFloatBuffer(JEmalloc.nje_malloc(length << 2), length); + } + + public static java.nio.IntBuffer getIntBuffer(java.nio.ByteBuffer byteBuffer) { + return MemoryUtil.memIntBuffer(MemoryUtil.memAddress(byteBuffer), byteBuffer.capacity() >> 2); + } + + public static java.nio.FloatBuffer getFloatBuffer(java.nio.ByteBuffer byteBuffer) { + return MemoryUtil.memFloatBuffer(MemoryUtil.memAddress(byteBuffer), byteBuffer.capacity() >> 2); + } + + public static java.nio.ByteBuffer getAsByteBuffer(java.nio.IntBuffer intBuffer) { + return MemoryUtil.memByteBuffer(MemoryUtil.memAddress(intBuffer), intBuffer.capacity() << 2); + } + + public static java.nio.ByteBuffer getAsByteBuffer(java.nio.FloatBuffer floatBuffer) { + return MemoryUtil.memByteBuffer(MemoryUtil.memAddress(floatBuffer), floatBuffer.capacity() << 2); + } + + public static void freeByteBuffer(java.nio.ByteBuffer byteBuffer) { + JEmalloc.nje_free(MemoryUtil.memAddress(byteBuffer)); + } + + public static void freeIntBuffer(java.nio.IntBuffer intBuffer) { + JEmalloc.nje_free(MemoryUtil.memAddress(intBuffer)); + } + + public static void freeFloatBuffer(java.nio.FloatBuffer floatBuffer) { + JEmalloc.nje_free(MemoryUtil.memAddress(floatBuffer)); + } + + } + + public static boolean isDebugRuntime() { + return true; + } + + public static void writeCrashReport(String crashDump) { + File file1 = new File("./crash-reports"); + if(!file1.exists()) { + if(!file1.mkdirs()) { + PlatformRuntime.logger.fatal("Could not create crash report directory: {}", file1.getAbsolutePath()); + return; + } + } + File file2 = new File(file1, + "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-client.txt"); + try(FileOutputStream os = new FileOutputStream(file2)) { + os.write(crashDump.getBytes(StandardCharsets.UTF_8)); + }catch(IOException ex) { + PlatformRuntime.logger.fatal("Could not write crash report: {}", file2.getAbsolutePath()); + PlatformRuntime.logger.fatal(ex); + return; + } + PlatformRuntime.logger.fatal("Crash report was written to: {}", file2.getAbsolutePath()); + } + + public static void getStackTrace(Throwable t, Consumer ret) { + StackTraceElement[] stackTrace = t.getStackTrace(); + for(int i = 0; i < stackTrace.length; ++i) { + ret.accept(stackTrace[i].toString()); + } + } + + public static boolean printJSExceptionIfBrowser(Throwable t) { + return false; + } + + public static void exit() { + System.exit(0); + } + + public static void setThreadName(String string) { + Thread.currentThread().setName(string); + } + + public static long maxMemory() { + return Runtime.getRuntime().maxMemory(); + } + + public static long totalMemory() { + return Runtime.getRuntime().totalMemory(); + } + + public static long freeMemory() { + return Runtime.getRuntime().freeMemory(); + } + + public static String getCallingClass(int backTrace) { + StackTraceElement[] astacktraceelement = Thread.currentThread().getStackTrace(); + StackTraceElement stacktraceelement = astacktraceelement[Math.min(backTrace + 1, astacktraceelement.length)]; + return "" + stacktraceelement.getFileName() + ":" + stacktraceelement.getLineNumber(); + } + + public static OutputStream newDeflaterOutputStream(OutputStream os) throws IOException { + return new DeflaterOutputStream(os); + } + + public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException { + return new GZIPOutputStream(os); + } + + public static InputStream newInflaterInputStream(InputStream is) throws IOException { + return new InflaterInputStream(is); + } + + public static InputStream newGZIPInputStream(InputStream is) throws IOException { + return new GZIPInputStream(is); + } + + public static boolean requireSSL() { + return false; + } + + public static IClientConfigAdapter getClientConfigAdapter() { + return DesktopClientConfigAdapter.instance; + } + + public static String getRecText() { + return "recording.unsupported"; + } + + public static boolean recSupported() { + return false; + } + + public static void toggleRec() { + // + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketPlayClient.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketPlayClient.java new file mode 100644 index 0000000..5a36131 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketPlayClient.java @@ -0,0 +1,70 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +class WebSocketPlayClient extends WebSocketClient { + + public static final Logger logger = LogManager.getLogger("WebSocket"); + + WebSocketPlayClient(URI serverUri) { + super(serverUri); + this.setConnectionLostTimeout(15); + } + + @Override + public void onOpen(ServerHandshake arg0) { + PlatformNetworking.playConnectState = EnumEaglerConnectionState.CONNECTED; + PlatformNetworking.serverRateLimit = EnumServerRateLimit.OK; + logger.info("Connection opened: {}", this.uri.toString()); + } + + @Override + public void onClose(int arg0, String arg1, boolean arg2) { + logger.info("Connection closed: {}", this.uri.toString()); + } + + @Override + public void onError(Exception arg0) { + logger.error("Exception thrown by websocket \"" + this.getURI().toString() + "\"!"); + logger.error(arg0); + PlatformNetworking.playConnectState = EnumEaglerConnectionState.FAILED; + } + + @Override + public void onMessage(String arg0) { + if(arg0.equalsIgnoreCase("BLOCKED")) { + logger.error("Reached full IP ratelimit!"); + PlatformNetworking.serverRateLimit = EnumServerRateLimit.BLOCKED; + }else if(arg0.equalsIgnoreCase("LOCKED")) { + logger.error("Reached full IP ratelimit lockout!"); + PlatformNetworking.serverRateLimit = EnumServerRateLimit.LOCKED_OUT; + } + } + + @Override + public void onMessage(ByteBuffer arg0) { + PlatformNetworking.recievedPlayPacket(arg0.array()); + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketServerQuery.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketServerQuery.java new file mode 100644 index 0000000..d2af34a --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketServerQuery.java @@ -0,0 +1,166 @@ +package net.lax1dude.eaglercraft.v1_8.internal; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; + +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; +import org.json.JSONObject; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +class WebSocketServerQuery extends WebSocketClient implements IServerQuery { + + public static final Logger logger = LogManager.getLogger("WebSocketQuery"); + + private final List queryResponses = new LinkedList(); + private final List queryResponsesBytes = new LinkedList(); + private final String type; + private boolean open = true; + private boolean alive = false; + private long pingStart = -1l; + private long pingTimer = -1l; + private EnumServerRateLimit rateLimit = EnumServerRateLimit.OK; + + WebSocketServerQuery(String type, URI serverUri) { + super(serverUri); + this.type = type; + this.setConnectionLostTimeout(5); + this.setTcpNoDelay(true); + this.connect(); + } + + @Override + public int responsesAvailable() { + synchronized(queryResponses) { + return queryResponses.size(); + } + } + + @Override + public QueryResponse getResponse() { + synchronized(queryResponses) { + if(queryResponses.size() > 0) { + return queryResponses.remove(0); + }else { + return null; + } + } + } + + @Override + public int binaryResponsesAvailable() { + synchronized(queryResponsesBytes) { + return queryResponsesBytes.size(); + } + } + + @Override + public byte[] getBinaryResponse() { + synchronized(queryResponsesBytes) { + if(queryResponsesBytes.size() > 0) { + return queryResponsesBytes.remove(0); + }else { + return null; + } + } + } + + @Override + public QueryReadyState readyState() { + return open ? (alive ? QueryReadyState.OPEN : QueryReadyState.CONNECTING) + : (alive ? QueryReadyState.CLOSED : QueryReadyState.FAILED); + } + + @Override + public EnumServerRateLimit getRateLimit() { + return rateLimit; + } + + @Override + public void onClose(int arg0, String arg1, boolean arg2) { + open = false; + } + + @Override + public void onError(Exception arg0) { + logger.error("Exception thrown by websocket query \"" + this.getURI().toString() + "\"!"); + logger.error(arg0); + } + + @Override + public void onMessage(String arg0) { + alive = true; + if(pingTimer == -1) { + pingTimer = System.currentTimeMillis() - pingStart; + if(pingTimer < 1) { + pingTimer = 1; + } + } + if(arg0.equalsIgnoreCase("BLOCKED")) { + logger.error("Reached full IP ratelimit for {}!", this.uri.toString()); + rateLimit = EnumServerRateLimit.BLOCKED; + return; + } + if(arg0.equalsIgnoreCase("LOCKED")) { + logger.error("Reached full IP ratelimit lockout for {}!", this.uri.toString()); + rateLimit = EnumServerRateLimit.LOCKED_OUT; + return; + } + try { + JSONObject obj = new JSONObject(arg0); + if("blocked".equalsIgnoreCase(obj.optString("type", null))) { + logger.error("Reached query ratelimit for {}!", this.uri.toString()); + rateLimit = EnumServerRateLimit.BLOCKED; + }else if("locked".equalsIgnoreCase(obj.optString("type", null))) { + logger.error("Reached query ratelimit lockout for {}!", this.uri.toString()); + rateLimit = EnumServerRateLimit.LOCKED_OUT; + }else { + QueryResponse response = new QueryResponse(obj, pingTimer); + synchronized(queryResponses) { + queryResponses.add(response); + } + } + }catch(Throwable t) { + logger.error("Exception thrown parsing websocket query response from \"" + this.getURI().toString() + "\"!"); + logger.error(t); + } + } + + @Override + public void onMessage(ByteBuffer arg0) { + alive = true; + if(pingTimer == -1) { + pingTimer = System.currentTimeMillis() - pingStart; + if(pingTimer < 1) { + pingTimer = 1; + } + } + synchronized(queryResponsesBytes) { + queryResponsesBytes.add(arg0.array()); + } + } + + @Override + public void onOpen(ServerHandshake arg0) { + pingStart = System.currentTimeMillis(); + send("Accept: " + type); + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java new file mode 100644 index 0000000..5ba26bf --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLAllocator.java @@ -0,0 +1,146 @@ +package net.lax1dude.eaglercraft.v1_8.internal.buffer; + +import org.lwjgl.system.jemalloc.JEmalloc; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EaglerLWJGLAllocator { + + public static class WrongBufferClassType extends RuntimeException { + public WrongBufferClassType(String msg) { + super(msg); + } + } + + public static ByteBuffer allocByteBuffer(int len) { + return new EaglerLWJGLByteBuffer(JEmalloc.nje_malloc(len), len, true); + } + + public static ShortBuffer allocShortBuffer(int len) { + return new EaglerLWJGLShortBuffer(JEmalloc.nje_malloc(len << 1), len, true); + } + + public static IntBuffer allocIntBuffer(int len) { + return new EaglerLWJGLIntBuffer(JEmalloc.nje_malloc(len << 2), len, true); + } + + public static FloatBuffer allocFloatBuffer(int len) { + return new EaglerLWJGLFloatBuffer(JEmalloc.nje_malloc(len << 2), len, true); + } + + public static void freeByteBuffer(ByteBuffer buffer) { + if(buffer instanceof EaglerLWJGLByteBuffer) { + EaglerLWJGLByteBuffer buf = (EaglerLWJGLByteBuffer)buffer; + if(buf.original) { + JEmalloc.nje_free(buf.address); + }else { + throwNotOriginal(buffer); + } + }else { + throwNotEagler(buffer); + } + } + + public static long getAddress(ByteBuffer buffer) { + if(buffer instanceof EaglerLWJGLByteBuffer) { + EaglerLWJGLByteBuffer b = (EaglerLWJGLByteBuffer)buffer; + return b.address + b.position(); + }else { + throw notEagler(buffer); + } + } + + public static void freeShortBuffer(ShortBuffer buffer) { + if(buffer instanceof EaglerLWJGLShortBuffer) { + EaglerLWJGLShortBuffer buf = (EaglerLWJGLShortBuffer)buffer; + if(buf.original) { + JEmalloc.nje_free(buf.address); + }else { + throwNotOriginal(buffer); + } + }else { + throwNotEagler(buffer); + } + } + + public static long getAddress(ShortBuffer buffer) { + if(buffer instanceof EaglerLWJGLShortBuffer) { + EaglerLWJGLShortBuffer b = (EaglerLWJGLShortBuffer)buffer; + return b.address + (b.position() << 1); + }else { + throw notEagler(buffer); + } + } + + public static void freeIntBuffer(IntBuffer buffer) { + if(buffer instanceof EaglerLWJGLIntBuffer) { + EaglerLWJGLIntBuffer buf = (EaglerLWJGLIntBuffer)buffer; + if(buf.original) { + JEmalloc.nje_free(buf.address); + }else { + throwNotOriginal(buffer); + } + }else { + throwNotEagler(buffer); + } + } + + public static long getAddress(IntBuffer buffer) { + if(buffer instanceof EaglerLWJGLIntBuffer) { + EaglerLWJGLIntBuffer b = (EaglerLWJGLIntBuffer)buffer; + return b.address + (b.position() << 2); + }else { + throw notEagler(buffer); + } + } + + public static void freeFloatBuffer(FloatBuffer buffer) { + if(buffer instanceof EaglerLWJGLFloatBuffer) { + EaglerLWJGLFloatBuffer buf = (EaglerLWJGLFloatBuffer)buffer; + if(buf.original) { + JEmalloc.nje_free(buf.address); + }else { + throwNotOriginal(buffer); + } + }else { + throwNotEagler(buffer); + } + } + + public static long getAddress(FloatBuffer buffer) { + if(buffer instanceof EaglerLWJGLFloatBuffer) { + EaglerLWJGLFloatBuffer b = (EaglerLWJGLFloatBuffer)buffer; + return b.address + (b.position() << 2); + }else { + throw notEagler(buffer); + } + } + + private static void throwNotOriginal(Object clazz) { + throw notOriginal(clazz); + } + + private static WrongBufferClassType notOriginal(Object clazz) { + return new WrongBufferClassType("Tried to pass a " + clazz.getClass().getSimpleName() + " which was not the original buffer"); + } + + private static void throwNotEagler(Object clazz) { + throw notEagler(clazz); + } + + private static WrongBufferClassType notEagler(Object clazz) { + return new WrongBufferClassType("Tried to pass a " + clazz.getClass().getSimpleName() + " which is not a native eagler buffer"); + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java new file mode 100644 index 0000000..c3527a0 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLByteBuffer.java @@ -0,0 +1,420 @@ +package net.lax1dude.eaglercraft.v1_8.internal.buffer; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.jemalloc.JEmalloc; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EaglerLWJGLByteBuffer implements ByteBuffer { + + final long address; + final boolean original; + + private final int capacity; + private int position; + private int limit; + private int mark; + + EaglerLWJGLByteBuffer(long address, int capacity, boolean original) { + this(address, capacity, 0, capacity, -1, original); + } + + EaglerLWJGLByteBuffer(long address, int capacity, int position, int limit, int mark, boolean original) { + this.address = address; + this.capacity = capacity; + this.position = position; + this.limit = limit; + this.mark = mark; + this.original = original; + } + + @Override + public int capacity() { + return capacity; + } + + @Override + public int position() { + return position; + } + + @Override + public int limit() { + return limit; + } + + @Override + public int remaining() { + return limit - position; + } + + @Override + public boolean hasRemaining() { + return position < limit; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public boolean hasArray() { + return false; + } + + @Override + public Object array() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDirect() { + return true; + } + + @Override + public ByteBuffer slice() { + return new EaglerLWJGLByteBuffer(address + position, limit - position, false); + } + + @Override + public ByteBuffer duplicate() { + return new EaglerLWJGLByteBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public ByteBuffer asReadOnlyBuffer() { + return new EaglerLWJGLByteBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public byte get() { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + return MemoryUtil.memGetByte(address + position++); + } + + @Override + public ByteBuffer put(byte b) { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutByte(address + position++, b); + return this; + } + + @Override + public byte get(int index) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetByte(address + index); + } + + @Override + public ByteBuffer put(int index, byte b) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutByte(address + index, b); + return this; + } + + @Override + public ByteBuffer get(byte[] dst, int offset, int length) { + if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + for(int i = 0; i < length; ++i) { + dst[offset + i] = MemoryUtil.memGetByte(address + position + i); + } + position += length; + return this; + } + + @Override + public ByteBuffer get(byte[] dst) { + if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + for(int i = 0; i < dst.length; ++i) { + dst[position + i] = MemoryUtil.memGetByte(address + position + i); + } + position += dst.length; + return this; + } + + @Override + public ByteBuffer put(ByteBuffer src) { + if(src instanceof EaglerLWJGLByteBuffer) { + EaglerLWJGLByteBuffer c = (EaglerLWJGLByteBuffer)src; + int l = c.limit - c.position; + if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + MemoryUtil.memCopy(c.address + c.position, address + position, l); + position += l; + c.position += l; + }else { + int l = src.remaining(); + if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + for(int i = 0; i < l; ++i) { + MemoryUtil.memPutByte(address + position + l, src.get()); + } + position += l; + } + return this; + } + + @Override + public ByteBuffer put(byte[] src, int offset, int length) { + if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + for(int i = 0; i < length; ++i) { + MemoryUtil.memPutByte(address + position + i, src[offset + i]); + } + position += length; + return this; + } + + @Override + public ByteBuffer put(byte[] src) { + if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + for(int i = 0; i < src.length; ++i) { + MemoryUtil.memPutByte(address + position + i, src[i]); + } + position += src.length; + return this; + } + + @Override + public int arrayOffset() { + return position; + } + + @Override + public ByteBuffer compact() { + if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); + if(position > limit) throw new ArrayIndexOutOfBoundsException(position); + + if(position == limit) { + return new EaglerLWJGLByteBuffer(0l, 0, false); + } + + int newLen = limit - position; + long newAlloc = JEmalloc.nje_malloc(newLen); + MemoryUtil.memCopy(address + position, newAlloc, newLen); + + return new EaglerLWJGLByteBuffer(newAlloc, newLen, true); + } + + @Override + public char getChar() { + if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); + char c = (char)MemoryUtil.memGetShort(address + position); + position += 2; + return c; + } + + @Override + public ByteBuffer putChar(char value) { + if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutShort(address + position, (short)value); + position += 2; + return this; + } + + @Override + public char getChar(int index) { + if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); + return (char)MemoryUtil.memGetShort(address + index); + } + + @Override + public ByteBuffer putChar(int index, char value) { + if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutShort(address + index, (short)value); + return this; + } + + @Override + public short getShort() { + if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); + short s = MemoryUtil.memGetShort(address + position); + position += 2; + return s; + } + + @Override + public ByteBuffer putShort(short value) { + if(position + 2 > limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutShort(address + position, value); + position += 2; + return this; + } + + @Override + public short getShort(int index) { + if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetShort(address + index); + } + + @Override + public ByteBuffer putShort(int index, short value) { + if(index + 2 > limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutShort(address + index, value); + return this; + } + + @Override + public ShortBuffer asShortBuffer() { + return new EaglerLWJGLShortBuffer(address, capacity >> 1, false); + } + + @Override + public int getInt() { + if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); + int i = MemoryUtil.memGetInt(address + position); + position += 4; + return i; + } + + @Override + public ByteBuffer putInt(int value) { + if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutInt(address + position, value); + position += 4; + return this; + } + + @Override + public int getInt(int index) { + if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetInt(address + index); + } + + @Override + public ByteBuffer putInt(int index, int value) { + if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutInt(address + index, value); + return this; + } + + @Override + public IntBuffer asIntBuffer() { + return new EaglerLWJGLIntBuffer(address, capacity >> 2, false); + } + + @Override + public long getLong() { + if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position); + long l = MemoryUtil.memGetLong(address + position); + position += 8; + return l; + } + + @Override + public ByteBuffer putLong(long value) { + if(position + 8 > limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutLong(address + position, value); + position += 8; + return this; + } + + @Override + public long getLong(int index) { + if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetLong(address + index); + } + + @Override + public ByteBuffer putLong(int index, long value) { + if(index + 8 > limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutLong(address + index, value); + return this; + } + + @Override + public float getFloat() { + if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); + float f = MemoryUtil.memGetFloat(address + position); + position += 4; + return f; + } + + @Override + public ByteBuffer putFloat(float value) { + if(position + 4 > limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutFloat(address + position, value); + position += 4; + return this; + } + + @Override + public float getFloat(int index) { + if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetFloat(address + index); + } + + @Override + public ByteBuffer putFloat(int index, float value) { + if(index + 4 > limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutFloat(address + index, value); + return this; + } + + @Override + public FloatBuffer asFloatBuffer() { + return new EaglerLWJGLFloatBuffer(address, capacity >> 2, false); + } + + @Override + public ByteBuffer mark() { + mark = position; + return this; + } + + @Override + public ByteBuffer reset() { + int m = mark; + if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + position = m; + return this; + } + + @Override + public ByteBuffer clear() { + position = 0; + limit = capacity; + mark = -1; + return this; + } + + @Override + public ByteBuffer flip() { + limit = position; + position = 0; + mark = -1; + return this; + } + + @Override + public ByteBuffer rewind() { + position = 0; + mark = -1; + return this; + } + + @Override + public ByteBuffer limit(int newLimit) { + if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + limit = newLimit; + return this; + } + + @Override + public ByteBuffer position(int newPosition) { + if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + position = newPosition; + return this; + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java new file mode 100644 index 0000000..ebcd330 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLFloatBuffer.java @@ -0,0 +1,279 @@ +package net.lax1dude.eaglercraft.v1_8.internal.buffer; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.jemalloc.JEmalloc; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EaglerLWJGLFloatBuffer implements FloatBuffer { + + final long address; + final boolean original; + + private final int capacity; + private int position; + private int limit; + private int mark; + + private static final int SHIFT = 2; + + EaglerLWJGLFloatBuffer(long address, int capacity, boolean original) { + this(address, capacity, 0, capacity, -1, original); + } + + EaglerLWJGLFloatBuffer(long address, int capacity, int position, int limit, int mark, boolean original) { + this.address = address; + this.capacity = capacity; + this.position = position; + this.limit = limit; + this.mark = mark; + this.original = original; + } + + @Override + public int capacity() { + return capacity; + } + + @Override + public int position() { + return position; + } + + @Override + public int limit() { + return limit; + } + + @Override + public int remaining() { + return limit - position; + } + + @Override + public boolean hasRemaining() { + return position < limit; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public boolean hasArray() { + return false; + } + + @Override + public Object array() { + throw new UnsupportedOperationException(); + } + + @Override + public int arrayOffset() { + return position; + } + + @Override + public FloatBuffer slice() { + return new EaglerLWJGLFloatBuffer(address + (position << SHIFT), limit - position, false); + } + + @Override + public FloatBuffer duplicate() { + return new EaglerLWJGLFloatBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public FloatBuffer asReadOnlyBuffer() { + return new EaglerLWJGLFloatBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public float get() { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + return MemoryUtil.memGetFloat(address + ((position++) << SHIFT)); + } + + @Override + public FloatBuffer put(float b) { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutFloat(address + ((position++) << SHIFT), b); + return this; + } + + @Override + public float get(int index) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetFloat(address + (index << SHIFT)); + } + + @Override + public FloatBuffer put(int index, float b) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutFloat(address + (index << SHIFT), b); + return this; + } + + @Override + public float getElement(int index) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetFloat(address + (index << SHIFT)); + } + + @Override + public void putElement(int index, float value) { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutFloat(address + ((position++) << SHIFT), value); + } + + @Override + public FloatBuffer get(float[] dst, int offset, int length) { + if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + for(int i = 0; i < length; ++i) { + dst[offset + i] = MemoryUtil.memGetFloat(address + ((position + i) << SHIFT)); + } + position += length; + return this; + } + + @Override + public FloatBuffer get(float[] dst) { + if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + for(int i = 0; i < dst.length; ++i) { + dst[i] = MemoryUtil.memGetFloat(address + ((position + i) << SHIFT)); + } + position += dst.length; + return this; + } + + @Override + public FloatBuffer put(FloatBuffer src) { + if(src instanceof EaglerLWJGLFloatBuffer) { + EaglerLWJGLFloatBuffer c = (EaglerLWJGLFloatBuffer)src; + int l = c.limit - c.position; + if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + MemoryUtil.memCopy(c.address + (c.position << SHIFT), address + (position << SHIFT), l << SHIFT); + position += l; + c.position += l; + }else { + int l = src.remaining(); + if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + for(int i = 0; i < l; ++i) { + MemoryUtil.memPutFloat(address + ((position + l) << SHIFT), src.get()); + } + position += l; + } + return this; + } + + @Override + public FloatBuffer put(float[] src, int offset, int length) { + if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + for(int i = 0; i < length; ++i) { + MemoryUtil.memPutFloat(address + ((position + i) << SHIFT), src[offset + i]); + } + position += length; + return this; + } + + @Override + public FloatBuffer put(float[] src) { + if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + for(int i = 0; i < src.length; ++i) { + MemoryUtil.memPutFloat(address + ((position + i) << SHIFT), src[i]); + } + position += src.length; + return this; + } + + @Override + public int getArrayOffset() { + return position; + } + + @Override + public FloatBuffer compact() { + if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); + if(position > limit) throw new ArrayIndexOutOfBoundsException(position); + + if(position == limit) { + return new EaglerLWJGLFloatBuffer(0l, 0, false); + } + + int newLen = limit - position; + long newAlloc = JEmalloc.nje_malloc(newLen); + MemoryUtil.memCopy(address + (position << SHIFT), newAlloc, newLen << SHIFT); + + return new EaglerLWJGLFloatBuffer(newAlloc, newLen, true); + } + + @Override + public boolean isDirect() { + return true; + } + + @Override + public FloatBuffer mark() { + mark = position; + return this; + } + + @Override + public FloatBuffer reset() { + int m = mark; + if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + position = m; + return this; + } + + @Override + public FloatBuffer clear() { + position = 0; + limit = capacity; + mark = -1; + return this; + } + + @Override + public FloatBuffer flip() { + limit = position; + position = 0; + mark = -1; + return this; + } + + @Override + public FloatBuffer rewind() { + position = 0; + mark = -1; + return this; + } + + @Override + public FloatBuffer limit(int newLimit) { + if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + limit = newLimit; + return this; + } + + @Override + public FloatBuffer position(int newPosition) { + if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + position = newPosition; + return this; + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java new file mode 100644 index 0000000..c3f24dc --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLIntBuffer.java @@ -0,0 +1,279 @@ +package net.lax1dude.eaglercraft.v1_8.internal.buffer; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.jemalloc.JEmalloc; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EaglerLWJGLIntBuffer implements IntBuffer { + + final long address; + final boolean original; + + private final int capacity; + private int position; + private int limit; + private int mark; + + private static final int SHIFT = 2; + + EaglerLWJGLIntBuffer(long address, int capacity, boolean original) { + this(address, capacity, 0, capacity, -1, original); + } + + EaglerLWJGLIntBuffer(long address, int capacity, int position, int limit, int mark, boolean original) { + this.address = address; + this.capacity = capacity; + this.position = position; + this.limit = limit; + this.mark = mark; + this.original = original; + } + + @Override + public int capacity() { + return capacity; + } + + @Override + public int position() { + return position; + } + + @Override + public int limit() { + return limit; + } + + @Override + public int remaining() { + return limit - position; + } + + @Override + public boolean hasRemaining() { + return position < limit; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public boolean hasArray() { + return false; + } + + @Override + public Object array() { + throw new UnsupportedOperationException(); + } + + @Override + public int arrayOffset() { + return position; + } + + @Override + public IntBuffer slice() { + return new EaglerLWJGLIntBuffer(address + (position << SHIFT), limit - position, false); + } + + @Override + public IntBuffer duplicate() { + return new EaglerLWJGLIntBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public IntBuffer asReadOnlyBuffer() { + return new EaglerLWJGLIntBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public int get() { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + return MemoryUtil.memGetInt(address + ((position++) << SHIFT)); + } + + @Override + public IntBuffer put(int b) { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutInt(address + ((position++) << SHIFT), b); + return this; + } + + @Override + public int get(int index) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetInt(address + (index << SHIFT)); + } + + @Override + public IntBuffer put(int index, int b) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutInt(address + (index << SHIFT), b); + return this; + } + + @Override + public int getElement(int index) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetInt(address + (index << SHIFT)); + } + + @Override + public void putElement(int index, int value) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutInt(address + (index << SHIFT), value); + } + + @Override + public IntBuffer get(int[] dst, int offset, int length) { + if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + for(int i = 0; i < length; ++i) { + dst[offset + i] = MemoryUtil.memGetInt(address + ((position + i) << SHIFT)); + } + position += length; + return this; + } + + @Override + public IntBuffer get(int[] dst) { + if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + for(int i = 0; i < dst.length; ++i) { + dst[i] = MemoryUtil.memGetInt(address + ((position + i) << SHIFT)); + } + position += dst.length; + return this; + } + + @Override + public IntBuffer put(IntBuffer src) { + if(src instanceof EaglerLWJGLIntBuffer) { + EaglerLWJGLIntBuffer c = (EaglerLWJGLIntBuffer)src; + int l = c.limit - c.position; + if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + MemoryUtil.memCopy(c.address + (c.position << SHIFT), address + (position << SHIFT), l << SHIFT); + position += l; + c.position += l; + }else { + int l = src.remaining(); + if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + for(int i = 0; i < l; ++i) { + MemoryUtil.memPutInt(address + ((position + l) << SHIFT), src.get()); + } + position += l; + } + return this; + } + + @Override + public IntBuffer put(int[] src, int offset, int length) { + if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + for(int i = 0; i < length; ++i) { + MemoryUtil.memPutInt(address + ((position + i) << SHIFT), src[offset + i]); + } + position += length; + return this; + } + + @Override + public IntBuffer put(int[] src) { + if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + for(int i = 0; i < src.length; ++i) { + MemoryUtil.memPutInt(address + ((position + i) << SHIFT), src[i]); + } + position += src.length; + return this; + } + + @Override + public int getArrayOffset() { + return position; + } + + @Override + public IntBuffer compact() { + if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); + if(position > limit) throw new ArrayIndexOutOfBoundsException(position); + + if(position == limit) { + return new EaglerLWJGLIntBuffer(0l, 0, false); + } + + int newLen = limit - position; + long newAlloc = JEmalloc.nje_malloc(newLen); + MemoryUtil.memCopy(address + (position << SHIFT), newAlloc, newLen << SHIFT); + + return new EaglerLWJGLIntBuffer(newAlloc, newLen, true); + } + + @Override + public boolean isDirect() { + return true; + } + + @Override + public IntBuffer mark() { + mark = position; + return this; + } + + @Override + public IntBuffer reset() { + int m = mark; + if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + position = m; + return this; + } + + @Override + public IntBuffer clear() { + position = 0; + limit = capacity; + mark = -1; + return this; + } + + @Override + public IntBuffer flip() { + limit = position; + position = 0; + mark = -1; + return this; + } + + @Override + public IntBuffer rewind() { + position = 0; + mark = -1; + return this; + } + + @Override + public IntBuffer limit(int newLimit) { + if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + limit = newLimit; + return this; + } + + @Override + public IntBuffer position(int newPosition) { + if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + position = newPosition; + return this; + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java new file mode 100644 index 0000000..7bb94ed --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/buffer/EaglerLWJGLShortBuffer.java @@ -0,0 +1,279 @@ +package net.lax1dude.eaglercraft.v1_8.internal.buffer; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.jemalloc.JEmalloc; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class EaglerLWJGLShortBuffer implements ShortBuffer { + + final long address; + final boolean original; + + private final int capacity; + private int position; + private int limit; + private int mark; + + private static final int SHIFT = 1; + + EaglerLWJGLShortBuffer(long address, int capacity, boolean original) { + this(address, capacity, 0, capacity, -1, original); + } + + EaglerLWJGLShortBuffer(long address, int capacity, int position, int limit, int mark, boolean original) { + this.address = address; + this.capacity = capacity; + this.position = position; + this.limit = limit; + this.mark = mark; + this.original = original; + } + + @Override + public int capacity() { + return capacity; + } + + @Override + public int position() { + return position; + } + + @Override + public int limit() { + return limit; + } + + @Override + public int remaining() { + return limit - position; + } + + @Override + public boolean hasRemaining() { + return position < limit; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public boolean hasArray() { + return false; + } + + @Override + public Object array() { + throw new UnsupportedOperationException(); + } + + @Override + public int arrayOffset() { + return position; + } + + @Override + public ShortBuffer slice() { + return new EaglerLWJGLShortBuffer(address + (position << SHIFT), limit - position, false); + } + + @Override + public ShortBuffer duplicate() { + return new EaglerLWJGLShortBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public ShortBuffer asReadOnlyBuffer() { + return new EaglerLWJGLShortBuffer(address, capacity, position, limit, mark, false); + } + + @Override + public short get() { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + return MemoryUtil.memGetShort(address + ((position++) << SHIFT)); + } + + @Override + public ShortBuffer put(short b) { + if(position >= limit) throw new ArrayIndexOutOfBoundsException(position); + MemoryUtil.memPutShort(address + ((position++) << SHIFT), b); + return this; + } + + @Override + public short get(int index) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetShort(address + (index << SHIFT)); + } + + @Override + public ShortBuffer put(int index, short b) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutShort(address + (index << SHIFT), b); + return this; + } + + @Override + public short getElement(int index) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + return MemoryUtil.memGetShort(address + (index << SHIFT)); + } + + @Override + public void putElement(int index, short value) { + if(index >= limit) throw new ArrayIndexOutOfBoundsException(index); + MemoryUtil.memPutShort(address + (index << SHIFT), value); + } + + @Override + public ShortBuffer get(short[] dst, int offset, int length) { + if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + for(int i = 0; i < length; ++i) { + dst[offset + i] = MemoryUtil.memGetShort(address + ((position + i) << SHIFT)); + } + position += length; + return this; + } + + @Override + public ShortBuffer get(short[] dst) { + if(position + dst.length > limit) throw new ArrayIndexOutOfBoundsException(position + dst.length - 1); + for(int i = 0; i < dst.length; ++i) { + dst[i] = MemoryUtil.memGetShort(address + ((position + i) << SHIFT)); + } + position += dst.length; + return this; + } + + @Override + public ShortBuffer put(ShortBuffer src) { + if(src instanceof EaglerLWJGLShortBuffer) { + EaglerLWJGLShortBuffer c = (EaglerLWJGLShortBuffer)src; + int l = c.limit - c.position; + if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + MemoryUtil.memCopy(c.address + (c.position << SHIFT), address + (position << SHIFT), l << SHIFT); + position += l; + c.position += l; + }else { + int l = src.remaining(); + if(position + l > limit) throw new ArrayIndexOutOfBoundsException(position + l - 1); + for(int i = 0; i < l; ++i) { + MemoryUtil.memPutShort(address + ((position + l) << SHIFT), src.get()); + } + position += l; + } + return this; + } + + @Override + public ShortBuffer put(short[] src, int offset, int length) { + if(position + length > limit) throw new ArrayIndexOutOfBoundsException(position + length - 1); + for(int i = 0; i < length; ++i) { + MemoryUtil.memPutShort(address + ((position + i) << SHIFT), src[offset + i]); + } + position += length; + return this; + } + + @Override + public ShortBuffer put(short[] src) { + if(position + src.length > limit) throw new ArrayIndexOutOfBoundsException(position + src.length - 1); + for(int i = 0; i < src.length; ++i) { + MemoryUtil.memPutShort(address + ((position + i) << SHIFT), src[i]); + } + position += src.length; + return this; + } + + @Override + public int getArrayOffset() { + return position; + } + + @Override + public ShortBuffer compact() { + if(limit > capacity) throw new ArrayIndexOutOfBoundsException(limit); + if(position > limit) throw new ArrayIndexOutOfBoundsException(position); + + if(position == limit) { + return new EaglerLWJGLShortBuffer(0l, 0, false); + } + + int newLen = limit - position; + long newAlloc = JEmalloc.nje_malloc(newLen); + MemoryUtil.memCopy(address + (position << SHIFT), newAlloc, newLen << SHIFT); + + return new EaglerLWJGLShortBuffer(newAlloc, newLen, true); + } + + @Override + public boolean isDirect() { + return true; + } + + @Override + public ShortBuffer mark() { + mark = position; + return this; + } + + @Override + public ShortBuffer reset() { + int m = mark; + if(m < 0) throw new ArrayIndexOutOfBoundsException("Invalid mark: " + m); + position = m; + return this; + } + + @Override + public ShortBuffer clear() { + position = 0; + limit = capacity; + mark = -1; + return this; + } + + @Override + public ShortBuffer flip() { + limit = position; + position = 0; + mark = -1; + return this; + } + + @Override + public ShortBuffer rewind() { + position = 0; + mark = -1; + return this; + } + + @Override + public ShortBuffer limit(int newLimit) { + if(newLimit < 0 || newLimit > capacity) throw new ArrayIndexOutOfBoundsException(newLimit); + limit = newLimit; + return this; + } + + @Override + public ShortBuffer position(int newPosition) { + if(newPosition < 0 || newPosition > limit) throw new ArrayIndexOutOfBoundsException(newPosition); + position = newPosition; + return this; + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java new file mode 100644 index 0000000..4f0e6bf --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/DesktopClientConfigAdapter.java @@ -0,0 +1,42 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.util.ArrayList; +import java.util.List; + +import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class DesktopClientConfigAdapter implements IClientConfigAdapter { + + public static final IClientConfigAdapter instance = new DesktopClientConfigAdapter(); + + public final List defaultServers = new ArrayList(); + + @Override + public String getDefaultLocale() { + return "en_US"; + } + + @Override + public List getDefaultServerList() { + return defaultServers; + } + + @Override + public String getServerToJoin() { + return null; + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java new file mode 100644 index 0000000..6a25b63 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LWJGLEntryPoint.java @@ -0,0 +1,64 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.internal.EnumPlatformANGLE; +import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime; +import net.minecraft.client.main.Main; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class LWJGLEntryPoint { + + public static void main_(String[] args) { + + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException + | UnsupportedLookAndFeelException e) { + System.err.println("Could not set system look and feel: " + e.toString()); + } + + LaunchRenderDocDialog lr = new LaunchRenderDocDialog(); + lr.setLocationRelativeTo(null); + lr.setVisible(true); + + while(lr.isVisible()) { + EagUtils.sleep(100l); + } + + lr.dispose(); + + getANGLEPlatformFromArgs(args); + + EagRuntime.create(); + + Main.appMain(new String[0]); + + } + + private static void getANGLEPlatformFromArgs(String[] args) { + for(int i = 0; i < args.length; ++i) { + EnumPlatformANGLE angle = EnumPlatformANGLE.fromId(args[i]); + if(angle != EnumPlatformANGLE.DEFAULT) { + PlatformRuntime.requestANGLE(angle); + break; + } + } + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LaunchRenderDocDialog.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LaunchRenderDocDialog.java new file mode 100644 index 0000000..abe6843 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/LaunchRenderDocDialog.java @@ -0,0 +1,111 @@ + +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Insets; +import java.awt.Toolkit; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; +import javax.swing.JLabel; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.lang.management.ManagementFactory; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; +import java.awt.Color; +import java.awt.Dimension; + +import javax.swing.JSeparator; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ +public class LaunchRenderDocDialog extends JDialog { + + private static final long serialVersionUID = 8312760039213612790L; + + private final JPanel contentPanel = new JPanel(); + + /** + * Create the dialog. + */ + public LaunchRenderDocDialog() { + setIconImage(Toolkit.getDefaultToolkit().getImage("icon32.png")); + setBounds(100, 100, 291, 103); + setModal(true); + setLocationByPlatform(true); + setModalExclusionType(ModalExclusionType.TOOLKIT_EXCLUDE); + setModalityType(ModalityType.TOOLKIT_MODAL); + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setAlwaysOnTop(true); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + setTitle("EaglercraftX: " + ManagementFactory.getRuntimeMXBean().getName()); + setResizable(false); + getContentPane().setLayout(new BorderLayout()); + contentPanel.setBackground(Color.WHITE); + contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + getContentPane().add(contentPanel, BorderLayout.CENTER); + contentPanel.setLayout(null); + { + JLabel lblNewLabel = new JLabel("Launch RenderDoc and press ok to continue..."); + lblNewLabel.setBounds(10, 11, 265, 14); + contentPanel.add(lblNewLabel); + } + { + JPanel buttonPane = new JPanel(); + buttonPane.setBackground(Color.WHITE); + FlowLayout fl_buttonPane = new FlowLayout(FlowLayout.RIGHT); + fl_buttonPane.setVgap(10); + fl_buttonPane.setHgap(10); + buttonPane.setLayout(fl_buttonPane); + getContentPane().add(buttonPane, BorderLayout.SOUTH); + { + JButton okButton = new JButton("OK"); + okButton.setPreferredSize(new Dimension(60, 20)); + okButton.setMargin(new Insets(0, 0, 0, 0)); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + LaunchRenderDocDialog.this.setVisible(false); + } + }); + okButton.setActionCommand("OK"); + buttonPane.add(okButton); + getRootPane().setDefaultButton(okButton); + } + { + JButton cancelButton = new JButton("Cancel"); + cancelButton.setPreferredSize(new Dimension(60, 20)); + cancelButton.setMargin(new Insets(0, 0, 0, 0)); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.exit(0); + } + }); + cancelButton.setActionCommand("Cancel"); + buttonPane.add(cancelButton); + } + } + + JSeparator separator = new JSeparator(); + getContentPane().add(separator, BorderLayout.NORTH); + } +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/MainClass.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/MainClass.java new file mode 100644 index 0000000..f44d053 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/MainClass.java @@ -0,0 +1,9 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +public class MainClass { + + public static void main(String[] args) { + LWJGLEntryPoint.main_(args); + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/TestProgram.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/TestProgram.java new file mode 100644 index 0000000..26f348e --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/TestProgram.java @@ -0,0 +1,37 @@ +package net.lax1dude.eaglercraft.v1_8.internal.lwjgl; + +import net.lax1dude.eaglercraft.v1_8.Display; +import net.lax1dude.eaglercraft.v1_8.EagRuntime; +import net.lax1dude.eaglercraft.v1_8.EagUtils; +import net.lax1dude.eaglercraft.v1_8.Keyboard; +import net.lax1dude.eaglercraft.v1_8.Mouse; +import net.lax1dude.eaglercraft.v1_8.internal.KeyboardConstants; + +public class TestProgram { + + private static boolean grab = false; + + public static void main_(String[] args) { + + while(!Display.isCloseRequested()) { + + Keyboard.enableRepeatEvents(true); + Display.update(); + + while(Keyboard.next()) { + if(Keyboard.getEventKey() == KeyboardConstants.KEY_E && Keyboard.getEventKeyState()) { + grab = !grab; + Mouse.setGrabbed(grab); + } + } + + System.out.println("" + Mouse.getDX() + ", " + Mouse.getDY()); + + EagUtils.sleep(100l); + } + + EagRuntime.destroy(); + + } + +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/ChannelLWJGLOpenAL.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/ChannelLWJGLOpenAL.java new file mode 100644 index 0000000..601b4e0 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/ChannelLWJGLOpenAL.java @@ -0,0 +1,602 @@ +package net.lax1dude.eaglercraft.v1_8.internal.paulscode.lwjgl3; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.LinkedList; +import javax.sound.sampled.AudioFormat; + +// From the lwjgl library, http://www.lwjgl.org +import org.lwjgl.BufferUtils; +import org.lwjgl.openal.AL10; +import org.lwjgl.openal.AL11; + +import paulscode.sound.Channel; +import paulscode.sound.SoundSystemConfig; + +/** + * The ChannelLWJGLOpenAL class is used to reserve a sound-card voice using the + * lwjgl binding of OpenAL. Channels can be either normal or streaming channels. + *
+ *
+ * This software is based on or using the LWJGL Lightweight Java Gaming Library + * available from http://www.lwjgl.org/.

+ *
+ * LWJGL License:
+ * Copyright (c) 2002-2008 Lightweight Java Game Library Project All rights + * reserved.
+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *
+ * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution.
+ * * Neither the name of 'Light Weight Java Game Library' nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ *
SoundSystem LibraryLWJGLOpenAL License:
+ *
+ * You are free to use this library for any purpose, commercial or + * otherwise. You may modify this library or source code, and distribute it any + * way you like, provided the following conditions are met:
+ * 1) You must abide by the conditions of the aforementioned LWJGL License.
+ * 2) You may not falsely claim to be the author of this library or any + * unmodified portion of it.
+ * 3) You may not copyright this library or a modified version of it and then + * sue me for copyright infringement.
+ * 4) If you modify the source code, you must clearly document the changes made + * before redistributing the modified source code, so other users know it is not + * the original code.
+ * 5) You are not required to give me credit for this library in any derived + * work, but if you do, you must also mention my website: + * http://www.paulscode.com
+ * 6) I the author will not be responsible for any damages (physical, financial, + * or otherwise) caused by the use if this library or any part of it.
+ * 7) I the author do not guarantee, warrant, or make any representations, + * either expressed or implied, regarding the use of this library or any part of + * it.
+ *
+ * Author: Paul Lamb
+ * http://www.paulscode.com
+ */ +public class ChannelLWJGLOpenAL extends Channel { + /** + * OpenAL's IntBuffer identifier for this channel. + */ + public IntBuffer ALSource; + + /** + * OpenAL data format to use when playing back the assigned source. + */ + public int ALformat; // OpenAL data format + + /** + * Sample rate (speed) to use for play-back. + */ + public int sampleRate; // sample rate + + /** + * Miliseconds of buffers previously played (streaming sources). + */ + public float millisPreviouslyPlayed = 0; + + /** + * Constructor: takes channelType identifier and a handle to the OpenAL + * IntBuffer identifier to use for this channel. Possible values for channel + * type can be found in the {@link paulscode.sound.SoundSystemConfig + * SoundSystemConfig} class. + * + * @param type Type of channel (normal or streaming). + * @param src Handle to the OpenAL source identifier. + */ + public ChannelLWJGLOpenAL(int type, IntBuffer src) { + super(type); + libraryType = LibraryLWJGLOpenAL.class; + ALSource = src; + } + + /** + * Empties the streamBuffers list, stops and deletes the ALSource, shuts the + * channel down, and removes references to all instantiated objects. + */ + @Override + public void cleanup() { + if (ALSource != null) { + try { + // Stop playing the source: + AL10.alSourceStop(ALSource.get(0)); + AL10.alGetError(); + } catch (Exception e) { + } + try { + // Delete the source: + AL10.alDeleteSources(ALSource); + AL10.alGetError(); + } catch (Exception e) { + } + ALSource.clear(); + } + ALSource = null; + + super.cleanup(); + } + + /** + * Attaches an OpenAL sound-buffer identifier for the sound data to be played + * back for a normal source. + * + * @param buf Intbuffer identifier for the sound data to play. + * @return False if an error occurred. + */ + public boolean attachBuffer(IntBuffer buf) { + // A sound buffer can only be attached to a normal source: + if (errorCheck(channelType != SoundSystemConfig.TYPE_NORMAL, + "Sound buffers may only be attached to normal " + "sources.")) + return false; + + // send the sound buffer to the channel: + AL10.alSourcei(ALSource.get(0), AL10.AL_BUFFER, buf.get(0)); + + // save the format for later, for determining milliseconds played + if (attachedSource != null && attachedSource.soundBuffer != null + && attachedSource.soundBuffer.audioFormat != null) + setAudioFormat(attachedSource.soundBuffer.audioFormat); + + // Check for errors and return: + return checkALError(); + } + + /** + * Sets the channel up to receive the specified audio format. + * + * @param audioFormat Format to use when playing the stream data. + */ + @Override + public void setAudioFormat(AudioFormat audioFormat) { + int soundFormat = 0; + if (audioFormat.getChannels() == 1) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_MONO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_MONO16; + } else { + errorMessage("Illegal sample size in method " + "'setAudioFormat'"); + return; + } + } else if (audioFormat.getChannels() == 2) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_STEREO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_STEREO16; + } else { + errorMessage("Illegal sample size in method " + "'setAudioFormat'"); + return; + } + } else { + errorMessage("Audio data neither mono nor stereo in " + "method 'setAudioFormat'"); + return; + } + ALformat = soundFormat; + sampleRate = (int) audioFormat.getSampleRate(); + } + + /** + * Sets the channel up to receive the specified OpenAL audio format and sample + * rate. + * + * @param format Format to use. + * @param rate Sample rate (speed) to use. + */ + public void setFormat(int format, int rate) { + ALformat = format; + sampleRate = rate; + } + + /** + * Queues up the initial byte[] buffers of data to be streamed. + * + * @param bufferList List of the first buffers to be played for a streaming + * source. + * @return False if problem occurred or if end of stream was reached. + */ + @Override + public boolean preLoadBuffers(LinkedList bufferList) { + // Stream buffers can only be queued for streaming sources: + if (errorCheck(channelType != SoundSystemConfig.TYPE_STREAMING, + "Buffers may only be queued for streaming sources.")) + return false; + + if (errorCheck(bufferList == null, "Buffer List null in method 'preLoadBuffers'")) + return false; + + IntBuffer streamBuffers; + + // Remember if the channel was playing: + boolean playing = playing(); + // stop the channel if it is playing: + if (playing) { + AL10.alSourceStop(ALSource.get(0)); + checkALError(); + } + // Clear out any previously queued buffers: + int processed = AL10.alGetSourcei(ALSource.get(0), AL10.AL_BUFFERS_PROCESSED); + if (processed > 0) { + streamBuffers = BufferUtils.createIntBuffer(processed); + AL10.alGenBuffers(streamBuffers); + if (errorCheck(checkALError(), "Error clearing stream buffers in method 'preLoadBuffers'")) + return false; + AL10.alSourceUnqueueBuffers(ALSource.get(0), streamBuffers); + if (errorCheck(checkALError(), "Error unqueuing stream buffers in method 'preLoadBuffers'")) + return false; + } + + // restart the channel if it was previously playing: + if (playing) { + AL10.alSourcePlay(ALSource.get(0)); + checkALError(); + } + + streamBuffers = BufferUtils.createIntBuffer(bufferList.size()); + AL10.alGenBuffers(streamBuffers); + if (errorCheck(checkALError(), "Error generating stream buffers in method 'preLoadBuffers'")) + return false; + + ByteBuffer byteBuffer = null; + for (int i = 0; i < bufferList.size(); i++) { + // byteBuffer = ByteBuffer.wrap( bufferList.get(i), 0, + // bufferList.get(i).length ); + byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer(bufferList.get(i).length).put(bufferList.get(i)) + .flip(); + + try { + AL10.alBufferData(streamBuffers.get(i), ALformat, byteBuffer, sampleRate); + } catch (Exception e) { + errorMessage("Error creating buffers in method " + "'preLoadBuffers'"); + printStackTrace(e); + return false; + } + if (errorCheck(checkALError(), "Error creating buffers in method 'preLoadBuffers'")) + return false; + + } + + try { + AL10.alSourceQueueBuffers(ALSource.get(0), streamBuffers); + } catch (Exception e) { + errorMessage("Error queuing buffers in method 'preLoadBuffers'"); + printStackTrace(e); + return false; + } + if (errorCheck(checkALError(), "Error queuing buffers in method 'preLoadBuffers'")) + return false; + + AL10.alSourcePlay(ALSource.get(0)); + if (errorCheck(checkALError(), "Error playing source in method 'preLoadBuffers'")) + return false; + + // Success: + return true; + } + + /** + * Queues up a byte[] buffer of data to be streamed. + * + * @param buffer The next buffer to be played for a streaming source. + * @return False if an error occurred or if the channel is shutting down. + */ + @Override + public boolean queueBuffer(byte[] buffer) { + // Stream buffers can only be queued for streaming sources: + if (errorCheck(channelType != SoundSystemConfig.TYPE_STREAMING, + "Buffers may only be queued for streaming sources.")) + return false; + + // ByteBuffer byteBuffer = ByteBuffer.wrap( buffer, 0, buffer.length ); + ByteBuffer byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer(buffer.length).put(buffer).flip(); + + IntBuffer intBuffer = BufferUtils.createIntBuffer(1); + + AL10.alSourceUnqueueBuffers(ALSource.get(0), intBuffer); + if (checkALError()) + return false; + + if (AL10.alIsBuffer(intBuffer.get(0))) + millisPreviouslyPlayed += millisInBuffer(intBuffer.get(0)); + checkALError(); + + AL10.alBufferData(intBuffer.get(0), ALformat, byteBuffer, sampleRate); + if (checkALError()) + return false; + + AL10.alSourceQueueBuffers(ALSource.get(0), intBuffer); + if (checkALError()) + return false; + + return true; + } + + /** + * Feeds raw data to the stream. + * + * @param buffer Buffer containing raw audio data to stream. + * @return Number of prior buffers that have been processed., or -1 if error. + */ + @Override + public int feedRawAudioData(byte[] buffer) { + // Stream buffers can only be queued for streaming sources: + if (errorCheck(channelType != SoundSystemConfig.TYPE_STREAMING, + "Raw audio data can only be fed to streaming sources.")) + return -1; + + // ByteBuffer byteBuffer = ByteBuffer.wrap( buffer, 0, buffer.length ); + ByteBuffer byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer(buffer.length).put(buffer).flip(); + + IntBuffer intBuffer; + + // Clear out any previously queued buffers: + int processed = AL10.alGetSourcei(ALSource.get(0), AL10.AL_BUFFERS_PROCESSED); + if (processed > 0) { + intBuffer = BufferUtils.createIntBuffer(processed); + AL10.alGenBuffers(intBuffer); + if (errorCheck(checkALError(), "Error clearing stream buffers in method 'feedRawAudioData'")) + return -1; + AL10.alSourceUnqueueBuffers(ALSource.get(0), intBuffer); + if (errorCheck(checkALError(), "Error unqueuing stream buffers in method 'feedRawAudioData'")) + return -1; + int i; + intBuffer.rewind(); + while (intBuffer.hasRemaining()) { + i = intBuffer.get(); + if (AL10.alIsBuffer(i)) { + millisPreviouslyPlayed += millisInBuffer(i); + } + checkALError(); + } + AL10.alDeleteBuffers(intBuffer); + checkALError(); + } + intBuffer = BufferUtils.createIntBuffer(1); + AL10.alGenBuffers(intBuffer); + if (errorCheck(checkALError(), "Error generating stream buffers in method 'preLoadBuffers'")) + return -1; + + AL10.alBufferData(intBuffer.get(0), ALformat, byteBuffer, sampleRate); + if (checkALError()) + return -1; + + AL10.alSourceQueueBuffers(ALSource.get(0), intBuffer); + if (checkALError()) + return -1; + + if (attachedSource != null && attachedSource.channel == this && attachedSource.active()) { + // restart the channel if it was previously playing: + if (!playing()) { + AL10.alSourcePlay(ALSource.get(0)); + checkALError(); + } + } + + return processed; + } + + /** + * Returns the number of milliseconds of audio contained in specified buffer. + * + * @return milliseconds, or 0 if unable to calculate. + */ + public float millisInBuffer(int alBufferi) { + return (((float) AL10.alGetBufferi(alBufferi, AL10.AL_SIZE) + / (float) AL10.alGetBufferi(alBufferi, AL10.AL_CHANNELS) + / ((float) AL10.alGetBufferi(alBufferi, AL10.AL_BITS) / 8.0f) / (float) sampleRate) * 1000); + } + + /** + * Calculates the number of milliseconds since the channel began playing. + * + * @return Milliseconds, or -1 if unable to calculate. + */ + @Override + public float millisecondsPlayed() { + // get number of samples played in current buffer + float offset = (float) AL10.alGetSourcei(ALSource.get(0), AL11.AL_BYTE_OFFSET); + + float bytesPerFrame = 1f; + switch (ALformat) { + case AL10.AL_FORMAT_MONO8: + bytesPerFrame = 1f; + break; + case AL10.AL_FORMAT_MONO16: + bytesPerFrame = 2f; + break; + case AL10.AL_FORMAT_STEREO8: + bytesPerFrame = 2f; + break; + case AL10.AL_FORMAT_STEREO16: + bytesPerFrame = 4f; + break; + default: + break; + } + + offset = (((float) offset / bytesPerFrame) / (float) sampleRate) * 1000; + + // add the milliseconds from stream-buffers that played previously + if (channelType == SoundSystemConfig.TYPE_STREAMING) + offset += millisPreviouslyPlayed; + + // Return millis played: + return (offset); + } + + /** + * Returns the number of queued byte[] buffers that have finished playing. + * + * @return Number of buffers processed. + */ + @Override + public int buffersProcessed() { + // Only streaming sources process buffers: + if (channelType != SoundSystemConfig.TYPE_STREAMING) + return 0; + + // determine how many have been processed: + int processed = AL10.alGetSourcei(ALSource.get(0), AL10.AL_BUFFERS_PROCESSED); + + // Check for errors: + if (checkALError()) + return 0; + + // Return how many were processed: + return processed; + } + + /** + * Dequeues all previously queued data. + */ + @Override + public void flush() { + // Only a streaming source can be flushed, because only streaming + // sources have queued buffers: + if (channelType != SoundSystemConfig.TYPE_STREAMING) + return; + + // determine how many buffers have been queued: + int queued = AL10.alGetSourcei(ALSource.get(0), AL10.AL_BUFFERS_QUEUED); + // Check for errors: + if (checkALError()) + return; + + IntBuffer intBuffer = BufferUtils.createIntBuffer(1); + while (queued > 0) { + try { + AL10.alSourceUnqueueBuffers(ALSource.get(0), intBuffer); + } catch (Exception e) { + return; + } + if (checkALError()) + return; + queued--; + } + millisPreviouslyPlayed = 0; + } + + /** + * Stops the channel, dequeues any queued data, and closes the channel. + */ + @Override + public void close() { + try { + AL10.alSourceStop(ALSource.get(0)); + AL10.alGetError(); + } catch (Exception e) { + } + + if (channelType == SoundSystemConfig.TYPE_STREAMING) + flush(); + } + + /** + * Plays the currently attached normal source, opens this channel up for + * streaming, or resumes playback if this channel was paused. + */ + @Override + public void play() { + AL10.alSourcePlay(ALSource.get(0)); + checkALError(); + } + + /** + * Temporarily stops playback for this channel. + */ + @Override + public void pause() { + AL10.alSourcePause(ALSource.get(0)); + checkALError(); + } + + /** + * Stops playback for this channel and rewinds the attached source to the + * beginning. + */ + @Override + public void stop() { + AL10.alSourceStop(ALSource.get(0)); + if (!checkALError()) + millisPreviouslyPlayed = 0; + } + + /** + * Rewinds the attached source to the beginning. Stops the source if it was + * paused. + */ + @Override + public void rewind() { + // rewinding for streaming sources is handled elsewhere + if (channelType == SoundSystemConfig.TYPE_STREAMING) + return; + + AL10.alSourceRewind(ALSource.get(0)); + if (!checkALError()) + millisPreviouslyPlayed = 0; + } + + /** + * Used to determine if a channel is actively playing a source. This method will + * return false if the channel is paused or stopped and when no data is queued + * to be streamed. + * + * @return True if this channel is playing a source. + */ + @Override + public boolean playing() { + int state = AL10.alGetSourcei(ALSource.get(0), AL10.AL_SOURCE_STATE); + if (checkALError()) + return false; + + return (state == AL10.AL_PLAYING); + } + + /** + * Checks for OpenAL errors, and prints a message if there is an error. + * + * @return True if there was an error, False if not. + */ + private boolean checkALError() { + switch (AL10.alGetError()) { + case AL10.AL_NO_ERROR: + return false; + case AL10.AL_INVALID_NAME: + errorMessage("Invalid name parameter."); + return true; + case AL10.AL_INVALID_ENUM: + errorMessage("Invalid parameter."); + return true; + case AL10.AL_INVALID_VALUE: + errorMessage("Invalid enumerated parameter value."); + return true; + case AL10.AL_INVALID_OPERATION: + errorMessage("Illegal call."); + return true; + case AL10.AL_OUT_OF_MEMORY: + errorMessage("Unable to allocate memory."); + return true; + default: + errorMessage("An unrecognized error occurred."); + return true; + } + } +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/LibraryLWJGLOpenAL.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/LibraryLWJGLOpenAL.java new file mode 100644 index 0000000..42a58b2 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/LibraryLWJGLOpenAL.java @@ -0,0 +1,984 @@ +package net.lax1dude.eaglercraft.v1_8.internal.paulscode.lwjgl3; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.FloatBuffer; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; +import javax.sound.sampled.AudioFormat; + +import org.lwjgl.BufferUtils; +import org.lwjgl.openal.AL; +import org.lwjgl.openal.AL10; +import org.lwjgl.openal.ALC; +import org.lwjgl.openal.ALC11; +import org.lwjgl.openal.ALCCapabilities; +import org.lwjgl.openal.SOFTHRTF; + +import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; +import net.lax1dude.eaglercraft.v1_8.log4j.Logger; +import paulscode.sound.Channel; +import paulscode.sound.FilenameURL; +import paulscode.sound.ICodec; +import paulscode.sound.Library; +import paulscode.sound.ListenerData; +import paulscode.sound.SoundBuffer; +import paulscode.sound.SoundSystemConfig; +import paulscode.sound.SoundSystemException; +import paulscode.sound.Source; + +/** + * The LibraryLWJGLOpenAL class interfaces the lwjgl binding of OpenAL.
+ *
+ * This software is based on or using the LWJGL Lightweight Java Gaming Library + * available from http://www.lwjgl.org/.

+ *
+ * LWJGL License:
+ * Copyright (c) 2002-2008 Lightweight Java Game Library Project All rights + * reserved.
+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *
+ * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution.
+ * * Neither the name of 'Light Weight Java Game Library' nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ *
SoundSystem LibraryLWJGLOpenAL License:
+ *
+ * You are free to use this library for any purpose, commercial or + * otherwise. You may modify this library or source code, and distribute it any + * way you like, provided the following conditions are met:
+ * 1) You must abide by the conditions of the aforementioned LWJGL License.
+ * 2) You may not falsely claim to be the author of this library or any + * unmodified portion of it.
+ * 3) You may not copyright this library or a modified version of it and then + * sue me for copyright infringement.
+ * 4) If you modify the source code, you must clearly document the changes made + * before redistributing the modified source code, so other users know it is not + * the original code.
+ * 5) You are not required to give me credit for this library in any derived + * work, but if you do, you must also mention my website: + * http://www.paulscode.com
+ * 6) I the author will not be responsible for any damages (physical, financial, + * or otherwise) caused by the use if this library or any part of it.
+ * 7) I the author do not guarantee, warrant, or make any representations, + * either expressed or implied, regarding the use of this library or any part of + * it.
+ *
+ * Author: Paul Lamb
+ * http://www.paulscode.com
+ */ +public class LibraryLWJGLOpenAL extends Library { + /** + * Used to return a current value from one of the synchronized boolean-interface + * methods. + */ + private static final boolean GET = false; + /** + * Used to set the value in one of the synchronized boolean-interface methods. + */ + private static final boolean SET = true; + /** + * Used when a parameter for one of the synchronized boolean-interface methods + * is not aplicable. + */ + private static final boolean XXX = false; + + /** + * Position of the listener in 3D space. + */ + private FloatBuffer listenerPositionAL = null; + /** + * Information about the listener's orientation. + */ + private FloatBuffer listenerOrientation = null; + /** + * Velocity of the listener. + */ + private FloatBuffer listenerVelocity = null; + /** + * Map containing OpenAL identifiers for sound buffers. + */ + private HashMap ALBufferMap = null; + + /** + * Whether or not the AL_PITCH control is supported. + */ + private static boolean alPitchSupported = true; + + /** + * Constructor: Instantiates the source map, buffer map and listener + * information. Also sets the library type to SoundSystemConfig.LIBRARY_OPENAL + */ + public LibraryLWJGLOpenAL() throws SoundSystemException { + super(); + ALBufferMap = new HashMap(); + reverseByteOrder = true; + } + + private long openALDevice = 0l; + private long openALContext = 0l; + + private static final Logger logger = LogManager.getLogger("SoundSystem"); + + /** + * Initializes OpenAL, creates the listener, and grabs up audio channels. + */ + @Override + public void init() throws SoundSystemException { + boolean errors = false; // set to 'true' if error(s) occur: + + //TODO: eaglercraft + + openALDevice = ALC11.alcOpenDevice((String)null); + + ALCCapabilities caps; + if (openALDevice == 0l) { + logger.error("Unable to initialize OpenAL!"); + throw new LibraryLWJGLOpenAL.Exception("Unable to initialize OpenAL", LibraryLWJGLOpenAL.Exception.CREATE); + }else { + caps = ALC.createCapabilities(openALDevice); + logger.info("Device opened: {}", openALDevice); + } + + openALContext = ALC11.alcCreateContext(openALDevice, new int[] { SOFTHRTF.ALC_HRTF_SOFT, 1, 0 }); + if(!ALC11.alcMakeContextCurrent(openALContext)) { + ALC11.alcCloseDevice(openALDevice); + logger.error("Unable to initialize AL context!"); + throw new LibraryLWJGLOpenAL.Exception("Unable to initialize OpenAL", LibraryLWJGLOpenAL.Exception.CREATE); + } + + AL.createCapabilities(caps); + + // Let user know if the library loaded properly + if (errors) + importantMessage("OpenAL did not initialize properly!"); + else + message("OpenAL initialized."); + + // Listener is at the origin, facing along the z axis, no velocity: + listenerPositionAL = BufferUtils.createFloatBuffer(3) + .put(new float[] { listener.position.x, listener.position.y, listener.position.z }); + listenerOrientation = BufferUtils.createFloatBuffer(6).put(new float[] { listener.lookAt.x, listener.lookAt.y, + listener.lookAt.z, listener.up.x, listener.up.y, listener.up.z }); + listenerVelocity = BufferUtils.createFloatBuffer(3).put(new float[] { 0.0f, 0.0f, 0.0f }); + + // Flip the buffers, so they can be used: + listenerPositionAL.flip(); + listenerOrientation.flip(); + listenerVelocity.flip(); + + // Pass the buffers to the sound system, and check for potential errors: + AL10.alListenerfv(AL10.AL_POSITION, listenerPositionAL); + errors = checkALError() || errors; + AL10.alListenerfv(AL10.AL_ORIENTATION, listenerOrientation); + errors = checkALError() || errors; + AL10.alListenerfv(AL10.AL_VELOCITY, listenerVelocity); + errors = checkALError() || errors; + + AL10.alDopplerFactor(SoundSystemConfig.getDopplerFactor()); + errors = checkALError() || errors; + + AL10.alDopplerVelocity(SoundSystemConfig.getDopplerVelocity()); + errors = checkALError() || errors; + + // Let user know what caused the above error messages: + if (errors) { + importantMessage("OpenAL did not initialize properly!"); + throw new LibraryLWJGLOpenAL.Exception("Problem encountered " + "while loading OpenAL or " + + "creating the listener. " + "Probable cause: OpenAL not " + "supported", + LibraryLWJGLOpenAL.Exception.CREATE); + } + + super.init(); + + // Check if we can use the AL_PITCH control: + ChannelLWJGLOpenAL channel = (ChannelLWJGLOpenAL) normalChannels.get(1); + try { + AL10.alSourcef(channel.ALSource.get(0), AL10.AL_PITCH, 1.0f); + if (checkALError()) { + alPitchSupported(SET, false); + throw new LibraryLWJGLOpenAL.Exception("OpenAL: AL_PITCH not " + "supported.", + LibraryLWJGLOpenAL.Exception.NO_AL_PITCH); + } else { + alPitchSupported(SET, true); + } + } catch (java.lang.Exception e) { + alPitchSupported(SET, false); + throw new LibraryLWJGLOpenAL.Exception("OpenAL: AL_PITCH not " + "supported.", + LibraryLWJGLOpenAL.Exception.NO_AL_PITCH); + } + } + + /** + * Checks if the OpenAL library type is compatible. + * + * @return True or false. + */ + public static boolean libraryCompatible() { + return true; + } + + /** + * Creates a new channel of the specified type (normal or streaming). Possible + * values for channel type can be found in the + * {@link paulscode.sound.SoundSystemConfig SoundSystemConfig} class. + * + * @param type Type of channel. + */ + @Override + protected Channel createChannel(int type) { + ChannelLWJGLOpenAL channel; + IntBuffer ALSource; + + ALSource = BufferUtils.createIntBuffer(1); + try { + AL10.alGenSources(ALSource); + } catch (java.lang.Exception e) { + AL10.alGetError(); + return null; // no more voices left + } + + if (AL10.alGetError() != AL10.AL_NO_ERROR) + return null; + + channel = new ChannelLWJGLOpenAL(type, ALSource); + return channel; + } + + /** + * Stops all sources, shuts down OpenAL, and removes references to all + * instantiated objects. + */ + @Override + public void cleanup() { + super.cleanup(); + + Set keys = bufferMap.keySet(); + Iterator iter = keys.iterator(); + String filename; + IntBuffer buffer; + + // loop through and clear all sound buffers: + while (iter.hasNext()) { + filename = iter.next(); + buffer = ALBufferMap.get(filename); + if (buffer != null) { + AL10.alDeleteBuffers(buffer); + checkALError(); + buffer.clear(); + } + } + + bufferMap.clear(); + + ALC11.alcMakeContextCurrent(0l); + ALC11.alcDestroyContext(openALContext); + ALC11.alcCloseDevice(openALDevice); + ALC.destroy(); + + bufferMap = null; + listenerPositionAL = null; + listenerOrientation = null; + listenerVelocity = null; + } + + /** + * Pre-loads a sound into memory. + * + * @param filenameURL Filename/URL of the sound file to load. + * @return True if the sound loaded properly. + */ + @Override + public boolean loadSound(FilenameURL filenameURL) { + // Make sure the buffer map exists: + if (bufferMap == null) { + bufferMap = new HashMap(); + importantMessage("Buffer Map was null in method 'loadSound'"); + } + // Make sure the OpenAL buffer map exists: + if (ALBufferMap == null) { + ALBufferMap = new HashMap(); + importantMessage("Open AL Buffer Map was null in method" + "'loadSound'"); + } + + // make sure they gave us a filename: + if (errorCheck(filenameURL == null, "Filename/URL not specified in method 'loadSound'")) + return false; + + // check if it is already loaded: + if (bufferMap.get(filenameURL.getFilename()) != null) + return true; + + ICodec codec = SoundSystemConfig.getCodec(filenameURL.getFilename()); + if (errorCheck(codec == null, + "No codec found for file '" + filenameURL.getFilename() + "' in method 'loadSound'")) + return false; + codec.reverseByteOrder(true); + + URL url = filenameURL.getURL(); + if (errorCheck(url == null, "Unable to open file '" + filenameURL.getFilename() + "' in method 'loadSound'")) + return false; + + codec.initialize(url); + SoundBuffer buffer = codec.readAll(); + codec.cleanup(); + codec = null; + if (errorCheck(buffer == null, "Sound buffer null in method 'loadSound'")) + return false; + + bufferMap.put(filenameURL.getFilename(), buffer); + + AudioFormat audioFormat = buffer.audioFormat; + int soundFormat = 0; + if (audioFormat.getChannels() == 1) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_MONO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_MONO16; + } else { + errorMessage("Illegal sample size in method 'loadSound'"); + return false; + } + } else if (audioFormat.getChannels() == 2) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_STEREO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_STEREO16; + } else { + errorMessage("Illegal sample size in method 'loadSound'"); + return false; + } + } else { + errorMessage("File neither mono nor stereo in method " + "'loadSound'"); + return false; + } + + IntBuffer intBuffer = BufferUtils.createIntBuffer(1); + AL10.alGenBuffers(intBuffer); + if (errorCheck(AL10.alGetError() != AL10.AL_NO_ERROR, + "alGenBuffers error when loading " + filenameURL.getFilename())) + return false; + +// AL10.alBufferData( intBuffer.get( 0 ), soundFormat, +// ByteBuffer.wrap( buffer.audioData ), +// (int) audioFormat.getSampleRate() ); + AL10.alBufferData(intBuffer.get(0), soundFormat, + (ByteBuffer) BufferUtils.createByteBuffer(buffer.audioData.length).put(buffer.audioData).flip(), + (int) audioFormat.getSampleRate()); + + if (errorCheck(AL10.alGetError() != AL10.AL_NO_ERROR, + "alBufferData error when loading " + filenameURL.getFilename())) + + if (errorCheck(intBuffer == null, "Sound buffer was not created for " + filenameURL.getFilename())) + return false; + + ALBufferMap.put(filenameURL.getFilename(), intBuffer); + + return true; + } + + /** + * Saves the specified sample data, under the specified identifier. This + * identifier can be later used in place of 'filename' parameters to reference + * the sample data. + * + * @param buffer the sample data and audio format to save. + * @param identifier What to call the sample. + * @return True if there weren't any problems. + */ + @Override + public boolean loadSound(SoundBuffer buffer, String identifier) { + // Make sure the buffer map exists: + if (bufferMap == null) { + bufferMap = new HashMap(); + importantMessage("Buffer Map was null in method 'loadSound'"); + } + // Make sure the OpenAL buffer map exists: + if (ALBufferMap == null) { + ALBufferMap = new HashMap(); + importantMessage("Open AL Buffer Map was null in method" + "'loadSound'"); + } + + // make sure they gave us an identifier: + if (errorCheck(identifier == null, "Identifier not specified in method 'loadSound'")) + return false; + + // check if it is already loaded: + if (bufferMap.get(identifier) != null) + return true; + + if (errorCheck(buffer == null, "Sound buffer null in method 'loadSound'")) + return false; + + bufferMap.put(identifier, buffer); + + AudioFormat audioFormat = buffer.audioFormat; + int soundFormat = 0; + if (audioFormat.getChannels() == 1) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_MONO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_MONO16; + } else { + errorMessage("Illegal sample size in method 'loadSound'"); + return false; + } + } else if (audioFormat.getChannels() == 2) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_STEREO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_STEREO16; + } else { + errorMessage("Illegal sample size in method 'loadSound'"); + return false; + } + } else { + errorMessage("File neither mono nor stereo in method " + "'loadSound'"); + return false; + } + + IntBuffer intBuffer = BufferUtils.createIntBuffer(1); + AL10.alGenBuffers(intBuffer); + if (errorCheck(AL10.alGetError() != AL10.AL_NO_ERROR, "alGenBuffers error when saving " + identifier)) + return false; + +// AL10.alBufferData( intBuffer.get( 0 ), soundFormat, +// ByteBuffer.wrap( buffer.audioData ), +// (int) audioFormat.getSampleRate() ); + AL10.alBufferData(intBuffer.get(0), soundFormat, + (ByteBuffer) BufferUtils.createByteBuffer(buffer.audioData.length).put(buffer.audioData).flip(), + (int) audioFormat.getSampleRate()); + + if (errorCheck(AL10.alGetError() != AL10.AL_NO_ERROR, "alBufferData error when saving " + identifier)) + + if (errorCheck(intBuffer == null, "Sound buffer was not created for " + identifier)) + return false; + + ALBufferMap.put(identifier, intBuffer); + + return true; + } + + /** + * Removes a pre-loaded sound from memory. This is a good method to use for + * freeing up memory after a large sound file is no longer needed. NOTE: the + * source will remain in memory after this method has been called, for as long + * as the sound is attached to an existing source. + * + * @param filename Filename/identifier of the sound file to unload. + */ + @Override + public void unloadSound(String filename) { + ALBufferMap.remove(filename); + super.unloadSound(filename); + } + + /** + * Sets the overall volume to the specified value, affecting all sources. + * + * @param value New volume, float value ( 0.0f - 1.0f ). + */ + @Override + public void setMasterVolume(float value) { + super.setMasterVolume(value); + + AL10.alListenerf(AL10.AL_GAIN, value); + checkALError(); + } + + /** + * Creates a new source and places it into the source map. + * + * @param priority Setting this to true will prevent other sounds from + * overriding this one. + * @param toStream Setting this to true will load the sound in pieces rather + * than all at once. + * @param toLoop Should this source loop, or play only once. + * @param sourcename A unique identifier for this source. Two sources may not + * use the same sourcename. + * @param filenameURL Filename/URL of the sound file to play at this source. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on + * the value of "attmodel". + */ + @Override + public void newSource(boolean priority, boolean toStream, boolean toLoop, String sourcename, + FilenameURL filenameURL, float x, float y, float z, int attModel, float distOrRoll) { + IntBuffer myBuffer = null; + if (!toStream) { + // Grab the sound buffer for this file: + myBuffer = ALBufferMap.get(filenameURL.getFilename()); + + // if not found, try loading it: + if (myBuffer == null) { + if (!loadSound(filenameURL)) { + errorMessage("Source '" + sourcename + "' was not created " + + "because an error occurred while loading " + filenameURL.getFilename()); + return; + } + } + + // try and grab the sound buffer again: + myBuffer = ALBufferMap.get(filenameURL.getFilename()); + // see if it was there this time: + if (myBuffer == null) { + errorMessage("Source '" + sourcename + "' was not created " + + "because a sound buffer was not found for " + filenameURL.getFilename()); + return; + } + } + SoundBuffer buffer = null; + + if (!toStream) { + // Grab the audio data for this file: + buffer = bufferMap.get(filenameURL.getFilename()); + // if not found, try loading it: + if (buffer == null) { + if (!loadSound(filenameURL)) { + errorMessage("Source '" + sourcename + "' was not created " + + "because an error occurred while loading " + filenameURL.getFilename()); + return; + } + } + // try and grab the sound buffer again: + buffer = bufferMap.get(filenameURL.getFilename()); + // see if it was there this time: + if (buffer == null) { + errorMessage("Source '" + sourcename + "' was not created " + "because audio data was not found for " + + filenameURL.getFilename()); + return; + } + } + + sourceMap.put(sourcename, new SourceLWJGLOpenAL(listenerPositionAL, myBuffer, priority, toStream, toLoop, + sourcename, filenameURL, buffer, x, y, z, attModel, distOrRoll, false)); + } + + /** + * Opens a direct line for streaming audio data. + * + * @param audioFormat Format that the data will be in. + * @param priority Setting this to true will prevent other sounds from + * overriding this one. + * @param sourcename A unique identifier for this source. Two sources may not + * use the same sourcename. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on + * the value of "attmodel". + */ + @Override + public void rawDataStream(AudioFormat audioFormat, boolean priority, String sourcename, float x, float y, float z, + int attModel, float distOrRoll) { + sourceMap.put(sourcename, new SourceLWJGLOpenAL(listenerPositionAL, audioFormat, priority, sourcename, x, y, z, + attModel, distOrRoll)); + } + + /** + * Creates and immediately plays a new source. + * + * @param priority Setting this to true will prevent other sounds from + * overriding this one. + * @param toStream Setting this to true will load the sound in pieces rather + * than all at once. + * @param toLoop Should this source loop, or play only once. + * @param sourcename A unique identifier for this source. Two sources may not + * use the same sourcename. + * @param filenameURL Filename/URL of the sound file to play at this source. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on + * the value of "attmodel". + * @param temporary Whether or not this source should be removed after it + * finishes playing. + */ + @Override + public void quickPlay(boolean priority, boolean toStream, boolean toLoop, String sourcename, + FilenameURL filenameURL, float x, float y, float z, int attModel, float distOrRoll, boolean temporary) { + IntBuffer myBuffer = null; + if (!toStream) { + // Grab the sound buffer for this file: + myBuffer = ALBufferMap.get(filenameURL.getFilename()); + // if not found, try loading it: + if (myBuffer == null) + loadSound(filenameURL); + // try and grab the sound buffer again: + myBuffer = ALBufferMap.get(filenameURL.getFilename()); + // see if it was there this time: + if (myBuffer == null) { + errorMessage("Sound buffer was not created for " + filenameURL.getFilename()); + return; + } + } + + SoundBuffer buffer = null; + + if (!toStream) { + // Grab the sound buffer for this file: + buffer = bufferMap.get(filenameURL.getFilename()); + // if not found, try loading it: + if (buffer == null) { + if (!loadSound(filenameURL)) { + errorMessage("Source '" + sourcename + "' was not created " + + "because an error occurred while loading " + filenameURL.getFilename()); + return; + } + } + // try and grab the sound buffer again: + buffer = bufferMap.get(filenameURL.getFilename()); + // see if it was there this time: + if (buffer == null) { + errorMessage("Source '" + sourcename + "' was not created " + "because audio data was not found for " + + filenameURL.getFilename()); + return; + } + } + SourceLWJGLOpenAL s = new SourceLWJGLOpenAL(listenerPositionAL, myBuffer, priority, toStream, toLoop, + sourcename, filenameURL, buffer, x, y, z, attModel, distOrRoll, false); + + sourceMap.put(sourcename, s); + play(s); + if (temporary) + s.setTemporary(true); + } + + /** + * Creates sources based on the source map provided. + * + * @param srcMap Sources to copy. + */ + @Override + public void copySources(HashMap srcMap) { + if (srcMap == null) + return; + Set keys = srcMap.keySet(); + Iterator iter = keys.iterator(); + String sourcename; + Source source; + + // Make sure the buffer map exists: + if (bufferMap == null) { + bufferMap = new HashMap(); + importantMessage("Buffer Map was null in method 'copySources'"); + } + // Make sure the OpenAL buffer map exists: + if (ALBufferMap == null) { + ALBufferMap = new HashMap(); + importantMessage("Open AL Buffer Map was null in method" + "'copySources'"); + } + + // remove any existing sources before starting: + sourceMap.clear(); + + SoundBuffer buffer; + // loop through and copy all the sources: + while (iter.hasNext()) { + sourcename = iter.next(); + source = srcMap.get(sourcename); + if (source != null) { + buffer = null; + if (!source.toStream) { + loadSound(source.filenameURL); + buffer = bufferMap.get(source.filenameURL.getFilename()); + } + if (source.toStream || buffer != null) + sourceMap.put(sourcename, new SourceLWJGLOpenAL(listenerPositionAL, + ALBufferMap.get(source.filenameURL.getFilename()), source, buffer)); + } + } + } + + /** + * Changes the listener's position. + * + * @param x Destination X coordinate. + * @param y Destination Y coordinate. + * @param z Destination Z coordinate. + */ + @Override + public void setListenerPosition(float x, float y, float z) { + super.setListenerPosition(x, y, z); + + listenerPositionAL.put(0, x); + listenerPositionAL.put(1, y); + listenerPositionAL.put(2, z); + + // Update OpenAL listener position: + AL10.alListenerfv(AL10.AL_POSITION, listenerPositionAL); + // Check for errors: + checkALError(); + } + + /** + * Changes the listeners orientation to the specified 'angle' radians + * counterclockwise around the y-Axis. + * + * @param angle Radians. + */ + @Override + public void setListenerAngle(float angle) { + super.setListenerAngle(angle); + + listenerOrientation.put(0, listener.lookAt.x); + listenerOrientation.put(2, listener.lookAt.z); + + // Update OpenAL listener orientation: + AL10.alListenerfv(AL10.AL_ORIENTATION, listenerOrientation); + // Check for errors: + checkALError(); + } + + /** + * Changes the listeners orientation using the specified coordinates. + * + * @param lookX X element of the look-at direction. + * @param lookY Y element of the look-at direction. + * @param lookZ Z element of the look-at direction. + * @param upX X element of the up direction. + * @param upY Y element of the up direction. + * @param upZ Z element of the up direction. + */ + @Override + public void setListenerOrientation(float lookX, float lookY, float lookZ, float upX, float upY, float upZ) { + super.setListenerOrientation(lookX, lookY, lookZ, upX, upY, upZ); + listenerOrientation.put(0, lookX); + listenerOrientation.put(1, lookY); + listenerOrientation.put(2, lookZ); + listenerOrientation.put(3, upX); + listenerOrientation.put(4, upY); + listenerOrientation.put(5, upZ); + AL10.alListenerfv(AL10.AL_ORIENTATION, listenerOrientation); + checkALError(); + } + + /** + * Changes the listeners position and orientation using the specified listener + * data. + * + * @param l Listener data to use. + */ + @Override + public void setListenerData(ListenerData l) { + super.setListenerData(l); + + listenerPositionAL.put(0, l.position.x); + listenerPositionAL.put(1, l.position.y); + listenerPositionAL.put(2, l.position.z); + AL10.alListenerfv(AL10.AL_POSITION, listenerPositionAL); + checkALError(); + + listenerOrientation.put(0, l.lookAt.x); + listenerOrientation.put(1, l.lookAt.y); + listenerOrientation.put(2, l.lookAt.z); + listenerOrientation.put(3, l.up.x); + listenerOrientation.put(4, l.up.y); + listenerOrientation.put(5, l.up.z); + AL10.alListenerfv(AL10.AL_ORIENTATION, listenerOrientation); + checkALError(); + + listenerVelocity.put(0, l.velocity.x); + listenerVelocity.put(1, l.velocity.y); + listenerVelocity.put(2, l.velocity.z); + AL10.alListenerfv(AL10.AL_VELOCITY, listenerVelocity); + checkALError(); + } + + /** + * Sets the listener's velocity, for use in Doppler effect. + * + * @param x Velocity along world x-axis. + * @param y Velocity along world y-axis. + * @param z Velocity along world z-axis. + */ + @Override + public void setListenerVelocity(float x, float y, float z) { + super.setListenerVelocity(x, y, z); + + listenerVelocity.put(0, listener.velocity.x); + listenerVelocity.put(1, listener.velocity.y); + listenerVelocity.put(2, listener.velocity.z); + AL10.alListenerfv(AL10.AL_VELOCITY, listenerVelocity); + } + + /** + * The Doppler parameters have changed. + */ + @Override + public void dopplerChanged() { + super.dopplerChanged(); + + AL10.alDopplerFactor(SoundSystemConfig.getDopplerFactor()); + checkALError(); + AL10.alDopplerVelocity(SoundSystemConfig.getDopplerVelocity()); + checkALError(); + } + + /** + * Checks for OpenAL errors, and prints a message if there is an error. + * + * @return True if there was an error, False if not. + */ + private boolean checkALError() { + switch (AL10.alGetError()) { + case AL10.AL_NO_ERROR: + return false; + case AL10.AL_INVALID_NAME: + errorMessage("Invalid name parameter."); + return true; + case AL10.AL_INVALID_ENUM: + errorMessage("Invalid parameter."); + return true; + case AL10.AL_INVALID_VALUE: + errorMessage("Invalid enumerated parameter value."); + return true; + case AL10.AL_INVALID_OPERATION: + errorMessage("Illegal call."); + return true; + case AL10.AL_OUT_OF_MEMORY: + errorMessage("Unable to allocate memory."); + return true; + default: + errorMessage("An unrecognized error occurred."); + return true; + } + } + + /** + * Whether or not the AL_PITCH control is supported. + * + * @return True if AL_PITCH is supported. + */ + public static boolean alPitchSupported() { + return alPitchSupported(GET, XXX); + } + + /** + * Sets or returns the value of boolean 'alPitchSupported'. + * + * @param action Action to perform (GET or SET). + * @param value New value if action is SET, otherwise XXX. + * @return value of boolean 'alPitchSupported'. + */ + private static synchronized boolean alPitchSupported(boolean action, boolean value) { + if (action == SET) + alPitchSupported = value; + return alPitchSupported; + } + + /** + * Returns the short title of this library type. + * + * @return A short title. + */ + public static String getTitle() { + return "LWJGL OpenAL (Eaglercraft)"; + } + + /** + * Returns a longer description of this library type. + * + * @return A longer description. + */ + public static String getDescription() { + return "The Eaglercraft LWJGL3 binding of OpenAL"; + } + + /** + * Returns the name of the class. + * + * @return "Library" + library title. + */ + @Override + public String getClassName() { + return "LibraryLWJGLOpenAL"; + } + + /** + * The LibraryLWJGLOpenAL.Exception class provides library-specific error + * information. + */ + public static class Exception extends SoundSystemException { + private static final long serialVersionUID = -7502452059037798035L; + /** + * Global identifier for an exception during AL.create(). Probably means that + * OpenAL is not supported. + */ + public static final int CREATE = 101; + /** + * Global identifier for an invalid name parameter in OpenAL. + */ + public static final int INVALID_NAME = 102; + /** + * Global identifier for an invalid parameter in OpenAL. + */ + public static final int INVALID_ENUM = 103; + /** + * Global identifier for an invalid enumerated parameter value in OpenAL. + */ + public static final int INVALID_VALUE = 104; + /** + * Global identifier for an illegal call in OpenAL. + */ + public static final int INVALID_OPERATION = 105; + /** + * Global identifier for OpenAL out of memory. + */ + public static final int OUT_OF_MEMORY = 106; + /** + * Global identifier for an exception while creating the OpenAL Listener. + */ + public static final int LISTENER = 107; + /** + * Global identifier for OpenAL AL_PITCH not supported. + */ + public static final int NO_AL_PITCH = 108; + + /** + * Constructor: Generates a standard "unknown error" exception with the + * specified message. + * + * @param message A brief description of the problem that occurred. + */ + public Exception(String message) { + super(message); + } + + /** + * Constructor: Generates an exception of the specified type, with the specified + * message. + * + * @param message A brief description of the problem that occurred. + * @param type Identifier indicating they type of error. + */ + public Exception(String message, int type) { + super(message, type); + } + } +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/SourceLWJGLOpenAL.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/SourceLWJGLOpenAL.java new file mode 100644 index 0000000..62b0332 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/paulscode/lwjgl3/SourceLWJGLOpenAL.java @@ -0,0 +1,703 @@ +package net.lax1dude.eaglercraft.v1_8.internal.paulscode.lwjgl3; + +import java.nio.IntBuffer; +import java.nio.FloatBuffer; +import java.util.LinkedList; +import javax.sound.sampled.AudioFormat; + +// From the lwjgl library, http://www.lwjgl.org +import org.lwjgl.BufferUtils; +import org.lwjgl.openal.AL10; + +import paulscode.sound.Channel; +import paulscode.sound.FilenameURL; +import paulscode.sound.Source; +import paulscode.sound.SoundBuffer; +import paulscode.sound.SoundSystemConfig; + +/** + * The SourceLWJGLOpenAL class provides an interface to the lwjgl binding of + * OpenAL.
+ *
+ * This software is based on or using the LWJGL Lightweight Java Gaming Library + * available from http://www.lwjgl.org/.

+ *
+ * LWJGL License:
+ * Copyright (c) 2002-2008 Lightweight Java Game Library Project All rights + * reserved.
+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *
+ * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution.
+ * * Neither the name of 'Light Weight Java Game Library' nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ *
SoundSystem LibraryLWJGLOpenAL License:
+ *
+ * You are free to use this library for any purpose, commercial or + * otherwise. You may modify this library or source code, and distribute it any + * way you like, provided the following conditions are met:
+ * 1) You must abide by the conditions of the aforementioned LWJGL License.
+ * 2) You may not falsely claim to be the author of this library or any + * unmodified portion of it.
+ * 3) You may not copyright this library or a modified version of it and then + * sue me for copyright infringement.
+ * 4) If you modify the source code, you must clearly document the changes made + * before redistributing the modified source code, so other users know it is not + * the original code.
+ * 5) You are not required to give me credit for this library in any derived + * work, but if you do, you must also mention my website: + * http://www.paulscode.com
+ * 6) I the author will not be responsible for any damages (physical, financial, + * or otherwise) caused by the use if this library or any part of it.
+ * 7) I the author do not guarantee, warrant, or make any representations, + * either expressed or implied, regarding the use of this library or any part of + * it.
+ *
+ * Author: Paul Lamb
+ * http://www.paulscode.com
+ */ +public class SourceLWJGLOpenAL extends Source { + /** + * The source's basic Channel type-cast to a ChannelLWJGLOpenAL. + */ + private ChannelLWJGLOpenAL channelOpenAL = (ChannelLWJGLOpenAL) channel; + + /** + * OpenAL IntBuffer sound-buffer identifier for this source if it is a normal + * source. + */ + private IntBuffer myBuffer; + + /** + * FloatBuffer containing the listener's 3D coordinates. + */ + private FloatBuffer listenerPosition; + + /** + * FloatBuffer containing the source's 3D coordinates. + */ + private FloatBuffer sourcePosition; + + /** + * FloatBuffer containing the source's velocity vector. + */ + private FloatBuffer sourceVelocity; + + /** + * Constructor: Creates a new source using the specified parameters. + * + * @param listenerPosition FloatBuffer containing the listener's 3D coordinates. + * @param myBuffer OpenAL IntBuffer sound-buffer identifier to use for a + * new normal source. + * @param priority Setting this to true will prevent other sounds from + * overriding this one. + * @param toStream Setting this to true will create a streaming source. + * @param toLoop Should this source loop, or play only once. + * @param sourcename A unique identifier for this source. Two sources may + * not use the same sourcename. + * @param filenameURL Filename/URL of the sound file to play at this + * source. + * @param soundBuffer Buffer containing audio data, or null if not loaded + * yet. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, + * depending on the value of 'att'. + * @param temporary Whether or not to remove this source after it + * finishes playing. + */ + public SourceLWJGLOpenAL(FloatBuffer listenerPosition, IntBuffer myBuffer, boolean priority, boolean toStream, + boolean toLoop, String sourcename, FilenameURL filenameURL, SoundBuffer soundBuffer, float x, float y, + float z, int attModel, float distOrRoll, boolean temporary) { + super(priority, toStream, toLoop, sourcename, filenameURL, soundBuffer, x, y, z, attModel, distOrRoll, + temporary); + if (codec != null) + codec.reverseByteOrder(true); + this.listenerPosition = listenerPosition; + this.myBuffer = myBuffer; + libraryType = LibraryLWJGLOpenAL.class; + pitch = 1.0f; + resetALInformation(); + } + + /** + * Constructor: Creates a new source matching the specified source. + * + * @param listenerPosition FloatBuffer containing the listener's 3D coordinates. + * @param myBuffer OpenAL IntBuffer sound-buffer identifier to use for a + * new normal source. + * @param old Source to copy information from. + * @param soundBuffer Buffer containing audio data, or null if not loaded + * yet. + */ + public SourceLWJGLOpenAL(FloatBuffer listenerPosition, IntBuffer myBuffer, Source old, SoundBuffer soundBuffer) { + super(old, soundBuffer); + if (codec != null) + codec.reverseByteOrder(true); + this.listenerPosition = listenerPosition; + this.myBuffer = myBuffer; + libraryType = LibraryLWJGLOpenAL.class; + pitch = 1.0f; + resetALInformation(); + } + + /** + * Constructor: Creates a new streaming source that will be directly fed with + * raw audio data. + * + * @param listenerPosition FloatBuffer containing the listener's 3D coordinates. + * @param audioFormat Format that the data will be in. + * @param priority Setting this to true will prevent other sounds from + * overriding this one. + * @param sourcename A unique identifier for this source. Two sources may + * not use the same sourcename. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, + * depending on the value of 'att'. + */ + public SourceLWJGLOpenAL(FloatBuffer listenerPosition, AudioFormat audioFormat, boolean priority, String sourcename, + float x, float y, float z, int attModel, float distOrRoll) { + super(audioFormat, priority, sourcename, x, y, z, attModel, distOrRoll); + this.listenerPosition = listenerPosition; + libraryType = LibraryLWJGLOpenAL.class; + pitch = 1.0f; + resetALInformation(); + } + + /** + * Shuts the source down and removes references to all instantiated objects. + */ + @Override + public void cleanup() { + + super.cleanup(); + } + + /** + * Changes the peripheral information about the source using the specified + * parameters. + * + * @param listenerPosition FloatBuffer containing the listener's 3D coordinates. + * @param myBuffer OpenAL IntBuffer sound-buffer identifier to use for a + * new normal source. + * @param priority Setting this to true will prevent other sounds from + * overriding this one. + * @param toStream Setting this to true will create a streaming source. + * @param toLoop Should this source loop, or play only once. + * @param sourcename A unique identifier for this source. Two sources may + * not use the same sourcename. + * @param filenameURL Filename/URL of the sound file to play at this + * source. + * @param soundBuffer Buffer containing audio data, or null if not loaded + * yet. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, + * depending on the value of 'att'. + * @param temporary Whether or not to remove this source after it + * finishes playing. + */ + public void changeSource(FloatBuffer listenerPosition, IntBuffer myBuffer, boolean priority, boolean toStream, + boolean toLoop, String sourcename, FilenameURL filenameURL, SoundBuffer soundBuffer, float x, float y, + float z, int attModel, float distOrRoll, boolean temporary) { + super.changeSource(priority, toStream, toLoop, sourcename, filenameURL, soundBuffer, x, y, z, attModel, + distOrRoll, temporary); + this.listenerPosition = listenerPosition; + this.myBuffer = myBuffer; + pitch = 1.0f; + resetALInformation(); + } + + /** + * Removes the next filename from the sound sequence queue and assigns it to + * this source. This method has no effect on non-streaming sources. This method + * is used internally by SoundSystem, and it is unlikely that the user will ever + * need to use it. + * + * @return True if there was something in the queue. + */ + @Override + public boolean incrementSoundSequence() { + if (!toStream) { + errorMessage("Method 'incrementSoundSequence' may only be used " + "for streaming sources."); + return false; + } + synchronized (soundSequenceLock) { + if (soundSequenceQueue != null && soundSequenceQueue.size() > 0) { + filenameURL = soundSequenceQueue.remove(0); + if (codec != null) + codec.cleanup(); + codec = SoundSystemConfig.getCodec(filenameURL.getFilename()); + if (codec != null) { + codec.reverseByteOrder(true); + if (codec.getAudioFormat() == null) + codec.initialize(filenameURL.getURL()); + + AudioFormat audioFormat = codec.getAudioFormat(); + + if (audioFormat == null) { + errorMessage("Audio Format null in method " + "'incrementSoundSequence'"); + return false; + } + + int soundFormat = 0; + if (audioFormat.getChannels() == 1) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_MONO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_MONO16; + } else { + errorMessage("Illegal sample size in method " + "'incrementSoundSequence'"); + return false; + } + } else if (audioFormat.getChannels() == 2) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_STEREO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_STEREO16; + } else { + errorMessage("Illegal sample size in method " + "'incrementSoundSequence'"); + return false; + } + } else { + errorMessage("Audio data neither mono nor stereo in " + "method 'incrementSoundSequence'"); + return false; + } + + // Let the channel know what format and sample rate to use: + channelOpenAL.setFormat(soundFormat, (int) audioFormat.getSampleRate()); + preLoad = true; + } + return true; + } + } + return false; + } + + /** + * Called every time the listener's position or orientation changes. + */ + @Override + public void listenerMoved() { + positionChanged(); + } + + /** + * Moves the source to the specified position. + * + * @param x X coordinate to move to. + * @param y Y coordinate to move to. + * @param z Z coordinate to move to. + */ + @Override + public void setPosition(float x, float y, float z) { + super.setPosition(x, y, z); + + // Make sure OpenAL information has been created + if (sourcePosition == null) + resetALInformation(); + else + positionChanged(); + + // put the new position information into the buffer: + sourcePosition.put(0, x); + sourcePosition.put(1, y); + sourcePosition.put(2, z); + + // make sure we are assigned to a channel: + if (channel != null && channel.attachedSource == this && channelOpenAL != null + && channelOpenAL.ALSource != null) { + // move the source: + AL10.alSourcefv(channelOpenAL.ALSource.get(0), AL10.AL_POSITION, sourcePosition); + checkALError(); + } + } + + /** + * Recalculates the distance from the listner and the gain. + */ + @Override + public void positionChanged() { + calculateDistance(); + calculateGain(); + + if (channel != null && channel.attachedSource == this && channelOpenAL != null + && channelOpenAL.ALSource != null) { + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_GAIN, + (gain * sourceVolume * (float) Math.abs(fadeOutGain) * fadeInGain)); + checkALError(); + } + checkPitch(); + } + + /** + * Checks the source's pitch. + */ + private void checkPitch() { + if (channel != null && channel.attachedSource == this && LibraryLWJGLOpenAL.alPitchSupported() + && channelOpenAL != null && channelOpenAL.ALSource != null) { + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_PITCH, pitch); + checkALError(); + } + } + + /** + * Sets whether this source should loop or only play once. + * + * @param lp True or false. + */ + @Override + public void setLooping(boolean lp) { + super.setLooping(lp); + + // make sure we are assigned to a channel: + if (channel != null && channel.attachedSource == this && channelOpenAL != null + && channelOpenAL.ALSource != null) { + if (lp) + AL10.alSourcei(channelOpenAL.ALSource.get(0), AL10.AL_LOOPING, AL10.AL_TRUE); + else + AL10.alSourcei(channelOpenAL.ALSource.get(0), AL10.AL_LOOPING, AL10.AL_FALSE); + checkALError(); + } + } + + /** + * Sets this source's attenuation model. + * + * @param model Attenuation model to use. + */ + @Override + public void setAttenuation(int model) { + super.setAttenuation(model); + // make sure we are assigned to a channel: + if (channel != null && channel.attachedSource == this && channelOpenAL != null + && channelOpenAL.ALSource != null) { + // attenuation changed, so update the rolloff factor accordingly + if (model == SoundSystemConfig.ATTENUATION_ROLLOFF) + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_ROLLOFF_FACTOR, distOrRoll); + else + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_ROLLOFF_FACTOR, 0.0f); + checkALError(); + if (model == SoundSystemConfig.ATTENUATION_NONE) + AL10.alSourcei(channelOpenAL.ALSource.get(0), AL10.AL_SOURCE_RELATIVE, 1); + else + AL10.alSourcei(channelOpenAL.ALSource.get(0), AL10.AL_SOURCE_RELATIVE, 0); + checkALError(); + } + } + + /** + * Sets this source's fade distance or rolloff factor, depending on the + * attenuation model. + * + * @param dr New value for fade distance or rolloff factor. + */ + @Override + public void setDistOrRoll(float dr) { + super.setDistOrRoll(dr); + // make sure we are assigned to a channel: + if (channel != null && channel.attachedSource == this && channelOpenAL != null + && channelOpenAL.ALSource != null) { + // if we are using rolloff attenuation, then dr is a rolloff factor: + if (attModel == SoundSystemConfig.ATTENUATION_ROLLOFF) + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_ROLLOFF_FACTOR, dr); + else + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_ROLLOFF_FACTOR, 0.0f); + checkALError(); + } + } + + /** + * Sets this source's velocity, for use in Doppler effect. + * + * @param x Velocity along world x-axis. + * @param y Velocity along world y-axis. + * @param z Velocity along world z-axis. + */ + @Override + public void setVelocity(float x, float y, float z) { + super.setVelocity(x, y, z); + + sourceVelocity = BufferUtils.createFloatBuffer(3).put(new float[] { x, y, z }); + sourceVelocity.flip(); + // make sure we are assigned to a channel: + if (channel != null && channel.attachedSource == this && channelOpenAL != null + && channelOpenAL.ALSource != null) { + AL10.alSourcefv(channelOpenAL.ALSource.get(0), AL10.AL_VELOCITY, sourceVelocity); + checkALError(); + } + } + + /** + * Manually sets this source's pitch. + * + * @param value A float value ( 0.5f - 2.0f ). + */ + @Override + public void setPitch(float value) { + super.setPitch(value); + checkPitch(); + } + + /** + * Plays the source on the specified channel. + * + * @param c Channel to play on. + */ + @Override + public void play(Channel c) { + if (!active()) { + if (toLoop) + toPlay = true; + return; + } + + if (c == null) { + errorMessage("Unable to play source, because channel was null"); + return; + } + + boolean newChannel = (channel != c); + if (channel != null && channel.attachedSource != this) + newChannel = true; + + boolean wasPaused = paused(); + + super.play(c); + + channelOpenAL = (ChannelLWJGLOpenAL) channel; + + // Make sure the channel exists: + // check if we are already on this channel: + if (newChannel) { + setPosition(position.x, position.y, position.z); + checkPitch(); + + // Send the source's attributes to the channel: + if (channelOpenAL != null && channelOpenAL.ALSource != null) { + if (LibraryLWJGLOpenAL.alPitchSupported()) { + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_PITCH, pitch); + checkALError(); + } + AL10.alSourcefv(channelOpenAL.ALSource.get(0), AL10.AL_POSITION, sourcePosition); + checkALError(); + + AL10.alSourcefv(channelOpenAL.ALSource.get(0), AL10.AL_VELOCITY, sourceVelocity); + + checkALError(); + + if (attModel == SoundSystemConfig.ATTENUATION_ROLLOFF) + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_ROLLOFF_FACTOR, distOrRoll); + else + AL10.alSourcef(channelOpenAL.ALSource.get(0), AL10.AL_ROLLOFF_FACTOR, 0.0f); + checkALError(); + + if (attModel == SoundSystemConfig.ATTENUATION_NONE) + AL10.alSourcei(channelOpenAL.ALSource.get(0), AL10.AL_SOURCE_RELATIVE, 1); + else + AL10.alSourcei(channelOpenAL.ALSource.get(0), AL10.AL_SOURCE_RELATIVE, 0); + checkALError(); + + if (toLoop && (!toStream)) + AL10.alSourcei(channelOpenAL.ALSource.get(0), AL10.AL_LOOPING, AL10.AL_TRUE); + else + AL10.alSourcei(channelOpenAL.ALSource.get(0), AL10.AL_LOOPING, AL10.AL_FALSE); + checkALError(); + } + if (!toStream) { + // This is not a streaming source, so make sure there is + // a sound buffer loaded to play: + if (myBuffer == null) { + errorMessage("No sound buffer to play"); + return; + } + + channelOpenAL.attachBuffer(myBuffer); + } + } + + // See if we are already playing: + if (!playing()) { + if (toStream && !wasPaused) { + if (codec == null) { + errorMessage("Decoder null in method 'play'"); + return; + } + if (codec.getAudioFormat() == null) + codec.initialize(filenameURL.getURL()); + + AudioFormat audioFormat = codec.getAudioFormat(); + + if (audioFormat == null) { + errorMessage("Audio Format null in method 'play'"); + return; + } + + int soundFormat = 0; + if (audioFormat.getChannels() == 1) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_MONO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_MONO16; + } else { + errorMessage("Illegal sample size in method 'play'"); + return; + } + } else if (audioFormat.getChannels() == 2) { + if (audioFormat.getSampleSizeInBits() == 8) { + soundFormat = AL10.AL_FORMAT_STEREO8; + } else if (audioFormat.getSampleSizeInBits() == 16) { + soundFormat = AL10.AL_FORMAT_STEREO16; + } else { + errorMessage("Illegal sample size in method 'play'"); + return; + } + } else { + errorMessage("Audio data neither mono nor stereo in " + "method 'play'"); + return; + } + + // Let the channel know what format and sample rate to use: + channelOpenAL.setFormat(soundFormat, (int) audioFormat.getSampleRate()); + preLoad = true; + } + channel.play(); + if (pitch != 1.0f) + checkPitch(); + } + } + + /** + * Queues up the initial stream-buffers for the stream. + * + * @return False if the end of the stream was reached. + */ + @Override + public boolean preLoad() { + if (codec == null) + return false; + + codec.initialize(filenameURL.getURL()); + LinkedList preLoadBuffers = new LinkedList(); + for (int i = 0; i < SoundSystemConfig.getNumberStreamingBuffers(); i++) { + soundBuffer = codec.read(); + + if (soundBuffer == null || soundBuffer.audioData == null) + break; + + preLoadBuffers.add(soundBuffer.audioData); + } + positionChanged(); + + channel.preLoadBuffers(preLoadBuffers); + + preLoad = false; + return true; + } + + /** + * Resets all the information OpenAL uses to play this source. + */ + private void resetALInformation() { + // Create buffers for the source's position and velocity + sourcePosition = BufferUtils.createFloatBuffer(3).put(new float[] { position.x, position.y, position.z }); + sourceVelocity = BufferUtils.createFloatBuffer(3).put(new float[] { velocity.x, velocity.y, velocity.z }); + + // flip the buffers, so they can be used: + sourcePosition.flip(); + sourceVelocity.flip(); + + positionChanged(); + } + + /** + * Calculates this source's distance from the listener. + */ + private void calculateDistance() { + if (listenerPosition != null) { + // Calculate the source's distance from the listener: + double dX = position.x - listenerPosition.get(0); + double dY = position.y - listenerPosition.get(1); + double dZ = position.z - listenerPosition.get(2); + distanceFromListener = (float) Math.sqrt(dX * dX + dY * dY + dZ * dZ); + } + } + + /** + * If using linear attenuation, calculates the gain for this source based on its + * distance from the listener. + */ + private void calculateGain() { + // If using linear attenuation, calculate the source's gain: + if (attModel == SoundSystemConfig.ATTENUATION_LINEAR) { + if (distanceFromListener <= 0) { + gain = 1.0f; + } else if (distanceFromListener >= distOrRoll) { + gain = 0.0f; + } else { + gain = 1.0f - (distanceFromListener / distOrRoll); + } + if (gain > 1.0f) + gain = 1.0f; + if (gain < 0.0f) + gain = 0.0f; + } else { + gain = 1.0f; + } + } + + /** + * Checks for OpenAL errors, and prints a message if there is an error. + * + * @return True if there was an error, False if not. + */ + private boolean checkALError() { + switch (AL10.alGetError()) { + case AL10.AL_NO_ERROR: + return false; + case AL10.AL_INVALID_NAME: + errorMessage("Invalid name parameter."); + return true; + case AL10.AL_INVALID_ENUM: + errorMessage("Invalid parameter."); + return true; + case AL10.AL_INVALID_VALUE: + errorMessage("Invalid enumerated parameter value."); + return true; + case AL10.AL_INVALID_OPERATION: + errorMessage("Illegal call."); + return true; + case AL10.AL_OUT_OF_MEMORY: + errorMessage("Unable to allocate memory."); + return true; + default: + errorMessage("An unrecognized error occurred."); + return true; + } + } +} diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/vfs/FolderResourcePack.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/vfs/FolderResourcePack.java new file mode 100644 index 0000000..b084908 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/vfs/FolderResourcePack.java @@ -0,0 +1,41 @@ +package net.lax1dude.eaglercraft.v1_8.internal.vfs; + +import com.google.common.collect.Sets; +import net.minecraft.client.resources.AbstractResourcePack; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Set; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ + +public class FolderResourcePack extends AbstractResourcePack { + public FolderResourcePack(String resourcePackFileIn, String prefix) { + super(resourcePackFileIn); + } + + protected InputStream getInputStreamByName(String name) { + return new BufferedInputStream(new ByteArrayInputStream(new byte[0])); + } + + protected boolean hasResourceName(String name) { + return false; + } + + public Set getResourceDomains() { + return Sets.newHashSet(); + } +} \ No newline at end of file diff --git a/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/vfs/SYS.java b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/vfs/SYS.java new file mode 100644 index 0000000..82b2269 --- /dev/null +++ b/sources/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/vfs/SYS.java @@ -0,0 +1,41 @@ +package net.lax1dude.eaglercraft.v1_8.internal.vfs; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * Copyright (c) 2022 LAX1DUDE. All Rights Reserved. + * + * WITH THE EXCEPTION OF PATCH FILES, MINIFIED JAVASCRIPT, AND ALL FILES + * NORMALLY FOUND IN AN UNMODIFIED MINECRAFT RESOURCE PACK, YOU ARE NOT ALLOWED + * TO SHARE, DISTRIBUTE, OR REPURPOSE ANY FILE USED BY OR PRODUCED BY THE + * SOFTWARE IN THIS REPOSITORY WITHOUT PRIOR PERMISSION FROM THE PROJECT AUTHOR. + * + * NOT FOR COMMERCIAL OR MALICIOUS USE + * + * (please read the 'LICENSE' file this repo's root directory for more info) + * + */ + +public class SYS { + + public static final Object VFS = null; + + public static final void loadRemoteResourcePack(String url, String hash, Consumer cb, Consumer ast, Runnable loading) { + return; + } + + public static final boolean loadResourcePack(String name, InputStream data, String hash) { + return false; + } + + public static final List getResourcePackNames() { + return new ArrayList<>(); + } + + public static final void deleteResourcePack(String packName) { + // + } +} diff --git a/sources/main/java/com/google/common/annotations/Beta.java b/sources/main/java/com/google/common/annotations/Beta.java new file mode 100644 index 0000000..e111099 --- /dev/null +++ b/sources/main/java/com/google/common/annotations/Beta.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Signifies that a public API (public class, method or field) is subject to + * incompatible changes, or even removal, in a future release. An API bearing + * this annotation is exempt from any compatibility guarantees made by its + * containing library. Note that the presence of this annotation implies nothing + * about the quality or performance of the API in question, only the fact that + * it is not "API-frozen." + * + *

+ * It is generally safe for applications to depend on beta APIs, at the + * cost of some extra work during upgrades. However it is generally inadvisable + * for libraries (which get included on users' CLASSPATHs, outside the + * library developers' control) to do so. + * + * + * @author Kevin Bourrillion + */ +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, + ElementType.TYPE }) +@Documented +@GwtCompatible +public @interface Beta { +} diff --git a/sources/main/java/com/google/common/annotations/GwtCompatible.java b/sources/main/java/com/google/common/annotations/GwtCompatible.java new file mode 100644 index 0000000..4b14426 --- /dev/null +++ b/sources/main/java/com/google/common/annotations/GwtCompatible.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The presence of this annotation on a type indicates that the type may be used + * with the Google Web Toolkit + * (GWT). When applied to a method, the return type of the method is GWT + * compatible. It's useful to indicate that an instance created by factory + * methods has a GWT serializable type. In the following example, + * + *

+ * {@literal @}GwtCompatible
+ * class Lists {
+ *   ...
+ *   {@literal @}GwtCompatible(serializable = true)
+ *   static <E> List<E> newArrayList(E... elements) {
+ *     ...
+ *   }
+ * }
+ * 
+ *

+ * The return value of {@code Lists.newArrayList(E[])} has GWT serializable + * type. It is also useful in specifying contracts of interface methods. In the + * following example, + * + *

+ * {@literal @}GwtCompatible
+ * interface ListFactory {
+ *   ...
+ *   {@literal @}GwtCompatible(serializable = true)
+ *   <E> List<E> newArrayList(E... elements);
+ * }
+ * 
+ *

+ * The {@code newArrayList(E[])} method of all implementations of {@code + * ListFactory} is expected to return a value with a GWT serializable type. + * + *

+ * Note that a {@code GwtCompatible} type may have some {@link GwtIncompatible} + * methods. + * + * @author Charles Fry + * @author Hayward Chan + */ +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@GwtCompatible +public @interface GwtCompatible { + + /** + * When {@code true}, the annotated type or the type of the method return value + * is GWT serializable. + * + * @see + * Documentation about GWT serialization + */ + boolean serializable() default false; + + /** + * When {@code true}, the annotated type is emulated in GWT. The emulated source + * (also known as super-source) is different from the implementation used by the + * JVM. + * + * @see + * Documentation about GWT emulated source + */ + boolean emulated() default false; +} diff --git a/sources/main/java/com/google/common/annotations/GwtIncompatible.java b/sources/main/java/com/google/common/annotations/GwtIncompatible.java new file mode 100644 index 0000000..6c8c25b --- /dev/null +++ b/sources/main/java/com/google/common/annotations/GwtIncompatible.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The presence of this annotation on a method indicates that the method may + * not be used with the + * Google Web Toolkit (GWT), + * even though its type is annotated as {@link GwtCompatible} and accessible in + * GWT. They can cause GWT compilation errors or simply unexpected exceptions + * when used in GWT. + * + *

+ * Note that this annotation should only be applied to methods, fields, or inner + * classes of types which are annotated as {@link GwtCompatible}. + * + * @author Charles Fry + */ +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD }) +@Documented +@GwtCompatible +public @interface GwtIncompatible { + /** + * Describes why the annotated element is incompatible with GWT. Since this is + * generally due to a dependence on a type/method which GWT doesn't support, it + * is sufficient to simply reference the unsupported type/method. E.g. + * "Class.isInstance". + */ + String value(); +} diff --git a/sources/main/java/com/google/common/annotations/VisibleForTesting.java b/sources/main/java/com/google/common/annotations/VisibleForTesting.java new file mode 100644 index 0000000..6f867db --- /dev/null +++ b/sources/main/java/com/google/common/annotations/VisibleForTesting.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.annotations; + +/** + * Annotates a program element that exists, or is more widely visible than + * otherwise necessary, only for use in test code. + * + * @author Johannes Henkel + */ +@GwtCompatible +public @interface VisibleForTesting { +} diff --git a/sources/main/java/com/google/common/annotations/package-info.java b/sources/main/java/com/google/common/annotations/package-info.java new file mode 100644 index 0000000..2bf8b21 --- /dev/null +++ b/sources/main/java/com/google/common/annotations/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +/** + * Common annotation types. This package is a part of the open-source + * Guava libraries. + */ +package com.google.common.annotations; diff --git a/sources/main/java/com/google/common/base/Absent.java b/sources/main/java/com/google/common/base/Absent.java new file mode 100644 index 0000000..1814d06 --- /dev/null +++ b/sources/main/java/com/google/common/base/Absent.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Implementation of an {@link Optional} not containing a reference. + */ +@GwtCompatible +final class Absent extends Optional { + static final Absent INSTANCE = new Absent(); + + @SuppressWarnings("unchecked") // implementation is "fully variant" + static Optional withType() { + return (Optional) INSTANCE; + } + + private Absent() { + } + + @Override + public boolean isPresent() { + return false; + } + + @Override + public T get() { + throw new IllegalStateException("Optional.get() cannot be called on an absent value"); + } + + @Override + public T or(T defaultValue) { + return checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)"); + } + + @SuppressWarnings("unchecked") // safe covariant cast + @Override + public Optional or(Optional secondChoice) { + return (Optional) checkNotNull(secondChoice); + } + + @Override + public T or(Supplier supplier) { + return checkNotNull(supplier.get(), "use Optional.orNull() instead of a Supplier that returns null"); + } + + @Override + @Nullable + public T orNull() { + return null; + } + + @Override + public Set asSet() { + return Collections.emptySet(); + } + + @Override + public Optional transform(Function function) { + checkNotNull(function); + return Optional.absent(); + } + + @Override + public boolean equals(@Nullable Object object) { + return object == this; + } + + @Override + public int hashCode() { + return 0x598df91c; + } + + @Override + public String toString() { + return "Optional.absent()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/base/AbstractIterator.java b/sources/main/java/com/google/common/base/AbstractIterator.java new file mode 100644 index 0000000..91dad3c --- /dev/null +++ b/sources/main/java/com/google/common/base/AbstractIterator.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkState; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import com.google.common.annotations.GwtCompatible; + +/** + * Note this class is a copy of + * {@link com.google.common.collect.AbstractIterator} (for dependency reasons). + */ +@GwtCompatible +abstract class AbstractIterator implements Iterator { + private State state = State.NOT_READY; + + protected AbstractIterator() { + } + + private enum State { + READY, NOT_READY, DONE, FAILED, + } + + private T next; + + protected abstract T computeNext(); + + 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; + } + + @Override + public final void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/sources/main/java/com/google/common/base/Ascii.java b/sources/main/java/com/google/common/base/Ascii.java new file mode 100644 index 0000000..e9d85b0 --- /dev/null +++ b/sources/main/java/com/google/common/base/Ascii.java @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.CheckReturnValue; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Static methods pertaining to ASCII characters (those in the range of values + * {@code 0x00} through {@code 0x7F}), and to strings containing such + * characters. + * + *

+ * ASCII utilities also exist in other classes of this package: + *

    + * + *
  • {@link Charsets#US_ASCII} specifies the {@code Charset} of ASCII + * characters. + *
  • {@link CharMatcher#ASCII} matches ASCII characters and provides text + * processing methods which operate only on the ASCII characters of a string. + *
+ * + * @author Craig Berry + * @author Gregory Kick + * @since 7.0 + */ +@GwtCompatible +public final class Ascii { + + private Ascii() { + } + + /* The ASCII control characters, per RFC 20. */ + /** + * Null ('\0'): The all-zeros character which may serve to accomplish time fill + * and media fill. Normally used as a C string terminator. + *

+ * Although RFC 20 names this as "Null", note that it is distinct from the C/C++ + * "NULL" pointer. + * + * @since 8.0 + */ + public static final byte NUL = 0; + + /** + * Start of Heading: A communication control character used at the beginning of + * a sequence of characters which constitute a machine-sensible address or + * routing information. Such a sequence is referred to as the "heading." An STX + * character has the effect of terminating a heading. + * + * @since 8.0 + */ + public static final byte SOH = 1; + + /** + * Start of Text: A communication control character which precedes a sequence of + * characters that is to be treated as an entity and entirely transmitted + * through to the ultimate destination. Such a sequence is referred to as + * "text." STX may be used to terminate a sequence of characters started by SOH. + * + * @since 8.0 + */ + public static final byte STX = 2; + + /** + * End of Text: A communication control character used to terminate a sequence + * of characters started with STX and transmitted as an entity. + * + * @since 8.0 + */ + public static final byte ETX = 3; + + /** + * End of Transmission: A communication control character used to indicate the + * conclusion of a transmission, which may have contained one or more texts and + * any associated headings. + * + * @since 8.0 + */ + public static final byte EOT = 4; + + /** + * Enquiry: A communication control character used in data communication systems + * as a request for a response from a remote station. It may be used as a "Who + * Are You" (WRU) to obtain identification, or may be used to obtain station + * status, or both. + * + * @since 8.0 + */ + public static final byte ENQ = 5; + + /** + * Acknowledge: A communication control character transmitted by a receiver as + * an affirmative response to a sender. + * + * @since 8.0 + */ + public static final byte ACK = 6; + + /** + * Bell ('\a'): A character for use when there is a need to call for human + * attention. It may control alarm or attention devices. + * + * @since 8.0 + */ + public static final byte BEL = 7; + + /** + * Backspace ('\b'): A format effector which controls the movement of the + * printing position one printing space backward on the same printing line. + * (Applicable also to display devices.) + * + * @since 8.0 + */ + public static final byte BS = 8; + + /** + * Horizontal Tabulation ('\t'): A format effector which controls the movement + * of the printing position to the next in a series of predetermined positions + * along the printing line. (Applicable also to display devices and the skip + * function on punched cards.) + * + * @since 8.0 + */ + public static final byte HT = 9; + + /** + * Line Feed ('\n'): A format effector which controls the movement of the + * printing position to the next printing line. (Applicable also to display + * devices.) Where appropriate, this character may have the meaning "New Line" + * (NL), a format effector which controls the movement of the printing point to + * the first printing position on the next printing line. Use of this convention + * requires agreement between sender and recipient of data. + * + * @since 8.0 + */ + public static final byte LF = 10; + + /** + * Alternate name for {@link #LF}. ({@code LF} is preferred.) + * + * @since 8.0 + */ + public static final byte NL = 10; + + /** + * Vertical Tabulation ('\v'): A format effector which controls the movement of + * the printing position to the next in a series of predetermined printing + * lines. (Applicable also to display devices.) + * + * @since 8.0 + */ + public static final byte VT = 11; + + /** + * Form Feed ('\f'): A format effector which controls the movement of the + * printing position to the first pre-determined printing line on the next form + * or page. (Applicable also to display devices.) + * + * @since 8.0 + */ + public static final byte FF = 12; + + /** + * Carriage Return ('\r'): A format effector which controls the movement of the + * printing position to the first printing position on the same printing line. + * (Applicable also to display devices.) + * + * @since 8.0 + */ + public static final byte CR = 13; + + /** + * Shift Out: A control character indicating that the code combinations which + * follow shall be interpreted as outside of the character set of the standard + * code table until a Shift In character is reached. + * + * @since 8.0 + */ + public static final byte SO = 14; + + /** + * Shift In: A control character indicating that the code combinations which + * follow shall be interpreted according to the standard code table. + * + * @since 8.0 + */ + public static final byte SI = 15; + + /** + * Data Link Escape: A communication control character which will change the + * meaning of a limited number of contiguously following characters. It is used + * exclusively to provide supplementary controls in data communication networks. + * + * @since 8.0 + */ + public static final byte DLE = 16; + + /** + * Device Control 1. Characters for the control of ancillary devices associated + * with data processing or telecommunication systems, more especially switching + * devices "on" or "off." (If a single "stop" control is required to interrupt + * or turn off ancillary devices, DC4 is the preferred assignment.) + * + * @since 8.0 + */ + public static final byte DC1 = 17; // aka XON + + /** + * Transmission On: Although originally defined as DC1, this ASCII control + * character is now better known as the XON code used for software flow control + * in serial communications. The main use is restarting the transmission after + * the communication has been stopped by the XOFF control code. + * + * @since 8.0 + */ + public static final byte XON = 17; // aka DC1 + + /** + * Device Control 2. Characters for the control of ancillary devices associated + * with data processing or telecommunication systems, more especially switching + * devices "on" or "off." (If a single "stop" control is required to interrupt + * or turn off ancillary devices, DC4 is the preferred assignment.) + * + * @since 8.0 + */ + public static final byte DC2 = 18; + + /** + * Device Control 3. Characters for the control of ancillary devices associated + * with data processing or telecommunication systems, more especially switching + * devices "on" or "off." (If a single "stop" control is required to interrupt + * or turn off ancillary devices, DC4 is the preferred assignment.) + * + * @since 8.0 + */ + public static final byte DC3 = 19; // aka XOFF + + /** + * Transmission off. See {@link #XON} for explanation. + * + * @since 8.0 + */ + public static final byte XOFF = 19; // aka DC3 + + /** + * Device Control 4. Characters for the control of ancillary devices associated + * with data processing or telecommunication systems, more especially switching + * devices "on" or "off." (If a single "stop" control is required to interrupt + * or turn off ancillary devices, DC4 is the preferred assignment.) + * + * @since 8.0 + */ + public static final byte DC4 = 20; + + /** + * Negative Acknowledge: A communication control character transmitted by a + * receiver as a negative response to the sender. + * + * @since 8.0 + */ + public static final byte NAK = 21; + + /** + * Synchronous Idle: A communication control character used by a synchronous + * transmission system in the absence of any other character to provide a signal + * from which synchronism may be achieved or retained. + * + * @since 8.0 + */ + public static final byte SYN = 22; + + /** + * End of Transmission Block: A communication control character used to indicate + * the end of a block of data for communication purposes. ETB is used for + * blocking data where the block structure is not necessarily related to the + * processing format. + * + * @since 8.0 + */ + public static final byte ETB = 23; + + /** + * Cancel: A control character used to indicate that the data with which it is + * sent is in error or is to be disregarded. + * + * @since 8.0 + */ + public static final byte CAN = 24; + + /** + * End of Medium: A control character associated with the sent data which may be + * used to identify the physical end of the medium, or the end of the used, or + * wanted, portion of information recorded on a medium. (The position of this + * character does not necessarily correspond to the physical end of the medium.) + * + * @since 8.0 + */ + public static final byte EM = 25; + + /** + * Substitute: A character that may be substituted for a character which is + * determined to be invalid or in error. + * + * @since 8.0 + */ + public static final byte SUB = 26; + + /** + * Escape: A control character intended to provide code extension (supplementary + * characters) in general information interchange. The Escape character itself + * is a prefix affecting the interpretation of a limited number of contiguously + * following characters. + * + * @since 8.0 + */ + public static final byte ESC = 27; + + /** + * File Separator: These four information separators may be used within data in + * optional fashion, except that their hierarchical relationship shall be: FS is + * the most inclusive, then GS, then RS, and US is least inclusive. (The content + * and length of a File, Group, Record, or Unit are not specified.) + * + * @since 8.0 + */ + public static final byte FS = 28; + + /** + * Group Separator: These four information separators may be used within data in + * optional fashion, except that their hierarchical relationship shall be: FS is + * the most inclusive, then GS, then RS, and US is least inclusive. (The content + * and length of a File, Group, Record, or Unit are not specified.) + * + * @since 8.0 + */ + public static final byte GS = 29; + + /** + * Record Separator: These four information separators may be used within data + * in optional fashion, except that their hierarchical relationship shall be: FS + * is the most inclusive, then GS, then RS, and US is least inclusive. (The + * content and length of a File, Group, Record, or Unit are not specified.) + * + * @since 8.0 + */ + public static final byte RS = 30; + + /** + * Unit Separator: These four information separators may be used within data in + * optional fashion, except that their hierarchical relationship shall be: FS is + * the most inclusive, then GS, then RS, and US is least inclusive. (The content + * and length of a File, Group, Record, or Unit are not specified.) + * + * @since 8.0 + */ + public static final byte US = 31; + + /** + * Space: A normally non-printing graphic character used to separate words. It + * is also a format effector which controls the movement of the printing + * position, one printing position forward. (Applicable also to display + * devices.) + * + * @since 8.0 + */ + public static final byte SP = 32; + + /** + * Alternate name for {@link #SP}. + * + * @since 8.0 + */ + public static final byte SPACE = 32; + + /** + * Delete: This character is used primarily to "erase" or "obliterate" erroneous + * or unwanted characters in perforated tape. + * + * @since 8.0 + */ + public static final byte DEL = 127; + + /** + * The minimum value of an ASCII character. + * + * @since 9.0 (was type {@code int} before 12.0) + */ + public static final char MIN = 0; + + /** + * The maximum value of an ASCII character. + * + * @since 9.0 (was type {@code int} before 12.0) + */ + public static final char MAX = 127; + + /** + * Returns a copy of the input string in which all + * {@linkplain #isUpperCase(char) uppercase ASCII characters} have been + * converted to lowercase. All other characters are copied without modification. + */ + public static String toLowerCase(String string) { + int length = string.length(); + for (int i = 0; i < length; i++) { + if (isUpperCase(string.charAt(i))) { + char[] chars = string.toCharArray(); + for (; i < length; i++) { + char c = chars[i]; + if (isUpperCase(c)) { + chars[i] = (char) (c ^ 0x20); + } + } + return String.valueOf(chars); + } + } + return string; + } + + /** + * Returns a copy of the input character sequence in which all + * {@linkplain #isUpperCase(char) uppercase ASCII characters} have been + * converted to lowercase. All other characters are copied without modification. + * + * @since 14.0 + */ + public static String toLowerCase(CharSequence chars) { + if (chars instanceof String) { + return toLowerCase((String) chars); + } + int length = chars.length(); + StringBuilder builder = new StringBuilder(length); + for (int i = 0; i < length; i++) { + builder.append(toLowerCase(chars.charAt(i))); + } + return builder.toString(); + } + + /** + * If the argument is an {@linkplain #isUpperCase(char) uppercase ASCII + * character} returns the lowercase equivalent. Otherwise returns the argument. + */ + public static char toLowerCase(char c) { + return isUpperCase(c) ? (char) (c ^ 0x20) : c; + } + + /** + * Returns a copy of the input string in which all + * {@linkplain #isLowerCase(char) lowercase ASCII characters} have been + * converted to uppercase. All other characters are copied without modification. + */ + public static String toUpperCase(String string) { + int length = string.length(); + for (int i = 0; i < length; i++) { + if (isLowerCase(string.charAt(i))) { + char[] chars = string.toCharArray(); + for (; i < length; i++) { + char c = chars[i]; + if (isLowerCase(c)) { + chars[i] = (char) (c & 0x5f); + } + } + return String.valueOf(chars); + } + } + return string; + } + + /** + * Returns a copy of the input character sequence in which all + * {@linkplain #isLowerCase(char) lowercase ASCII characters} have been + * converted to uppercase. All other characters are copied without modification. + * + * @since 14.0 + */ + public static String toUpperCase(CharSequence chars) { + if (chars instanceof String) { + return toUpperCase((String) chars); + } + int length = chars.length(); + StringBuilder builder = new StringBuilder(length); + for (int i = 0; i < length; i++) { + builder.append(toUpperCase(chars.charAt(i))); + } + return builder.toString(); + } + + /** + * If the argument is a {@linkplain #isLowerCase(char) lowercase ASCII + * character} returns the uppercase equivalent. Otherwise returns the argument. + */ + public static char toUpperCase(char c) { + return isLowerCase(c) ? (char) (c & 0x5f) : c; + } + + /** + * Indicates whether {@code c} is one of the twenty-six lowercase ASCII + * alphabetic characters between {@code 'a'} and {@code 'z'} inclusive. All + * others (including non-ASCII characters) return {@code false}. + */ + public static boolean isLowerCase(char c) { + // Note: This was benchmarked against the alternate expression "(char)(c - 'a') + // < 26" (Nov '13) + // and found to perform at least as well, or better. + return (c >= 'a') && (c <= 'z'); + } + + /** + * Indicates whether {@code c} is one of the twenty-six uppercase ASCII + * alphabetic characters between {@code 'A'} and {@code 'Z'} inclusive. All + * others (including non-ASCII characters) return {@code false}. + */ + public static boolean isUpperCase(char c) { + return (c >= 'A') && (c <= 'Z'); + } + + /** + * Truncates the given character sequence to the given maximum length. If the + * length of the sequence is greater than {@code maxLength}, the returned string + * will be exactly {@code maxLength} chars in length and will end with the given + * {@code truncationIndicator}. Otherwise, the sequence will be returned as a + * string with no changes to the content. + * + *

+ * Examples: + * + *

+	 *    {@code
+	 *   Ascii.truncate("foobar", 7, "..."); // returns "foobar"
+	 *   Ascii.truncate("foobar", 5, "..."); // returns "fo..." }
+	 * 
+ * + *

+ * Note: This method may work with certain non-ASCII text but is + * not safe for use with arbitrary Unicode text. It is mostly intended for use + * with text that is known to be safe for use with it (such as all-ASCII text) + * and for simple debugging text. When using this method, consider the + * following: + * + *

    + *
  • it may split surrogate pairs
  • + *
  • it may split characters and combining characters
  • + *
  • it does not consider word boundaries
  • + *
  • if truncating for display to users, there are other considerations that + * must be taken into account
  • + *
  • the appropriate truncation indicator may be locale-dependent
  • + *
  • it is safe to use non-ASCII characters in the truncation indicator
  • + *
+ * + * + * @throws IllegalArgumentException if {@code maxLength} is less than the length + * of {@code truncationIndicator} + * @since 16.0 + */ + @Beta + @CheckReturnValue + public static String truncate(CharSequence seq, int maxLength, String truncationIndicator) { + checkNotNull(seq); + + // length to truncate the sequence to, not including the truncation indicator + int truncationLength = maxLength - truncationIndicator.length(); + + // in this worst case, this allows a maxLength equal to the length of the + // truncationIndicator, + // meaning that a string will be truncated to just the truncation indicator + // itself + checkArgument(truncationLength >= 0, "maxLength (%s) must be >= length of the truncation indicator (%s)", + maxLength, truncationIndicator.length()); + + if (seq.length() <= maxLength) { + String string = seq.toString(); + if (string.length() <= maxLength) { + return string; + } + // if the length of the toString() result was > maxLength for some reason, + // truncate that + seq = string; + } + + return new StringBuilder(maxLength).append(seq, 0, truncationLength).append(truncationIndicator).toString(); + } + + /** + * Indicates whether the contents of the given character sequences {@code s1} + * and {@code s2} are equal, ignoring the case of any ASCII alphabetic + * characters between {@code 'a'} and {@code 'z'} or {@code 'A'} and {@code 'Z'} + * inclusive. + * + *

+ * This method is significantly faster than {@link String#equalsIgnoreCase} and + * should be used in preference if at least one of the parameters is known to + * contain only ASCII characters. + * + *

+ * Note however that this method does not always behave identically to + * expressions such as: + *

    + *
  • {@code string.toUpperCase().equals("UPPER CASE ASCII")} + *
  • {@code string.toLowerCase().equals("lower case ascii")} + *
+ *

+ * due to case-folding of some non-ASCII characters (which does not occur in + * {@link String#equalsIgnoreCase}). However in almost all cases that ASCII + * strings are used, the author probably wanted the behavior provided by this + * method rather than the subtle and sometimes surprising behavior of + * {@code toUpperCase()} and {@code toLowerCase()}. + * + * @since 16.0 + */ + @Beta + public static boolean equalsIgnoreCase(CharSequence s1, CharSequence s2) { + // Calling length() is the null pointer check (so do it before we can exit + // early). + int length = s1.length(); + if (s1 == s2) { + return true; + } + if (length != s2.length()) { + return false; + } + for (int i = 0; i < length; i++) { + char c1 = s1.charAt(i); + char c2 = s2.charAt(i); + if (c1 == c2) { + continue; + } + int alphaIndex = getAlphaIndex(c1); + // This was also benchmarked using '&' to avoid branching (but always evaluate + // the rhs), + // however this showed no obvious improvement. + if (alphaIndex < 26 && alphaIndex == getAlphaIndex(c2)) { + continue; + } + return false; + } + return true; + } + + /** + * Returns the non-negative index value of the alpha character {@code c}, + * regardless of case. Ie, 'a'/'A' returns 0 and 'z'/'Z' returns 25. Non-alpha + * characters return a value of 26 or greater. + */ + private static int getAlphaIndex(char c) { + // Fold upper-case ASCII to lower-case and make zero-indexed and unsigned (by + // casting to char). + return (char) ((c | 0x20) - 'a'); + } +} diff --git a/sources/main/java/com/google/common/base/CaseFormat.java b/sources/main/java/com/google/common/base/CaseFormat.java new file mode 100644 index 0000000..8f7eeb7 --- /dev/null +++ b/sources/main/java/com/google/common/base/CaseFormat.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2006 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Utility class for converting between various ASCII case formats. Behavior is + * undefined for non-ASCII input. + * + * @author Mike Bostock + * @since 1.0 + */ +@GwtCompatible +public enum CaseFormat { + /** + * Hyphenated variable naming convention, e.g., "lower-hyphen". + */ + LOWER_HYPHEN(CharMatcher.is('-'), "-") { + @Override + String normalizeWord(String word) { + return Ascii.toLowerCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_UNDERSCORE) { + return s.replace('-', '_'); + } + if (format == UPPER_UNDERSCORE) { + return Ascii.toUpperCase(s.replace('-', '_')); + } + return super.convert(format, s); + } + }, + + /** + * C++ variable naming convention, e.g., "lower_underscore". + */ + LOWER_UNDERSCORE(CharMatcher.is('_'), "_") { + @Override + String normalizeWord(String word) { + return Ascii.toLowerCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_HYPHEN) { + return s.replace('_', '-'); + } + if (format == UPPER_UNDERSCORE) { + return Ascii.toUpperCase(s); + } + return super.convert(format, s); + } + }, + + /** + * Java variable naming convention, e.g., "lowerCamel". + */ + LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { + @Override + String normalizeWord(String word) { + return firstCharOnlyToUpper(word); + } + }, + + /** + * Java and C++ class naming convention, e.g., "UpperCamel". + */ + UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { + @Override + String normalizeWord(String word) { + return firstCharOnlyToUpper(word); + } + }, + + /** + * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE". + */ + UPPER_UNDERSCORE(CharMatcher.is('_'), "_") { + @Override + String normalizeWord(String word) { + return Ascii.toUpperCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_HYPHEN) { + return Ascii.toLowerCase(s.replace('_', '-')); + } + if (format == LOWER_UNDERSCORE) { + return Ascii.toLowerCase(s); + } + return super.convert(format, s); + } + }; + + private final CharMatcher wordBoundary; + private final String wordSeparator; + + CaseFormat(CharMatcher wordBoundary, String wordSeparator) { + this.wordBoundary = wordBoundary; + this.wordSeparator = wordSeparator; + } + + /** + * Converts the specified {@code String str} from this format to the specified + * {@code format}. A "best effort" approach is taken; if {@code str} does not + * conform to the assumed format, then the behavior of this method is undefined + * but we make a reasonable effort at converting anyway. + */ + public final String to(CaseFormat format, String str) { + checkNotNull(format); + checkNotNull(str); + return (format == this) ? str : convert(format, str); + } + + /** + * Enum values can override for performance reasons. + */ + String convert(CaseFormat format, String s) { + // deal with camel conversion + StringBuilder out = null; + int i = 0; + int j = -1; + while ((j = wordBoundary.indexIn(s, ++j)) != -1) { + if (i == 0) { + // include some extra space for separators + out = new StringBuilder(s.length() + 4 * wordSeparator.length()); + out.append(format.normalizeFirstWord(s.substring(i, j))); + } else { + out.append(format.normalizeWord(s.substring(i, j))); + } + out.append(format.wordSeparator); + i = j + wordSeparator.length(); + } + return (i == 0) ? format.normalizeFirstWord(s) : out.append(format.normalizeWord(s.substring(i))).toString(); + } + + /** + * Returns a {@code Converter} that converts strings from this format to + * {@code targetFormat}. + * + * @since 16.0 + */ + @Beta + public Converter converterTo(CaseFormat targetFormat) { + return new StringConverter(this, targetFormat); + } + + private static final class StringConverter extends Converter implements Serializable { + + private final CaseFormat sourceFormat; + private final CaseFormat targetFormat; + + StringConverter(CaseFormat sourceFormat, CaseFormat targetFormat) { + this.sourceFormat = checkNotNull(sourceFormat); + this.targetFormat = checkNotNull(targetFormat); + } + + @Override + protected String doForward(String s) { + // TODO(kevinb): remove null boilerplate (convert() will do it automatically) + return s == null ? null : sourceFormat.to(targetFormat, s); + } + + @Override + protected String doBackward(String s) { + // TODO(kevinb): remove null boilerplate (convert() will do it automatically) + return s == null ? null : targetFormat.to(sourceFormat, s); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof StringConverter) { + StringConverter that = (StringConverter) object; + return sourceFormat.equals(that.sourceFormat) && targetFormat.equals(that.targetFormat); + } + return false; + } + + @Override + public int hashCode() { + return sourceFormat.hashCode() ^ targetFormat.hashCode(); + } + + @Override + public String toString() { + return sourceFormat + ".converterTo(" + targetFormat + ")"; + } + + private static final long serialVersionUID = 0L; + } + + abstract String normalizeWord(String word); + + private String normalizeFirstWord(String word) { + return (this == LOWER_CAMEL) ? Ascii.toLowerCase(word) : normalizeWord(word); + } + + private static String firstCharOnlyToUpper(String word) { + return (word.isEmpty()) ? word + : new StringBuilder(word.length()).append(Ascii.toUpperCase(word.charAt(0))) + .append(Ascii.toLowerCase(word.substring(1))).toString(); + } +} diff --git a/sources/main/java/com/google/common/base/CharMatcher.java b/sources/main/java/com/google/common/base/CharMatcher.java new file mode 100644 index 0000000..e84449d --- /dev/null +++ b/sources/main/java/com/google/common/base/CharMatcher.java @@ -0,0 +1,1509 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Arrays; +import java.util.BitSet; + +import javax.annotation.CheckReturnValue; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * Determines a true or false value for any Java {@code char} value, just as + * {@link Predicate} does for any {@link Object}. Also offers basic text + * processing methods based on this function. Implementations are strongly + * encouraged to be side-effect-free and immutable. + * + *

+ * Throughout the documentation of this class, the phrase "matching character" + * is used to mean "any character {@code c} for which {@code this.matches(c)} + * returns {@code true}". + * + *

+ * Note: This class deals only with {@code char} values; it does not + * understand supplementary Unicode code points in the range {@code 0x10000} to + * {@code 0x10FFFF}. Such logical characters are encoded into a {@code String} + * using surrogate pairs, and a {@code CharMatcher} treats these just as two + * separate characters. + * + *

+ * Example usages: + * + *

+ *   String trimmed = {@link #WHITESPACE WHITESPACE}.{@link #trimFrom trimFrom}(userInput);
+ *   if ({@link #ASCII ASCII}.{@link #matchesAllOf matchesAllOf}(s)) { ... }
+ * 
+ * + *

+ * See the Guava User Guide article on + * {@code CharMatcher}. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@Beta // Possibly change from chars to code points; decide constants vs. methods +@GwtCompatible(emulated = true) +public abstract class CharMatcher implements Predicate { + + // Constants + /** + * Determines whether a character is a breaking whitespace (that is, a + * whitespace which can be interpreted as a break between words for formatting + * purposes). See {@link #WHITESPACE} for a discussion of that term. + * + * @since 2.0 + */ + public static final CharMatcher BREAKING_WHITESPACE = new CharMatcher() { + @Override + public boolean matches(char c) { + switch (c) { + case '\t': + case '\n': + case '\013': + case '\f': + case '\r': + case ' ': + case '\u0085': + case '\u1680': + case '\u2028': + case '\u2029': + case '\u205f': + case '\u3000': + return true; + case '\u2007': + return false; + default: + return c >= '\u2000' && c <= '\u200a'; + } + } + + @Override + public String toString() { + return "CharMatcher.BREAKING_WHITESPACE"; + } + }; + + /** + * Determines whether a character is ASCII, meaning that its code point is less + * than 128. + */ + public static final CharMatcher ASCII = inRange('\0', '\u007f', "CharMatcher.ASCII"); + + private static class RangesMatcher extends CharMatcher { + private final char[] rangeStarts; + private final char[] rangeEnds; + + RangesMatcher(String description, char[] rangeStarts, char[] rangeEnds) { + super(description); + this.rangeStarts = rangeStarts; + this.rangeEnds = rangeEnds; + checkArgument(rangeStarts.length == rangeEnds.length); + for (int i = 0; i < rangeStarts.length; i++) { + checkArgument(rangeStarts[i] <= rangeEnds[i]); + if (i + 1 < rangeStarts.length) { + checkArgument(rangeEnds[i] < rangeStarts[i + 1]); + } + } + } + + @Override + public boolean matches(char c) { + int index = Arrays.binarySearch(rangeStarts, c); + if (index >= 0) { + return true; + } else { + index = ~index - 1; + return index >= 0 && c <= rangeEnds[index]; + } + } + } + + // Must be in ascending order. + private static final String ZEROES = "0\u0660\u06f0\u07c0\u0966\u09e6\u0a66\u0ae6\u0b66\u0be6" + + "\u0c66\u0ce6\u0d66\u0e50\u0ed0\u0f20\u1040\u1090\u17e0\u1810\u1946\u19d0\u1b50\u1bb0" + + "\u1c40\u1c50\ua620\ua8d0\ua900\uaa50\uff10"; + + private static final String NINES; + static { + StringBuilder builder = new StringBuilder(ZEROES.length()); + for (int i = 0; i < ZEROES.length(); i++) { + builder.append((char) (ZEROES.charAt(i) + 9)); + } + NINES = builder.toString(); + } + + /** + * Determines whether a character is a digit according to Unicode. + * If you only care to match ASCII digits, you can use + * {@code inRange('0', '9')}. + */ + public static final CharMatcher DIGIT = new RangesMatcher("CharMatcher.DIGIT", ZEROES.toCharArray(), + NINES.toCharArray()); + + /** + * Determines whether a character is a digit according to + * {@linkplain Character#isDigit(char) Java's definition}. If you only care to + * match ASCII digits, you can use {@code + * inRange('0', '9')}. + */ + public static final CharMatcher JAVA_DIGIT = new CharMatcher("CharMatcher.JAVA_DIGIT") { + @Override + public boolean matches(char c) { + return Character.isDigit(c); + } + }; + + /** + * Determines whether a character is a letter according to + * {@linkplain Character#isLetter(char) Java's definition}. If you only care to + * match letters of the Latin alphabet, you can use {@code + * inRange('a', 'z').or(inRange('A', 'Z'))}. + */ + public static final CharMatcher JAVA_LETTER = new CharMatcher("CharMatcher.JAVA_LETTER") { + @Override + public boolean matches(char c) { + return Character.isLetter(c); + } + }; + + /** + * Determines whether a character is a letter or digit according to + * {@linkplain Character#isLetterOrDigit(char) Java's definition}. + */ + public static final CharMatcher JAVA_LETTER_OR_DIGIT = new CharMatcher("CharMatcher.JAVA_LETTER_OR_DIGIT") { + @Override + public boolean matches(char c) { + return Character.isLetterOrDigit(c); + } + }; + + /** + * Determines whether a character is upper case according to + * {@linkplain Character#isUpperCase(char) Java's definition}. + */ + public static final CharMatcher JAVA_UPPER_CASE = new CharMatcher("CharMatcher.JAVA_UPPER_CASE") { + @Override + public boolean matches(char c) { + return Character.isUpperCase(c); + } + }; + + /** + * Determines whether a character is lower case according to + * {@linkplain Character#isLowerCase(char) Java's definition}. + */ + public static final CharMatcher JAVA_LOWER_CASE = new CharMatcher("CharMatcher.JAVA_LOWER_CASE") { + @Override + public boolean matches(char c) { + return Character.isLowerCase(c); + } + }; + + /** + * Determines whether a character is an ISO control character as specified by + * {@link Character#isISOControl(char)}. + */ + public static final CharMatcher JAVA_ISO_CONTROL = inRange('\u0000', '\u001f').or(inRange('\u007f', '\u009f')) + .withToString("CharMatcher.JAVA_ISO_CONTROL"); + + /** + * Determines whether a character is invisible; that is, if its Unicode category + * is any of SPACE_SEPARATOR, LINE_SEPARATOR, PARAGRAPH_SEPARATOR, CONTROL, + * FORMAT, SURROGATE, and PRIVATE_USE according to ICU4J. + */ + public static final CharMatcher INVISIBLE = new RangesMatcher("CharMatcher.INVISIBLE", + ("\u0000\u007f\u00ad\u0600\u061c\u06dd\u070f\u1680\u180e\u2000\u2028\u205f\u2066\u2067\u2068" + + "\u2069\u206a\u3000\ud800\ufeff\ufff9\ufffa").toCharArray(), + ("\u0020\u00a0\u00ad\u0604\u061c\u06dd\u070f\u1680\u180e\u200f\u202f\u2064\u2066\u2067\u2068" + + "\u2069\u206f\u3000\uf8ff\ufeff\ufff9\ufffb").toCharArray()); + + private static String showCharacter(char c) { + String hex = "0123456789ABCDEF"; + char[] tmp = { '\\', 'u', '\0', '\0', '\0', '\0' }; + for (int i = 0; i < 4; i++) { + tmp[5 - i] = hex.charAt(c & 0xF); + c >>= 4; + } + return String.copyValueOf(tmp); + + } + + /** + * Determines whether a character is single-width (not double-width). When in + * doubt, this matcher errs on the side of returning {@code false} (that is, it + * tends to assume a character is double-width). + * + *

+ * Note: as the reference file evolves, we will modify this constant to + * keep it up to date. + */ + public static final CharMatcher SINGLE_WIDTH = new RangesMatcher("CharMatcher.SINGLE_WIDTH", + "\u0000\u05be\u05d0\u05f3\u0600\u0750\u0e00\u1e00\u2100\ufb50\ufe70\uff61".toCharArray(), + "\u04f9\u05be\u05ea\u05f4\u06ff\u077f\u0e7f\u20af\u213a\ufdff\ufeff\uffdc".toCharArray()); + + /** Matches any character. */ + public static final CharMatcher ANY = new FastMatcher("CharMatcher.ANY") { + @Override + public boolean matches(char c) { + return true; + } + + @Override + public int indexIn(CharSequence sequence) { + return (sequence.length() == 0) ? -1 : 0; + } + + @Override + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + Preconditions.checkPositionIndex(start, length); + return (start == length) ? -1 : start; + } + + @Override + public int lastIndexIn(CharSequence sequence) { + return sequence.length() - 1; + } + + @Override + public boolean matchesAllOf(CharSequence sequence) { + checkNotNull(sequence); + return true; + } + + @Override + public boolean matchesNoneOf(CharSequence sequence) { + return sequence.length() == 0; + } + + @Override + public String removeFrom(CharSequence sequence) { + checkNotNull(sequence); + return ""; + } + + @Override + public String replaceFrom(CharSequence sequence, char replacement) { + char[] array = new char[sequence.length()]; + Arrays.fill(array, replacement); + return new String(array); + } + + @Override + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + StringBuilder retval = new StringBuilder(sequence.length() * replacement.length()); + for (int i = 0; i < sequence.length(); i++) { + retval.append(replacement); + } + return retval.toString(); + } + + @Override + public String collapseFrom(CharSequence sequence, char replacement) { + return (sequence.length() == 0) ? "" : String.valueOf(replacement); + } + + @Override + public String trimFrom(CharSequence sequence) { + checkNotNull(sequence); + return ""; + } + + @Override + public int countIn(CharSequence sequence) { + return sequence.length(); + } + + @Override + public CharMatcher and(CharMatcher other) { + return checkNotNull(other); + } + + @Override + public CharMatcher or(CharMatcher other) { + checkNotNull(other); + return this; + } + + @Override + public CharMatcher negate() { + return NONE; + } + }; + + /** Matches no characters. */ + public static final CharMatcher NONE = new FastMatcher("CharMatcher.NONE") { + @Override + public boolean matches(char c) { + return false; + } + + @Override + public int indexIn(CharSequence sequence) { + checkNotNull(sequence); + return -1; + } + + @Override + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + Preconditions.checkPositionIndex(start, length); + return -1; + } + + @Override + public int lastIndexIn(CharSequence sequence) { + checkNotNull(sequence); + return -1; + } + + @Override + public boolean matchesAllOf(CharSequence sequence) { + return sequence.length() == 0; + } + + @Override + public boolean matchesNoneOf(CharSequence sequence) { + checkNotNull(sequence); + return true; + } + + @Override + public String removeFrom(CharSequence sequence) { + return sequence.toString(); + } + + @Override + public String replaceFrom(CharSequence sequence, char replacement) { + return sequence.toString(); + } + + @Override + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + checkNotNull(replacement); + return sequence.toString(); + } + + @Override + public String collapseFrom(CharSequence sequence, char replacement) { + return sequence.toString(); + } + + @Override + public String trimFrom(CharSequence sequence) { + return sequence.toString(); + } + + @Override + public String trimLeadingFrom(CharSequence sequence) { + return sequence.toString(); + } + + @Override + public String trimTrailingFrom(CharSequence sequence) { + return sequence.toString(); + } + + @Override + public int countIn(CharSequence sequence) { + checkNotNull(sequence); + return 0; + } + + @Override + public CharMatcher and(CharMatcher other) { + checkNotNull(other); + return this; + } + + @Override + public CharMatcher or(CharMatcher other) { + return checkNotNull(other); + } + + @Override + public CharMatcher negate() { + return ANY; + } + }; + + // Static factories + + /** + * Returns a {@code char} matcher that matches only one specified character. + */ + public static CharMatcher is(final char match) { + String description = "CharMatcher.is('" + showCharacter(match) + "')"; + return new FastMatcher(description) { + @Override + public boolean matches(char c) { + return c == match; + } + + @Override + public String replaceFrom(CharSequence sequence, char replacement) { + return sequence.toString().replace(match, replacement); + } + + @Override + public CharMatcher and(CharMatcher other) { + return other.matches(match) ? this : NONE; + } + + @Override + public CharMatcher or(CharMatcher other) { + return other.matches(match) ? other : super.or(other); + } + + @Override + public CharMatcher negate() { + return isNot(match); + } + + @GwtIncompatible("java.util.BitSet") + @Override + void setBits(BitSet table) { + table.set(match); + } + }; + } + + /** + * Returns a {@code char} matcher that matches any character except the one + * specified. + * + *

+ * To negate another {@code CharMatcher}, use {@link #negate()}. + */ + public static CharMatcher isNot(final char match) { + String description = "CharMatcher.isNot('" + showCharacter(match) + "')"; + return new FastMatcher(description) { + @Override + public boolean matches(char c) { + return c != match; + } + + @Override + public CharMatcher and(CharMatcher other) { + return other.matches(match) ? super.and(other) : other; + } + + @Override + public CharMatcher or(CharMatcher other) { + return other.matches(match) ? ANY : this; + } + + @GwtIncompatible("java.util.BitSet") + @Override + void setBits(BitSet table) { + table.set(0, match); + table.set(match + 1, Character.MAX_VALUE + 1); + } + + @Override + public CharMatcher negate() { + return is(match); + } + }; + } + + /** + * Returns a {@code char} matcher that matches any character present in the + * given character sequence. + */ + public static CharMatcher anyOf(final CharSequence sequence) { + switch (sequence.length()) { + case 0: + return NONE; + case 1: + return is(sequence.charAt(0)); + case 2: + return isEither(sequence.charAt(0), sequence.charAt(1)); + default: + // continue below to handle the general case + } + // TODO(user): is it potentially worth just going ahead and building a + // precomputed matcher? + final char[] chars = sequence.toString().toCharArray(); + Arrays.sort(chars); + StringBuilder description = new StringBuilder("CharMatcher.anyOf(\""); + for (char c : chars) { + description.append(showCharacter(c)); + } + description.append("\")"); + return new CharMatcher(description.toString()) { + @Override + public boolean matches(char c) { + return Arrays.binarySearch(chars, c) >= 0; + } + + @Override + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + for (char c : chars) { + table.set(c); + } + } + }; + } + + private static CharMatcher isEither(final char match1, final char match2) { + String description = "CharMatcher.anyOf(\"" + showCharacter(match1) + showCharacter(match2) + "\")"; + return new FastMatcher(description) { + @Override + public boolean matches(char c) { + return c == match1 || c == match2; + } + + @GwtIncompatible("java.util.BitSet") + @Override + void setBits(BitSet table) { + table.set(match1); + table.set(match2); + } + }; + } + + /** + * Returns a {@code char} matcher that matches any character not present in the + * given character sequence. + */ + public static CharMatcher noneOf(CharSequence sequence) { + return anyOf(sequence).negate(); + } + + /** + * Returns a {@code char} matcher that matches any character in a given range + * (both endpoints are inclusive). For example, to match any lowercase letter of + * the English alphabet, use {@code + * CharMatcher.inRange('a', 'z')}. + * + * @throws IllegalArgumentException if {@code endInclusive < startInclusive} + */ + public static CharMatcher inRange(final char startInclusive, final char endInclusive) { + checkArgument(endInclusive >= startInclusive); + String description = "CharMatcher.inRange('" + showCharacter(startInclusive) + "', '" + + showCharacter(endInclusive) + "')"; + return inRange(startInclusive, endInclusive, description); + } + + static CharMatcher inRange(final char startInclusive, final char endInclusive, String description) { + return new FastMatcher(description) { + @Override + public boolean matches(char c) { + return startInclusive <= c && c <= endInclusive; + } + + @GwtIncompatible("java.util.BitSet") + @Override + void setBits(BitSet table) { + table.set(startInclusive, endInclusive + 1); + } + }; + } + + /** + * Returns a matcher with identical behavior to the given + * {@link Character}-based predicate, but which operates on primitive + * {@code char} instances instead. + */ + public static CharMatcher forPredicate(final Predicate predicate) { + checkNotNull(predicate); + if (predicate instanceof CharMatcher) { + return (CharMatcher) predicate; + } + String description = "CharMatcher.forPredicate(" + predicate + ")"; + return new CharMatcher(description) { + @Override + public boolean matches(char c) { + return predicate.apply(c); + } + + @Override + public boolean apply(Character character) { + return predicate.apply(checkNotNull(character)); + } + }; + } + + // State + final String description; + + // Constructors + + /** + * Sets the {@code toString()} from the given description. + */ + CharMatcher(String description) { + this.description = description; + } + + /** + * Constructor for use by subclasses. When subclassing, you may want to override + * {@code toString()} to provide a useful description. + */ + protected CharMatcher() { + description = super.toString(); + } + + // Abstract methods + + /** Determines a true or false value for the given character. */ + public abstract boolean matches(char c); + + // Non-static factories + + /** + * Returns a matcher that matches any character not matched by this matcher. + */ + public CharMatcher negate() { + return new NegatedMatcher(this); + } + + private static class NegatedMatcher extends CharMatcher { + final CharMatcher original; + + NegatedMatcher(String toString, CharMatcher original) { + super(toString); + this.original = original; + } + + NegatedMatcher(CharMatcher original) { + this(original + ".negate()", original); + } + + @Override + public boolean matches(char c) { + return !original.matches(c); + } + + @Override + public boolean matchesAllOf(CharSequence sequence) { + return original.matchesNoneOf(sequence); + } + + @Override + public boolean matchesNoneOf(CharSequence sequence) { + return original.matchesAllOf(sequence); + } + + @Override + public int countIn(CharSequence sequence) { + return sequence.length() - original.countIn(sequence); + } + + @GwtIncompatible("java.util.BitSet") + @Override + void setBits(BitSet table) { + BitSet tmp = new BitSet(); + original.setBits(tmp); + tmp.flip(Character.MIN_VALUE, Character.MAX_VALUE + 1); + table.or(tmp); + } + + @Override + public CharMatcher negate() { + return original; + } + + @Override + CharMatcher withToString(String description) { + return new NegatedMatcher(description, original); + } + } + + /** + * Returns a matcher that matches any character matched by both this matcher and + * {@code other}. + */ + public CharMatcher and(CharMatcher other) { + return new And(this, checkNotNull(other)); + } + + private static class And extends CharMatcher { + final CharMatcher first; + final CharMatcher second; + + And(CharMatcher a, CharMatcher b) { + this(a, b, "CharMatcher.and(" + a + ", " + b + ")"); + } + + And(CharMatcher a, CharMatcher b, String description) { + super(description); + first = checkNotNull(a); + second = checkNotNull(b); + } + + @Override + public boolean matches(char c) { + return first.matches(c) && second.matches(c); + } + + @GwtIncompatible("java.util.BitSet") + @Override + void setBits(BitSet table) { + BitSet tmp1 = new BitSet(); + first.setBits(tmp1); + BitSet tmp2 = new BitSet(); + second.setBits(tmp2); + tmp1.and(tmp2); + table.or(tmp1); + } + + @Override + CharMatcher withToString(String description) { + return new And(first, second, description); + } + } + + /** + * Returns a matcher that matches any character matched by either this matcher + * or {@code other}. + */ + public CharMatcher or(CharMatcher other) { + return new Or(this, checkNotNull(other)); + } + + private static class Or extends CharMatcher { + final CharMatcher first; + final CharMatcher second; + + Or(CharMatcher a, CharMatcher b, String description) { + super(description); + first = checkNotNull(a); + second = checkNotNull(b); + } + + Or(CharMatcher a, CharMatcher b) { + this(a, b, "CharMatcher.or(" + a + ", " + b + ")"); + } + + @GwtIncompatible("java.util.BitSet") + @Override + void setBits(BitSet table) { + first.setBits(table); + second.setBits(table); + } + + @Override + public boolean matches(char c) { + return first.matches(c) || second.matches(c); + } + + @Override + CharMatcher withToString(String description) { + return new Or(first, second, description); + } + } + + /** + * Returns a {@code char} matcher functionally equivalent to this one, but which + * may be faster to query than the original; your mileage may vary. + * Precomputation takes time and is likely to be worthwhile only if the + * precomputed matcher is queried many thousands of times. + * + *

+ * This method has no effect (returns {@code this}) when called in GWT: it's + * unclear whether a precomputed matcher is faster, but it certainly consumes + * more memory, which doesn't seem like a worthwhile tradeoff in a browser. + */ + public CharMatcher precomputed() { + return Platform.precomputeCharMatcher(this); + } + + /** + * Subclasses should provide a new CharMatcher with the same characteristics as + * {@code this}, but with their {@code toString} method overridden with the new + * description. + * + *

+ * This is unsupported by default. + */ + CharMatcher withToString(String description) { + throw new UnsupportedOperationException(); + } + + private static final int DISTINCT_CHARS = Character.MAX_VALUE - Character.MIN_VALUE + 1; + + /** + * This is the actual implementation of {@link #precomputed}, but we bounce + * calls through a method on {@link Platform} so that we can have different + * behavior in GWT. + * + *

+ * This implementation tries to be smart in a number of ways. It recognizes + * cases where the negation is cheaper to precompute than the matcher itself; it + * tries to build small hash tables for matchers that only match a few + * characters, and so on. In the worst-case scenario, it constructs an + * eight-kilobyte bit array and queries that. In many situations this produces a + * matcher which is faster to query than the original. + */ + @GwtIncompatible("java.util.BitSet") + CharMatcher precomputedInternal() { + final BitSet table = new BitSet(); + setBits(table); + int totalCharacters = table.cardinality(); + if (totalCharacters * 2 <= DISTINCT_CHARS) { + return precomputedPositive(totalCharacters, table, description); + } else { + // TODO(user): is it worth it to worry about the last character of large + // matchers? + table.flip(Character.MIN_VALUE, Character.MAX_VALUE + 1); + int negatedCharacters = DISTINCT_CHARS - totalCharacters; + String suffix = ".negate()"; + String negatedDescription = description.endsWith(suffix) + ? description.substring(0, description.length() - suffix.length()) + : description + suffix; + return new NegatedFastMatcher(toString(), + precomputedPositive(negatedCharacters, table, negatedDescription)); + } + } + + /** + * A matcher for which precomputation will not yield any significant benefit. + */ + abstract static class FastMatcher extends CharMatcher { + FastMatcher() { + super(); + } + + FastMatcher(String description) { + super(description); + } + + @Override + public final CharMatcher precomputed() { + return this; + } + + @Override + public CharMatcher negate() { + return new NegatedFastMatcher(this); + } + } + + static final class NegatedFastMatcher extends NegatedMatcher { + NegatedFastMatcher(CharMatcher original) { + super(original); + } + + NegatedFastMatcher(String toString, CharMatcher original) { + super(toString, original); + } + + @Override + public final CharMatcher precomputed() { + return this; + } + + @Override + CharMatcher withToString(String description) { + return new NegatedFastMatcher(description, original); + } + } + + /** + * Helper method for {@link #precomputedInternal} that doesn't test if the + * negation is cheaper. + */ + @GwtIncompatible("java.util.BitSet") + private static CharMatcher precomputedPositive(int totalCharacters, BitSet table, String description) { + switch (totalCharacters) { + case 0: + return NONE; + case 1: + return is((char) table.nextSetBit(0)); + case 2: + char c1 = (char) table.nextSetBit(0); + char c2 = (char) table.nextSetBit(c1 + 1); + return isEither(c1, c2); + default: + return isSmall(totalCharacters, table.length()) ? SmallCharMatcher.from(table, description) + : new BitSetMatcher(table, description); + } + } + + @GwtIncompatible("SmallCharMatcher") + private static boolean isSmall(int totalCharacters, int tableLength) { + return totalCharacters <= SmallCharMatcher.MAX_SIZE && tableLength > (totalCharacters * 4 * Character.SIZE); + // err on the side of BitSetMatcher + } + + @GwtIncompatible("java.util.BitSet") + private static class BitSetMatcher extends FastMatcher { + private final BitSet table; + + private BitSetMatcher(BitSet table, String description) { + super(description); + if (table.length() + Long.SIZE < table.size()) { + table = (BitSet) table.clone(); + // If only we could actually call BitSet.trimToSize() ourselves... + } + this.table = table; + } + + @Override + public boolean matches(char c) { + return table.get(c); + } + + @Override + void setBits(BitSet bitSet) { + bitSet.or(table); + } + } + + /** + * Sets bits in {@code table} matched by this matcher. + */ + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + for (int c = Character.MAX_VALUE; c >= Character.MIN_VALUE; c--) { + if (matches((char) c)) { + table.set(c); + } + } + } + + // Text processing routines + + /** + * Returns {@code true} if a character sequence contains at least one matching + * character. Equivalent to {@code !matchesNoneOf(sequence)}. + * + *

+ * The default implementation iterates over the sequence, invoking + * {@link #matches} for each character, until this returns {@code true} or the + * end is reached. + * + * @param sequence the character sequence to examine, possibly empty + * @return {@code true} if this matcher matches at least one character in the + * sequence + * @since 8.0 + */ + public boolean matchesAnyOf(CharSequence sequence) { + return !matchesNoneOf(sequence); + } + + /** + * Returns {@code true} if a character sequence contains only matching + * characters. + * + *

+ * The default implementation iterates over the sequence, invoking + * {@link #matches} for each character, until this returns {@code false} or the + * end is reached. + * + * @param sequence the character sequence to examine, possibly empty + * @return {@code true} if this matcher matches every character in the sequence, + * including when the sequence is empty + */ + public boolean matchesAllOf(CharSequence sequence) { + for (int i = sequence.length() - 1; i >= 0; i--) { + if (!matches(sequence.charAt(i))) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} if a character sequence contains no matching characters. + * Equivalent to {@code !matchesAnyOf(sequence)}. + * + *

+ * The default implementation iterates over the sequence, invoking + * {@link #matches} for each character, until this returns {@code false} or the + * end is reached. + * + * @param sequence the character sequence to examine, possibly empty + * @return {@code true} if this matcher matches every character in the sequence, + * including when the sequence is empty + */ + public boolean matchesNoneOf(CharSequence sequence) { + return indexIn(sequence) == -1; + } + + /** + * Returns the index of the first matching character in a character sequence, or + * {@code -1} if no matching character is present. + * + *

+ * The default implementation iterates over the sequence in forward order + * calling {@link #matches} for each character. + * + * @param sequence the character sequence to examine from the beginning + * @return an index, or {@code -1} if no character matches + */ + public int indexIn(CharSequence sequence) { + int length = sequence.length(); + for (int i = 0; i < length; i++) { + if (matches(sequence.charAt(i))) { + return i; + } + } + return -1; + } + + /** + * Returns the index of the first matching character in a character sequence, + * starting from a given position, or {@code -1} if no character matches after + * that position. + * + *

+ * The default implementation iterates over the sequence in forward order, + * beginning at {@code + * start}, calling {@link #matches} for each character. + * + * @param sequence the character sequence to examine + * @param start the first index to examine; must be nonnegative and no + * greater than {@code + * sequence.length()} + * @return the index of the first matching character, guaranteed to be no less + * than {@code start}, or {@code -1} if no character matches + * @throws IndexOutOfBoundsException if start is negative or greater than {@code + * sequence.length()} + */ + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + Preconditions.checkPositionIndex(start, length); + for (int i = start; i < length; i++) { + if (matches(sequence.charAt(i))) { + return i; + } + } + return -1; + } + + /** + * Returns the index of the last matching character in a character sequence, or + * {@code -1} if no matching character is present. + * + *

+ * The default implementation iterates over the sequence in reverse order + * calling {@link #matches} for each character. + * + * @param sequence the character sequence to examine from the end + * @return an index, or {@code -1} if no character matches + */ + public int lastIndexIn(CharSequence sequence) { + for (int i = sequence.length() - 1; i >= 0; i--) { + if (matches(sequence.charAt(i))) { + return i; + } + } + return -1; + } + + /** + * Returns the number of matching characters found in a character sequence. + */ + public int countIn(CharSequence sequence) { + int count = 0; + for (int i = 0; i < sequence.length(); i++) { + if (matches(sequence.charAt(i))) { + count++; + } + } + return count; + } + + /** + * Returns a string containing all non-matching characters of a character + * sequence, in order. For example: + * + *

+	 *    {@code
+	 *
+	 *   CharMatcher.is('a').removeFrom("bazaar")}
+	 * 
+ * + * ... returns {@code "bzr"}. + */ + @CheckReturnValue + public String removeFrom(CharSequence sequence) { + String string = sequence.toString(); + int pos = indexIn(string); + if (pos == -1) { + return string; + } + + char[] chars = string.toCharArray(); + int spread = 1; + + // This unusual loop comes from extensive benchmarking + OUT: while (true) { + pos++; + while (true) { + if (pos == chars.length) { + break OUT; + } + if (matches(chars[pos])) { + break; + } + chars[pos - spread] = chars[pos]; + pos++; + } + spread++; + } + return new String(chars, 0, pos - spread); + } + + /** + * Returns a string containing all matching characters of a character sequence, + * in order. For example: + * + *
+	 *    {@code
+	 *
+	 *   CharMatcher.is('a').retainFrom("bazaar")}
+	 * 
+ * + * ... returns {@code "aaa"}. + */ + @CheckReturnValue + public String retainFrom(CharSequence sequence) { + return negate().removeFrom(sequence); + } + + /** + * Returns a string copy of the input character sequence, with each character + * that matches this matcher replaced by a given replacement character. For + * example: + * + *
+	 *    {@code
+	 *
+	 *   CharMatcher.is('a').replaceFrom("radar", 'o')}
+	 * 
+ * + * ... returns {@code "rodor"}. + * + *

+ * The default implementation uses {@link #indexIn(CharSequence)} to find the + * first matching character, then iterates the remainder of the sequence calling + * {@link #matches(char)} for each character. + * + * @param sequence the character sequence to replace matching characters in + * @param replacement the character to append to the result string in place of + * each matching character in {@code sequence} + * @return the new string + */ + @CheckReturnValue + public String replaceFrom(CharSequence sequence, char replacement) { + String string = sequence.toString(); + int pos = indexIn(string); + if (pos == -1) { + return string; + } + char[] chars = string.toCharArray(); + chars[pos] = replacement; + for (int i = pos + 1; i < chars.length; i++) { + if (matches(chars[i])) { + chars[i] = replacement; + } + } + return new String(chars); + } + + /** + * Returns a string copy of the input character sequence, with each character + * that matches this matcher replaced by a given replacement sequence. For + * example: + * + *

+	 *    {@code
+	 *
+	 *   CharMatcher.is('a').replaceFrom("yaha", "oo")}
+	 * 
+ * + * ... returns {@code "yoohoo"}. + * + *

+ * Note: If the replacement is a fixed string with only one character, + * you are better off calling {@link #replaceFrom(CharSequence, char)} directly. + * + * @param sequence the character sequence to replace matching characters in + * @param replacement the characters to append to the result string in place of + * each matching character in {@code sequence} + * @return the new string + */ + @CheckReturnValue + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + int replacementLen = replacement.length(); + if (replacementLen == 0) { + return removeFrom(sequence); + } + if (replacementLen == 1) { + return replaceFrom(sequence, replacement.charAt(0)); + } + + String string = sequence.toString(); + int pos = indexIn(string); + if (pos == -1) { + return string; + } + + int len = string.length(); + StringBuilder buf = new StringBuilder((len * 3 / 2) + 16); + + int oldpos = 0; + do { + buf.append(string, oldpos, pos); + buf.append(replacement); + oldpos = pos + 1; + pos = indexIn(string, oldpos); + } while (pos != -1); + + buf.append(string, oldpos, len); + return buf.toString(); + } + + /** + * Returns a substring of the input character sequence that omits all characters + * this matcher matches from the beginning and from the end of the string. For + * example: + * + *

+	 *    {@code
+	 *
+	 *   CharMatcher.anyOf("ab").trimFrom("abacatbab")}
+	 * 
+ * + * ... returns {@code "cat"}. + * + *

+ * Note that: + * + *

+	 *    {@code
+	 *
+	 *   CharMatcher.inRange('\0', ' ').trimFrom(str)}
+	 * 
+ * + * ... is equivalent to {@link String#trim()}. + */ + @CheckReturnValue + public String trimFrom(CharSequence sequence) { + int len = sequence.length(); + int first; + int last; + + for (first = 0; first < len; first++) { + if (!matches(sequence.charAt(first))) { + break; + } + } + for (last = len - 1; last > first; last--) { + if (!matches(sequence.charAt(last))) { + break; + } + } + + return sequence.subSequence(first, last + 1).toString(); + } + + /** + * Returns a substring of the input character sequence that omits all characters + * this matcher matches from the beginning of the string. For example: + * + *
+	 *  {@code
+	 *
+	 *   CharMatcher.anyOf("ab").trimLeadingFrom("abacatbab")}
+	 * 
+ * + * ... returns {@code "catbab"}. + */ + @CheckReturnValue + public String trimLeadingFrom(CharSequence sequence) { + int len = sequence.length(); + for (int first = 0; first < len; first++) { + if (!matches(sequence.charAt(first))) { + return sequence.subSequence(first, len).toString(); + } + } + return ""; + } + + /** + * Returns a substring of the input character sequence that omits all characters + * this matcher matches from the end of the string. For example: + * + *
+	 *  {@code
+	 *
+	 *   CharMatcher.anyOf("ab").trimTrailingFrom("abacatbab")}
+	 * 
+ * + * ... returns {@code "abacat"}. + */ + @CheckReturnValue + public String trimTrailingFrom(CharSequence sequence) { + int len = sequence.length(); + for (int last = len - 1; last >= 0; last--) { + if (!matches(sequence.charAt(last))) { + return sequence.subSequence(0, last + 1).toString(); + } + } + return ""; + } + + /** + * Returns a string copy of the input character sequence, with each group of + * consecutive characters that match this matcher replaced by a single + * replacement character. For example: + * + *
+	 *    {@code
+	 *
+	 *   CharMatcher.anyOf("eko").collapseFrom("bookkeeper", '-')}
+	 * 
+ * + * ... returns {@code "b-p-r"}. + * + *

+ * The default implementation uses {@link #indexIn(CharSequence)} to find the + * first matching character, then iterates the remainder of the sequence calling + * {@link #matches(char)} for each character. + * + * @param sequence the character sequence to replace matching groups of + * characters in + * @param replacement the character to append to the result string in place of + * each group of matching characters in {@code sequence} + * @return the new string + */ + @CheckReturnValue + public String collapseFrom(CharSequence sequence, char replacement) { + // This implementation avoids unnecessary allocation. + int len = sequence.length(); + for (int i = 0; i < len; i++) { + char c = sequence.charAt(i); + if (matches(c)) { + if (c == replacement && (i == len - 1 || !matches(sequence.charAt(i + 1)))) { + // a no-op replacement + i++; + } else { + StringBuilder builder = new StringBuilder(len).append(sequence.subSequence(0, i)) + .append(replacement); + return finishCollapseFrom(sequence, i + 1, len, replacement, builder, true); + } + } + } + // no replacement needed + return sequence.toString(); + } + + /** + * Collapses groups of matching characters exactly as {@link #collapseFrom} + * does, except that groups of matching characters at the start or end of the + * sequence are removed without replacement. + */ + @CheckReturnValue + public String trimAndCollapseFrom(CharSequence sequence, char replacement) { + // This implementation avoids unnecessary allocation. + int len = sequence.length(); + int first; + int last; + + for (first = 0; first < len && matches(sequence.charAt(first)); first++) { + } + for (last = len - 1; last > first && matches(sequence.charAt(last)); last--) { + } + + return (first == 0 && last == len - 1) ? collapseFrom(sequence, replacement) + : finishCollapseFrom(sequence, first, last + 1, replacement, new StringBuilder(last + 1 - first), + false); + } + + private String finishCollapseFrom(CharSequence sequence, int start, int end, char replacement, + StringBuilder builder, boolean inMatchingGroup) { + for (int i = start; i < end; i++) { + char c = sequence.charAt(i); + if (matches(c)) { + if (!inMatchingGroup) { + builder.append(replacement); + inMatchingGroup = true; + } + } else { + builder.append(c); + inMatchingGroup = false; + } + } + return builder.toString(); + } + + /** + * @deprecated Provided only to satisfy the {@link Predicate} interface; use + * {@link #matches} instead. + */ + @Deprecated + @Override + public boolean apply(Character character) { + return matches(character); + } + + /** + * Returns a string representation of this {@code CharMatcher}, such as + * {@code CharMatcher.or(WHITESPACE, JAVA_DIGIT)}. + */ + @Override + public String toString() { + return description; + } + + static final String WHITESPACE_TABLE = "" + "\u2002\u3000\r\u0085\u200A\u2005\u2000\u3000" + + "\u2029\u000B\u3000\u2008\u2003\u205F\u3000\u1680" + "\u0009\u0020\u2006\u2001\u202F\u00A0\u000C\u2009" + + "\u3000\u2004\u3000\u3000\u2028\n\u2007\u3000"; + static final int WHITESPACE_MULTIPLIER = 1682554634; + static final int WHITESPACE_SHIFT = Integer.numberOfLeadingZeros(WHITESPACE_TABLE.length() - 1); + + /** + * Determines whether a character is whitespace according to the latest Unicode + * standard, as illustrated here. + * This is not the same definition used by other Java APIs. (See a comparison + * of several definitions of "whitespace".) + * + *

+ * Note: as the Unicode definition evolves, we will modify this constant + * to keep it up to date. + */ + public static final CharMatcher WHITESPACE = new FastMatcher("WHITESPACE") { + @Override + public boolean matches(char c) { + return WHITESPACE_TABLE.charAt((WHITESPACE_MULTIPLIER * c) >>> WHITESPACE_SHIFT) == c; + } + + @GwtIncompatible("java.util.BitSet") + @Override + void setBits(BitSet table) { + for (int i = 0; i < WHITESPACE_TABLE.length(); i++) { + table.set(WHITESPACE_TABLE.charAt(i)); + } + } + }; +} diff --git a/sources/main/java/com/google/common/base/Charsets.java b/sources/main/java/com/google/common/base/Charsets.java new file mode 100644 index 0000000..2657704 --- /dev/null +++ b/sources/main/java/com/google/common/base/Charsets.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.nio.charset.Charset; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * Contains constant definitions for the six standard {@link Charset} instances, + * which are guaranteed to be supported by all Java platform implementations. + * + *

+ * Assuming you're free to choose, note that {@link #UTF_8} is widely + * preferred. + * + *

+ * See the Guava User Guide article on + * {@code Charsets}. + * + * @author Mike Bostock + * @since 1.0 + */ +@GwtCompatible(emulated = true) +public final class Charsets { + private Charsets() { + } + + /** + * UTF-8: eight-bit UCS Transformation Format. + * + */ + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + /* + * Please do not add new Charset references to this class, unless those + * character encodings are part of the set required to be supported by all Java + * platform implementations! Any Charsets initialized here may cause unexpected + * delays when this class is loaded. See the Charset Javadocs for the list of + * built-in character encodings. + */ +} diff --git a/sources/main/java/com/google/common/base/Converter.java b/sources/main/java/com/google/common/base/Converter.java new file mode 100644 index 0000000..481a998 --- /dev/null +++ b/sources/main/java/com/google/common/base/Converter.java @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Iterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A function from {@code A} to {@code B} with an associated reverse + * function from {@code B} to {@code A}; used for converting back and forth + * between different representations of the same information. + * + *

Invertibility

+ * + *

+ * The reverse operation may be a strict inverse (meaning that + * {@code + * converter.reverse().convert(converter.convert(a)).equals(a)} is always true). + * However, it is very common (perhaps more common) for round-trip + * conversion to be lossy. Consider an example round-trip using + * {@link com.google.common.primitives.Doubles#stringConverter}: + * + *

    + *
  1. {@code stringConverter().convert("1.00")} returns the {@code Double} + * value {@code 1.0} + *
  2. {@code stringConverter().reverse().convert(1.0)} returns the string + * {@code "1.0"} -- not the same string ({@code "1.00"}) we started with + *
+ * + *

+ * Note that it should still be the case that the round-tripped and original + * objects are similar. + * + *

Nullability

+ * + *

+ * A converter always converts {@code null} to {@code null} and non-null + * references to non-null references. It would not make sense to consider + * {@code null} and a non-null reference to be "different representations of the + * same information", since one is distinguishable from missing + * information and the other is not. The {@link #convert} method handles this + * null behavior for all converters; implementations of {@link #doForward} and + * {@link #doBackward} are guaranteed to never be passed {@code null}, and must + * never return {@code null}. + * + * + *

Common ways to use

+ * + *

+ * Getting a converter: + * + *

    + *
  • Use a provided converter implementation, such as + * {@link Enums#stringConverter}, + * {@link com.google.common.primitives.Ints#stringConverter + * Ints.stringConverter} or the {@linkplain #reverse reverse} views of these. + *
  • Convert between specific preset values using + * {@link com.google.common.collect.Maps#asConverter Maps.asConverter}. For + * example, use this to create a "fake" converter for a unit test. It is + * unnecessary (and confusing) to mock the {@code Converter} type using a + * mocking framework. + *
  • Otherwise, extend this class and implement its {@link #doForward} and + * {@link #doBackward} methods. + *
+ * + *

+ * Using a converter: + * + *

    + *
  • Convert one instance in the "forward" direction using + * {@code converter.convert(a)}. + *
  • Convert multiple instances "forward" using + * {@code converter.convertAll(as)}. + *
  • Convert in the "backward" direction using + * {@code converter.reverse().convert(b)} or {@code + * converter.reverse().convertAll(bs)}. + *
  • Use {@code converter} or {@code converter.reverse()} anywhere a + * {@link Function} is accepted + *
  • Do not call {@link #doForward} or {@link #doBackward} directly; + * these exist only to be overridden. + *
+ * + * @author Mike Ward + * @author Kurt Alfred Kluever + * @author Gregory Kick + * @since 16.0 + */ +@Beta +@GwtCompatible +public abstract class Converter implements Function { + private final boolean handleNullAutomatically; + + // We lazily cache the reverse view to avoid allocating on every call to + // reverse(). + private transient Converter reverse; + + /** Constructor for use by subclasses. */ + protected Converter() { + this(true); + } + + /** + * Constructor used only by {@code LegacyConverter} to suspend automatic + * null-handling. + */ + Converter(boolean handleNullAutomatically) { + this.handleNullAutomatically = handleNullAutomatically; + } + + // SPI methods (what subclasses must implement) + + /** + * Returns a representation of {@code a} as an instance of type {@code B}. If + * {@code a} cannot be converted, an unchecked exception (such as + * {@link IllegalArgumentException}) should be thrown. + * + * @param a the instance to convert; will never be null + * @return the converted instance; must not be null + */ + protected abstract B doForward(A a); + + /** + * Returns a representation of {@code b} as an instance of type {@code A}. If + * {@code b} cannot be converted, an unchecked exception (such as + * {@link IllegalArgumentException}) should be thrown. + * + * @param b the instance to convert; will never be null + * @return the converted instance; must not be null + * @throws UnsupportedOperationException if backward conversion is not + * implemented; this should be very rare. + * Note that if backward conversion is not + * only unimplemented but + * unimplementable (for example, + * consider a + * {@code Converter}), + * then this is not logically a + * {@code Converter} at all, and should + * just implement {@link Function}. + */ + protected abstract A doBackward(B b); + + // API (consumer-side) methods + + /** + * Returns a representation of {@code a} as an instance of type {@code B}. + * + * @return the converted value; is null if and only if {@code a} is null + */ + @Nullable + public final B convert(@Nullable A a) { + return correctedDoForward(a); + } + + @Nullable + B correctedDoForward(@Nullable A a) { + if (handleNullAutomatically) { + // TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert? + return a == null ? null : checkNotNull(doForward(a)); + } else { + return doForward(a); + } + } + + @Nullable + A correctedDoBackward(@Nullable B b) { + if (handleNullAutomatically) { + // TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert? + return b == null ? null : checkNotNull(doBackward(b)); + } else { + return doBackward(b); + } + } + + /** + * Returns an iterable that applies {@code convert} to each element of + * {@code fromIterable}. The conversion is done lazily. + * + *

+ * The returned iterable's iterator supports {@code remove()} if the input + * iterator does. After a successful {@code remove()} call, {@code fromIterable} + * no longer contains the corresponding element. + */ + public Iterable convertAll(final Iterable fromIterable) { + checkNotNull(fromIterable, "fromIterable"); + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator fromIterator = fromIterable.iterator(); + + @Override + public boolean hasNext() { + return fromIterator.hasNext(); + } + + @Override + public B next() { + return convert(fromIterator.next()); + } + + @Override + public void remove() { + fromIterator.remove(); + } + }; + } + }; + } + + /** + * Returns the reversed view of this converter, which converts + * {@code this.convert(a)} back to a value roughly equivalent to {@code a}. + * + *

+ * The returned converter is serializable if {@code this} converter is. + */ + // TODO(user): Make this method final + public Converter reverse() { + Converter result = reverse; + return (result == null) ? reverse = new ReverseConverter(this) : result; + } + + private static final class ReverseConverter extends Converter implements Serializable { + final Converter original; + + ReverseConverter(Converter original) { + this.original = original; + } + + /* + * These gymnastics are a little confusing. Basically this class has neither + * legacy nor non-legacy behavior; it just needs to let the behavior of the + * backing converter shine through. So, we override the correctedDo* methods, + * after which the do* methods should never be reached. + */ + + @Override + protected A doForward(B b) { + throw new AssertionError(); + } + + @Override + protected B doBackward(A a) { + throw new AssertionError(); + } + + @Override + @Nullable + A correctedDoForward(@Nullable B b) { + return original.correctedDoBackward(b); + } + + @Override + @Nullable + B correctedDoBackward(@Nullable A a) { + return original.correctedDoForward(a); + } + + @Override + public Converter reverse() { + return original; + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof ReverseConverter) { + ReverseConverter that = (ReverseConverter) object; + return this.original.equals(that.original); + } + return false; + } + + @Override + public int hashCode() { + return ~original.hashCode(); + } + + @Override + public String toString() { + return original + ".reverse()"; + } + + private static final long serialVersionUID = 0L; + } + + /** + * Returns a converter whose {@code convert} method applies + * {@code secondConverter} to the result of this converter. Its {@code reverse} + * method applies the converters in reverse order. + * + *

+ * The returned converter is serializable if {@code this} converter and + * {@code secondConverter} are. + */ + public Converter andThen(Converter secondConverter) { + return new ConverterComposition(this, checkNotNull(secondConverter)); + } + + private static final class ConverterComposition extends Converter implements Serializable { + final Converter first; + final Converter second; + + ConverterComposition(Converter first, Converter second) { + this.first = first; + this.second = second; + } + + /* + * These gymnastics are a little confusing. Basically this class has neither + * legacy nor non-legacy behavior; it just needs to let the behaviors of the + * backing converters shine through (which might even differ from each other!). + * So, we override the correctedDo* methods, after which the do* methods should + * never be reached. + */ + + @Override + protected C doForward(A a) { + throw new AssertionError(); + } + + @Override + protected A doBackward(C c) { + throw new AssertionError(); + } + + @Override + @Nullable + C correctedDoForward(@Nullable A a) { + return second.correctedDoForward(first.correctedDoForward(a)); + } + + @Override + @Nullable + A correctedDoBackward(@Nullable C c) { + return first.correctedDoBackward(second.correctedDoBackward(c)); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof ConverterComposition) { + ConverterComposition that = (ConverterComposition) object; + return this.first.equals(that.first) && this.second.equals(that.second); + } + return false; + } + + @Override + public int hashCode() { + return 31 * first.hashCode() + second.hashCode(); + } + + @Override + public String toString() { + return first + ".andThen(" + second + ")"; + } + + private static final long serialVersionUID = 0L; + } + + /** + * @deprecated Provided to satisfy the {@code Function} interface; use + * {@link #convert} instead. + */ + @Deprecated + @Override + @Nullable + public final B apply(@Nullable A a) { + return convert(a); + } + + /** + * Indicates whether another object is equal to this converter. + * + *

+ * Most implementations will have no reason to override the behavior of + * {@link Object#equals}. However, an implementation may also choose to return + * {@code true} whenever {@code object} is a {@link Converter} that it considers + * interchangeable with this one. "Interchangeable" typically + * means that {@code Objects.equal(this.convert(a), that.convert(a))} is true + * for all {@code a} of type {@code A} (and similarly for {@code reverse}). Note + * that a {@code false} result from this method does not imply that the + * converters are known not to be interchangeable. + */ + @Override + public boolean equals(@Nullable Object object) { + return super.equals(object); + } + + // Static converters + + /** + * Returns a converter based on existing forward and backward functions. + * Note that it is unnecessary to create new classes implementing + * {@code Function} just to pass them in here. Instead, simply subclass + * {@code Converter} and implement its {@link #doForward} and + * {@link #doBackward} methods directly. + * + *

+ * These functions will never be passed {@code null} and must not under any + * circumstances return {@code null}. If a value cannot be converted, the + * function should throw an unchecked exception (typically, but not necessarily, + * {@link IllegalArgumentException}). + * + *

+ * The returned converter is serializable if both provided functions are. + * + * @since 17.0 + */ + public static Converter from(Function forwardFunction, + Function backwardFunction) { + return new FunctionBasedConverter(forwardFunction, backwardFunction); + } + + private static final class FunctionBasedConverter extends Converter implements Serializable { + private final Function forwardFunction; + private final Function backwardFunction; + + private FunctionBasedConverter(Function forwardFunction, + Function backwardFunction) { + this.forwardFunction = checkNotNull(forwardFunction); + this.backwardFunction = checkNotNull(backwardFunction); + } + + @Override + protected B doForward(A a) { + return forwardFunction.apply(a); + } + + @Override + protected A doBackward(B b) { + return backwardFunction.apply(b); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof FunctionBasedConverter) { + FunctionBasedConverter that = (FunctionBasedConverter) object; + return this.forwardFunction.equals(that.forwardFunction) + && this.backwardFunction.equals(that.backwardFunction); + } + return false; + } + + @Override + public int hashCode() { + return forwardFunction.hashCode() * 31 + backwardFunction.hashCode(); + } + + @Override + public String toString() { + return "Converter.from(" + forwardFunction + ", " + backwardFunction + ")"; + } + } + + /** + * Returns a serializable converter that always converts or reverses an object + * to itself. + */ + @SuppressWarnings("unchecked") // implementation is "fully variant" + public static Converter identity() { + return (IdentityConverter) IdentityConverter.INSTANCE; + } + + /** + * A converter that always converts or reverses an object to itself. Note that T + * is now a "pass-through type". + */ + private static final class IdentityConverter extends Converter implements Serializable { + static final IdentityConverter INSTANCE = new IdentityConverter(); + + @Override + protected T doForward(T t) { + return t; + } + + @Override + protected T doBackward(T t) { + return t; + } + + @Override + public IdentityConverter reverse() { + return this; + } + + @Override + public Converter andThen(Converter otherConverter) { + return checkNotNull(otherConverter, "otherConverter"); + } + + /* + * We *could* override convertAll() to return its input, but it's a rather + * pointless optimization and opened up a weird type-safety problem. + */ + + @Override + public String toString() { + return "Converter.identity()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0L; + } +} diff --git a/sources/main/java/com/google/common/base/Defaults.java b/sources/main/java/com/google/common/base/Defaults.java new file mode 100644 index 0000000..1eda021 --- /dev/null +++ b/sources/main/java/com/google/common/base/Defaults.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * This class provides default values for all Java types, as defined by the JLS. + * + * @author Ben Yu + * @since 1.0 + */ +public final class Defaults { + private Defaults() { + } + + private static final Map, Object> DEFAULTS; + + static { + // Only add to this map via put(Map, Class, T) + Map, Object> map = new HashMap, Object>(); + put(map, boolean.class, false); + put(map, char.class, '\0'); + put(map, byte.class, (byte) 0); + put(map, short.class, (short) 0); + put(map, int.class, 0); + put(map, long.class, 0L); + put(map, float.class, 0f); + put(map, double.class, 0d); + DEFAULTS = Collections.unmodifiableMap(map); + } + + private static void put(Map, Object> map, Class type, T value) { + map.put(type, value); + } + + /** + * Returns the default value of {@code type} as defined by JLS --- {@code 0} for + * numbers, {@code + * false} for {@code boolean} and {@code '\0'} for {@code char}. For + * non-primitive types and {@code void}, null is returned. + */ + public static T defaultValue(Class type) { + // Primitives.wrap(type).cast(...) would avoid the warning, but we can't use + // that from here + @SuppressWarnings("unchecked") // the put method enforces this key-value relationship + T t = (T) DEFAULTS.get(checkNotNull(type)); + return t; + } +} diff --git a/sources/main/java/com/google/common/base/Enums.java b/sources/main/java/com/google/common/base/Enums.java new file mode 100644 index 0000000..952f077 --- /dev/null +++ b/sources/main/java/com/google/common/base/Enums.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.lang.ref.WeakReference; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * Utility methods for working with {@link Enum} instances. + * + * @author Steve McKay + * + * @since 9.0 + */ +@GwtCompatible(emulated = true) +@Beta +public final class Enums { + + private Enums() { + } + + /** + * Returns a {@link Function} that maps an {@link Enum} name to the associated + * {@code Enum} constant. The {@code Function} will return {@code null} if the + * {@code Enum} constant does not exist. + * + * @param enumClass the {@link Class} of the {@code Enum} declaring the constant + * values + * @deprecated Use {@link Enums#stringConverter} instead. Note that the string + * converter has slightly different behavior: it throws + * {@link IllegalArgumentException} if the enum constant does not + * exist rather than returning {@code null}. It also converts + * {@code null} to {@code null} rather than throwing + * {@link NullPointerException}. This method is scheduled for + * removal in Guava 18.0. + */ + @Deprecated + public static > Function valueOfFunction(Class enumClass) { + return new ValueOfFunction(enumClass); + } + + /** + * A {@link Function} that maps an {@link Enum} name to the associated constant, + * or {@code null} if the constant does not exist. + */ + private static final class ValueOfFunction> implements Function, Serializable { + + private final Class enumClass; + + private ValueOfFunction(Class enumClass) { + this.enumClass = checkNotNull(enumClass); + } + + @Override + public T apply(String value) { + try { + return Enum.valueOf(enumClass, value); + } catch (IllegalArgumentException e) { + return null; + } + } + + @Override + public boolean equals(@Nullable Object obj) { + return obj instanceof ValueOfFunction && enumClass.equals(((ValueOfFunction) obj).enumClass); + } + + @Override + public int hashCode() { + return enumClass.hashCode(); + } + + @Override + public String toString() { + return "Enums.valueOf(" + enumClass + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an optional enum constant for the given type, using + * {@link Enum#valueOf}. If the constant does not exist, {@link Optional#absent} + * is returned. A common use case is for parsing user input or falling back to a + * default enum constant. For example, + * {@code Enums.getIfPresent(Country.class, countryInput).or(Country.DEFAULT);} + * + * @since 12.0 + */ + public static > Optional getIfPresent(Class enumClass, String value) { + checkNotNull(enumClass); + checkNotNull(value); + return Platform.getEnumIfPresent(enumClass, value); + } + + @GwtIncompatible("java.lang.ref.WeakReference") + private static final Map>, Map>>> enumConstantCache = new HashMap>, Map>>>(); + + @GwtIncompatible("java.lang.ref.WeakReference") + private static > Map>> populateCache(Class enumClass) { + Map>> result = new HashMap>>(); + for (T enumInstance : EnumSet.allOf(enumClass)) { + result.put(enumInstance.name(), new WeakReference>(enumInstance)); + } + enumConstantCache.put(enumClass, result); + return result; + } + + @GwtIncompatible("java.lang.ref.WeakReference") + static > Map>> getEnumConstants(Class enumClass) { + synchronized (enumConstantCache) { + Map>> constants = enumConstantCache.get(enumClass); + if (constants == null) { + constants = populateCache(enumClass); + } + return constants; + } + } + + /** + * Returns a converter that converts between strings and {@code enum} values of + * type {@code enumClass} using {@link Enum#valueOf(Class, String)} and + * {@link Enum#name()}. The converter will throw an + * {@code IllegalArgumentException} if the argument is not the name of any enum + * constant in the specified enum. + * + * @since 16.0 + */ + public static > Converter stringConverter(final Class enumClass) { + return new StringConverter(enumClass); + } + + private static final class StringConverter> extends Converter implements Serializable { + + private final Class enumClass; + + StringConverter(Class enumClass) { + this.enumClass = checkNotNull(enumClass); + } + + @Override + protected T doForward(String value) { + return Enum.valueOf(enumClass, value); + } + + @Override + protected String doBackward(T enumValue) { + return enumValue.name(); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof StringConverter) { + StringConverter that = (StringConverter) object; + return this.enumClass.equals(that.enumClass); + } + return false; + } + + @Override + public int hashCode() { + return enumClass.hashCode(); + } + + @Override + public String toString() { + return "Enums.stringConverter(" + enumClass.getName() + ".class)"; + } + + private static final long serialVersionUID = 0L; + } +} diff --git a/sources/main/java/com/google/common/base/Equivalence.java b/sources/main/java/com/google/common/base/Equivalence.java new file mode 100644 index 0000000..77d943b --- /dev/null +++ b/sources/main/java/com/google/common/base/Equivalence.java @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A strategy for determining whether two instances are considered equivalent. + * Examples of equivalences are the {@linkplain #identity() identity + * equivalence} and {@linkplain #equals equals equivalence}. + * + * @author Bob Lee + * @author Ben Yu + * @author Gregory Kick + * @since 10.0 + * (mostly source-compatible since 4.0) + */ +@GwtCompatible +public abstract class Equivalence { + /** + * Constructor for use by subclasses. + */ + protected Equivalence() { + } + + /** + * Returns {@code true} if the given objects are considered equivalent. + * + *

+ * The {@code equivalent} method implements an equivalence relation on object + * references: + * + *

    + *
  • It is reflexive: for any reference {@code x}, including null, + * {@code + * equivalent(x, x)} returns {@code true}. + *
  • It is symmetric: for any references {@code x} and {@code y}, + * {@code + * equivalent(x, y) == equivalent(y, x)}. + *
  • It is transitive: for any references {@code x}, {@code y}, and + * {@code z}, if {@code equivalent(x, y)} returns {@code true} and + * {@code equivalent(y, z)} returns {@code + * true}, then {@code equivalent(x, z)} returns {@code true}. + *
  • It is consistent: for any references {@code x} and {@code y}, + * multiple invocations of {@code equivalent(x, y)} consistently return + * {@code true} or consistently return {@code + * false} (provided that neither {@code x} nor {@code y} is modified). + *
+ */ + public final boolean equivalent(@Nullable T a, @Nullable T b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + return doEquivalent(a, b); + } + + /** + * Returns {@code true} if {@code a} and {@code b} are considered equivalent. + * + *

+ * Called by {@link #equivalent}. {@code a} and {@code b} are not the same + * object and are not nulls. + * + * @since 10.0 (previously, subclasses would override equivalent()) + */ + protected abstract boolean doEquivalent(T a, T b); + + /** + * Returns a hash code for {@code t}. + * + *

+ * The {@code hash} has the following properties: + *

    + *
  • It is consistent: for any reference {@code x}, multiple + * invocations of {@code hash(x}} consistently return the same value provided + * {@code x} remains unchanged according to the definition of the equivalence. + * The hash need not remain consistent from one execution of an application to + * another execution of the same application. + *
  • It is distributable across equivalence: for any references + * {@code x} and {@code y}, if {@code equivalent(x, y)}, then + * {@code hash(x) == hash(y)}. It is not necessary that the hash be + * distributable across inequivalence. If {@code equivalence(x, y)} is + * false, {@code hash(x) == hash(y)} may still be true. + *
  • {@code hash(null)} is {@code 0}. + *
+ */ + public final int hash(@Nullable T t) { + if (t == null) { + return 0; + } + return doHash(t); + } + + /** + * Returns a hash code for non-null object {@code t}. + * + *

+ * Called by {@link #hash}. + * + * @since 10.0 (previously, subclasses would override hash()) + */ + protected abstract int doHash(T t); + + /** + * Returns a new equivalence relation for {@code F} which evaluates equivalence + * by first applying {@code function} to the argument, then evaluating using + * {@code this}. That is, for any pair of non-null objects {@code x} and + * {@code y}, {@code + * equivalence.onResultOf(function).equivalent(a, b)} is true if and only if + * {@code + * equivalence.equivalent(function.apply(a), function.apply(b))} is true. + * + *

+ * For example: + * + *

+	 * {
+	 * 	@code
+	 * 	Equivalence SAME_AGE = Equivalence.equals().onResultOf(GET_PERSON_AGE);
+	 * }
+	 * 
+ * + *

+ * {@code function} will never be invoked with a null value. + * + *

+ * Note that {@code function} must be consistent according to {@code this} + * equivalence relation. That is, invoking {@link Function#apply} multiple times + * for a given value must return equivalent results. For example, + * {@code Equivalence.identity().onResultOf(Functions.toStringFunction())} is + * broken because it's not guaranteed that {@link Object#toString}) always + * returns the same string instance. + * + * @since 10.0 + */ + public final Equivalence onResultOf(Function function) { + return new FunctionalEquivalence(function, this); + } + + /** + * Returns a wrapper of {@code reference} that implements + * {@link Wrapper#equals(Object) Object.equals()} such that + * {@code wrap(a).equals(wrap(b))} if and only if {@code equivalent(a, b)}. + * + * @since 10.0 + */ + public final Wrapper wrap(@Nullable S reference) { + return new Wrapper(this, reference); + } + + /** + * Wraps an object so that {@link #equals(Object)} and {@link #hashCode()} + * delegate to an {@link Equivalence}. + * + *

+ * For example, given an {@link Equivalence} for {@link String strings} named + * {@code equiv} that tests equivalence using their lengths: + * + *

+	 *    {@code
+	 *   equiv.wrap("a").equals(equiv.wrap("b")) // true
+	 *   equiv.wrap("a").equals(equiv.wrap("hello")) // false}
+	 * 
+ * + *

+ * Note in particular that an equivalence wrapper is never equal to the object + * it wraps. + * + *

+	 *    {@code
+	 *   equiv.wrap(obj).equals(obj) // always false}
+	 * 
+ * + * @since 10.0 + */ + public static final class Wrapper implements Serializable { + private final Equivalence equivalence; + @Nullable + private final T reference; + + private Wrapper(Equivalence equivalence, @Nullable T reference) { + this.equivalence = checkNotNull(equivalence); + this.reference = reference; + } + + /** Returns the (possibly null) reference wrapped by this instance. */ + @Nullable + public T get() { + return reference; + } + + /** + * Returns {@code true} if {@link Equivalence#equivalent(Object, Object)} + * applied to the wrapped references is {@code true} and both wrappers use the + * {@link Object#equals(Object) same} equivalence. + */ + @Override + public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Wrapper) { + Wrapper that = (Wrapper) obj; // note: not necessarily a Wrapper + + if (this.equivalence.equals(that.equivalence)) { + /* + * We'll accept that as sufficient "proof" that either equivalence should be + * able to handle either reference, so it's safe to circumvent compile-time type + * checking. + */ + @SuppressWarnings("unchecked") + Equivalence equivalence = (Equivalence) this.equivalence; + return equivalence.equivalent(this.reference, that.reference); + } + } + return false; + } + + /** + * Returns the result of {@link Equivalence#hash(Object)} applied to the wrapped + * reference. + */ + @Override + public int hashCode() { + return equivalence.hash(reference); + } + + /** + * Returns a string representation for this equivalence wrapper. The form of + * this string representation is not specified. + */ + @Override + public String toString() { + return equivalence + ".wrap(" + reference + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an equivalence over iterables based on the equivalence of their + * elements. More specifically, two iterables are considered equivalent if they + * both contain the same number of elements, and each pair of corresponding + * elements is equivalent according to {@code this}. Null iterables are + * equivalent to one another. + * + *

+ * Note that this method performs a similar function for equivalences as + * {@link com.google.common.collect.Ordering#lexicographical} does for + * orderings. + * + * @since 10.0 + */ + @GwtCompatible(serializable = true) + public final Equivalence> pairwise() { + // Ideally, the returned equivalence would support Iterable. + // However, + // the need for this is so rare that it's not worth making callers deal with the + // ugly wildcard. + return new PairwiseEquivalence(this); + } + + /** + * Returns a predicate that evaluates to true if and only if the input is + * equivalent to {@code target} according to this equivalence relation. + * + * @since 10.0 + */ + @Beta + public final Predicate equivalentTo(@Nullable T target) { + return new EquivalentToPredicate(this, target); + } + + private static final class EquivalentToPredicate implements Predicate, Serializable { + + private final Equivalence equivalence; + @Nullable + private final T target; + + EquivalentToPredicate(Equivalence equivalence, @Nullable T target) { + this.equivalence = checkNotNull(equivalence); + this.target = target; + } + + @Override + public boolean apply(@Nullable T input) { + return equivalence.equivalent(input, target); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof EquivalentToPredicate) { + EquivalentToPredicate that = (EquivalentToPredicate) obj; + return equivalence.equals(that.equivalence) && Objects.equal(target, that.target); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(equivalence, target); + } + + @Override + public String toString() { + return equivalence + ".equivalentTo(" + target + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an equivalence that delegates to {@link Object#equals} and + * {@link Object#hashCode}. {@link Equivalence#equivalent} returns {@code true} + * if both values are null, or if neither value is null and + * {@link Object#equals} returns {@code true}. {@link Equivalence#hash} returns + * {@code 0} if passed a null value. + * + * @since 13.0 + * @since 8.0 (in Equivalences with null-friendly behavior) + * @since 4.0 (in Equivalences) + */ + public static Equivalence equals() { + return Equals.INSTANCE; + } + + /** + * Returns an equivalence that uses {@code ==} to compare values and + * {@link System#identityHashCode(Object)} to compute the hash code. + * {@link Equivalence#equivalent} returns {@code true} if {@code a == b}, + * including in the case that a and b are both null. + * + * @since 13.0 + * @since 4.0 (in Equivalences) + */ + public static Equivalence identity() { + return Identity.INSTANCE; + } + + static final class Equals extends Equivalence implements Serializable { + + static final Equals INSTANCE = new Equals(); + + @Override + protected boolean doEquivalent(Object a, Object b) { + return a.equals(b); + } + + @Override + public int doHash(Object o) { + return o.hashCode(); + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } + + static final class Identity extends Equivalence implements Serializable { + + static final Identity INSTANCE = new Identity(); + + @Override + protected boolean doEquivalent(Object a, Object b) { + return false; + } + + @Override + protected int doHash(Object o) { + return System.identityHashCode(o); + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } +} diff --git a/sources/main/java/com/google/common/base/FinalizablePhantomReference.java b/sources/main/java/com/google/common/base/FinalizablePhantomReference.java new file mode 100644 index 0000000..633f757 --- /dev/null +++ b/sources/main/java/com/google/common/base/FinalizablePhantomReference.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; + +/** + * Phantom reference with a {@code finalizeReferent()} method which a background + * thread invokes after the garbage collector reclaims the referent. This is a + * simpler alternative to using a {@link ReferenceQueue}. + * + *

+ * Unlike a normal phantom reference, this reference will be cleared + * automatically. + * + * @author Bob Lee + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class FinalizablePhantomReference extends PhantomReference implements FinalizableReference { + /** + * Constructs a new finalizable phantom reference. + * + * @param referent to phantom reference + * @param queue that should finalize the referent + */ + protected FinalizablePhantomReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/sources/main/java/com/google/common/base/FinalizableReference.java b/sources/main/java/com/google/common/base/FinalizableReference.java new file mode 100644 index 0000000..6ae2be6 --- /dev/null +++ b/sources/main/java/com/google/common/base/FinalizableReference.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +/** + * Implemented by references that have code to run after garbage collection of + * their referents. + * + * @see FinalizableReferenceQueue + * @author Bob Lee + * @since 2.0 (imported from Google Collections Library) + */ +public interface FinalizableReference { + /** + * Invoked on a background thread after the referent has been garbage collected + * unless security restrictions prevented starting a background thread, in which + * case this method is invoked when new references are created. + */ + void finalizeReferent(); +} diff --git a/sources/main/java/com/google/common/base/FinalizableReferenceQueue.java b/sources/main/java/com/google/common/base/FinalizableReferenceQueue.java new file mode 100644 index 0000000..ff01f89 --- /dev/null +++ b/sources/main/java/com/google/common/base/FinalizableReferenceQueue.java @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.io.Closeable; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.google.common.annotations.VisibleForTesting; + +/** + * A reference queue with an associated background thread that dequeues + * references and invokes {@link FinalizableReference#finalizeReferent()} on + * them. + * + *

+ * Keep a strong reference to this object until all of the associated referents + * have been finalized. If this object is garbage collected earlier, the backing + * thread will not invoke {@code + * finalizeReferent()} on the remaining references. + * + *

+ * As an example of how this is used, imagine you have a class {@code MyServer} + * that creates a a {@link java.net.ServerSocket ServerSocket}, and you would + * like to ensure that the {@code ServerSocket} is closed even if the + * {@code MyServer} object is garbage-collected without calling its + * {@code close} method. You could use a finalizer to accomplish this, + * but that has a number of well-known problems. Here is how you might use this + * class instead: + * + *

+ * public class MyServer implements Closeable {
+ *   private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue();
+ *   // You might also share this between several objects.
+ *
+ *   private static final Set<Reference<?>> references = Sets.newConcurrentHashSet();
+ *   // This ensures that the FinalizablePhantomReference itself is not garbage-collected.
+ *
+ *   private final ServerSocket serverSocket;
+ *
+ *   private MyServer(...) {
+ *     ...
+ *     this.serverSocket = new ServerSocket(...);
+ *     ...
+ *   }
+ *
+ *   public static MyServer create(...) {
+ *     MyServer myServer = new MyServer(...);
+ *     final ServerSocket serverSocket = myServer.serverSocket;
+ *     Reference<?> reference = new FinalizablePhantomReference<MyServer>(myServer, frq) {
+ *       @Override public void finalizeReferent() {
+ *         references.remove(this):
+ *         if (!serverSocket.isClosed()) {
+ *           ...log a message about how nobody called close()...
+ *           try {
+ *             serverSocket.close();
+ *           } catch (IOException e) {
+ *             ...
+ *           }
+ *         }
+ *       }
+ *     };
+ *     references.add(reference);
+ *     return myServer;
+ *   }
+ *
+ *   @Override public void close() {
+ *     serverSocket.close();
+ *   }
+ * }
+ * 
+ * + * @author Bob Lee + * @since 2.0 (imported from Google Collections Library) + */ +public class FinalizableReferenceQueue implements Closeable { + /* + * The Finalizer thread keeps a phantom reference to this object. When the + * client (for example, a map built by MapMaker) no longer has a strong + * reference to this object, the garbage collector will reclaim it and enqueue + * the phantom reference. The enqueued reference will trigger the Finalizer to + * stop. + * + * If this library is loaded in the system class loader, + * FinalizableReferenceQueue can load Finalizer directly with no problems. + * + * If this library is loaded in an application class loader, it's important that + * Finalizer not have a strong reference back to the class loader. Otherwise, + * you could have a graph like this: + * + * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application + * class loader which loaded -> ReferenceMap.class which has a static -> + * FinalizableReferenceQueue instance + * + * Even if no other references to classes from the application class loader + * remain, the Finalizer thread keeps an indirect strong reference to the queue + * in ReferenceMap, which keeps the Finalizer running, and as a result, the + * application class loader can never be reclaimed. + * + * This means that dynamically loaded web applications and OSGi bundles can't be + * unloaded. + * + * If the library is loaded in an application class loader, we try to break the + * cycle by loading Finalizer in its own independent class loader: + * + * System class loader -> Application class loader -> ReferenceMap -> + * FinalizableReferenceQueue -> etc. -> Decoupled class loader -> Finalizer + * + * Now, Finalizer no longer keeps an indirect strong reference to the static + * FinalizableReferenceQueue field in ReferenceMap. The application class loader + * can be reclaimed at which point the Finalizer thread will stop and its + * decoupled class loader can also be reclaimed. + * + * If any of this fails along the way, we fall back to loading Finalizer + * directly in the application class loader. + */ + + private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName()); + + private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer"; + + /** Reference to Finalizer.startFinalizer(). */ + private static final Method startFinalizer; + static { + Class finalizer = loadFinalizer(new SystemLoader(), new DecoupledLoader(), new DirectLoader()); + startFinalizer = getStartFinalizer(finalizer); + } + + /** + * The actual reference queue that our background thread will poll. + */ + final ReferenceQueue queue; + + final PhantomReference frqRef; + + /** + * Whether or not the background thread started successfully. + */ + final boolean threadStarted; + + /** + * Constructs a new queue. + */ + public FinalizableReferenceQueue() { + // We could start the finalizer lazily, but I'd rather it blow up early. + queue = new ReferenceQueue(); + frqRef = new PhantomReference(this, queue); + boolean threadStarted = false; + try { + startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef); + threadStarted = true; + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); // startFinalizer() is public + } catch (Throwable t) { + logger.log(Level.INFO, "Failed to start reference finalizer thread." + + " Reference cleanup will only occur when new references are created.", t); + } + + this.threadStarted = threadStarted; + } + + @Override + public void close() { + frqRef.enqueue(); + cleanUp(); + } + + /** + * Repeatedly dequeues references from the queue and invokes + * {@link FinalizableReference#finalizeReferent()} on them until the queue is + * empty. This method is a no-op if the background thread was created + * successfully. + */ + void cleanUp() { + if (threadStarted) { + return; + } + + Reference reference; + while ((reference = queue.poll()) != null) { + /* + * This is for the benefit of phantom references. Weak and soft references will + * have already been cleared by this point. + */ + reference.clear(); + try { + ((FinalizableReference) reference).finalizeReferent(); + } catch (Throwable t) { + logger.log(Level.SEVERE, "Error cleaning up after reference.", t); + } + } + } + + /** + * Iterates through the given loaders until it finds one that can load + * Finalizer. + * + * @return Finalizer.class + */ + private static Class loadFinalizer(FinalizerLoader... loaders) { + for (FinalizerLoader loader : loaders) { + Class finalizer = loader.loadFinalizer(); + if (finalizer != null) { + return finalizer; + } + } + + throw new AssertionError(); + } + + /** + * Loads Finalizer.class. + */ + interface FinalizerLoader { + + /** + * Returns Finalizer.class or null if this loader shouldn't or can't load it. + * + * @throws SecurityException if we don't have the appropriate privileges + */ + Class loadFinalizer(); + } + + /** + * Tries to load Finalizer from the system class loader. If Finalizer is in the + * system class path, we needn't create a separate loader. + */ + static class SystemLoader implements FinalizerLoader { + // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to + // disable + // finding Finalizer on the system class path even if it is there. + @VisibleForTesting + static boolean disabled; + + @Override + public Class loadFinalizer() { + if (disabled) { + return null; + } + ClassLoader systemLoader; + try { + systemLoader = ClassLoader.getSystemClassLoader(); + } catch (SecurityException e) { + logger.info("Not allowed to access system class loader."); + return null; + } + if (systemLoader != null) { + try { + return systemLoader.loadClass(FINALIZER_CLASS_NAME); + } catch (ClassNotFoundException e) { + // Ignore. Finalizer is simply in a child class loader. + return null; + } + } else { + return null; + } + } + } + + /** + * Try to load Finalizer in its own class loader. If Finalizer's thread had a + * direct reference to our class loader (which could be that of a dynamically + * loaded web application or OSGi bundle), it would prevent our class loader + * from getting garbage collected. + */ + static class DecoupledLoader implements FinalizerLoader { + private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader." + + "Loading Finalizer in the current class loader instead. As a result, you will not be able" + + "to garbage collect this class loader. To support reclaiming this class loader, either" + + "resolve the underlying issue, or move Google Collections to your system class path."; + + @Override + public Class loadFinalizer() { + try { + /* + * We use URLClassLoader because it's the only concrete class loader + * implementation in the JDK. If we used our own ClassLoader subclass, Finalizer + * would indirectly reference this class loader: + * + * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class + * loader + * + * System class loader will (and must) be the parent. + */ + ClassLoader finalizerLoader = newLoader(getBaseUrl()); + return finalizerLoader.loadClass(FINALIZER_CLASS_NAME); + } catch (Exception e) { + logger.log(Level.WARNING, LOADING_ERROR, e); + return null; + } + } + + /** + * Gets URL for base of path containing Finalizer.class. + */ + URL getBaseUrl() throws IOException { + // Find URL pointing to Finalizer.class file. + String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class"; + URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath); + if (finalizerUrl == null) { + throw new FileNotFoundException(finalizerPath); + } + + // Find URL pointing to base of class path. + String urlString = finalizerUrl.toString(); + if (!urlString.endsWith(finalizerPath)) { + throw new IOException("Unsupported path style: " + urlString); + } + urlString = urlString.substring(0, urlString.length() - finalizerPath.length()); + return new URL(finalizerUrl, urlString); + } + + /** Creates a class loader with the given base URL as its classpath. */ + URLClassLoader newLoader(URL base) { + // We use the bootstrap class loader as the parent because Finalizer by design + // uses + // only standard Java classes. That also means that + // FinalizableReferenceQueueTest + // doesn't pick up the wrong version of the Finalizer class. + return new URLClassLoader(new URL[] { base }, null); + } + } + + /** + * Loads Finalizer directly using the current class loader. We won't be able to + * garbage collect this class loader, but at least the world doesn't end. + */ + static class DirectLoader implements FinalizerLoader { + @Override + public Class loadFinalizer() { + try { + return Class.forName(FINALIZER_CLASS_NAME); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + } + + /** + * Looks up Finalizer.startFinalizer(). + */ + static Method getStartFinalizer(Class finalizer) { + try { + return finalizer.getMethod("startFinalizer", Class.class, ReferenceQueue.class, PhantomReference.class); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } +} diff --git a/sources/main/java/com/google/common/base/FinalizableSoftReference.java b/sources/main/java/com/google/common/base/FinalizableSoftReference.java new file mode 100644 index 0000000..88903fa --- /dev/null +++ b/sources/main/java/com/google/common/base/FinalizableSoftReference.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; + +/** + * Soft reference with a {@code finalizeReferent()} method which a background + * thread invokes after the garbage collector reclaims the referent. This is a + * simpler alternative to using a {@link ReferenceQueue}. + * + * @author Bob Lee + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class FinalizableSoftReference extends SoftReference implements FinalizableReference { + /** + * Constructs a new finalizable soft reference. + * + * @param referent to softly reference + * @param queue that should finalize the referent + */ + protected FinalizableSoftReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/sources/main/java/com/google/common/base/FinalizableWeakReference.java b/sources/main/java/com/google/common/base/FinalizableWeakReference.java new file mode 100644 index 0000000..d99c551 --- /dev/null +++ b/sources/main/java/com/google/common/base/FinalizableWeakReference.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +/** + * Weak reference with a {@code finalizeReferent()} method which a background + * thread invokes after the garbage collector reclaims the referent. This is a + * simpler alternative to using a {@link ReferenceQueue}. + * + * @author Bob Lee + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class FinalizableWeakReference extends WeakReference implements FinalizableReference { + /** + * Constructs a new finalizable weak reference. + * + * @param referent to weakly reference + * @param queue that should finalize the referent + */ + protected FinalizableWeakReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/sources/main/java/com/google/common/base/Function.java b/sources/main/java/com/google/common/base/Function.java new file mode 100644 index 0000000..6dc2185 --- /dev/null +++ b/sources/main/java/com/google/common/base/Function.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Determines an output value based on an input value. + * + *

+ * The {@link Functions} class provides common functions and related utilites. + * + *

+ * See the Guava User Guide article on the use + * of {@code + * Function}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface Function { + /** + * Returns the result of applying this function to {@code input}. 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 (a, b)} implies that + * {@code Objects.equal(function.apply(a), + * function.apply(b))}. + *
+ * + * @throws NullPointerException if {@code input} is null and this function does + * not accept null arguments + */ + @Nullable + T apply(@Nullable F input); + + /** + * Indicates whether another object is equal to this function. + * + *

+ * Most implementations will have no reason to override the behavior of + * {@link Object#equals}. However, an implementation may also choose to return + * {@code true} whenever {@code object} is a {@link Function} that it considers + * interchangeable with this one. "Interchangeable" typically + * means that {@code Objects.equal(this.apply(f), that.apply(f))} is true for + * all {@code f} of type {@code F}. Note that a {@code false} result from this + * method does not imply that the functions are known not to be + * interchangeable. + */ + @Override + boolean equals(@Nullable Object object); +} diff --git a/sources/main/java/com/google/common/base/FunctionalEquivalence.java b/sources/main/java/com/google/common/base/FunctionalEquivalence.java new file mode 100644 index 0000000..e8e387d --- /dev/null +++ b/sources/main/java/com/google/common/base/FunctionalEquivalence.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Equivalence applied on functional result. + * + * @author Bob Lee + * @since 10.0 + */ +@Beta +@GwtCompatible +final class FunctionalEquivalence extends Equivalence implements Serializable { + + private static final long serialVersionUID = 0; + + private final Function function; + private final Equivalence resultEquivalence; + + FunctionalEquivalence(Function function, Equivalence resultEquivalence) { + this.function = checkNotNull(function); + this.resultEquivalence = checkNotNull(resultEquivalence); + } + + @Override + protected boolean doEquivalent(F a, F b) { + return resultEquivalence.equivalent(function.apply(a), function.apply(b)); + } + + @Override + protected int doHash(F a) { + return resultEquivalence.hash(function.apply(a)); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof FunctionalEquivalence) { + FunctionalEquivalence that = (FunctionalEquivalence) obj; + return function.equals(that.function) && resultEquivalence.equals(that.resultEquivalence); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(function, resultEquivalence); + } + + @Override + public String toString() { + return resultEquivalence + ".onResultOf(" + function + ")"; + } +} diff --git a/sources/main/java/com/google/common/base/Functions.java b/sources/main/java/com/google/common/base/Functions.java new file mode 100644 index 0000000..28c41ba --- /dev/null +++ b/sources/main/java/com/google/common/base/Functions.java @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Static utility methods pertaining to {@code Function} instances. + * + *

+ * All methods return serializable functions as long as they're given + * serializable parameters. + * + *

+ * See the Guava User Guide article on the use + * of {@code + * Function}. + * + * @author Mike Bostock + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public final class Functions { + private Functions() { + } + + /** + * Returns a function that calls {@code toString()} on its argument. The + * function does not accept nulls; it will throw a {@link NullPointerException} + * when applied to {@code null}. + * + *

+ * Warning: The returned function may not be consistent with + * equals (as documented at {@link Function#apply}). For example, this + * function yields different results for the two equal instances + * {@code ImmutableSet.of(1, 2)} and {@code ImmutableSet.of(2, 1)}. + */ + public static Function toStringFunction() { + return ToStringFunction.INSTANCE; + } + + // enum singleton pattern + private enum ToStringFunction implements Function { + INSTANCE; + + @Override + public String apply(Object o) { + checkNotNull(o); // eager for GWT. + return o.toString(); + } + + @Override + public String toString() { + return "toString"; + } + } + + /** + * Returns the identity function. + */ + // implementation is "fully variant"; E has become a "pass-through" type + @SuppressWarnings("unchecked") + public static Function identity() { + return (Function) IdentityFunction.INSTANCE; + } + + // enum singleton pattern + private enum IdentityFunction implements Function { + INSTANCE; + + @Override + @Nullable + public Object apply(@Nullable Object o) { + return o; + } + + @Override + public String toString() { + return "identity"; + } + } + + /** + * Returns a function which performs a map lookup. The returned function throws + * an {@link IllegalArgumentException} if given a key that does not exist in the + * map. See also {@link #forMap(Map, Object)}, which returns a default value in + * this case. + * + *

+ * Note: if {@code map} is a {@link com.google.common.collect.BiMap BiMap} (or + * can be one), you can use {@link com.google.common.collect.Maps#asConverter + * Maps.asConverter} instead to get a function that also supports reverse + * conversion. + */ + public static Function forMap(Map map) { + return new FunctionForMapNoDefault(map); + } + + private static class FunctionForMapNoDefault implements Function, Serializable { + final Map map; + + FunctionForMapNoDefault(Map map) { + this.map = checkNotNull(map); + } + + @Override + public V apply(@Nullable K key) { + V result = map.get(key); + checkArgument(result != null || map.containsKey(key), "Key '%s' not present in map", key); + return result; + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof FunctionForMapNoDefault) { + FunctionForMapNoDefault that = (FunctionForMapNoDefault) o; + return map.equals(that.map); + } + return false; + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public String toString() { + return "forMap(" + map + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a function which performs a map lookup with a default value. The + * function created by this method returns {@code defaultValue} for all inputs + * that do not belong to the map's key set. See also {@link #forMap(Map)}, which + * throws an exception in this case. + * + * @param map source map that determines the function behavior + * @param defaultValue the value to return for inputs that aren't map keys + * @return function that returns {@code map.get(a)} when {@code a} is a key, or + * {@code + * defaultValue} otherwise + */ + public static Function forMap(Map map, @Nullable V defaultValue) { + return new ForMapWithDefault(map, defaultValue); + } + + private static class ForMapWithDefault implements Function, Serializable { + final Map map; + final V defaultValue; + + ForMapWithDefault(Map map, @Nullable V defaultValue) { + this.map = checkNotNull(map); + this.defaultValue = defaultValue; + } + + @Override + public V apply(@Nullable K key) { + V result = map.get(key); + return (result != null || map.containsKey(key)) ? result : defaultValue; + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof ForMapWithDefault) { + ForMapWithDefault that = (ForMapWithDefault) o; + return map.equals(that.map) && Objects.equal(defaultValue, that.defaultValue); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(map, defaultValue); + } + + @Override + public String toString() { + return "forMap(" + map + ", defaultValue=" + defaultValue + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the composition of two functions. For {@code f: A->B} and + * {@code g: B->C}, composition is defined as the function h such that + * {@code h(a) == g(f(a))} for each {@code a}. + * + * @param g the second function to apply + * @param f the first function to apply + * @return the composition of {@code f} and {@code g} + * @see function + * composition + */ + public static Function compose(Function g, Function f) { + return new FunctionComposition(g, f); + } + + private static class FunctionComposition implements Function, Serializable { + private final Function g; + private final Function f; + + public FunctionComposition(Function g, Function f) { + this.g = checkNotNull(g); + this.f = checkNotNull(f); + } + + @Override + public C apply(@Nullable A a) { + return g.apply(f.apply(a)); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof FunctionComposition) { + FunctionComposition that = (FunctionComposition) obj; + return f.equals(that.f) && g.equals(that.g); + } + return false; + } + + @Override + public int hashCode() { + return f.hashCode() ^ g.hashCode(); + } + + @Override + public String toString() { + return g + "(" + f + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates a function that returns the same boolean output as the given + * predicate for all inputs. + * + *

+ * The returned function is consistent with equals (as documented at + * {@link Function#apply}) if and only if {@code predicate} is itself consistent + * with equals. + */ + public static Function forPredicate(Predicate predicate) { + return new PredicateFunction(predicate); + } + + /** @see Functions#forPredicate */ + private static class PredicateFunction implements Function, Serializable { + private final Predicate predicate; + + private PredicateFunction(Predicate predicate) { + this.predicate = checkNotNull(predicate); + } + + @Override + public Boolean apply(@Nullable T t) { + return predicate.apply(t); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof PredicateFunction) { + PredicateFunction that = (PredicateFunction) obj; + return predicate.equals(that.predicate); + } + return false; + } + + @Override + public int hashCode() { + return predicate.hashCode(); + } + + @Override + public String toString() { + return "forPredicate(" + predicate + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates a function that returns {@code value} for any input. + * + * @param value the constant value for the function to return + * @return a function that always returns {@code value} + */ + public static Function constant(@Nullable E value) { + return new ConstantFunction(value); + } + + private static class ConstantFunction implements Function, Serializable { + private final E value; + + public ConstantFunction(@Nullable E value) { + this.value = value; + } + + @Override + public E apply(@Nullable Object from) { + return value; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof ConstantFunction) { + ConstantFunction that = (ConstantFunction) obj; + return Objects.equal(value, that.value); + } + return false; + } + + @Override + public int hashCode() { + return (value == null) ? 0 : value.hashCode(); + } + + @Override + public String toString() { + return "constant(" + value + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a function that always returns the result of invoking + * {@link Supplier#get} on {@code + * supplier}, regardless of its input. + * + * @since 10.0 + */ + @Beta + public static Function forSupplier(Supplier supplier) { + return new SupplierFunction(supplier); + } + + /** @see Functions#forSupplier */ + private static class SupplierFunction implements Function, Serializable { + + private final Supplier supplier; + + private SupplierFunction(Supplier supplier) { + this.supplier = checkNotNull(supplier); + } + + @Override + public T apply(@Nullable Object input) { + return supplier.get(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof SupplierFunction) { + SupplierFunction that = (SupplierFunction) obj; + return this.supplier.equals(that.supplier); + } + return false; + } + + @Override + public int hashCode() { + return supplier.hashCode(); + } + + @Override + public String toString() { + return "forSupplier(" + supplier + ")"; + } + + private static final long serialVersionUID = 0; + } +} diff --git a/sources/main/java/com/google/common/base/Joiner.java b/sources/main/java/com/google/common/base/Joiner.java new file mode 100644 index 0000000..7ac6e32 --- /dev/null +++ b/sources/main/java/com/google/common/base/Joiner.java @@ -0,0 +1,501 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * An object which joins pieces of text (specified as an array, + * {@link Iterable}, varargs or even a {@link Map}) with a separator. It either + * appends the results to an {@link Appendable} or returns them as a + * {@link String}. Example: + * + *

+ *    {@code
+ *
+ *   Joiner joiner = Joiner.on("; ").skipNulls();
+ *    . . .
+ *   return joiner.join("Harry", null, "Ron", "Hermione");}
+ * 
+ * + *

+ * This returns the string {@code "Harry; Ron; Hermione"}. Note that all input + * elements are converted to strings using {@link Object#toString()} before + * being appended. + * + *

+ * If neither {@link #skipNulls()} nor {@link #useForNull(String)} is specified, + * the joining methods will throw {@link NullPointerException} if any given + * element is null. + * + *

+ * Warning: joiner instances are always immutable; a configuration method + * such as {@code + * useForNull} has no effect on the instance it is invoked on! You must store + * and use the new joiner instance returned by the method. This makes joiners + * thread-safe, and safe to store as {@code + * static final} constants. + * + *

+ * {
+ * 	@code
+ *
+ * 	// Bad! Do not do this!
+ * 	Joiner joiner = Joiner.on(',');
+ * 	joiner.skipNulls(); // does nothing!
+ * 	return joiner.join("wrong", null, "wrong");
+ * }
+ * 
+ * + *

+ * See the Guava User Guide article on {@code Joiner}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public class Joiner { + /** + * Returns a joiner which automatically places {@code separator} between + * consecutive elements. + */ + public static Joiner on(String separator) { + return new Joiner(separator); + } + + /** + * Returns a joiner which automatically places {@code separator} between + * consecutive elements. + */ + public static Joiner on(char separator) { + return new Joiner(String.valueOf(separator)); + } + + private final String separator; + + private Joiner(String separator) { + this.separator = checkNotNull(separator); + } + + private Joiner(Joiner prototype) { + this.separator = prototype.separator; + } + + /** + * Appends the string representation of each of {@code parts}, using the + * previously configured separator between each, to {@code appendable}. + */ + public A appendTo(A appendable, Iterable parts) throws IOException { + return appendTo(appendable, parts.iterator()); + } + + /** + * Appends the string representation of each of {@code parts}, using the + * previously configured separator between each, to {@code appendable}. + * + * @since 11.0 + */ + public A appendTo(A appendable, Iterator parts) throws IOException { + checkNotNull(appendable); + if (parts.hasNext()) { + appendable.append(toString(parts.next())); + while (parts.hasNext()) { + appendable.append(separator); + appendable.append(toString(parts.next())); + } + } + return appendable; + } + + /** + * Appends the string representation of each of {@code parts}, using the + * previously configured separator between each, to {@code appendable}. + */ + public final A appendTo(A appendable, Object[] parts) throws IOException { + return appendTo(appendable, Arrays.asList(parts)); + } + + /** + * Appends to {@code appendable} the string representation of each of the + * remaining arguments. + */ + public final A appendTo(A appendable, @Nullable Object first, @Nullable Object second, + Object... rest) throws IOException { + return appendTo(appendable, iterable(first, second, rest)); + } + + /** + * Appends the string representation of each of {@code parts}, using the + * previously configured separator between each, to {@code builder}. Identical + * to {@link #appendTo(Appendable, Iterable)}, except that it does not throw + * {@link IOException}. + */ + public final StringBuilder appendTo(StringBuilder builder, Iterable parts) { + return appendTo(builder, parts.iterator()); + } + + /** + * Appends the string representation of each of {@code parts}, using the + * previously configured separator between each, to {@code builder}. Identical + * to {@link #appendTo(Appendable, Iterable)}, except that it does not throw + * {@link IOException}. + * + * @since 11.0 + */ + public final StringBuilder appendTo(StringBuilder builder, Iterator parts) { + try { + appendTo((Appendable) builder, parts); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + return builder; + } + + /** + * Appends the string representation of each of {@code parts}, using the + * previously configured separator between each, to {@code builder}. Identical + * to {@link #appendTo(Appendable, Iterable)}, except that it does not throw + * {@link IOException}. + */ + public final StringBuilder appendTo(StringBuilder builder, Object[] parts) { + return appendTo(builder, Arrays.asList(parts)); + } + + /** + * Appends to {@code builder} the string representation of each of the remaining + * arguments. Identical to + * {@link #appendTo(Appendable, Object, Object, Object...)}, except that it does + * not throw {@link IOException}. + */ + public final StringBuilder appendTo(StringBuilder builder, @Nullable Object first, @Nullable Object second, + Object... rest) { + return appendTo(builder, iterable(first, second, rest)); + } + + /** + * Returns a string containing the string representation of each of + * {@code parts}, using the previously configured separator between each. + */ + public final String join(Iterable parts) { + return join(parts.iterator()); + } + + /** + * Returns a string containing the string representation of each of + * {@code parts}, using the previously configured separator between each. + * + * @since 11.0 + */ + public final String join(Iterator parts) { + return appendTo(new StringBuilder(), parts).toString(); + } + + /** + * Returns a string containing the string representation of each of + * {@code parts}, using the previously configured separator between each. + */ + public final String join(Object[] parts) { + return join(Arrays.asList(parts)); + } + + /** + * Returns a string containing the string representation of each argument, using + * the previously configured separator between each. + */ + public final String join(@Nullable Object first, @Nullable Object second, Object... rest) { + return join(iterable(first, second, rest)); + } + + /** + * Returns a joiner with the same behavior as this one, except automatically + * substituting {@code + * nullText} for any provided null elements. + */ + @CheckReturnValue + public Joiner useForNull(final String nullText) { + checkNotNull(nullText); + return new Joiner(this) { + @Override + CharSequence toString(@Nullable Object part) { + return (part == null) ? nullText : Joiner.this.toString(part); + } + + @Override + public Joiner useForNull(String nullText) { + throw new UnsupportedOperationException("already specified useForNull"); + } + + @Override + public Joiner skipNulls() { + throw new UnsupportedOperationException("already specified useForNull"); + } + }; + } + + /** + * Returns a joiner with the same behavior as this joiner, except automatically + * skipping over any provided null elements. + */ + @CheckReturnValue + public Joiner skipNulls() { + return new Joiner(this) { + @Override + public A appendTo(A appendable, Iterator parts) throws IOException { + checkNotNull(appendable, "appendable"); + checkNotNull(parts, "parts"); + while (parts.hasNext()) { + Object part = parts.next(); + if (part != null) { + appendable.append(Joiner.this.toString(part)); + break; + } + } + while (parts.hasNext()) { + Object part = parts.next(); + if (part != null) { + appendable.append(separator); + appendable.append(Joiner.this.toString(part)); + } + } + return appendable; + } + + @Override + public Joiner useForNull(String nullText) { + throw new UnsupportedOperationException("already specified skipNulls"); + } + + @Override + public MapJoiner withKeyValueSeparator(String kvs) { + throw new UnsupportedOperationException("can't use .skipNulls() with maps"); + } + }; + } + + /** + * Returns a {@code MapJoiner} using the given key-value separator, and the same + * configuration as this {@code Joiner} otherwise. + */ + @CheckReturnValue + public MapJoiner withKeyValueSeparator(String keyValueSeparator) { + return new MapJoiner(this, keyValueSeparator); + } + + /** + * An object that joins map entries in the same manner as {@code Joiner} joins + * iterables and arrays. Like {@code Joiner}, it is thread-safe and immutable. + * + *

+ * In addition to operating on {@code Map} instances, {@code MapJoiner} can + * operate on {@code + * Multimap} entries in two distinct modes: + * + *

+ * + * @since 2.0 (imported from Google Collections Library) + */ + public static final class MapJoiner { + private final Joiner joiner; + private final String keyValueSeparator; + + private MapJoiner(Joiner joiner, String keyValueSeparator) { + this.joiner = joiner; // only "this" is ever passed, so don't checkNotNull + this.keyValueSeparator = checkNotNull(keyValueSeparator); + } + + /** + * Appends the string representation of each entry of {@code map}, using the + * previously configured separator and key-value separator, to + * {@code appendable}. + */ + public A appendTo(A appendable, Map map) throws IOException { + return appendTo(appendable, map.entrySet()); + } + + /** + * Appends the string representation of each entry of {@code map}, using the + * previously configured separator and key-value separator, to {@code builder}. + * Identical to {@link #appendTo(Appendable, Map)}, except that it does not + * throw {@link IOException}. + */ + public StringBuilder appendTo(StringBuilder builder, Map map) { + return appendTo(builder, map.entrySet()); + } + + /** + * Returns a string containing the string representation of each entry of + * {@code map}, using the previously configured separator and key-value + * separator. + */ + public String join(Map map) { + return join(map.entrySet()); + } + + /** + * Appends the string representation of each entry in {@code entries}, using the + * previously configured separator and key-value separator, to + * {@code appendable}. + * + * @since 10.0 + */ + @Beta + public A appendTo(A appendable, Iterable> entries) + throws IOException { + return appendTo(appendable, entries.iterator()); + } + + /** + * Appends the string representation of each entry in {@code entries}, using the + * previously configured separator and key-value separator, to + * {@code appendable}. + * + * @since 11.0 + */ + @Beta + public A appendTo(A appendable, Iterator> parts) + throws IOException { + checkNotNull(appendable); + if (parts.hasNext()) { + Entry entry = parts.next(); + appendable.append(joiner.toString(entry.getKey())); + appendable.append(keyValueSeparator); + appendable.append(joiner.toString(entry.getValue())); + while (parts.hasNext()) { + appendable.append(joiner.separator); + Entry e = parts.next(); + appendable.append(joiner.toString(e.getKey())); + appendable.append(keyValueSeparator); + appendable.append(joiner.toString(e.getValue())); + } + } + return appendable; + } + + /** + * Appends the string representation of each entry in {@code entries}, using the + * previously configured separator and key-value separator, to {@code builder}. + * Identical to {@link #appendTo(Appendable, Iterable)}, except that it does not + * throw {@link IOException}. + * + * @since 10.0 + */ + @Beta + public StringBuilder appendTo(StringBuilder builder, Iterable> entries) { + return appendTo(builder, entries.iterator()); + } + + /** + * Appends the string representation of each entry in {@code entries}, using the + * previously configured separator and key-value separator, to {@code builder}. + * Identical to {@link #appendTo(Appendable, Iterable)}, except that it does not + * throw {@link IOException}. + * + * @since 11.0 + */ + @Beta + public StringBuilder appendTo(StringBuilder builder, Iterator> entries) { + try { + appendTo((Appendable) builder, entries); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + return builder; + } + + /** + * Returns a string containing the string representation of each entry in + * {@code entries}, using the previously configured separator and key-value + * separator. + * + * @since 10.0 + */ + @Beta + public String join(Iterable> entries) { + return join(entries.iterator()); + } + + /** + * Returns a string containing the string representation of each entry in + * {@code entries}, using the previously configured separator and key-value + * separator. + * + * @since 11.0 + */ + @Beta + public String join(Iterator> entries) { + return appendTo(new StringBuilder(), entries).toString(); + } + + /** + * Returns a map joiner with the same behavior as this one, except automatically + * substituting {@code nullText} for any provided null keys or values. + */ + @CheckReturnValue + public MapJoiner useForNull(String nullText) { + return new MapJoiner(joiner.useForNull(nullText), keyValueSeparator); + } + } + + CharSequence toString(Object part) { + checkNotNull(part); // checkNotNull for GWT (do not optimize). + return (part instanceof CharSequence) ? (CharSequence) part : part.toString(); + } + + private static Iterable iterable(final Object first, final Object second, final Object[] rest) { + checkNotNull(rest); + return new AbstractList() { + @Override + public int size() { + return rest.length + 2; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + default: + return rest[index - 2]; + } + } + }; + } +} diff --git a/sources/main/java/com/google/common/base/Objects.java b/sources/main/java/com/google/common/base/Objects.java new file mode 100644 index 0000000..def8ea0 --- /dev/null +++ b/sources/main/java/com/google/common/base/Objects.java @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Arrays; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Helper functions that can operate on any {@code Object}. + * + *

+ * See the Guava User Guide on writing + * {@code Object} methods with {@code Objects}. + * + * @author Laurence Gonsalves + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public final class Objects { + private Objects() { + } + + /** + * Determines whether two possibly-null objects are equal. Returns: + * + *

    + *
  • {@code true} if {@code a} and {@code b} are both null. + *
  • {@code true} if {@code a} and {@code b} are both non-null and they are + * equal according to {@link Object#equals(Object)}. + *
  • {@code false} in all other situations. + *
+ * + *

+ * This assumes that any non-null objects passed to this function conform to the + * {@code equals()} contract. + */ + @CheckReturnValue + public static boolean equal(@Nullable Object a, @Nullable Object b) { + return a == b || (a != null && a.equals(b)); + } + + /** + * Generates a hash code for multiple values. The hash code is generated by + * calling {@link Arrays#hashCode(Object[])}. Note that array arguments to this + * method, with the exception of a single Object array, do not get any special + * handling; their hash codes are based on identity and not contents. + * + *

+ * This is useful for implementing {@link Object#hashCode()}. For example, in an + * object that has three properties, {@code x}, {@code y}, and {@code z}, one + * could write: + * + *

+	 *    {@code
+	 *   public int hashCode() {
+	 *     return Objects.hashCode(getX(), getY(), getZ());
+	 *   }}
+	 * 
+ * + *

+ * Warning: When a single object is supplied, the returned hash code does + * not equal the hash code of that object. + */ + public static int hashCode(@Nullable Object... objects) { + return Arrays.hashCode(objects); + } + + /** + * Creates an instance of {@link ToStringHelper}. + * + *

+ * This is helpful for implementing {@link Object#toString()}. Specification by + * example: + * + *

+	 *    {@code
+	 *   // Returns "ClassName{}"
+	 *   Objects.toStringHelper(this)
+	 *       .toString();
+	 *
+	 *   // Returns "ClassName{x=1}"
+	 *   Objects.toStringHelper(this)
+	 *       .add("x", 1)
+	 *       .toString();
+	 *
+	 *   // Returns "MyObject{x=1}"
+	 *   Objects.toStringHelper("MyObject")
+	 *       .add("x", 1)
+	 *       .toString();
+	 *
+	 *   // Returns "ClassName{x=1, y=foo}"
+	 *   Objects.toStringHelper(this)
+	 *       .add("x", 1)
+	 *       .add("y", "foo")
+	 *       .toString();
+	 *
+	 *   // Returns "ClassName{x=1}"
+	 *   Objects.toStringHelper(this)
+	 *       .omitNullValues()
+	 *       .add("x", 1)
+	 *       .add("y", null)
+	 *       .toString();
+	 *   }}
+	 * 
+ * + *

+ * Note that in GWT, class names are often obfuscated. + * + * @param self the object to generate the string for (typically {@code this}), + * used only for its class name + * @since 2.0 + */ + public static ToStringHelper toStringHelper(Object self) { + return new ToStringHelper(simpleName(self.getClass())); + } + + /** + * Creates an instance of {@link ToStringHelper} in the same manner as + * {@link Objects#toStringHelper(Object)}, but using the name of {@code clazz} + * instead of using an instance's {@link Object#getClass()}. + * + *

+ * Note that in GWT, class names are often obfuscated. + * + * @param clazz the {@link Class} of the instance + * @since 7.0 (source-compatible since 2.0) + */ + public static ToStringHelper toStringHelper(Class clazz) { + return new ToStringHelper(simpleName(clazz)); + } + + /** + * Creates an instance of {@link ToStringHelper} in the same manner as + * {@link Objects#toStringHelper(Object)}, but using {@code className} instead + * of using an instance's {@link Object#getClass()}. + * + * @param className the name of the instance type + * @since 7.0 (source-compatible since 2.0) + */ + public static ToStringHelper toStringHelper(String className) { + return new ToStringHelper(className); + } + + /** + * {@link Class#getSimpleName()} is not GWT compatible yet, so we provide our + * own implementation. + */ + private static String simpleName(Class clazz) { + String name = clazz.getName(); + + // the nth anonymous class has a class name ending in "Outer$n" + // and local inner classes have names ending in "Outer.$1Inner" + name = name.replaceAll("\\$[0-9]+", "\\$"); + + // we want the name of the inner class all by its lonesome + int start = name.lastIndexOf('$'); + + // if this isn't an inner class, just find the start of the + // top level class name. + if (start == -1) { + start = name.lastIndexOf('.'); + } + return name.substring(start + 1); + } + + /** + * Returns the first of two given parameters that is not {@code null}, if either + * is, or otherwise throws a {@link NullPointerException}. + * + *

+ * Note: if {@code first} is represented as an {@link Optional}, this can + * be accomplished with {@linkplain Optional#or(Object) first.or(second)}. That + * approach also allows for lazy evaluation of the fallback instance, using + * {@linkplain Optional#or(Supplier) first.or(Supplier)}. + * + * @return {@code first} if {@code first} is not {@code null}, or {@code second} + * if {@code first} is {@code null} and {@code second} is not + * {@code null} + * @throws NullPointerException if both {@code first} and {@code second} were + * {@code null} + * @since 3.0 + */ + public static T firstNonNull(@Nullable T first, @Nullable T second) { + return first != null ? first : checkNotNull(second); + } + + /** + * Support class for {@link Objects#toStringHelper}. + * + * @author Jason Lee + * @since 2.0 + */ + public static final class ToStringHelper { + private final String className; + private ValueHolder holderHead = new ValueHolder(); + private ValueHolder holderTail = holderHead; + private boolean omitNullValues = false; + + /** + * Use {@link Objects#toStringHelper(Object)} to create an instance. + */ + private ToStringHelper(String className) { + this.className = checkNotNull(className); + } + + /** + * Configures the {@link ToStringHelper} so {@link #toString()} will ignore + * properties with null value. The order of calling this method, relative to the + * {@code add()}/{@code addValue()} methods, is not significant. + * + * @since 12.0 + */ + public ToStringHelper omitNullValues() { + omitNullValues = true; + return this; + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * If {@code value} is {@code null}, the string {@code "null"} is used, unless + * {@link #omitNullValues()} is called, in which case this name/value pair will + * not be added. + */ + public ToStringHelper add(String name, @Nullable Object value) { + return addHolder(name, value); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper add(String name, boolean value) { + return addHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper add(String name, char value) { + return addHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper add(String name, double value) { + return addHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper add(String name, float value) { + return addHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper add(String name, int value) { + return addHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper add(String name, long value) { + return addHolder(name, String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

+ * It is strongly encouraged to use {@link #add(String, Object)} instead and + * give value a readable name. + */ + public ToStringHelper addValue(@Nullable Object value) { + return addHolder(value); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

+ * It is strongly encouraged to use {@link #add(String, boolean)} instead and + * give value a readable name. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper addValue(boolean value) { + return addHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

+ * It is strongly encouraged to use {@link #add(String, char)} instead and give + * value a readable name. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper addValue(char value) { + return addHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

+ * It is strongly encouraged to use {@link #add(String, double)} instead and + * give value a readable name. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper addValue(double value) { + return addHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

+ * It is strongly encouraged to use {@link #add(String, float)} instead and give + * value a readable name. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper addValue(float value) { + return addHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

+ * It is strongly encouraged to use {@link #add(String, int)} instead and give + * value a readable name. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper addValue(int value) { + return addHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

+ * It is strongly encouraged to use {@link #add(String, long)} instead and give + * value a readable name. + * + * @since 11.0 (source-compatible since 2.0) + */ + public ToStringHelper addValue(long value) { + return addHolder(String.valueOf(value)); + } + + /** + * Returns a string in the format specified by + * {@link Objects#toStringHelper(Object)}. + * + *

+ * After calling this method, you can keep adding more properties to later call + * toString() again and get a more complete representation of the same object; + * but properties cannot be removed, so this only allows limited reuse of the + * helper instance. The helper allows duplication of properties (multiple + * name/value pairs with the same name can be added). + */ + @Override + public String toString() { + // create a copy to keep it consistent in case value changes + boolean omitNullValuesSnapshot = omitNullValues; + String nextSeparator = ""; + StringBuilder builder = new StringBuilder(32).append(className).append('{'); + for (ValueHolder valueHolder = holderHead.next; valueHolder != null; valueHolder = valueHolder.next) { + if (!omitNullValuesSnapshot || valueHolder.value != null) { + builder.append(nextSeparator); + nextSeparator = ", "; + + if (valueHolder.name != null) { + builder.append(valueHolder.name).append('='); + } + builder.append(valueHolder.value); + } + } + return builder.append('}').toString(); + } + + private ValueHolder addHolder() { + ValueHolder valueHolder = new ValueHolder(); + holderTail = holderTail.next = valueHolder; + return valueHolder; + } + + private ToStringHelper addHolder(@Nullable Object value) { + ValueHolder valueHolder = addHolder(); + valueHolder.value = value; + return this; + } + + private ToStringHelper addHolder(String name, @Nullable Object value) { + ValueHolder valueHolder = addHolder(); + valueHolder.value = value; + valueHolder.name = checkNotNull(name); + return this; + } + + private static final class ValueHolder { + String name; + Object value; + ValueHolder next; + } + } +} diff --git a/sources/main/java/com/google/common/base/Optional.java b/sources/main/java/com/google/common/base/Optional.java new file mode 100644 index 0000000..43b8eb2 --- /dev/null +++ b/sources/main/java/com/google/common/base/Optional.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * An immutable object that may contain a non-null reference to another object. + * Each instance of this type either contains a non-null reference, or contains + * nothing (in which case we say that the reference is "absent"); it is never + * said to "contain {@code + * null}". + * + *

+ * A non-null {@code Optional} reference can be used as a replacement for a + * nullable {@code T} reference. It allows you to represent "a {@code T} that + * must be present" and a "a {@code T} that might be absent" as two distinct + * types in your program, which can aid clarity. + * + *

+ * Some uses of this class include + * + *

    + *
  • As a method return type, as an alternative to returning {@code null} to + * indicate that no value was available + *
  • To distinguish between "unknown" (for example, not present in a map) and + * "known to have no value" (present in the map, with value + * {@code Optional.absent()}) + *
  • To wrap nullable references for storage in a collection that does not + * support {@code null} (though there are + * several other approaches to this that should be considered first) + *
+ * + *

+ * A common alternative to using this class is to find or create a suitable + * null object + * for the type in question. + * + *

+ * This class is not intended as a direct analogue of any existing "option" or + * "maybe" construct from other programming environments, though it may bear + * some similarities. + * + *

+ * See the Guava User Guide article on + * using {@code Optional}. + * + * @param the type of instance that can be contained. {@code Optional} is + * naturally covariant on this type, so it is safe to cast an + * {@code Optional} to {@code + * Optional} for any supertype {@code S} of {@code T}. + * @author Kurt Alfred Kluever + * @author Kevin Bourrillion + * @since 10.0 + */ +@GwtCompatible(serializable = true) +public abstract class Optional implements Serializable { + /** + * Returns an {@code Optional} instance with no contained reference. + */ + public static Optional absent() { + return Absent.withType(); + } + + /** + * Returns an {@code Optional} instance containing the given non-null reference. + */ + public static Optional of(T reference) { + return new Present(checkNotNull(reference)); + } + + /** + * If {@code nullableReference} is non-null, returns an {@code Optional} + * instance containing that reference; otherwise returns + * {@link Optional#absent}. + */ + public static Optional fromNullable(@Nullable T nullableReference) { + return (nullableReference == null) ? Optional.absent() : new Present(nullableReference); + } + + Optional() { + } + + /** + * Returns {@code true} if this holder contains a (non-null) instance. + */ + public abstract boolean isPresent(); + + /** + * Returns the contained instance, which must be present. If the instance might + * be absent, use {@link #or(Object)} or {@link #orNull} instead. + * + * @throws IllegalStateException if the instance is absent ({@link #isPresent} + * returns {@code false}) + */ + public abstract T get(); + + /** + * Returns the contained instance if it is present; {@code defaultValue} + * otherwise. If no default value should be required because the instance is + * known to be present, use {@link #get()} instead. For a default value of + * {@code null}, use {@link #orNull}. + * + *

+ * Note about generics: The signature {@code public T or(T defaultValue)} is + * overly restrictive. However, the ideal signature, + * {@code public S or(S)}, is not legal Java. As a result, some + * sensible operations involving subtypes are compile errors: + * + *

+	 *    {@code
+	 *
+	 *   Optional optionalInt = getSomeOptionalInt();
+	 *   Number value = optionalInt.or(0.5); // error
+	 *
+	 *   FluentIterable numbers = getSomeNumbers();
+	 *   Optional first = numbers.first();
+	 *   Number value = first.or(0.5); // error}
+	 * 
+ * + *

+ * As a workaround, it is always safe to cast an {@code Optional} + * to {@code + * Optional}. Casting either of the above example {@code Optional} instances + * to {@code + * Optional} (where {@code Number} is the desired output type) solves + * the problem: + * + *

+	 *    {@code
+	 *
+	 *   Optional optionalInt = (Optional) getSomeOptionalInt();
+	 *   Number value = optionalInt.or(0.5); // fine
+	 *
+	 *   FluentIterable numbers = getSomeNumbers();
+	 *   Optional first = (Optional) numbers.first();
+	 *   Number value = first.or(0.5); // fine}
+	 * 
+ */ + public abstract T or(T defaultValue); + + /** + * Returns this {@code Optional} if it has a value present; {@code secondChoice} + * otherwise. + */ + public abstract Optional or(Optional secondChoice); + + /** + * Returns the contained instance if it is present; {@code supplier.get()} + * otherwise. If the supplier returns {@code null}, a + * {@link NullPointerException} is thrown. + * + * @throws NullPointerException if the supplier returns {@code null} + */ + @Beta + public abstract T or(Supplier supplier); + + /** + * Returns the contained instance if it is present; {@code null} otherwise. If + * the instance is known to be present, use {@link #get()} instead. + */ + @Nullable + public abstract T orNull(); + + /** + * Returns an immutable singleton {@link Set} whose only element is the + * contained instance if it is present; an empty immutable {@link Set} + * otherwise. + * + * @since 11.0 + */ + public abstract Set asSet(); + + /** + * If the instance is present, it is transformed with the given + * {@link Function}; otherwise, {@link Optional#absent} is returned. If the + * function returns {@code null}, a {@link NullPointerException} is thrown. + * + * @throws NullPointerException if the function returns {@code null} + * + * @since 12.0 + */ + public abstract Optional transform(Function function); + + /** + * Returns {@code true} if {@code object} is an {@code Optional} instance, and + * either the contained references are {@linkplain Object#equals equal} to each + * other or both are absent. Note that {@code Optional} instances of differing + * parameterized types can be equal. + */ + @Override + public abstract boolean equals(@Nullable Object object); + + /** + * Returns a hash code for this instance. + */ + @Override + public abstract int hashCode(); + + /** + * Returns a string representation for this instance. The form of this string + * representation is unspecified. + */ + @Override + public abstract String toString(); + + /** + * Returns the value of each present instance from the supplied + * {@code optionals}, in order, skipping over occurrences of + * {@link Optional#absent}. Iterators are unmodifiable and are evaluated lazily. + * + * @since 11.0 (generics widened in 13.0) + */ + @Beta + public static Iterable presentInstances(final Iterable> optionals) { + checkNotNull(optionals); + return new Iterable() { + @Override + public Iterator iterator() { + return new AbstractIterator() { + private final Iterator> iterator = checkNotNull( + optionals.iterator()); + + @Override + protected T computeNext() { + while (iterator.hasNext()) { + Optional optional = iterator.next(); + if (optional.isPresent()) { + return optional.get(); + } + } + return endOfData(); + } + }; + } + }; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/base/PairwiseEquivalence.java b/sources/main/java/com/google/common/base/PairwiseEquivalence.java new file mode 100644 index 0000000..f3528d4 --- /dev/null +++ b/sources/main/java/com/google/common/base/PairwiseEquivalence.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.io.Serializable; +import java.util.Iterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible(serializable = true) +final class PairwiseEquivalence extends Equivalence> implements Serializable { + + final Equivalence elementEquivalence; + + PairwiseEquivalence(Equivalence elementEquivalence) { + this.elementEquivalence = Preconditions.checkNotNull(elementEquivalence); + } + + @Override + protected boolean doEquivalent(Iterable iterableA, Iterable iterableB) { + Iterator iteratorA = iterableA.iterator(); + Iterator iteratorB = iterableB.iterator(); + + while (iteratorA.hasNext() && iteratorB.hasNext()) { + if (!elementEquivalence.equivalent(iteratorA.next(), iteratorB.next())) { + return false; + } + } + + return !iteratorA.hasNext() && !iteratorB.hasNext(); + } + + @Override + protected int doHash(Iterable iterable) { + int hash = 78721; + for (T element : iterable) { + hash = hash * 24943 + elementEquivalence.hash(element); + } + return hash; + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof PairwiseEquivalence) { + PairwiseEquivalence that = (PairwiseEquivalence) object; + return this.elementEquivalence.equals(that.elementEquivalence); + } + + return false; + } + + @Override + public int hashCode() { + return elementEquivalence.hashCode() ^ 0x46a3eb07; + } + + @Override + public String toString() { + return elementEquivalence + ".pairwise()"; + } + + private static final long serialVersionUID = 1; +} diff --git a/sources/main/java/com/google/common/base/Platform.java b/sources/main/java/com/google/common/base/Platform.java new file mode 100644 index 0000000..04b2fdd --- /dev/null +++ b/sources/main/java/com/google/common/base/Platform.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.lang.ref.WeakReference; + +import com.google.common.annotations.GwtCompatible; + +/** + * Methods factored out so that they can be emulated differently in GWT. + * + * @author Jesse Wilson + */ +@GwtCompatible(emulated = true) +final class Platform { + private Platform() { + } + + /** Calls {@link System#nanoTime()}. */ + static long systemNanoTime() { + return System.nanoTime(); + } + + static CharMatcher precomputeCharMatcher(CharMatcher matcher) { + return matcher.precomputedInternal(); + } + + static > Optional getEnumIfPresent(Class enumClass, String value) { + WeakReference> ref = Enums.getEnumConstants(enumClass).get(value); + return ref == null ? Optional.absent() : Optional.of(enumClass.cast(ref.get())); + } +} diff --git a/sources/main/java/com/google/common/base/Preconditions.java b/sources/main/java/com/google/common/base/Preconditions.java new file mode 100644 index 0000000..3ff8b69 --- /dev/null +++ b/sources/main/java/com/google/common/base/Preconditions.java @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.base; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Static convenience methods that help a method or constructor check whether it + * was invoked correctly (whether its preconditions have been met). These + * methods generally accept a {@code boolean} expression which is expected to be + * {@code true} (or in the case of {@code + * checkNotNull}, an object reference which is expected to be non-null). When + * {@code false} (or {@code null}) is passed instead, the {@code Preconditions} + * method throws an unchecked exception, which helps the calling method + * communicate to its caller that that caller has made a mistake. + * Example: + * + *
+ *    {@code
+ *
+ *   /**
+ *    * Returns the positive square root of the given value.
+ *    *
+ *    * @throws IllegalArgumentException if the value is negative
+ *    *}{@code /
+ *   public static double sqrt(double value) {
+ *     Preconditions.checkArgument(value >= 0.0, "negative value: %s", value);
+ *     // calculate the square root
+ *   }
+ *
+ *   void exampleBadCaller() {
+ *     double d = sqrt(-1.0);
+ *   }}
+ * 
+ * + * In this example, {@code checkArgument} throws an + * {@code IllegalArgumentException} to indicate that {@code exampleBadCaller} + * made an error in its call to {@code sqrt}. + * + *

Warning about performance

+ * + *

+ * The goal of this class is to improve readability of code, but in some + * circumstances this may come at a significant performance cost. Remember that + * parameter values for message construction must all be computed eagerly, and + * autoboxing and varargs array creation may happen as well, even when the + * precondition check then succeeds (as it should almost always do in + * production). In some circumstances these wasted CPU cycles and allocations + * can add up to a real problem. Performance-sensitive precondition checks can + * always be converted to the customary form: + * + *

+ *    {@code
+ *
+ *   if (value < 0.0) {
+ *     throw new IllegalArgumentException("negative value: " + value);
+ *   }}
+ * 
+ * + *

Other types of preconditions

+ * + *

+ * Not every type of precondition failure is supported by these methods. + * Continue to throw standard JDK exceptions such as + * {@link java.util.NoSuchElementException} or + * {@link UnsupportedOperationException} in the situations they are intended + * for. + * + *

Non-preconditions

+ * + *

+ * It is of course possible to use the methods of this class to check for + * invalid conditions which are not the caller's fault. Doing so is + * not recommended because it is misleading to future readers of the code + * and of stack traces. See Conditional + * failures explained in the Guava User Guide for more advice. + * + *

{@code java.util.Objects.requireNonNull()}

+ * + *

+ * Projects which use {@code com.google.common} should generally avoid the use + * of {@link java.util.Objects#requireNonNull(Object)}. Instead, use whichever + * of {@link #checkNotNull(Object)} or {@link Verify#verifyNotNull(Object)} is + * appropriate to the situation. (The same goes for the message-accepting + * overloads.) + * + *

Only {@code %s} is supported

+ * + *

+ * In {@code Preconditions} error message template strings, only the + * {@code "%s"} specifier is supported, not the full range of + * {@link java.util.Formatter} specifiers. However, note that if the number of + * arguments does not match the number of occurrences of {@code "%s"} in the + * format string, {@code Preconditions} will still behave as expected, and will + * still include all argument values in the error message; the message will + * simply not be formatted exactly as intended. + * + *

More information

+ * + *

+ * See the Guava User Guide on using + * {@code + * Preconditions}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public 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, @Nullable 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, @Nullable String errorMessageTemplate, + @Nullable Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException(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, @Nullable 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, @Nullable String errorMessageTemplate, + @Nullable Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException(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, @Nullable 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, @Nullable String errorMessageTemplate, + @Nullable 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, @Nullable String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); + } + return index; + } + + private static String badElementIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index >= size + return format("%s (%s) must be less than size (%s)", desc, 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, @Nullable String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); + } + return index; + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index > size + return format("%s (%s) 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 format("end index (%s) must not be less than start index (%s)", end, start); + } + + /** + * 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, @Nullable 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/sources/main/java/com/google/common/base/Predicate.java b/sources/main/java/com/google/common/base/Predicate.java new file mode 100644 index 0000000..f3eda00 --- /dev/null +++ b/sources/main/java/com/google/common/base/Predicate.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Determines a true or false value for a given input. + * + *

+ * The {@link Predicates} class provides common predicates and related + * utilities. + * + *

+ * See the Guava User Guide article on the use + * of {@code + * Predicate}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface Predicate { + /** + * Returns the result of applying this predicate to {@code input}. 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 (a, b)} implies that + * {@code predicate.apply(a) == + * predicate.apply(b))}. + *
+ * + * @throws NullPointerException if {@code input} is null and this predicate does + * not accept null arguments + */ + boolean apply(@Nullable T input); + + /** + * Indicates whether another object is equal to this predicate. + * + *

+ * Most implementations will have no reason to override the behavior of + * {@link Object#equals}. However, an implementation may also choose to return + * {@code true} whenever {@code object} is a {@link Predicate} that it considers + * interchangeable with this one. "Interchangeable" typically + * means that {@code this.apply(t) == that.apply(t)} for all {@code t} of type + * {@code T}). Note that a {@code false} result from this method does not imply + * that the predicates are known not to be interchangeable. + */ + @Override + boolean equals(@Nullable Object object); +} diff --git a/sources/main/java/com/google/common/base/Predicates.java b/sources/main/java/com/google/common/base/Predicates.java new file mode 100644 index 0000000..4aa9b7c --- /dev/null +++ b/sources/main/java/com/google/common/base/Predicates.java @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * 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) + */ +@GwtCompatible(emulated = true) +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}. + */ + @GwtCompatible(serializable = true) + public static Predicate alwaysTrue() { + return ObjectPredicate.ALWAYS_TRUE.withNarrowedType(); + } + + /** + * Returns a predicate that always evaluates to {@code false}. + */ + @GwtCompatible(serializable = true) + 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. + */ + @GwtCompatible(serializable = true) + 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. + */ + @GwtCompatible(serializable = true) + 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(checkNotNull(first), checkNotNull(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(@Nullable 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 com.google.common.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)}. + */ + @GwtIncompatible("Class.isInstance") + 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 + */ + @GwtIncompatible("Class.isAssignableFrom") + @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 + */ + @GwtIncompatible(value = "java.util.regex.Pattern") + 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 + */ + @GwtIncompatible(value = "java.util.regex.Pattern") + 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 apply(@Nullable Object o) { + return true; + } + + @Override + public String toString() { + return "Predicates.alwaysTrue()"; + } + }, + /** @see Predicates#alwaysFalse() */ + ALWAYS_FALSE { + @Override + public boolean apply(@Nullable Object o) { + return false; + } + + @Override + public String toString() { + return "Predicates.alwaysFalse()"; + } + }, + /** @see Predicates#isNull() */ + IS_NULL { + @Override + public boolean apply(@Nullable Object o) { + return o == null; + } + + @Override + public String toString() { + return "Predicates.isNull()"; + } + }, + /** @see Predicates#notNull() */ + NOT_NULL { + @Override + public boolean apply(@Nullable Object o) { + return o != null; + } + + @Override + public String toString() { + return "Predicates.notNull()"; + } + }; + + @SuppressWarnings("unchecked") // 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 = checkNotNull(predicate); + } + + @Override + public boolean apply(@Nullable T t) { + return !predicate.apply(t); + } + + @Override + public int hashCode() { + return ~predicate.hashCode(); + } + + @Override + public boolean equals(@Nullable 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 apply(@Nullable T t) { + // Avoid using the Iterator to avoid generating garbage (issue 820). + for (int i = 0; i < components.size(); i++) { + if (!components.get(i).apply(t)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + // add a random number to avoid collisions with OrPredicate + return components.hashCode() + 0x12472c2c; + } + + @Override + public boolean equals(@Nullable 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 apply(@Nullable T t) { + // Avoid using the Iterator to avoid generating garbage (issue 820). + for (int i = 0; i < components.size(); i++) { + if (components.get(i).apply(t)) { + return true; + } + } + return false; + } + + @Override + public int hashCode() { + // add a random number to avoid collisions with AndPredicate + return components.hashCode() + 0x053c91cf; + } + + @Override + public boolean equals(@Nullable 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 apply(T t) { + return target.equals(t); + } + + @Override + public int hashCode() { + return target.hashCode(); + } + + @Override + public boolean equals(@Nullable 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) */ + @GwtIncompatible("Class.isInstance") + private static class InstanceOfPredicate implements Predicate, Serializable { + private final Class clazz; + + private InstanceOfPredicate(Class clazz) { + this.clazz = checkNotNull(clazz); + } + + @Override + public boolean apply(@Nullable Object o) { + return clazz.isInstance(o); + } + + @Override + public int hashCode() { + return clazz.hashCode(); + } + + @Override + public boolean equals(@Nullable 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) */ + @GwtIncompatible("Class.isAssignableFrom") + private static class AssignableFromPredicate implements Predicate>, Serializable { + private final Class clazz; + + private AssignableFromPredicate(Class clazz) { + this.clazz = checkNotNull(clazz); + } + + @Override + public boolean apply(Class input) { + return clazz.isAssignableFrom(input); + } + + @Override + public int hashCode() { + return clazz.hashCode(); + } + + @Override + public boolean equals(@Nullable 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 = checkNotNull(target); + } + + @Override + public boolean apply(@Nullable T t) { + try { + return target.contains(t); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException e) { + return false; + } + } + + @Override + public boolean equals(@Nullable 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 = checkNotNull(p); + this.f = checkNotNull(f); + } + + @Override + public boolean apply(@Nullable A a) { + return p.apply(f.apply(a)); + } + + @Override + public boolean equals(@Nullable 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) */ + @GwtIncompatible("Only used by other GWT-incompatible code.") + private static class ContainsPatternPredicate implements Predicate, Serializable { + final Pattern pattern; + + ContainsPatternPredicate(Pattern pattern) { + this.pattern = checkNotNull(pattern); + } + + @Override + public boolean apply(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 Objects.hashCode(pattern.pattern(), pattern.flags()); + } + + @Override + public boolean equals(@Nullable 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) */ + @GwtIncompatible("Only used by other GWT-incompatible code.") + 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(checkNotNull(element)); + } + return list; + } +} diff --git a/sources/main/java/com/google/common/base/Present.java b/sources/main/java/com/google/common/base/Present.java new file mode 100644 index 0000000..1c19c40 --- /dev/null +++ b/sources/main/java/com/google/common/base/Present.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Implementation of an {@link Optional} containing a reference. + */ +@GwtCompatible +final class Present extends Optional { + private final T reference; + + Present(T reference) { + this.reference = reference; + } + + @Override + public boolean isPresent() { + return true; + } + + @Override + public T get() { + return reference; + } + + @Override + public T or(T defaultValue) { + checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)"); + return reference; + } + + @Override + public Optional or(Optional secondChoice) { + checkNotNull(secondChoice); + return this; + } + + @Override + public T or(Supplier supplier) { + checkNotNull(supplier); + return reference; + } + + @Override + public T orNull() { + return reference; + } + + @Override + public Set asSet() { + return Collections.singleton(reference); + } + + @Override + public Optional transform(Function function) { + return new Present(checkNotNull(function.apply(reference), + "the Function passed to Optional.transform() must not return null.")); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof Present) { + Present other = (Present) object; + return reference.equals(other.reference); + } + return false; + } + + @Override + public int hashCode() { + return 0x598df91c + reference.hashCode(); + } + + @Override + public String toString() { + return "Optional.of(" + reference + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/base/SmallCharMatcher.java b/sources/main/java/com/google/common/base/SmallCharMatcher.java new file mode 100644 index 0000000..24a8d3a --- /dev/null +++ b/sources/main/java/com/google/common/base/SmallCharMatcher.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.util.BitSet; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.CharMatcher.FastMatcher; + +/** + * An immutable version of CharMatcher for smallish sets of characters that uses + * a hash table with linear probing to check for matches. + * + * @author Christopher Swenson + */ +@GwtIncompatible("no precomputation is done in GWT") +final class SmallCharMatcher extends FastMatcher { + static final int MAX_SIZE = 1023; + private final char[] table; + private final boolean containsZero; + private final long filter; + + private SmallCharMatcher(char[] table, long filter, boolean containsZero, String description) { + super(description); + this.table = table; + this.filter = filter; + this.containsZero = containsZero; + } + + private static final int C1 = 0xcc9e2d51; + private static final int C2 = 0x1b873593; + + /* + * This method was rewritten in Java from an intermediate step of the Murmur + * hash function in + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which + * contained the following header: + * + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. The author hereby disclaims copyright to this source code. + */ + static int smear(int hashCode) { + return C2 * Integer.rotateLeft(hashCode * C1, 15); + } + + private boolean checkFilter(int c) { + return 1 == (1 & (filter >> c)); + } + + // This is all essentially copied from ImmutableSet, but we have to duplicate + // because + // of dependencies. + + // Represents how tightly we can pack things, as a maximum. + private static final double DESIRED_LOAD_FACTOR = 0.5; + + /** + * Returns an array size suitable for the backing array of a hash table that + * uses open addressing with linear probing in its implementation. The returned + * size is the smallest power of two that can hold setSize elements with the + * desired load factor. + */ + @VisibleForTesting + static int chooseTableSize(int setSize) { + if (setSize == 1) { + return 2; + } + // Correct the size for open addressing to match desired load factor. + // Round up to the next highest power of 2. + int tableSize = Integer.highestOneBit(setSize - 1) << 1; + while (tableSize * DESIRED_LOAD_FACTOR < setSize) { + tableSize <<= 1; + } + return tableSize; + } + + static CharMatcher from(BitSet chars, String description) { + // Compute the filter. + long filter = 0; + int size = chars.cardinality(); + boolean containsZero = chars.get(0); + // Compute the hash table. + char[] table = new char[chooseTableSize(size)]; + int mask = table.length - 1; + for (int c = chars.nextSetBit(0); c != -1; c = chars.nextSetBit(c + 1)) { + // Compute the filter at the same time. + filter |= 1L << c; + int index = smear(c) & mask; + while (true) { + // Check for empty. + if (table[index] == 0) { + table[index] = (char) c; + break; + } + // Linear probing. + index = (index + 1) & mask; + } + } + return new SmallCharMatcher(table, filter, containsZero, description); + } + + @Override + public boolean matches(char c) { + if (c == 0) { + return containsZero; + } + if (!checkFilter(c)) { + return false; + } + int mask = table.length - 1; + int startingIndex = smear(c) & mask; + int index = startingIndex; + do { + // Check for empty. + if (table[index] == 0) { + return false; + // Check for match. + } else if (table[index] == c) { + return true; + } else { + // Linear probing. + index = (index + 1) & mask; + } + // Check to see if we wrapped around the whole table. + } while (index != startingIndex); + return false; + } + + @Override + void setBits(BitSet table) { + if (containsZero) { + table.set(0); + } + for (char c : this.table) { + if (c != 0) { + table.set(c); + } + } + } +} diff --git a/sources/main/java/com/google/common/base/Splitter.java b/sources/main/java/com/google/common/base/Splitter.java new file mode 100644 index 0000000..e047c54 --- /dev/null +++ b/sources/main/java/com/google/common/base/Splitter.java @@ -0,0 +1,650 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.CheckReturnValue; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * Extracts non-overlapping substrings from an input string, typically by + * recognizing appearances of a separator sequence. This separator can be + * specified as a single {@linkplain #on(char) character}, fixed + * {@linkplain #on(String) string}, {@linkplain #onPattern regular expression} + * or {@link #on(CharMatcher) CharMatcher} instance. Or, instead of using a + * separator at all, a splitter can extract adjacent substrings of a given + * {@linkplain #fixedLength fixed length}. + * + *

+ * For example, this expression: + * + *

+ *    {@code
+ *
+ *   Splitter.on(',').split("foo,bar,qux")}
+ * 
+ * + * ... produces an {@code Iterable} containing {@code "foo"}, {@code "bar"} and + * {@code "qux"}, in that order. + * + *

+ * By default, {@code Splitter}'s behavior is simplistic and unassuming. The + * following expression: + * + *

+ *    {@code
+ *
+ *   Splitter.on(',').split(" foo,,,  bar ,")}
+ * 
+ * + * ... yields the substrings {@code [" foo", "", "", " bar ", ""]}. If this is + * not the desired behavior, use configuration methods to obtain a new + * splitter instance with modified behavior: + * + *
+ * {
+ * 	@code
+ *
+ * 	private static final Splitter MY_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
+ * }
+ * 
+ * + *

+ * Now {@code MY_SPLITTER.split("foo,,, bar ,")} returns just {@code ["foo", + * "bar"]}. Note that the order in which these configuration methods are called + * is never significant. + * + *

+ * Warning: Splitter instances are immutable. Invoking a configuration + * method has no effect on the receiving instance; you must store and use the + * new splitter instance it returns instead. + * + *

+ * {
+ * 	@code
+ *
+ * 	// Do NOT do this
+ * 	Splitter splitter = Splitter.on('/');
+ * 	splitter.trimResults(); // does nothing!
+ * 	return splitter.split("wrong / wrong / wrong");
+ * }
+ * 
+ * + *

+ * For separator-based splitters that do not use {@code omitEmptyStrings}, an + * input string containing {@code n} occurrences of the separator naturally + * yields an iterable of size {@code n + 1}. So if the separator does not occur + * anywhere in the input, a single substring is returned containing the entire + * input. Consequently, all splitters split the empty string to {@code [""]} + * (note: even fixed-length splitters). + * + *

+ * Splitter instances are thread-safe immutable, and are therefore safe to store + * as {@code static final} constants. + * + *

+ * The {@link Joiner} class provides the inverse operation to splitting, but + * note that a round-trip between the two should be assumed to be lossy. + * + *

+ * Exception: for consistency with separator-based splitters, {@code + * split("")} does not yield an empty iterable, but an iterable containing + * {@code ""}. This is the only case in which {@code + * Iterables.size(split(input))} does not equal {@code + * IntMath.divide(input.length(), length, CEILING)}. To avoid this behavior, use + * {@code omitEmptyStrings}. + * + * @param length the desired length of pieces after splitting, a positive + * integer + * @return a splitter, with default settings, that can split into fixed sized + * pieces + * @throws IllegalArgumentException if {@code length} is zero or negative + */ + public static Splitter fixedLength(final int length) { + checkArgument(length > 0, "The length may not be less than 1"); + + return new Splitter(new Strategy() { + @Override + public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) { + return new SplittingIterator(splitter, toSplit) { + @Override + public int separatorStart(int start) { + int nextChunkStart = start + length; + return (nextChunkStart < toSplit.length() ? nextChunkStart : -1); + } + + @Override + public int separatorEnd(int separatorPosition) { + return separatorPosition; + } + }; + } + }); + } + + /** + * Returns a splitter that behaves equivalently to {@code this} splitter, but + * automatically omits empty strings from the results. For example, {@code + * Splitter.on(',').omitEmptyStrings().split(",a,,,b,c,,")} returns an iterable + * containing only {@code ["a", "b", "c"]}. + * + *

+ * If either {@code trimResults} option is also specified when creating a + * splitter, that splitter always trims results first before checking for + * emptiness. So, for example, {@code + * Splitter.on(':').omitEmptyStrings().trimResults().split(": : : ")} returns an + * empty iterable. + * + *

+ * Note that it is ordinarily not possible for {@link #split(CharSequence)} to + * return an empty iterable, but when using this option, it can (if the input + * sequence consists of nothing but separators). + * + * @return a splitter with the desired configuration + */ + @CheckReturnValue + public Splitter omitEmptyStrings() { + return new Splitter(strategy, true, trimmer, limit); + } + + /** + * Returns a splitter that behaves equivalently to {@code this} splitter but + * stops splitting after it reaches the limit. The limit defines the maximum + * number of items returned by the iterator. + * + *

+ * For example, {@code Splitter.on(',').limit(3).split("a,b,c,d")} returns an + * iterable containing {@code ["a", "b", "c,d"]}. When omitting empty strings, + * the omitted strings do no count. Hence, + * {@code Splitter.on(',').limit(3).omitEmptyStrings().split("a,,,b,,,c,d")} + * returns an iterable containing {@code ["a", "b", "c,d"}. When trim is + * requested, all entries, including the last are trimmed. Hence + * {@code Splitter.on(',').limit(3).trimResults().split(" a , b , c , d ")} + * results in @{code ["a", "b", "c , d"]}. + * + * @param limit the maximum number of items returns + * @return a splitter with the desired configuration + * @since 9.0 + */ + @CheckReturnValue + public Splitter limit(int limit) { + checkArgument(limit > 0, "must be greater than zero: %s", limit); + return new Splitter(strategy, omitEmptyStrings, trimmer, limit); + } + + /** + * Returns a splitter that behaves equivalently to {@code this} splitter, but + * automatically removes leading and trailing {@linkplain CharMatcher#WHITESPACE + * whitespace} from each returned substring; equivalent to + * {@code trimResults(CharMatcher.WHITESPACE)}. For example, {@code + * Splitter.on(',').trimResults().split(" a, b ,c ")} returns an iterable + * containing {@code ["a", "b", "c"]}. + * + * @return a splitter with the desired configuration + */ + @CheckReturnValue + public Splitter trimResults() { + return trimResults(CharMatcher.WHITESPACE); + } + + /** + * Returns a splitter that behaves equivalently to {@code this} splitter, but + * removes all leading or trailing characters matching the given {@code + * CharMatcher} from each returned substring. For example, {@code + * Splitter.on(',').trimResults(CharMatcher.is('_')).split("_a ,_b_ ,c__")} + * returns an iterable containing {@code ["a ", "b_ ", "c"]}. + * + * @param trimmer a {@link CharMatcher} that determines whether a character + * should be removed from the beginning/end of a subsequence + * @return a splitter with the desired configuration + */ + // TODO(kevinb): throw if a trimmer was already specified! + @CheckReturnValue + public Splitter trimResults(CharMatcher trimmer) { + checkNotNull(trimmer); + return new Splitter(strategy, omitEmptyStrings, trimmer, limit); + } + + /** + * Splits {@code sequence} into string components and makes them available + * through an {@link Iterator}, which may be lazily evaluated. If you want an + * eagerly computed {@link List}, use {@link #splitToList(CharSequence)}. + * + * @param sequence the sequence of characters to split + * @return an iteration over the segments split from the parameter. + */ + public Iterable split(final CharSequence sequence) { + checkNotNull(sequence); + + return new Iterable() { + @Override + public Iterator iterator() { + return splittingIterator(sequence); + } + + @Override + public String toString() { + return Joiner.on(", ").appendTo(new StringBuilder().append('['), this).append(']').toString(); + } + }; + } + + private Iterator splittingIterator(CharSequence sequence) { + return strategy.iterator(this, sequence); + } + + /** + * Splits {@code sequence} into string components and returns them as an + * immutable list. If you want an {@link Iterable} which may be lazily + * evaluated, use {@link #split(CharSequence)}. + * + * @param sequence the sequence of characters to split + * @return an immutable list of the segments split from the parameter + * @since 15.0 + */ + @Beta + public List splitToList(CharSequence sequence) { + checkNotNull(sequence); + + Iterator iterator = splittingIterator(sequence); + List result = new ArrayList(); + + while (iterator.hasNext()) { + result.add(iterator.next()); + } + + return Collections.unmodifiableList(result); + } + + /** + * Returns a {@code MapSplitter} which splits entries based on this splitter, + * and splits entries into keys and values using the specified separator. + * + * @since 10.0 + */ + @CheckReturnValue + @Beta + public MapSplitter withKeyValueSeparator(String separator) { + return withKeyValueSeparator(on(separator)); + } + + /** + * Returns a {@code MapSplitter} which splits entries based on this splitter, + * and splits entries into keys and values using the specified separator. + * + * @since 14.0 + */ + @CheckReturnValue + @Beta + public MapSplitter withKeyValueSeparator(char separator) { + return withKeyValueSeparator(on(separator)); + } + + /** + * Returns a {@code MapSplitter} which splits entries based on this splitter, + * and splits entries into keys and values using the specified key-value + * splitter. + * + * @since 10.0 + */ + @CheckReturnValue + @Beta + public MapSplitter withKeyValueSeparator(Splitter keyValueSplitter) { + return new MapSplitter(this, keyValueSplitter); + } + + /** + * An object that splits strings into maps as {@code Splitter} splits iterables + * and lists. Like {@code Splitter}, it is thread-safe and immutable. + * + * @since 10.0 + */ + @Beta + public static final class MapSplitter { + private static final String INVALID_ENTRY_MESSAGE = "Chunk [%s] is not a valid entry"; + private final Splitter outerSplitter; + private final Splitter entrySplitter; + + private MapSplitter(Splitter outerSplitter, Splitter entrySplitter) { + this.outerSplitter = outerSplitter; // only "this" is passed + this.entrySplitter = checkNotNull(entrySplitter); + } + + /** + * Splits {@code sequence} into substrings, splits each substring into an entry, + * and returns an unmodifiable map with each of the entries. For example, + * Splitter.on(';').trimResults().withKeyValueSeparator("=>") + * .split("a=>b ; c=>b") + * will return a mapping from {@code "a"} to {@code "b"} and {@code "c"} + * to {@code b}. + * + *

+ * The returned map preserves the order of the entries from {@code sequence}. + * + * @throws IllegalArgumentException if the specified sequence does not split + * into valid map entries, or if there are + * duplicate keys + */ + public Map split(CharSequence sequence) { + Map map = new LinkedHashMap(); + for (String entry : outerSplitter.split(sequence)) { + Iterator entryFields = entrySplitter.splittingIterator(entry); + + checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry); + String key = entryFields.next(); + checkArgument(!map.containsKey(key), "Duplicate key [%s] found.", key); + + checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry); + String value = entryFields.next(); + map.put(key, value); + + checkArgument(!entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry); + } + return Collections.unmodifiableMap(map); + } + } + + private interface Strategy { + Iterator iterator(Splitter splitter, CharSequence toSplit); + } + + private abstract static class SplittingIterator extends AbstractIterator { + final CharSequence toSplit; + final CharMatcher trimmer; + final boolean omitEmptyStrings; + + /** + * Returns the first index in {@code toSplit} at or after {@code start} that + * contains the separator. + */ + abstract int separatorStart(int start); + + /** + * Returns the first index in {@code toSplit} after {@code + * separatorPosition} that does not contain a separator. This method is only + * invoked after a call to {@code separatorStart}. + */ + abstract int separatorEnd(int separatorPosition); + + int offset = 0; + int limit; + + protected SplittingIterator(Splitter splitter, CharSequence toSplit) { + this.trimmer = splitter.trimmer; + this.omitEmptyStrings = splitter.omitEmptyStrings; + this.limit = splitter.limit; + this.toSplit = toSplit; + } + + @Override + protected String computeNext() { + /* + * The returned string will be from the end of the last match to the beginning + * of the next one. nextStart is the start position of the returned substring, + * while offset is the place to start looking for a separator. + */ + int nextStart = offset; + while (offset != -1) { + int start = nextStart; + int end; + + int separatorPosition = separatorStart(offset); + if (separatorPosition == -1) { + end = toSplit.length(); + offset = -1; + } else { + end = separatorPosition; + offset = separatorEnd(separatorPosition); + } + if (offset == nextStart) { + /* + * This occurs when some pattern has an empty match, even if it doesn't match + * the empty string -- for example, if it requires lookahead or the like. The + * offset must be increased to look for separators beyond this point, without + * changing the start position of the next returned substring -- so nextStart + * stays the same. + */ + offset++; + if (offset >= toSplit.length()) { + offset = -1; + } + continue; + } + + while (start < end && trimmer.matches(toSplit.charAt(start))) { + start++; + } + while (end > start && trimmer.matches(toSplit.charAt(end - 1))) { + end--; + } + + if (omitEmptyStrings && start == end) { + // Don't include the (unused) separator in next split string. + nextStart = offset; + continue; + } + + if (limit == 1) { + // The limit has been reached, return the rest of the string as the + // final item. This is tested after empty string removal so that + // empty strings do not count towards the limit. + end = toSplit.length(); + offset = -1; + // Since we may have changed the end, we need to trim it again. + while (end > start && trimmer.matches(toSplit.charAt(end - 1))) { + end--; + } + } else { + limit--; + } + + return toSplit.subSequence(start, end).toString(); + } + return endOfData(); + } + } +} diff --git a/sources/main/java/com/google/common/base/StandardSystemProperty.java b/sources/main/java/com/google/common/base/StandardSystemProperty.java new file mode 100644 index 0000000..d23f3c9 --- /dev/null +++ b/sources/main/java/com/google/common/base/StandardSystemProperty.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; + +/** + * Represents a {@linkplain System#getProperties() standard system property}. + * + * @author Kurt Alfred Kluever + * @since 15.0 + */ +@Beta +@GwtIncompatible("java.lang.System#getProperty") +public enum StandardSystemProperty { + + /** Java Runtime Environment version. */ + JAVA_VERSION("java.version"), + + /** Java Runtime Environment vendor. */ + JAVA_VENDOR("java.vendor"), + + /** Java vendor URL. */ + JAVA_VENDOR_URL("java.vendor.url"), + + /** Java installation directory. */ + JAVA_HOME("java.home"), + + /** Java Virtual Machine specification version. */ + JAVA_VM_SPECIFICATION_VERSION("java.vm.specification.version"), + + /** Java Virtual Machine specification vendor. */ + JAVA_VM_SPECIFICATION_VENDOR("java.vm.specification.vendor"), + + /** Java Virtual Machine specification name. */ + JAVA_VM_SPECIFICATION_NAME("java.vm.specification.name"), + + /** Java Virtual Machine implementation version. */ + JAVA_VM_VERSION("java.vm.version"), + + /** Java Virtual Machine implementation vendor. */ + JAVA_VM_VENDOR("java.vm.vendor"), + + /** Java Virtual Machine implementation name. */ + JAVA_VM_NAME("java.vm.name"), + + /** Java Runtime Environment specification version. */ + JAVA_SPECIFICATION_VERSION("java.specification.version"), + + /** Java Runtime Environment specification vendor. */ + JAVA_SPECIFICATION_VENDOR("java.specification.vendor"), + + /** Java Runtime Environment specification name. */ + JAVA_SPECIFICATION_NAME("java.specification.name"), + + /** Java class format version number. */ + JAVA_CLASS_VERSION("java.class.version"), + + /** Java class path. */ + JAVA_CLASS_PATH("java.class.path"), + + /** List of paths to search when loading libraries. */ + JAVA_LIBRARY_PATH("java.library.path"), + + /** Default temp file path. */ + JAVA_IO_TMPDIR("java.io.tmpdir"), + + /** Name of JIT compiler to use. */ + JAVA_COMPILER("java.compiler"), + + /** Path of extension directory or directories. */ + JAVA_EXT_DIRS("java.ext.dirs"), + + /** Operating system name. */ + OS_NAME("os.name"), + + /** Operating system architecture. */ + OS_ARCH("os.arch"), + + /** Operating system version. */ + OS_VERSION("os.version"), + + /** File separator ("/" on UNIX). */ + FILE_SEPARATOR("file.separator"), + + /** Path separator (":" on UNIX). */ + PATH_SEPARATOR("path.separator"), + + /** Line separator ("\n" on UNIX). */ + LINE_SEPARATOR("line.separator"), + + /** User's account name. */ + USER_NAME("user.name"), + + /** User's home directory. */ + USER_HOME("user.home"), + + /** User's current working directory. */ + USER_DIR("user.dir"); + + private final String key; + + private StandardSystemProperty(String key) { + this.key = key; + } + + /** + * Returns the key used to lookup this system property. + */ + public String key() { + return key; + } + + /** + * Returns the current value for this system property by delegating to + * {@link System#getProperty(String)}. + */ + public String value() { + return System.getProperty(key); + } + + /** + * Returns a string representation of this system property. + */ + @Override + public String toString() { + return key() + "=" + value(); + } +} diff --git a/sources/main/java/com/google/common/base/Strings.java b/sources/main/java/com/google/common/base/Strings.java new file mode 100644 index 0000000..bc69318 --- /dev/null +++ b/sources/main/java/com/google/common/base/Strings.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Formatter; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; + +/** + * Static utility methods pertaining to {@code String} or {@code CharSequence} + * instances. + * + * @author Kevin Bourrillion + * @since 3.0 + */ +@GwtCompatible +public final class Strings { + private Strings() { + } + + /** + * Returns the given string if it is non-null; the empty string otherwise. + * + * @param string the string to test and possibly return + * @return {@code string} itself if it is non-null; {@code ""} if it is null + */ + public static String nullToEmpty(@Nullable String string) { + return (string == null) ? "" : string; + } + + /** + * Returns the given string if it is nonempty; {@code null} otherwise. + * + * @param string the string to test and possibly return + * @return {@code string} itself if it is nonempty; {@code null} if it is empty + * or null + */ + public static @Nullable String emptyToNull(@Nullable String string) { + return isNullOrEmpty(string) ? null : string; + } + + /** + * Returns {@code true} if the given string is null or is the empty string. + * + *

+ * Consider normalizing your string references with {@link #nullToEmpty}. If you + * do, you can use {@link String#isEmpty()} instead of this method, and you + * won't need special null-safe forms of methods like {@link String#toUpperCase} + * either. Or, if you'd like to normalize "in the other direction," converting + * empty strings to {@code null}, you can use {@link #emptyToNull}. + * + * @param string a string reference to check + * @return {@code true} if the string is null or is the empty string + */ + public static boolean isNullOrEmpty(@Nullable String string) { + return string == null || string.length() == 0; // string.isEmpty() in Java 6 + } + + /** + * Returns a string, of length at least {@code minLength}, consisting of + * {@code string} prepended with as many copies of {@code padChar} as are + * necessary to reach that length. For example, + * + *

    + *
  • {@code padStart("7", 3, '0')} returns {@code "007"} + *
  • {@code padStart("2010", 3, '0')} returns {@code "2010"} + *
+ * + *

+ * See {@link Formatter} for a richer set of formatting capabilities. + * + * @param string the string which should appear at the end of the result + * @param minLength the minimum length the resulting string must have. Can be + * zero or negative, in which case the input string is always + * returned. + * @param padChar the character to insert at the beginning of the result until + * the minimum length is reached + * @return the padded string + */ + public static String padStart(String string, int minLength, char padChar) { + checkNotNull(string); // eager for GWT. + if (string.length() >= minLength) { + return string; + } + StringBuilder sb = new StringBuilder(minLength); + for (int i = string.length(); i < minLength; i++) { + sb.append(padChar); + } + sb.append(string); + return sb.toString(); + } + + /** + * Returns a string, of length at least {@code minLength}, consisting of + * {@code string} appended with as many copies of {@code padChar} as are + * necessary to reach that length. For example, + * + *

    + *
  • {@code padEnd("4.", 5, '0')} returns {@code "4.000"} + *
  • {@code padEnd("2010", 3, '!')} returns {@code "2010"} + *
+ * + *

+ * See {@link Formatter} for a richer set of formatting capabilities. + * + * @param string the string which should appear at the beginning of the + * result + * @param minLength the minimum length the resulting string must have. Can be + * zero or negative, in which case the input string is always + * returned. + * @param padChar the character to append to the end of the result until the + * minimum length is reached + * @return the padded string + */ + public static String padEnd(String string, int minLength, char padChar) { + checkNotNull(string); // eager for GWT. + if (string.length() >= minLength) { + return string; + } + StringBuilder sb = new StringBuilder(minLength); + sb.append(string); + for (int i = string.length(); i < minLength; i++) { + sb.append(padChar); + } + return sb.toString(); + } + + /** + * Returns a string consisting of a specific number of concatenated copies of an + * input string. For example, {@code repeat("hey", 3)} returns the string + * {@code "heyheyhey"}. + * + * @param string any non-null string + * @param count the number of times to repeat it; a nonnegative integer + * @return a string containing {@code string} repeated {@code count} times (the + * empty string if {@code count} is zero) + * @throws IllegalArgumentException if {@code count} is negative + */ + public static String repeat(String string, int count) { + checkNotNull(string); // eager for GWT. + + if (count <= 1) { + checkArgument(count >= 0, "invalid count: %s", count); + return (count == 0) ? "" : string; + } + + // IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark + final int len = string.length(); + final long longSize = (long) len * (long) count; + final int size = (int) longSize; + if (size != longSize) { + throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize); + } + + final char[] array = new char[size]; + string.getChars(0, len, array, 0); + int n; + for (n = len; n < size - n; n <<= 1) { + System.arraycopy(array, 0, array, n, n); + } + System.arraycopy(array, 0, array, n, size - n); + return new String(array); + } + + /** + * Returns the longest string {@code prefix} such that + * {@code a.toString().startsWith(prefix) && b.toString().startsWith(prefix)}, + * taking care not to split surrogate pairs. If {@code a} and {@code b} have no + * common prefix, returns the empty string. + * + * @since 11.0 + */ + public static String commonPrefix(CharSequence a, CharSequence b) { + checkNotNull(a); + checkNotNull(b); + + int maxPrefixLength = Math.min(a.length(), b.length()); + int p = 0; + while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) { + p++; + } + if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) { + p--; + } + return a.subSequence(0, p).toString(); + } + + /** + * Returns the longest string {@code suffix} such that + * {@code a.toString().endsWith(suffix) && b.toString().endsWith(suffix)}, + * taking care not to split surrogate pairs. If {@code a} and {@code b} have no + * common suffix, returns the empty string. + * + * @since 11.0 + */ + public static String commonSuffix(CharSequence a, CharSequence b) { + checkNotNull(a); + checkNotNull(b); + + int maxSuffixLength = Math.min(a.length(), b.length()); + int s = 0; + while (s < maxSuffixLength && a.charAt(a.length() - s - 1) == b.charAt(b.length() - s - 1)) { + s++; + } + if (validSurrogatePairAt(a, a.length() - s - 1) || validSurrogatePairAt(b, b.length() - s - 1)) { + s--; + } + return a.subSequence(a.length() - s, a.length()).toString(); + } + + /** + * True when a valid surrogate pair starts at the given {@code index} in the + * given {@code string}. Out-of-range indexes return false. + */ + @VisibleForTesting + static boolean validSurrogatePairAt(CharSequence string, int index) { + return index >= 0 && index <= (string.length() - 2) && Character.isHighSurrogate(string.charAt(index)) + && Character.isLowSurrogate(string.charAt(index + 1)); + } +} diff --git a/sources/main/java/com/google/common/base/Supplier.java b/sources/main/java/com/google/common/base/Supplier.java new file mode 100644 index 0000000..8e84e73 --- /dev/null +++ b/sources/main/java/com/google/common/base/Supplier.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; + +/** + * A class that can supply objects of a single type. Semantically, this could be + * a factory, generator, builder, closure, or something else entirely. No + * guarantees are implied by this interface. + * + * @author Harry Heymann + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface Supplier { + /** + * Retrieves an instance of the appropriate type. The returned object may or may + * not be a new instance, depending on the implementation. + * + * @return an instance of the appropriate type + */ + T get(); +} diff --git a/sources/main/java/com/google/common/base/Suppliers.java b/sources/main/java/com/google/common/base/Suppliers.java new file mode 100644 index 0000000..f1b6b79 --- /dev/null +++ b/sources/main/java/com/google/common/base/Suppliers.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import java.io.Serializable; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; + +/** + * Useful suppliers. + * + *

+ * All methods return serializable suppliers as long as they're given + * serializable parameters. + * + * @author Laurence Gonsalves + * @author Harry Heymann + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public final class Suppliers { + private Suppliers() { + } + + /** + * Returns a new supplier which is the composition of the provided function and + * supplier. In other words, the new supplier's value will be computed by + * retrieving the value from {@code supplier}, and then applying + * {@code function} to that value. Note that the resulting supplier will not + * call {@code supplier} or invoke {@code function} until it is called. + */ + public static Supplier compose(Function function, Supplier supplier) { + Preconditions.checkNotNull(function); + Preconditions.checkNotNull(supplier); + return new SupplierComposition(function, supplier); + } + + private static class SupplierComposition implements Supplier, Serializable { + final Function function; + final Supplier supplier; + + SupplierComposition(Function function, Supplier supplier) { + this.function = function; + this.supplier = supplier; + } + + @Override + public T get() { + return function.apply(supplier.get()); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof SupplierComposition) { + SupplierComposition that = (SupplierComposition) obj; + return function.equals(that.function) && supplier.equals(that.supplier); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(function, supplier); + } + + @Override + public String toString() { + return "Suppliers.compose(" + function + ", " + supplier + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a supplier which caches the instance retrieved during the first call + * to {@code get()} and returns that value on subsequent calls to {@code get()}. + * See: memoization + * + *

+ * The returned supplier is thread-safe. The supplier's serialized form does not + * contain the cached value, which will be recalculated when {@code + * get()} is called on the reserialized instance. + * + *

+ * If {@code delegate} is an instance created by an earlier call to {@code + * memoize}, it is returned directly. + */ + public static Supplier memoize(Supplier delegate) { + return (delegate instanceof MemoizingSupplier) ? delegate + : new MemoizingSupplier(Preconditions.checkNotNull(delegate)); + } + + @VisibleForTesting + static class MemoizingSupplier implements Supplier, Serializable { + final Supplier delegate; + transient volatile boolean initialized; + // "value" does not need to be volatile; visibility piggy-backs + // on volatile read of "initialized". + transient T value; + + MemoizingSupplier(Supplier delegate) { + this.delegate = delegate; + } + + @Override + public T get() { + // A 2-field variant of Double Checked Locking. + if (!initialized) { + synchronized (this) { + if (!initialized) { + T t = delegate.get(); + value = t; + initialized = true; + return t; + } + } + } + return value; + } + + @Override + public String toString() { + return "Suppliers.memoize(" + delegate + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a supplier that caches the instance supplied by the delegate and + * removes the cached value after the specified time has passed. Subsequent + * calls to {@code get()} return the cached value if the expiration time has not + * passed. After the expiration time, a new value is retrieved, cached, and + * returned. See: + * memoization + * + *

+ * The returned supplier is thread-safe. The supplier's serialized form does not + * contain the cached value, which will be recalculated when {@code + * get()} is called on the reserialized instance. + * + * @param duration the length of time after a value is created that it should + * stop being returned by subsequent {@code get()} calls + * @param unit the unit that {@code duration} is expressed in + * @throws IllegalArgumentException if {@code duration} is not positive + * @since 2.0 + */ + public static Supplier memoizeWithExpiration(Supplier delegate, long duration, TimeUnit unit) { + return new ExpiringMemoizingSupplier(delegate, duration, unit); + } + + @VisibleForTesting + static class ExpiringMemoizingSupplier implements Supplier, Serializable { + final Supplier delegate; + final long durationNanos; + transient volatile T value; + // The special value 0 means "not yet initialized". + transient volatile long expirationNanos; + + ExpiringMemoizingSupplier(Supplier delegate, long duration, TimeUnit unit) { + this.delegate = Preconditions.checkNotNull(delegate); + this.durationNanos = unit.toNanos(duration); + Preconditions.checkArgument(duration > 0); + } + + @Override + public T get() { + // Another variant of Double Checked Locking. + // + // We use two volatile reads. We could reduce this to one by + // putting our fields into a holder class, but (at least on x86) + // the extra memory consumption and indirection are more + // expensive than the extra volatile reads. + long nanos = expirationNanos; + long now = Platform.systemNanoTime(); + if (nanos == 0 || now - nanos >= 0) { + synchronized (this) { + if (nanos == expirationNanos) { // recheck for lost race + T t = delegate.get(); + value = t; + nanos = now + durationNanos; + // In the very unlikely event that nanos is 0, set it to 1; + // no one will notice 1 ns of tardiness. + expirationNanos = (nanos == 0) ? 1 : nanos; + return t; + } + } + } + return value; + } + + @Override + public String toString() { + // This is a little strange if the unit the user provided was not NANOS, + // but we don't want to store the unit just for toString + return "Suppliers.memoizeWithExpiration(" + delegate + ", " + durationNanos + ", NANOS)"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a supplier that always supplies {@code instance}. + */ + public static Supplier ofInstance(@Nullable T instance) { + return new SupplierOfInstance(instance); + } + + private static class SupplierOfInstance implements Supplier, Serializable { + final T instance; + + SupplierOfInstance(@Nullable T instance) { + this.instance = instance; + } + + @Override + public T get() { + return instance; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof SupplierOfInstance) { + SupplierOfInstance that = (SupplierOfInstance) obj; + return Objects.equal(instance, that.instance); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(instance); + } + + @Override + public String toString() { + return "Suppliers.ofInstance(" + instance + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a supplier whose {@code get()} method synchronizes on + * {@code delegate} before calling it, making it thread-safe. + */ + public static Supplier synchronizedSupplier(Supplier delegate) { + return new ThreadSafeSupplier(Preconditions.checkNotNull(delegate)); + } + + private static class ThreadSafeSupplier implements Supplier, Serializable { + final Supplier delegate; + + ThreadSafeSupplier(Supplier delegate) { + this.delegate = delegate; + } + + @Override + public T get() { + synchronized (delegate) { + return delegate.get(); + } + } + + @Override + public String toString() { + return "Suppliers.synchronizedSupplier(" + delegate + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a function that accepts a supplier and returns the result of invoking + * {@link Supplier#get} on that supplier. + * + * @since 8.0 + */ + @Beta + public static Function, T> supplierFunction() { + @SuppressWarnings("unchecked") // implementation is "fully variant" + SupplierFunction sf = (SupplierFunction) SupplierFunctionImpl.INSTANCE; + return sf; + } + + private interface SupplierFunction extends Function, T> { + } + + private enum SupplierFunctionImpl implements SupplierFunction { + INSTANCE; + + // Note: This makes T a "pass-through type" + @Override + public Object apply(Supplier input) { + return input.get(); + } + + @Override + public String toString() { + return "Suppliers.supplierFunction()"; + } + } +} diff --git a/sources/main/java/com/google/common/base/Throwables.java b/sources/main/java/com/google/common/base/Throwables.java new file mode 100644 index 0000000..cd7fb44 --- /dev/null +++ b/sources/main/java/com/google/common/base/Throwables.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; + +import net.lax1dude.eaglercraft.v1_8.EagRuntime; + +/** + * Static utility methods pertaining to instances of {@link Throwable}. + * + *

+ * See the Guava User Guide entry on + * + * Throwables. + * + * @author Kevin Bourrillion + * @author Ben Yu + * @since 1.0 + */ +public final class Throwables { + private Throwables() { + } + + /** + * Propagates {@code throwable} exactly as-is, if and only if it is an instance + * of {@code declaredType}. Example usage: + * + *

+	 * try {
+	 * 	someMethodThatCouldThrowAnything();
+	 * } catch (IKnowWhatToDoWithThisException e) {
+	 * 	handle(e);
+	 * } catch (Throwable t) {
+	 * 	Throwables.propagateIfInstanceOf(t, IOException.class);
+	 * 	Throwables.propagateIfInstanceOf(t, SQLException.class);
+	 * 	throw Throwables.propagate(t);
+	 * }
+	 * 
+ */ + public static void propagateIfInstanceOf(@Nullable Throwable throwable, Class declaredType) + throws X { + // Check for null is needed to avoid frequent JNI calls to isInstance(). + if (throwable != null && declaredType.isInstance(throwable)) { + throw declaredType.cast(throwable); + } + } + + /** + * Propagates {@code throwable} exactly as-is, if and only if it is an instance + * of {@link RuntimeException} or {@link Error}. Example usage: + * + *
+	 * try {
+	 * 	someMethodThatCouldThrowAnything();
+	 * } catch (IKnowWhatToDoWithThisException e) {
+	 * 	handle(e);
+	 * } catch (Throwable t) {
+	 * 	Throwables.propagateIfPossible(t);
+	 * 	throw new RuntimeException("unexpected", t);
+	 * }
+	 * 
+ */ + public static void propagateIfPossible(@Nullable Throwable throwable) { + propagateIfInstanceOf(throwable, Error.class); + propagateIfInstanceOf(throwable, RuntimeException.class); + } + + /** + * Propagates {@code throwable} exactly as-is, if and only if it is an instance + * of {@link RuntimeException}, {@link Error}, or {@code declaredType}. Example + * usage: + * + *
+	 * try {
+	 * 	someMethodThatCouldThrowAnything();
+	 * } catch (IKnowWhatToDoWithThisException e) {
+	 * 	handle(e);
+	 * } catch (Throwable t) {
+	 * 	Throwables.propagateIfPossible(t, OtherException.class);
+	 * 	throw new RuntimeException("unexpected", t);
+	 * }
+	 * 
+ * + * @param throwable the Throwable to possibly propagate + * @param declaredType the single checked exception type declared by the calling + * method + */ + public static void propagateIfPossible(@Nullable Throwable throwable, Class declaredType) + throws X { + propagateIfInstanceOf(throwable, declaredType); + propagateIfPossible(throwable); + } + + /** + * Propagates {@code throwable} exactly as-is, if and only if it is an instance + * of {@link RuntimeException}, {@link Error}, {@code declaredType1}, or + * {@code declaredType2}. In the unlikely case that you have three or more + * declared checked exception types, you can handle them all by invoking these + * methods repeatedly. See usage example in + * {@link #propagateIfPossible(Throwable, Class)}. + * + * @param throwable the Throwable to possibly propagate + * @param declaredType1 any checked exception type declared by the calling + * method + * @param declaredType2 any other checked exception type declared by the calling + * method + */ + public static void propagateIfPossible(@Nullable Throwable throwable, + Class declaredType1, Class declaredType2) throws X1, X2 { + checkNotNull(declaredType2); + propagateIfInstanceOf(throwable, declaredType1); + propagateIfPossible(throwable, declaredType2); + } + + /** + * Propagates {@code throwable} as-is if it is an instance of + * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps it + * in a {@code RuntimeException} then propagates. + *

+ * This method always throws an exception. The {@code RuntimeException} return + * type is only for client code to make Java type system happy in case a return + * value is required by the enclosing method. Example usage: + * + *

+	 * T doSomething() {
+	 * 	try {
+	 * 		return someMethodThatCouldThrowAnything();
+	 * 	} catch (IKnowWhatToDoWithThisException e) {
+	 * 		return handle(e);
+	 * 	} catch (Throwable t) {
+	 * 		throw Throwables.propagate(t);
+	 * 	}
+	 * }
+	 * 
+ * + * @param throwable the Throwable to propagate + * @return nothing will ever be returned; this return type is only for your + * convenience, as illustrated in the example above + */ + public static RuntimeException propagate(Throwable throwable) { + propagateIfPossible(checkNotNull(throwable)); + throw new RuntimeException(throwable); + } + + /** + * Returns the innermost cause of {@code throwable}. The first throwable in a + * chain provides context from when the error or exception was initially + * detected. Example usage: + * + *
+	 * assertEquals("Unable to assign a customer id", Throwables.getRootCause(e).getMessage());
+	 * 
+ */ + public static Throwable getRootCause(Throwable throwable) { + Throwable cause; + while ((cause = throwable.getCause()) != null) { + throwable = cause; + } + return throwable; + } + + /** + * Gets a {@code Throwable} cause chain as a list. The first entry in the list + * will be {@code throwable} followed by its cause hierarchy. Note that this is + * a snapshot of the cause chain and will not reflect any subsequent changes to + * the cause chain. + * + *

+ * Here's an example of how it can be used to find specific types of exceptions + * in the cause chain: + * + *

+	 * Iterables.filter(Throwables.getCausalChain(e), IOException.class));
+	 * 
+ * + * @param throwable the non-null {@code Throwable} to extract causes from + * @return an unmodifiable list containing the cause chain starting with + * {@code throwable} + */ + @Beta // TODO(kevinb): decide best return type + public static List getCausalChain(Throwable throwable) { + checkNotNull(throwable); + List causes = new ArrayList(4); + while (throwable != null) { + causes.add(throwable); + throwable = throwable.getCause(); + } + return Collections.unmodifiableList(causes); + } + + /** + * Returns a string containing the result of {@link Throwable#toString() + * toString()}, followed by the full, recursive stack trace of + * {@code throwable}. Note that you probably should not be parsing the resulting + * string; if you need programmatic access to the stack frames, you can call + * {@link Throwable#getStackTrace()}. + */ + public static String getStackTraceAsString(Throwable throwable) { + return EagRuntime.getStackTrace(throwable); + } +} diff --git a/sources/main/java/com/google/common/base/Ticker.java b/sources/main/java/com/google/common/base/Ticker.java new file mode 100644 index 0000000..b921f92 --- /dev/null +++ b/sources/main/java/com/google/common/base/Ticker.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A time source; returns a time value representing the number of nanoseconds + * elapsed since some fixed but arbitrary point in time. Note that most users + * should use {@link Stopwatch} instead of interacting with this class directly. + * + *

+ * Warning: this interface can only be used to measure elapsed time, not + * wall time. + * + * @author Kevin Bourrillion + * @since 10.0 + * (mostly source-compatible since 9.0) + */ +@Beta +@GwtCompatible +public abstract class Ticker { + /** + * Constructor for use by subclasses. + */ + protected Ticker() { + } + + /** + * Returns the number of nanoseconds elapsed since this ticker's fixed point of + * reference. + */ + public abstract long read(); + + /** + * A ticker that reads the current time using {@link System#nanoTime}. + * + * @since 10.0 + */ + public static Ticker systemTicker() { + return SYSTEM_TICKER; + } + + private static final Ticker SYSTEM_TICKER = new Ticker() { + @Override + public long read() { + return Platform.systemNanoTime(); + } + }; +} diff --git a/sources/main/java/com/google/common/base/Utf8.java b/sources/main/java/com/google/common/base/Utf8.java new file mode 100644 index 0000000..b798056 --- /dev/null +++ b/sources/main/java/com/google/common/base/Utf8.java @@ -0,0 +1,206 @@ +/* + * 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 com.google.common.base; + +import static com.google.common.base.Preconditions.checkPositionIndexes; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Low-level, high-performance utility methods related to the + * {@linkplain Charsets#UTF_8 UTF-8} character encoding. UTF-8 is defined in + * section D92 of + * The Unicode + * Standard Core Specification, Chapter 3. + * + *

+ * The variant of UTF-8 implemented by this class is the restricted definition + * of UTF-8 introduced in Unicode 3.1. One implication of this is that it + * rejects + * "non-shortest + * form" byte sequences, even though the JDK decoder may accept them. + * + * @author Martin Buchholz + * @author Clément Roux + * @since 16.0 + */ +@Beta +@GwtCompatible +public final class Utf8 { + /** + * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. + * For a string, this method is equivalent to + * {@code string.getBytes(UTF_8).length}, but is more efficient in both time and + * space. + * + * @throws IllegalArgumentException if {@code sequence} contains ill-formed + * UTF-16 (unpaired surrogates) + */ + public static int encodedLength(CharSequence sequence) { + // Warning to maintainers: this implementation is highly optimized. + int utf16Length = sequence.length(); + int utf8Length = utf16Length; + int i = 0; + + // This loop optimizes for pure ASCII. + while (i < utf16Length && sequence.charAt(i) < 0x80) { + i++; + } + + // This loop optimizes for chars less than 0x800. + for (; i < utf16Length; i++) { + char c = sequence.charAt(i); + if (c < 0x800) { + utf8Length += ((0x7f - c) >>> 31); // branch free! + } else { + utf8Length += encodedLengthGeneral(sequence, i); + break; + } + } + + if (utf8Length < utf16Length) { + // Necessary and sufficient condition for overflow because of maximum 3x + // expansion + throw new IllegalArgumentException("UTF-8 length does not fit in int: " + (utf8Length + (1L << 32))); + } + return utf8Length; + } + + private static int encodedLengthGeneral(CharSequence sequence, int start) { + int utf16Length = sequence.length(); + int utf8Length = 0; + for (int i = start; i < utf16Length; i++) { + char c = sequence.charAt(i); + if (c < 0x800) { + utf8Length += (0x7f - c) >>> 31; // branch free! + } else { + utf8Length += 2; + // jdk7+: if (Character.isSurrogate(c)) { + if (Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) { + // Check that we have a well-formed surrogate pair. + int cp = Character.codePointAt(sequence, i); + if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + throw new IllegalArgumentException("Unpaired surrogate at index " + i); + } + i++; + } + } + } + return utf8Length; + } + + /** + * Returns {@code true} if {@code bytes} is a well-formed UTF-8 byte + * sequence according to Unicode 6.0. Note that this is a stronger criterion + * than simply whether the bytes can be decoded. For example, some versions of + * the JDK decoder will accept "non-shortest form" byte sequences, but encoding + * never reproduces these. Such byte sequences are not considered + * well-formed. + * + *

+ * This method returns {@code true} if and only if + * {@code Arrays.equals(bytes, new + * String(bytes, UTF_8).getBytes(UTF_8))} does, but is more efficient in both + * time and space. + */ + public static boolean isWellFormed(byte[] bytes) { + return isWellFormed(bytes, 0, bytes.length); + } + + /** + * Returns whether the given byte array slice is a well-formed UTF-8 byte + * sequence, as defined by {@link #isWellFormed(byte[])}. Note that this can be + * false even when {@code + * isWellFormed(bytes)} is true. + * + * @param bytes the input buffer + * @param off the offset in the buffer of the first byte to read + * @param len the number of bytes to read from the buffer + */ + public static boolean isWellFormed(byte[] bytes, int off, int len) { + int end = off + len; + checkPositionIndexes(off, end, bytes.length); + // Look for the first non-ASCII character. + for (int i = off; i < end; i++) { + if (bytes[i] < 0) { + return isWellFormedSlowPath(bytes, i, end); + } + } + return true; + } + + private static boolean isWellFormedSlowPath(byte[] bytes, int off, int end) { + int index = off; + while (true) { + int byte1; + + // Optimize for interior runs of ASCII bytes. + do { + if (index >= end) { + return true; + } + } while ((byte1 = bytes[index++]) >= 0); + + if (byte1 < (byte) 0xE0) { + // Two-byte form. + if (index == end) { + return false; + } + // Simultaneously check for illegal trailing-byte in leading position + // and overlong 2-byte form. + if (byte1 < (byte) 0xC2 || bytes[index++] > (byte) 0xBF) { + return false; + } + } else if (byte1 < (byte) 0xF0) { + // Three-byte form. + if (index + 1 >= end) { + return false; + } + int byte2 = bytes[index++]; + if (byte2 > (byte) 0xBF + // Overlong? 5 most significant bits must not all be zero. + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // Check for illegal surrogate codepoints. + || (byte1 == (byte) 0xED && (byte) 0xA0 <= byte2) + // Third byte trailing-byte test. + || bytes[index++] > (byte) 0xBF) { + return false; + } + } else { + // Four-byte form. + if (index + 2 >= end) { + return false; + } + int byte2 = bytes[index++]; + if (byte2 > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 + // || byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 + // || byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // Third byte trailing-byte test + || bytes[index++] > (byte) 0xBF + // Fourth byte trailing-byte test + || bytes[index++] > (byte) 0xBF) { + return false; + } + } + } + } + + private Utf8() { + } +} diff --git a/sources/main/java/com/google/common/base/Verify.java b/sources/main/java/com/google/common/base/Verify.java new file mode 100644 index 0000000..a471014 --- /dev/null +++ b/sources/main/java/com/google/common/base/Verify.java @@ -0,0 +1,187 @@ +/* + * 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 com.google.common.base; + +import static com.google.common.base.Preconditions.format; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Static convenience methods that serve the same purpose as Java language + * + * assertions, except that they are always enabled. These methods should be + * used instead of Java assertions whenever there is a chance the check may fail + * "in real life". Example: + * + *

+ * {
+ * 	@code
+ *
+ * 	Bill bill = remoteService.getLastUnpaidBill();
+ *
+ * 	// In case bug 12345 happens again we'd rather just die
+ * 	Verify.verify(bill.status() == Status.UNPAID, "Unexpected bill status: %s", bill.status());
+ * }
+ * 
+ * + *

Comparison to alternatives

+ * + *

+ * Note: In some cases the differences explained below can be subtle. + * When it's unclear which approach to use, don't worry too much about + * it; just pick something that seems reasonable and it will be fine. + * + *

    + *
  • If checking whether the caller has violated your method or + * constructor's contract (such as by passing an invalid argument), use the + * utilities of the {@link Preconditions} class instead. + * + *
  • If checking an impossible condition (which cannot happen + * unless your own class or its trusted dependencies is badly broken), + * this is what ordinary Java assertions are for. Note that assertions are not + * enabled by default; they are essentially considered "compiled comments." + * + *
  • An explicit {@code if/throw} (as illustrated above) is always acceptable; + * we still recommend using our {@link VerifyException} exception type. Throwing + * a plain {@link RuntimeException} is frowned upon. + * + *
  • Use of {@link java.util.Objects#requireNonNull(Object)} is generally + * discouraged, since {@link #verifyNotNull(Object)} and + * {@link Preconditions#checkNotNull(Object)} perform the same function with + * more clarity. + *
+ * + *

Warning about performance

+ * + *

+ * Remember that parameter values for message construction must all be computed + * eagerly, and autoboxing and varargs array creation may happen as well, even + * when the verification succeeds and the message ends up unneeded. + * Performance-sensitive verification checks should continue to use usual form: + * + *

+ * {
+ * 	@code
+ *
+ * 	Bill bill = remoteService.getLastUnpaidBill();
+ * 	if (bill.status() != Status.UNPAID) {
+ * 		throw new VerifyException("Unexpected bill status: " + bill.status());
+ * 	}
+ * }
+ * 
+ * + *

Only {@code %s} is supported

+ * + *

+ * As with {@link Preconditions} error message template strings, only the + * {@code "%s"} specifier is supported, not the full range of + * {@link java.util.Formatter} specifiers. However, note that if the number of + * arguments does not match the number of occurrences of {@code "%s"} in the + * format string, {@code Verify} will still behave as expected, and will still + * include all argument values in the error message; the message will simply not + * be formatted exactly as intended. + * + *

More information

+ * + * See Conditional + * failures explained in the Guava User Guide for advice on when this class + * should be used. + * + * @since 17.0 + */ +@Beta +@GwtCompatible +public final class Verify { + /** + * Ensures that {@code expression} is {@code true}, throwing a + * {@code VerifyException} with no message otherwise. + */ + public static void verify(boolean expression) { + if (!expression) { + throw new VerifyException(); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a + * {@code VerifyException} with a custom message otherwise. + * + * @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 VerifyException if {@code expression} is {@code false} + */ + public static void verify(boolean expression, @Nullable String errorMessageTemplate, + @Nullable Object... errorMessageArgs) { + if (!expression) { + throw new VerifyException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures that {@code reference} is non-null, throwing a + * {@code VerifyException} with a default message otherwise. + * + * @return {@code reference}, guaranteed to be non-null, for convenience + */ + public static T verifyNotNull(@Nullable T reference) { + return verifyNotNull(reference, "expected a non-null reference"); + } + + /** + * Ensures that {@code reference} is non-null, throwing a + * {@code VerifyException} with a custom message otherwise. + * + * @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 {@code reference}, guaranteed to be non-null, for convenience + */ + public static T verifyNotNull(@Nullable T reference, @Nullable String errorMessageTemplate, + @Nullable Object... errorMessageArgs) { + verify(reference != null, errorMessageTemplate, errorMessageArgs); + return reference; + } + + // TODO(kevinb): consider T verifySingleton(Iterable) to take over for + // Iterables.getOnlyElement() + + private Verify() { + } +} diff --git a/sources/main/java/com/google/common/base/VerifyException.java b/sources/main/java/com/google/common/base/VerifyException.java new file mode 100644 index 0000000..814dbff --- /dev/null +++ b/sources/main/java/com/google/common/base/VerifyException.java @@ -0,0 +1,41 @@ +/* + * 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 com.google.common.base; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Exception thrown upon the failure of a verification + * check, including those performed by the convenience methods of the + * {@link Verify} class. + * + * @since 17.0 + */ +@Beta +@GwtCompatible +public class VerifyException extends RuntimeException { + /** Constructs a {@code VerifyException} with no message. */ + public VerifyException() { + } + + /** Constructs a {@code VerifyException} with the message {@code message}. */ + public VerifyException(@Nullable String message) { + super(message); + } +} diff --git a/sources/main/java/com/google/common/base/package-info.java b/sources/main/java/com/google/common/base/package-info.java new file mode 100644 index 0000000..4c2445d --- /dev/null +++ b/sources/main/java/com/google/common/base/package-info.java @@ -0,0 +1,67 @@ +/* + * 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. + */ + +/** + * Basic utility libraries and interfaces. + * + *

+ * This package is a part of the open-source + * Guava libraries. + * + *

Contents

+ * + *

String-related utilities

+ * + *
    + *
  • {@link com.google.common.base.Ascii} + *
  • {@link com.google.common.base.CaseFormat} + *
  • {@link com.google.common.base.CharMatcher} + *
  • {@link com.google.common.base.Charsets} + *
  • {@link com.google.common.base.Joiner} + *
  • {@link com.google.common.base.Splitter} + *
  • {@link com.google.common.base.Strings} + *
+ * + *

Function types

+ * + *
    + *
  • {@link com.google.common.base.Function}, + * {@link com.google.common.base.Functions} + *
  • {@link com.google.common.base.Predicate}, + * {@link com.google.common.base.Predicates} + *
  • {@link com.google.common.base.Equivalence} + *
  • {@link com.google.common.base.Converter} + *
  • {@link com.google.common.base.Supplier}, + * {@link com.google.common.base.Suppliers} + *
+ * + *

Other

+ * + *
    + *
  • {@link com.google.common.base.Defaults} + *
  • {@link com.google.common.base.Enums} + *
  • {@link com.google.common.base.Objects} + *
  • {@link com.google.common.base.Optional} + *
  • {@link com.google.common.base.Preconditions} + *
  • {@link com.google.common.base.Stopwatch} + *
  • {@link com.google.common.base.Throwables} + *
+ * + */ +@ParametersAreNonnullByDefault +package com.google.common.base; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sources/main/java/com/google/common/collect/AbstractBiMap.java b/sources/main/java/com/google/common/collect/AbstractBiMap.java new file mode 100644 index 0000000..88ae332 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractBiMap.java @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Objects; + +/** + * 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 + */ +@GwtCompatible(emulated = true) +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(@Nullable K key) { + return key; + } + + /** + * Returns its input, or throws an exception if this is not a valid value. + */ + V checkValue(@Nullable V value) { + return value; + } + + /** + * Specifies the delegate maps going in each direction. Called by the + * constructor and by subclasses during deserialization. + */ + 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(@Nullable Object value) { + return inverse.containsKey(value); + } + + // Modification Operations + + @Override + public V put(@Nullable K key, @Nullable V value) { + return putInBothMaps(key, value, false); + } + + @Override + public V forcePut(@Nullable K key, @Nullable V value) { + return putInBothMaps(key, value, true); + } + + private V putInBothMaps(@Nullable K key, @Nullable V value, boolean force) { + checkKey(key); + checkValue(value); + boolean containedKey = containsKey(key); + if (containedKey && Objects.equal(value, get(key))) { + return value; + } + 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(@Nullable 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 (Objects.equal(value, getValue())) { + return value; + } + checkArgument(!containsValue(value), "value already present: %s", value); + V oldValue = finalEntry.setValue(value); + checkState(Objects.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 + */ + @GwtIncompatible("java.io.ObjectOuputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(inverse()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + setInverse((AbstractBiMap) stream.readObject()); + } + + @GwtIncompatible("Not needed in the emulated source.") + Object readResolve() { + return inverse().inverse(); + } + + @GwtIncompatible("Not needed in emulated source.") + private static final long serialVersionUID = 0; + } + + @GwtIncompatible("Not needed in emulated source.") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/AbstractIndexedListIterator.java b/sources/main/java/com/google/common/collect/AbstractIndexedListIterator.java new file mode 100644 index 0000000..a66f65b --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractIndexedListIterator.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 com.google.common.collect; + +import static com.google.common.base.Preconditions.checkPositionIndex; + +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import com.google.common.annotations.GwtCompatible; + +/** + * 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 + */ +@GwtCompatible +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/sources/main/java/com/google/common/collect/AbstractIterator.java b/sources/main/java/com/google/common/collect/AbstractIterator.java new file mode 100644 index 0000000..160e081 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractIterator.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkState; + +import java.util.NoSuchElementException; + +import com.google.common.annotations.GwtCompatible; + +/** + * 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 +@GwtCompatible +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/sources/main/java/com/google/common/collect/AbstractListMultimap.java b/sources/main/java/com/google/common/collect/AbstractListMultimap.java new file mode 100644 index 0000000..caa5e96 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractListMultimap.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Basic implementation of the {@link ListMultimap} interface. It's a wrapper + * around {@link AbstractMapBasedMultimap} that converts the returned + * collections into {@code Lists}. The {@link #createCollection} method must + * return a {@code + * List}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +abstract class AbstractListMultimap extends AbstractMapBasedMultimap implements ListMultimap { + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + */ + protected AbstractListMultimap(Map> map) { + super(map); + } + + @Override + abstract List createCollection(); + + @Override + List createUnmodifiableEmptyCollection() { + return ImmutableList.of(); + } + + // Following Javadoc copied from ListMultimap. + + /** + * {@inheritDoc} + * + *

+ * Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public List get(@Nullable K key) { + return (List) super.get(key); + } + + /** + * {@inheritDoc} + * + *

+ * Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public List removeAll(@Nullable Object key) { + return (List) super.removeAll(key); + } + + /** + * {@inheritDoc} + * + *

+ * Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public List replaceValues(@Nullable K key, Iterable values) { + return (List) super.replaceValues(key, values); + } + + /** + * Stores a key-value pair in the multimap. + * + * @param key key to store in the multimap + * @param value value to store in the multimap + * @return {@code true} always + */ + @Override + public boolean put(@Nullable K key, @Nullable V value) { + return super.put(key, value); + } + + /** + * {@inheritDoc} + * + *

+ * Though the method signature doesn't say so explicitly, the returned map has + * {@link List} values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * Compares the specified object to this multimap for equality. + * + *

+ * Two {@code ListMultimap} instances are equal if, for each key, they contain + * the same values in the same order. If the value orderings disagree, the + * multimaps will not be considered equal. + */ + @Override + public boolean equals(@Nullable Object object) { + return super.equals(object); + } + + private static final long serialVersionUID = 6588350623831699109L; +} diff --git a/sources/main/java/com/google/common/collect/AbstractMapBasedMultimap.java b/sources/main/java/com/google/common/collect/AbstractMapBasedMultimap.java new file mode 100644 index 0000000..c11cd29 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractMapBasedMultimap.java @@ -0,0 +1,1591 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.RandomAccess; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.Maps.ImprovedAbstractMap; + +/** + * Basic implementation of the {@link Multimap} interface. This class represents + * a multimap as a map that associates each key with a collection of values. All + * methods of {@link Multimap} are supported, including those specified as + * optional in the interface. + * + *

+ * To implement a multimap, a subclass must define the method + * {@link #createCollection()}, which creates an empty collection of values for + * a key. + * + *

+ * The multimap constructor takes a map that has a single entry for each + * distinct key. When you insert a key-value pair with a key that isn't already + * in the multimap, {@code AbstractMapBasedMultimap} calls + * {@link #createCollection()} to create the collection of values for that key. + * The subclass should not call {@link #createCollection()} directly, and a new + * instance should be created every time the method is called. + * + *

+ * For example, the subclass could pass a {@link java.util.TreeMap} during + * construction, and {@link #createCollection()} could return a + * {@link java.util.TreeSet}, in which case the multimap's iterators would + * propagate through the keys and values in sorted order. + * + *

+ * Keys and values may be null, as long as the underlying collection classes + * support null elements. + * + *

+ * The collections created by {@link #createCollection()} may or may not allow + * duplicates. If the collection, such as a {@link Set}, does not support + * duplicates, an added key-value pair will replace an existing pair with the + * same key and value, if such a pair is present. With collections like + * {@link List} that allow duplicates, the collection will keep the existing + * key-value pairs while adding a new pair. + * + *

+ * This class is not threadsafe when any concurrent operations update the + * multimap, even if the underlying map and {@link #createCollection()} method + * return threadsafe classes. Concurrent read operations will work correctly. To + * allow concurrent update operations, wrap your multimap with a call to + * {@link Multimaps#synchronizedMultimap}. + * + *

+ * For serialization to work, the subclass must specify explicit + * {@code readObject} and {@code writeObject} methods. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +abstract class AbstractMapBasedMultimap extends AbstractMultimap implements Serializable { + /* + * Here's an outline of the overall design. + * + * The map variable contains the collection of values associated with each key. + * When a key-value pair is added to a multimap that didn't previously contain + * any values for that key, a new collection generated by createCollection is + * added to the map. That same collection instance remains in the map as long as + * the multimap has any values for the key. If all values for the key are + * removed, the key and collection are removed from the map. + * + * The get method returns a WrappedCollection, which decorates the collection in + * the map (if the key is present) or an empty collection (if the key is not + * present). When the collection delegate in the WrappedCollection is empty, the + * multimap may contain subsequently added values for that key. To handle that + * situation, the WrappedCollection checks whether map contains an entry for the + * provided key, and if so replaces the delegate. + */ + + private transient Map> map; + private transient int totalSize; + + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + * @throws IllegalArgumentException if {@code map} is not empty + */ + protected AbstractMapBasedMultimap(Map> map) { + checkArgument(map.isEmpty()); + this.map = map; + } + + /** Used during deserialization only. */ + final void setMap(Map> map) { + this.map = map; + totalSize = 0; + for (Collection values : map.values()) { + checkArgument(!values.isEmpty()); + totalSize += values.size(); + } + } + + /** + * Creates an unmodifiable, empty collection of values. + * + *

+ * This is used in {@link #removeAll} on an empty key. + */ + Collection createUnmodifiableEmptyCollection() { + return unmodifiableCollectionSubclass(createCollection()); + } + + /** + * Creates the collection of values for a single key. + * + *

+ * Collections with weak, soft, or phantom references are not supported. Each + * call to {@code createCollection} should create a new instance. + * + *

+ * The returned collection class determines whether duplicate key-value pairs + * are allowed. + * + * @return an empty collection of values + */ + abstract Collection createCollection(); + + /** + * Creates the collection of values for an explicitly provided key. By default, + * it simply calls {@link #createCollection()}, which is the correct behavior + * for most implementations. The {@link LinkedHashMultimap} class overrides it. + * + * @param key key to associate with values in the collection + * @return an empty collection of values + */ + Collection createCollection(@Nullable K key) { + return createCollection(); + } + + Map> backingMap() { + return map; + } + + // Query Operations + + @Override + public int size() { + return totalSize; + } + + @Override + public boolean containsKey(@Nullable Object key) { + return map.containsKey(key); + } + + // Modification Operations + + @Override + public boolean put(@Nullable K key, @Nullable V value) { + Collection collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + if (collection.add(value)) { + totalSize++; + map.put(key, collection); + return true; + } else { + throw new AssertionError("New Collection violated the Collection spec"); + } + } else if (collection.add(value)) { + totalSize++; + return true; + } else { + return false; + } + } + + private Collection getOrCreateCollection(@Nullable K key) { + Collection collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + map.put(key, collection); + } + return collection; + } + + // Bulk Operations + + /** + * {@inheritDoc} + * + *

+ * The returned collection is immutable. + */ + @Override + public Collection replaceValues(@Nullable K key, Iterable values) { + Iterator iterator = values.iterator(); + if (!iterator.hasNext()) { + return removeAll(key); + } + + // TODO(user): investigate atomic failure? + Collection collection = getOrCreateCollection(key); + Collection oldValues = createCollection(); + oldValues.addAll(collection); + + totalSize -= collection.size(); + collection.clear(); + + while (iterator.hasNext()) { + if (collection.add(iterator.next())) { + totalSize++; + } + } + + return unmodifiableCollectionSubclass(oldValues); + } + + /** + * {@inheritDoc} + * + *

+ * The returned collection is immutable. + */ + @Override + public Collection removeAll(@Nullable Object key) { + Collection collection = map.remove(key); + + if (collection == null) { + return createUnmodifiableEmptyCollection(); + } + + Collection output = createCollection(); + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + + return unmodifiableCollectionSubclass(output); + } + + Collection unmodifiableCollectionSubclass(Collection collection) { + // We don't deal with NavigableSet here yet for GWT reasons -- instead, + // non-GWT TreeMultimap explicitly overrides this and uses NavigableSet. + if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet) collection); + } else if (collection instanceof Set) { + return Collections.unmodifiableSet((Set) collection); + } else if (collection instanceof List) { + return Collections.unmodifiableList((List) collection); + } else { + return Collections.unmodifiableCollection(collection); + } + } + + @Override + public void clear() { + // Clear each collection, to make previously returned collections empty. + for (Collection collection : map.values()) { + collection.clear(); + } + map.clear(); + totalSize = 0; + } + + // Views + + /** + * {@inheritDoc} + * + *

+ * The returned collection is not serializable. + */ + @Override + public Collection get(@Nullable K key) { + Collection collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + } + return wrapCollection(key, collection); + } + + /** + * Generates a decorated collection that remains consistent with the values in + * the multimap for the provided key. Changes to the multimap may alter the + * returned collection, and vice versa. + */ + Collection wrapCollection(@Nullable K key, Collection collection) { + // We don't deal with NavigableSet here yet for GWT reasons -- instead, + // non-GWT TreeMultimap explicitly overrides this and uses NavigableSet. + if (collection instanceof SortedSet) { + return new WrappedSortedSet(key, (SortedSet) collection, null); + } else if (collection instanceof Set) { + return new WrappedSet(key, (Set) collection); + } else if (collection instanceof List) { + return wrapList(key, (List) collection, null); + } else { + return new WrappedCollection(key, collection, null); + } + } + + private List wrapList(@Nullable K key, List list, @Nullable WrappedCollection ancestor) { + return (list instanceof RandomAccess) ? new RandomAccessWrappedList(key, list, ancestor) + : new WrappedList(key, list, ancestor); + } + + /** + * Collection decorator that stays in sync with the multimap values for a key. + * There are two kinds of wrapped collections: full and subcollections. Both + * have a delegate pointing to the underlying collection class. + * + *

+ * Full collections, identified by a null ancestor field, contain all multimap + * values for a given key. Its delegate is a value in + * {@link AbstractMapBasedMultimap#map} whenever the delegate is non-empty. The + * {@code + * refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} methods ensure + * that the {@code WrappedCollection} and map remain consistent. + * + *

+ * A subcollection, such as a sublist, contains some of the values for a given + * key. Its ancestor field points to the full wrapped collection with all values + * for the key. The subcollection {@code refreshIfEmpty}, {@code + * removeIfEmpty}, and {@code addToMap} methods call the corresponding methods + * of the full wrapped collection. + */ + private class WrappedCollection extends AbstractCollection { + final K key; + Collection delegate; + final WrappedCollection ancestor; + final Collection ancestorDelegate; + + WrappedCollection(@Nullable K key, Collection delegate, @Nullable WrappedCollection ancestor) { + this.key = key; + this.delegate = delegate; + this.ancestor = ancestor; + this.ancestorDelegate = (ancestor == null) ? null : ancestor.getDelegate(); + } + + /** + * If the delegate collection is empty, but the multimap has values for the key, + * replace the delegate with the new collection for the key. + * + *

+ * For a subcollection, refresh its ancestor and validate that the ancestor + * delegate hasn't changed. + */ + void refreshIfEmpty() { + if (ancestor != null) { + ancestor.refreshIfEmpty(); + if (ancestor.getDelegate() != ancestorDelegate) { + throw new ConcurrentModificationException(); + } + } else if (delegate.isEmpty()) { + Collection newDelegate = map.get(key); + if (newDelegate != null) { + delegate = newDelegate; + } + } + } + + /** + * If collection is empty, remove it from + * {@code AbstractMapBasedMultimap.this.map}. For subcollections, check whether + * the ancestor collection is empty. + */ + void removeIfEmpty() { + if (ancestor != null) { + ancestor.removeIfEmpty(); + } else if (delegate.isEmpty()) { + map.remove(key); + } + } + + K getKey() { + return key; + } + + /** + * Add the delegate to the map. Other {@code WrappedCollection} methods should + * call this method after adding elements to a previously empty collection. + * + *

+ * Subcollection add the ancestor's delegate instead. + */ + void addToMap() { + if (ancestor != null) { + ancestor.addToMap(); + } else { + map.put(key, delegate); + } + } + + @Override + public int size() { + refreshIfEmpty(); + return delegate.size(); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + refreshIfEmpty(); + return delegate.equals(object); + } + + @Override + public int hashCode() { + refreshIfEmpty(); + return delegate.hashCode(); + } + + @Override + public String toString() { + refreshIfEmpty(); + return delegate.toString(); + } + + Collection getDelegate() { + return delegate; + } + + @Override + public Iterator iterator() { + refreshIfEmpty(); + return new WrappedIterator(); + } + + /** Collection iterator for {@code WrappedCollection}. */ + class WrappedIterator implements Iterator { + final Iterator delegateIterator; + final Collection originalDelegate = delegate; + + WrappedIterator() { + delegateIterator = iteratorOrListIterator(delegate); + } + + WrappedIterator(Iterator delegateIterator) { + this.delegateIterator = delegateIterator; + } + + /** + * If the delegate changed since the iterator was created, the iterator is no + * longer valid. + */ + void validateIterator() { + refreshIfEmpty(); + if (delegate != originalDelegate) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + validateIterator(); + return delegateIterator.hasNext(); + } + + @Override + public V next() { + validateIterator(); + return delegateIterator.next(); + } + + @Override + public void remove() { + delegateIterator.remove(); + totalSize--; + removeIfEmpty(); + } + + Iterator getDelegateIterator() { + validateIterator(); + return delegateIterator; + } + } + + @Override + public boolean add(V value) { + refreshIfEmpty(); + boolean wasEmpty = delegate.isEmpty(); + boolean changed = delegate.add(value); + if (changed) { + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + return changed; + } + + WrappedCollection getAncestor() { + return ancestor; + } + + // The following methods are provided for better performance. + + @Override + public boolean addAll(Collection collection) { + if (collection.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.addAll(collection); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + if (oldSize == 0) { + addToMap(); + } + } + return changed; + } + + @Override + public boolean contains(Object o) { + refreshIfEmpty(); + return delegate.contains(o); + } + + @Override + public boolean containsAll(Collection c) { + refreshIfEmpty(); + return delegate.containsAll(c); + } + + @Override + public void clear() { + int oldSize = size(); // calls refreshIfEmpty + if (oldSize == 0) { + return; + } + delegate.clear(); + totalSize -= oldSize; + removeIfEmpty(); // maybe shouldn't be removed if this is a sublist + } + + @Override + public boolean remove(Object o) { + refreshIfEmpty(); + boolean changed = delegate.remove(o); + if (changed) { + totalSize--; + removeIfEmpty(); + } + return changed; + } + + @Override + public boolean removeAll(Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.removeAll(c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.retainAll(c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + } + + private Iterator iteratorOrListIterator(Collection collection) { + return (collection instanceof List) ? ((List) collection).listIterator() : collection.iterator(); + } + + /** Set decorator that stays in sync with the multimap values for a key. */ + private class WrappedSet extends WrappedCollection implements Set { + WrappedSet(@Nullable K key, Set delegate) { + super(key, delegate, null); + } + + @Override + public boolean removeAll(Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + + // Guava issue 1013: AbstractSet and most JDK set implementations are + // susceptible to quadratic removeAll performance on lists; + // use a slightly smarter implementation here + boolean changed = Sets.removeAllImpl((Set) delegate, c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + } + + /** + * SortedSet decorator that stays in sync with the multimap values for a key. + */ + private class WrappedSortedSet extends WrappedCollection implements SortedSet { + WrappedSortedSet(@Nullable K key, SortedSet delegate, @Nullable WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + SortedSet getSortedSetDelegate() { + return (SortedSet) getDelegate(); + } + + @Override + public Comparator comparator() { + return getSortedSetDelegate().comparator(); + } + + @Override + public V first() { + refreshIfEmpty(); + return getSortedSetDelegate().first(); + } + + @Override + public V last() { + refreshIfEmpty(); + return getSortedSetDelegate().last(); + } + + @Override + public SortedSet headSet(V toElement) { + refreshIfEmpty(); + return new WrappedSortedSet(getKey(), getSortedSetDelegate().headSet(toElement), + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public SortedSet subSet(V fromElement, V toElement) { + refreshIfEmpty(); + return new WrappedSortedSet(getKey(), getSortedSetDelegate().subSet(fromElement, toElement), + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public SortedSet tailSet(V fromElement) { + refreshIfEmpty(); + return new WrappedSortedSet(getKey(), getSortedSetDelegate().tailSet(fromElement), + (getAncestor() == null) ? this : getAncestor()); + } + } + + @GwtIncompatible("NavigableSet") + class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet { + WrappedNavigableSet(@Nullable K key, NavigableSet delegate, @Nullable WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + @Override + NavigableSet getSortedSetDelegate() { + return (NavigableSet) super.getSortedSetDelegate(); + } + + @Override + public V lower(V v) { + return getSortedSetDelegate().lower(v); + } + + @Override + public V floor(V v) { + return getSortedSetDelegate().floor(v); + } + + @Override + public V ceiling(V v) { + return getSortedSetDelegate().ceiling(v); + } + + @Override + public V higher(V v) { + return getSortedSetDelegate().higher(v); + } + + @Override + public V pollFirst() { + return Iterators.pollNext(iterator()); + } + + @Override + public V pollLast() { + return Iterators.pollNext(descendingIterator()); + } + + private NavigableSet wrap(NavigableSet wrapped) { + return new WrappedNavigableSet(key, wrapped, (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public NavigableSet descendingSet() { + return wrap(getSortedSetDelegate().descendingSet()); + } + + @Override + public Iterator descendingIterator() { + return new WrappedIterator(getSortedSetDelegate().descendingIterator()); + } + + @Override + public NavigableSet subSet(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { + return wrap(getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet headSet(V toElement, boolean inclusive) { + return wrap(getSortedSetDelegate().headSet(toElement, inclusive)); + } + + @Override + public NavigableSet tailSet(V fromElement, boolean inclusive) { + return wrap(getSortedSetDelegate().tailSet(fromElement, inclusive)); + } + } + + /** List decorator that stays in sync with the multimap values for a key. */ + private class WrappedList extends WrappedCollection implements List { + WrappedList(@Nullable K key, List delegate, @Nullable WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + List getListDelegate() { + return (List) getDelegate(); + } + + @Override + public boolean addAll(int index, Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = getListDelegate().addAll(index, c); + if (changed) { + int newSize = getDelegate().size(); + totalSize += (newSize - oldSize); + if (oldSize == 0) { + addToMap(); + } + } + return changed; + } + + @Override + public V get(int index) { + refreshIfEmpty(); + return getListDelegate().get(index); + } + + @Override + public V set(int index, V element) { + refreshIfEmpty(); + return getListDelegate().set(index, element); + } + + @Override + public void add(int index, V element) { + refreshIfEmpty(); + boolean wasEmpty = getDelegate().isEmpty(); + getListDelegate().add(index, element); + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + + @Override + public V remove(int index) { + refreshIfEmpty(); + V value = getListDelegate().remove(index); + totalSize--; + removeIfEmpty(); + return value; + } + + @Override + public int indexOf(Object o) { + refreshIfEmpty(); + return getListDelegate().indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + refreshIfEmpty(); + return getListDelegate().lastIndexOf(o); + } + + @Override + public ListIterator listIterator() { + refreshIfEmpty(); + return new WrappedListIterator(); + } + + @Override + public ListIterator listIterator(int index) { + refreshIfEmpty(); + return new WrappedListIterator(index); + } + + @Override + public List subList(int fromIndex, int toIndex) { + refreshIfEmpty(); + return wrapList(getKey(), getListDelegate().subList(fromIndex, toIndex), + (getAncestor() == null) ? this : getAncestor()); + } + + /** ListIterator decorator. */ + private class WrappedListIterator extends WrappedIterator implements ListIterator { + WrappedListIterator() { + } + + public WrappedListIterator(int index) { + super(getListDelegate().listIterator(index)); + } + + private ListIterator getDelegateListIterator() { + return (ListIterator) getDelegateIterator(); + } + + @Override + public boolean hasPrevious() { + return getDelegateListIterator().hasPrevious(); + } + + @Override + public V previous() { + return getDelegateListIterator().previous(); + } + + @Override + public int nextIndex() { + return getDelegateListIterator().nextIndex(); + } + + @Override + public int previousIndex() { + return getDelegateListIterator().previousIndex(); + } + + @Override + public void set(V value) { + getDelegateListIterator().set(value); + } + + @Override + public void add(V value) { + boolean wasEmpty = isEmpty(); + getDelegateListIterator().add(value); + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + } + } + + /** + * List decorator that stays in sync with the multimap values for a key and + * supports rapid random access. + */ + private class RandomAccessWrappedList extends WrappedList implements RandomAccess { + RandomAccessWrappedList(@Nullable K key, List delegate, @Nullable WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + } + + @Override + Set createKeySet() { + // TreeMultimap uses NavigableKeySet explicitly, but we don't handle that here + // for GWT + // compatibility reasons + return (map instanceof SortedMap) ? new SortedKeySet((SortedMap>) map) : new KeySet(map); + } + + private class KeySet extends Maps.KeySet> { + KeySet(final Map> subMap) { + super(subMap); + } + + @Override + public Iterator iterator() { + final Iterator>> entryIterator = map().entrySet().iterator(); + return new Iterator() { + Map.Entry> entry; + + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + public K next() { + entry = entryIterator.next(); + return entry.getKey(); + } + + @Override + public void remove() { + checkRemove(entry != null); + Collection collection = entry.getValue(); + entryIterator.remove(); + totalSize -= collection.size(); + collection.clear(); + } + }; + } + + // The following methods are included for better performance. + + @Override + public boolean remove(Object key) { + int count = 0; + Collection collection = map().remove(key); + if (collection != null) { + count = collection.size(); + collection.clear(); + totalSize -= count; + } + return count > 0; + } + + @Override + public void clear() { + Iterators.clear(iterator()); + } + + @Override + public boolean containsAll(Collection c) { + return map().keySet().containsAll(c); + } + + @Override + public boolean equals(@Nullable Object object) { + return this == object || this.map().keySet().equals(object); + } + + @Override + public int hashCode() { + return map().keySet().hashCode(); + } + } + + private class SortedKeySet extends KeySet implements SortedSet { + + SortedKeySet(SortedMap> subMap) { + super(subMap); + } + + SortedMap> sortedMap() { + return (SortedMap>) super.map(); + } + + @Override + public Comparator comparator() { + return sortedMap().comparator(); + } + + @Override + public K first() { + return sortedMap().firstKey(); + } + + @Override + public SortedSet headSet(K toElement) { + return new SortedKeySet(sortedMap().headMap(toElement)); + } + + @Override + public K last() { + return sortedMap().lastKey(); + } + + @Override + public SortedSet subSet(K fromElement, K toElement) { + return new SortedKeySet(sortedMap().subMap(fromElement, toElement)); + } + + @Override + public SortedSet tailSet(K fromElement) { + return new SortedKeySet(sortedMap().tailMap(fromElement)); + } + } + + @GwtIncompatible("NavigableSet") + class NavigableKeySet extends SortedKeySet implements NavigableSet { + NavigableKeySet(NavigableMap> subMap) { + super(subMap); + } + + @Override + NavigableMap> sortedMap() { + return (NavigableMap>) super.sortedMap(); + } + + @Override + public K lower(K k) { + return sortedMap().lowerKey(k); + } + + @Override + public K floor(K k) { + return sortedMap().floorKey(k); + } + + @Override + public K ceiling(K k) { + return sortedMap().ceilingKey(k); + } + + @Override + public K higher(K k) { + return sortedMap().higherKey(k); + } + + @Override + public K pollFirst() { + return Iterators.pollNext(iterator()); + } + + @Override + public K pollLast() { + return Iterators.pollNext(descendingIterator()); + } + + @Override + public NavigableSet descendingSet() { + return new NavigableKeySet(sortedMap().descendingMap()); + } + + @Override + public Iterator descendingIterator() { + return descendingSet().iterator(); + } + + @Override + public NavigableSet headSet(K toElement) { + return headSet(toElement, false); + } + + @Override + public NavigableSet headSet(K toElement, boolean inclusive) { + return new NavigableKeySet(sortedMap().headMap(toElement, inclusive)); + } + + @Override + public NavigableSet subSet(K fromElement, K toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public NavigableSet subSet(K fromElement, boolean fromInclusive, K toElement, boolean toInclusive) { + return new NavigableKeySet(sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet tailSet(K fromElement) { + return tailSet(fromElement, true); + } + + @Override + public NavigableSet tailSet(K fromElement, boolean inclusive) { + return new NavigableKeySet(sortedMap().tailMap(fromElement, inclusive)); + } + } + + /** + * Removes all values for the provided key. Unlike {@link #removeAll}, it + * returns the number of removed mappings. + */ + private int removeValuesForKey(Object key) { + Collection collection = Maps.safeRemove(map, key); + + int count = 0; + if (collection != null) { + count = collection.size(); + collection.clear(); + totalSize -= count; + } + return count; + } + + private abstract class Itr implements Iterator { + final Iterator>> keyIterator; + K key; + Collection collection; + Iterator valueIterator; + + Itr() { + keyIterator = map.entrySet().iterator(); + key = null; + collection = null; + valueIterator = Iterators.emptyModifiableIterator(); + } + + abstract T output(K key, V value); + + @Override + public boolean hasNext() { + return keyIterator.hasNext() || valueIterator.hasNext(); + } + + @Override + public T next() { + if (!valueIterator.hasNext()) { + Map.Entry> mapEntry = keyIterator.next(); + key = mapEntry.getKey(); + collection = mapEntry.getValue(); + valueIterator = collection.iterator(); + } + return output(key, valueIterator.next()); + } + + @Override + public void remove() { + valueIterator.remove(); + if (collection.isEmpty()) { + keyIterator.remove(); + } + totalSize--; + } + } + + /** + * {@inheritDoc} + * + *

+ * The iterator generated by the returned collection traverses the values for + * one key, followed by the values of a second key, and so on. + */ + @Override + public Collection values() { + return super.values(); + } + + @Override + Iterator valueIterator() { + return new Itr() { + @Override + V output(K key, V value) { + return value; + } + }; + } + + /* + * TODO(kevinb): should we copy this javadoc to each concrete class, so that + * classes like LinkedHashMultimap that need to say something different are + * still able to {@inheritDoc} all the way from Multimap? + */ + + /** + * {@inheritDoc} + * + *

+ * The iterator generated by the returned collection traverses the values for + * one key, followed by the values of a second key, and so on. + * + *

+ * Each entry is an immutable snapshot of a key-value mapping in the multimap, + * taken at the time the entry is returned by a method call to the collection or + * its iterator. + */ + @Override + public Collection> entries() { + return super.entries(); + } + + /** + * Returns an iterator across all key-value map entries, used by {@code + * entries().iterator()} and {@code values().iterator()}. The default behavior, + * which traverses the values for one key, the values for a second key, and so + * on, suffices for most {@code AbstractMapBasedMultimap} implementations. + * + * @return an iterator across map entries + */ + @Override + Iterator> entryIterator() { + return new Itr>() { + @Override + Entry output(K key, V value) { + return Maps.immutableEntry(key, value); + } + }; + } + + @Override + Map> createAsMap() { + // TreeMultimap uses NavigableAsMap explicitly, but we don't handle that here + // for GWT + // compatibility reasons + return (map instanceof SortedMap) ? new SortedAsMap((SortedMap>) map) : new AsMap(map); + } + + private class AsMap extends ImprovedAbstractMap> { + /** + * Usually the same as map, but smaller for the headMap(), tailMap(), or + * subMap() of a SortedAsMap. + */ + final transient Map> submap; + + AsMap(Map> submap) { + this.submap = submap; + } + + @Override + protected Set>> createEntrySet() { + return new AsMapEntries(); + } + + // The following methods are included for performance. + + @Override + public boolean containsKey(Object key) { + return Maps.safeContainsKey(submap, key); + } + + @Override + public Collection get(Object key) { + Collection collection = Maps.safeGet(submap, key); + if (collection == null) { + return null; + } + @SuppressWarnings("unchecked") + K k = (K) key; + return wrapCollection(k, collection); + } + + @Override + public Set keySet() { + return AbstractMapBasedMultimap.this.keySet(); + } + + @Override + public int size() { + return submap.size(); + } + + @Override + public Collection remove(Object key) { + Collection collection = submap.remove(key); + if (collection == null) { + return null; + } + + Collection output = createCollection(); + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + return output; + } + + @Override + public boolean equals(@Nullable Object object) { + return this == object || submap.equals(object); + } + + @Override + public int hashCode() { + return submap.hashCode(); + } + + @Override + public String toString() { + return submap.toString(); + } + + @Override + public void clear() { + if (submap == map) { + AbstractMapBasedMultimap.this.clear(); + } else { + Iterators.clear(new AsMapIterator()); + } + } + + Entry> wrapEntry(Entry> entry) { + K key = entry.getKey(); + return Maps.immutableEntry(key, wrapCollection(key, entry.getValue())); + } + + class AsMapEntries extends Maps.EntrySet> { + @Override + Map> map() { + return AsMap.this; + } + + @Override + public Iterator>> iterator() { + return new AsMapIterator(); + } + + // The following methods are included for performance. + + @Override + public boolean contains(Object o) { + return Collections2.safeContains(submap.entrySet(), o); + } + + @Override + public boolean remove(Object o) { + if (!contains(o)) { + return false; + } + Map.Entry entry = (Map.Entry) o; + removeValuesForKey(entry.getKey()); + return true; + } + } + + /** Iterator across all keys and value collections. */ + class AsMapIterator implements Iterator>> { + final Iterator>> delegateIterator = submap.entrySet().iterator(); + Collection collection; + + @Override + public boolean hasNext() { + return delegateIterator.hasNext(); + } + + @Override + public Map.Entry> next() { + Map.Entry> entry = delegateIterator.next(); + collection = entry.getValue(); + return wrapEntry(entry); + } + + @Override + public void remove() { + delegateIterator.remove(); + totalSize -= collection.size(); + collection.clear(); + } + } + } + + private class SortedAsMap extends AsMap implements SortedMap> { + SortedAsMap(SortedMap> submap) { + super(submap); + } + + SortedMap> sortedMap() { + return (SortedMap>) submap; + } + + @Override + public Comparator comparator() { + return sortedMap().comparator(); + } + + @Override + public K firstKey() { + return sortedMap().firstKey(); + } + + @Override + public K lastKey() { + return sortedMap().lastKey(); + } + + @Override + public SortedMap> headMap(K toKey) { + return new SortedAsMap(sortedMap().headMap(toKey)); + } + + @Override + public SortedMap> subMap(K fromKey, K toKey) { + return new SortedAsMap(sortedMap().subMap(fromKey, toKey)); + } + + @Override + public SortedMap> tailMap(K fromKey) { + return new SortedAsMap(sortedMap().tailMap(fromKey)); + } + + SortedSet sortedKeySet; + + // returns a SortedSet, even though returning a Set would be sufficient to + // satisfy the SortedMap.keySet() interface + @Override + public SortedSet keySet() { + SortedSet result = sortedKeySet; + return (result == null) ? sortedKeySet = createKeySet() : result; + } + + @Override + SortedSet createKeySet() { + return new SortedKeySet(sortedMap()); + } + } + + @GwtIncompatible("NavigableAsMap") + class NavigableAsMap extends SortedAsMap implements NavigableMap> { + + NavigableAsMap(NavigableMap> submap) { + super(submap); + } + + @Override + NavigableMap> sortedMap() { + return (NavigableMap>) super.sortedMap(); + } + + @Override + public Entry> lowerEntry(K key) { + Entry> entry = sortedMap().lowerEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K lowerKey(K key) { + return sortedMap().lowerKey(key); + } + + @Override + public Entry> floorEntry(K key) { + Entry> entry = sortedMap().floorEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K floorKey(K key) { + return sortedMap().floorKey(key); + } + + @Override + public Entry> ceilingEntry(K key) { + Entry> entry = sortedMap().ceilingEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K ceilingKey(K key) { + return sortedMap().ceilingKey(key); + } + + @Override + public Entry> higherEntry(K key) { + Entry> entry = sortedMap().higherEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K higherKey(K key) { + return sortedMap().higherKey(key); + } + + @Override + public Entry> firstEntry() { + Entry> entry = sortedMap().firstEntry(); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public Entry> lastEntry() { + Entry> entry = sortedMap().lastEntry(); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public Entry> pollFirstEntry() { + return pollAsMapEntry(entrySet().iterator()); + } + + @Override + public Entry> pollLastEntry() { + return pollAsMapEntry(descendingMap().entrySet().iterator()); + } + + Map.Entry> pollAsMapEntry(Iterator>> entryIterator) { + if (!entryIterator.hasNext()) { + return null; + } + Entry> entry = entryIterator.next(); + Collection output = createCollection(); + output.addAll(entry.getValue()); + entryIterator.remove(); + return Maps.immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output)); + } + + @Override + public NavigableMap> descendingMap() { + return new NavigableAsMap(sortedMap().descendingMap()); + } + + @Override + public NavigableSet keySet() { + return (NavigableSet) super.keySet(); + } + + @Override + NavigableSet createKeySet() { + return new NavigableKeySet(sortedMap()); + } + + @Override + public NavigableSet navigableKeySet() { + return keySet(); + } + + @Override + public NavigableSet descendingKeySet() { + return descendingMap().navigableKeySet(); + } + + @Override + public NavigableMap> subMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return new NavigableAsMap(sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + @Override + public NavigableMap> headMap(K toKey) { + return headMap(toKey, false); + } + + @Override + public NavigableMap> headMap(K toKey, boolean inclusive) { + return new NavigableAsMap(sortedMap().headMap(toKey, inclusive)); + } + + @Override + public NavigableMap> tailMap(K fromKey) { + return tailMap(fromKey, true); + } + + @Override + public NavigableMap> tailMap(K fromKey, boolean inclusive) { + return new NavigableAsMap(sortedMap().tailMap(fromKey, inclusive)); + } + } + + private static final long serialVersionUID = 2447537837011683357L; +} diff --git a/sources/main/java/com/google/common/collect/AbstractMapBasedMultiset.java b/sources/main/java/com/google/common/collect/AbstractMapBasedMultiset.java new file mode 100644 index 0000000..4b200c3 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractMapBasedMultiset.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.primitives.Ints; + +/** + * Basic implementation of {@code Multiset} backed by an instance of {@code + * Map}. + * + *

+ * For serialization to work, the subclass must specify explicit {@code + * readObject} and {@code writeObject} methods. + * + * @author Kevin Bourrillion + */ +@GwtCompatible(emulated = true) +abstract class AbstractMapBasedMultiset extends AbstractMultiset implements Serializable { + + private transient Map backingMap; + + /* + * Cache the size for efficiency. Using a long lets us avoid the need for + * overflow checking and ensures that size() will function correctly even if the + * multiset had once been larger than Integer.MAX_VALUE. + */ + private transient long size; + + /** Standard constructor. */ + protected AbstractMapBasedMultiset(Map backingMap) { + this.backingMap = checkNotNull(backingMap); + this.size = super.size(); + } + + /** Used during deserialization only. The backing map must be empty. */ + void setBackingMap(Map backingMap) { + this.backingMap = backingMap; + } + + // Required Implementations + + /** + * {@inheritDoc} + * + *

+ * Invoking {@link Multiset.Entry#getCount} on an entry in the returned set + * always returns the current count of that element in the multiset, as opposed + * to the count at the time the entry was retrieved. + */ + @Override + public Set> entrySet() { + return super.entrySet(); + } + + @Override + Iterator> entryIterator() { + final Iterator> backingEntries = backingMap.entrySet().iterator(); + return new Iterator>() { + Map.Entry toRemove; + + @Override + public boolean hasNext() { + return backingEntries.hasNext(); + } + + @Override + public Multiset.Entry next() { + final Map.Entry mapEntry = backingEntries.next(); + toRemove = mapEntry; + return new Multisets.AbstractEntry() { + @Override + public E getElement() { + return mapEntry.getKey(); + } + + @Override + public int getCount() { + Count count = mapEntry.getValue(); + if (count == null || count.get() == 0) { + Count frequency = backingMap.get(getElement()); + if (frequency != null) { + return frequency.get(); + } + } + return (count == null) ? 0 : count.get(); + } + }; + } + + @Override + public void remove() { + checkRemove(toRemove != null); + size -= toRemove.getValue().getAndSet(0); + backingEntries.remove(); + toRemove = null; + } + }; + } + + @Override + public void clear() { + for (Count frequency : backingMap.values()) { + frequency.set(0); + } + backingMap.clear(); + size = 0L; + } + + @Override + int distinctElements() { + return backingMap.size(); + } + + // Optimizations - Query Operations + + @Override + public int size() { + return Ints.saturatedCast(size); + } + + @Override + public Iterator iterator() { + return new MapBasedMultisetIterator(); + } + + /* + * Not subclassing AbstractMultiset$MultisetIterator because next() needs to + * retrieve the Map.Entry entry, which can then be used for a more + * efficient remove() call. + */ + private class MapBasedMultisetIterator implements Iterator { + final Iterator> entryIterator; + Map.Entry currentEntry; + int occurrencesLeft; + boolean canRemove; + + MapBasedMultisetIterator() { + this.entryIterator = backingMap.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + return occurrencesLeft > 0 || entryIterator.hasNext(); + } + + @Override + public E next() { + if (occurrencesLeft == 0) { + currentEntry = entryIterator.next(); + occurrencesLeft = currentEntry.getValue().get(); + } + occurrencesLeft--; + canRemove = true; + return currentEntry.getKey(); + } + + @Override + public void remove() { + checkRemove(canRemove); + int frequency = currentEntry.getValue().get(); + if (frequency <= 0) { + throw new ConcurrentModificationException(); + } + if (currentEntry.getValue().addAndGet(-1) == 0) { + entryIterator.remove(); + } + size--; + canRemove = false; + } + } + + @Override + public int count(@Nullable Object element) { + Count frequency = Maps.safeGet(backingMap, element); + return (frequency == null) ? 0 : frequency.get(); + } + + // Optional Operations - Modification Operations + + /** + * {@inheritDoc} + * + * @throws IllegalArgumentException if the call would result in more than + * {@link Integer#MAX_VALUE} occurrences of + * {@code element} in this multiset. + */ + @Override + public int add(@Nullable E element, int occurrences) { + if (occurrences == 0) { + return count(element); + } + checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); + Count frequency = backingMap.get(element); + int oldCount; + if (frequency == null) { + oldCount = 0; + backingMap.put(element, new Count(occurrences)); + } else { + oldCount = frequency.get(); + long newCount = (long) oldCount + (long) occurrences; + checkArgument(newCount <= Integer.MAX_VALUE, "too many occurrences: %s", newCount); + frequency.getAndAdd(occurrences); + } + size += occurrences; + return oldCount; + } + + @Override + public int remove(@Nullable Object element, int occurrences) { + if (occurrences == 0) { + return count(element); + } + checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); + Count frequency = backingMap.get(element); + if (frequency == null) { + return 0; + } + + int oldCount = frequency.get(); + + int numberRemoved; + if (oldCount > occurrences) { + numberRemoved = occurrences; + } else { + numberRemoved = oldCount; + backingMap.remove(element); + } + + frequency.addAndGet(-numberRemoved); + size -= numberRemoved; + return oldCount; + } + + // Roughly a 33% performance improvement over AbstractMultiset.setCount(). + @Override + public int setCount(@Nullable E element, int count) { + checkNonnegative(count, "count"); + + Count existingCounter; + int oldCount; + if (count == 0) { + existingCounter = backingMap.remove(element); + oldCount = getAndSet(existingCounter, count); + } else { + existingCounter = backingMap.get(element); + oldCount = getAndSet(existingCounter, count); + + if (existingCounter == null) { + backingMap.put(element, new Count(count)); + } + } + + size += (count - oldCount); + return oldCount; + } + + private static int getAndSet(Count i, int count) { + if (i == null) { + return 0; + } + + return i.getAndSet(count); + } + + // Don't allow default serialization. + @GwtIncompatible("java.io.ObjectStreamException") + @SuppressWarnings("unused") // actually used during deserialization + private void readObjectNoData() throws ObjectStreamException { + throw new InvalidObjectException("Stream data required"); + } + + @GwtIncompatible("not needed in emulated source.") + private static final long serialVersionUID = -2250766705698539974L; +} diff --git a/sources/main/java/com/google/common/collect/AbstractMapEntry.java b/sources/main/java/com/google/common/collect/AbstractMapEntry.java new file mode 100644 index 0000000..3cf5309 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractMapEntry.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * Implementation of the {@code equals}, {@code hashCode}, and {@code toString} + * methods of {@code Entry}. + * + * @author Jared Levy + */ +@GwtCompatible +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(@Nullable Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Objects.equal(this.getKey(), that.getKey()) && Objects.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/sources/main/java/com/google/common/collect/AbstractMultimap.java b/sources/main/java/com/google/common/collect/AbstractMultimap.java new file mode 100644 index 0000000..1231e4d --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractMultimap.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A skeleton {@code Multimap} implementation, not necessarily in terms of a + * {@code Map}. + * + * @author Louis Wasserman + */ +@GwtCompatible +abstract class AbstractMultimap implements Multimap { + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsValue(@Nullable Object value) { + for (Collection collection : asMap().values()) { + if (collection.contains(value)) { + return true; + } + } + + return false; + } + + @Override + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { + Collection collection = asMap().get(key); + return collection != null && collection.contains(value); + } + + @Override + public boolean remove(@Nullable Object key, @Nullable Object value) { + Collection collection = asMap().get(key); + return collection != null && collection.remove(value); + } + + @Override + public boolean put(@Nullable K key, @Nullable V value) { + return get(key).add(value); + } + + @Override + public boolean putAll(@Nullable K key, Iterable values) { + checkNotNull(values); + // make sure we only call values.iterator() once + // and we only call get(key) if values is nonempty + if (values instanceof Collection) { + Collection valueCollection = (Collection) values; + return !valueCollection.isEmpty() && get(key).addAll(valueCollection); + } else { + Iterator valueItr = values.iterator(); + return valueItr.hasNext() && Iterators.addAll(get(key), valueItr); + } + } + + @Override + public boolean putAll(Multimap multimap) { + boolean changed = false; + for (Map.Entry entry : multimap.entries()) { + changed |= put(entry.getKey(), entry.getValue()); + } + return changed; + } + + @Override + public Collection replaceValues(@Nullable K key, Iterable values) { + checkNotNull(values); + Collection result = removeAll(key); + putAll(key, values); + return result; + } + + private transient Collection> entries; + + @Override + public Collection> entries() { + Collection> result = entries; + return (result == null) ? entries = createEntries() : result; + } + + Collection> createEntries() { + if (this instanceof SetMultimap) { + return new EntrySet(); + } else { + return new Entries(); + } + } + + private class Entries extends Multimaps.Entries { + @Override + Multimap multimap() { + return AbstractMultimap.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + } + + private class EntrySet extends Entries implements Set> { + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @Override + public boolean equals(@Nullable Object obj) { + return Sets.equalsImpl(this, obj); + } + } + + abstract Iterator> entryIterator(); + + private transient Set keySet; + + @Override + public Set keySet() { + Set result = keySet; + return (result == null) ? keySet = createKeySet() : result; + } + + Set createKeySet() { + return new Maps.KeySet>(asMap()); + } + + private transient Multiset keys; + + @Override + public Multiset keys() { + Multiset result = keys; + return (result == null) ? keys = createKeys() : result; + } + + Multiset createKeys() { + return new Multimaps.Keys(this); + } + + private transient Collection values; + + @Override + public Collection values() { + Collection result = values; + return (result == null) ? values = createValues() : result; + } + + Collection createValues() { + return new Values(); + } + + class Values extends AbstractCollection { + @Override + public Iterator iterator() { + return valueIterator(); + } + + @Override + public int size() { + return AbstractMultimap.this.size(); + } + + @Override + public boolean contains(@Nullable Object o) { + return AbstractMultimap.this.containsValue(o); + } + + @Override + public void clear() { + AbstractMultimap.this.clear(); + } + } + + Iterator valueIterator() { + return Maps.valueIterator(entries().iterator()); + } + + private transient Map> asMap; + + @Override + public Map> asMap() { + Map> result = asMap; + return (result == null) ? asMap = createAsMap() : result; + } + + abstract Map> createAsMap(); + + // Comparison and hashing + + @Override + public boolean equals(@Nullable Object object) { + return Multimaps.equalsImpl(this, object); + } + + /** + * Returns the hash code for this multimap. + * + *

+ * The hash code of a multimap is defined as the hash code of the map view, as + * returned by {@link Multimap#asMap}. + * + * @see Map#hashCode + */ + @Override + public int hashCode() { + return asMap().hashCode(); + } + + /** + * Returns a string representation of the multimap, generated by calling + * {@code toString} on the map returned by {@link Multimap#asMap}. + * + * @return a string representation of the multimap + */ + @Override + public String toString() { + return asMap().toString(); + } +} diff --git a/sources/main/java/com/google/common/collect/AbstractMultiset.java b/sources/main/java/com/google/common/collect/AbstractMultiset.java new file mode 100644 index 0000000..0af64f4 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractMultiset.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.Multisets.setCountImpl; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * This class provides a skeletal implementation of the {@link Multiset} + * interface. A new multiset implementation can be created easily by extending + * this class and implementing the {@link Multiset#entrySet()} method, plus + * optionally overriding {@link #add(Object, int)} and + * {@link #remove(Object, int)} to enable modifications to the multiset. + * + *

+ * The {@link #count} and {@link #size} implementations all iterate across the + * set returned by {@link Multiset#entrySet()}, as do many methods acting on the + * set returned by {@link #elementSet()}. Override those methods for better + * performance. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + */ +@GwtCompatible +abstract class AbstractMultiset extends AbstractCollection implements Multiset { + // Query Operations + + @Override + public int size() { + return Multisets.sizeImpl(this); + } + + @Override + public boolean isEmpty() { + return entrySet().isEmpty(); + } + + @Override + public boolean contains(@Nullable Object element) { + return count(element) > 0; + } + + @Override + public Iterator iterator() { + return Multisets.iteratorImpl(this); + } + + @Override + public int count(@Nullable Object element) { + for (Entry entry : entrySet()) { + if (Objects.equal(entry.getElement(), element)) { + return entry.getCount(); + } + } + return 0; + } + + // Modification Operations + + @Override + public boolean add(@Nullable E element) { + add(element, 1); + return true; + } + + @Override + public int add(@Nullable E element, int occurrences) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(@Nullable Object element) { + return remove(element, 1) > 0; + } + + @Override + public int remove(@Nullable Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + @Override + public int setCount(@Nullable E element, int count) { + return setCountImpl(this, element, count); + } + + @Override + public boolean setCount(@Nullable E element, int oldCount, int newCount) { + return setCountImpl(this, element, oldCount, newCount); + } + + // Bulk Operations + + /** + * {@inheritDoc} + * + *

+ * This implementation is highly efficient when {@code elementsToAdd} is itself + * a {@link Multiset}. + */ + @Override + public boolean addAll(Collection elementsToAdd) { + return Multisets.addAllImpl(this, elementsToAdd); + } + + @Override + public boolean removeAll(Collection elementsToRemove) { + return Multisets.removeAllImpl(this, elementsToRemove); + } + + @Override + public boolean retainAll(Collection elementsToRetain) { + return Multisets.retainAllImpl(this, elementsToRetain); + } + + @Override + public void clear() { + Iterators.clear(entryIterator()); + } + + // Views + + private transient Set elementSet; + + @Override + public Set elementSet() { + Set result = elementSet; + if (result == null) { + elementSet = result = createElementSet(); + } + return result; + } + + /** + * Creates a new instance of this multiset's element set, which will be returned + * by {@link #elementSet()}. + */ + Set createElementSet() { + return new ElementSet(); + } + + class ElementSet extends Multisets.ElementSet { + @Override + Multiset multiset() { + return AbstractMultiset.this; + } + } + + abstract Iterator> entryIterator(); + + abstract int distinctElements(); + + private transient Set> entrySet; + + @Override + public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + class EntrySet extends Multisets.EntrySet { + @Override + Multiset multiset() { + return AbstractMultiset.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + + @Override + public int size() { + return distinctElements(); + } + } + + Set> createEntrySet() { + return new EntrySet(); + } + + // Object methods + + /** + * {@inheritDoc} + * + *

+ * This implementation returns {@code true} if {@code object} is a multiset of + * the same size and if, for each element, the two multisets have the same + * count. + */ + @Override + public boolean equals(@Nullable Object object) { + return Multisets.equalsImpl(this, object); + } + + /** + * {@inheritDoc} + * + *

+ * This implementation returns the hash code of {@link Multiset#entrySet()}. + */ + @Override + public int hashCode() { + return entrySet().hashCode(); + } + + /** + * {@inheritDoc} + * + *

+ * This implementation returns the result of invoking {@code toString} on + * {@link Multiset#entrySet()}. + */ + @Override + public String toString() { + return entrySet().toString(); + } +} diff --git a/sources/main/java/com/google/common/collect/AbstractNavigableMap.java b/sources/main/java/com/google/common/collect/AbstractNavigableMap.java new file mode 100644 index 0000000..753f92d --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractNavigableMap.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.AbstractMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedMap; + +import javax.annotation.Nullable; + +/** + * Skeletal implementation of {@link NavigableMap}. + * + * @author Louis Wasserman + */ +abstract class AbstractNavigableMap extends AbstractMap implements NavigableMap { + + @Override + @Nullable + public abstract V get(@Nullable Object key); + + @Override + @Nullable + public Entry firstEntry() { + return Iterators.getNext(entryIterator(), null); + } + + @Override + @Nullable + public Entry lastEntry() { + return Iterators.getNext(descendingEntryIterator(), null); + } + + @Override + @Nullable + public Entry pollFirstEntry() { + return Iterators.pollNext(entryIterator()); + } + + @Override + @Nullable + public Entry pollLastEntry() { + return Iterators.pollNext(descendingEntryIterator()); + } + + @Override + public K firstKey() { + Entry entry = firstEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Override + public K lastKey() { + Entry entry = lastEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Override + @Nullable + public Entry lowerEntry(K key) { + return headMap(key, false).lastEntry(); + } + + @Override + @Nullable + public Entry floorEntry(K key) { + return headMap(key, true).lastEntry(); + } + + @Override + @Nullable + public Entry ceilingEntry(K key) { + return tailMap(key, true).firstEntry(); + } + + @Override + @Nullable + public Entry higherEntry(K key) { + return tailMap(key, false).firstEntry(); + } + + @Override + public K lowerKey(K key) { + return Maps.keyOrNull(lowerEntry(key)); + } + + @Override + public K floorKey(K key) { + return Maps.keyOrNull(floorEntry(key)); + } + + @Override + public K ceilingKey(K key) { + return Maps.keyOrNull(ceilingEntry(key)); + } + + @Override + public K higherKey(K key) { + return Maps.keyOrNull(higherEntry(key)); + } + + abstract Iterator> entryIterator(); + + abstract Iterator> descendingEntryIterator(); + + @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 NavigableSet navigableKeySet() { + return new Maps.NavigableKeySet(this); + } + + @Override + public Set keySet() { + return navigableKeySet(); + } + + @Override + public abstract int size(); + + @Override + public Set> entrySet() { + return new Maps.EntrySet() { + @Override + Map map() { + return AbstractNavigableMap.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + }; + } + + @Override + public NavigableSet descendingKeySet() { + return descendingMap().navigableKeySet(); + } + + @Override + public NavigableMap descendingMap() { + return new DescendingMap(); + } + + private final class DescendingMap extends Maps.DescendingMap { + @Override + NavigableMap forward() { + return AbstractNavigableMap.this; + } + + @Override + Iterator> entryIterator() { + return descendingEntryIterator(); + } + } + +} diff --git a/sources/main/java/com/google/common/collect/AbstractRangeSet.java b/sources/main/java/com/google/common/collect/AbstractRangeSet.java new file mode 100644 index 0000000..e8fc015 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractRangeSet.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import javax.annotation.Nullable; + +/** + * A skeletal implementation of {@code RangeSet}. + * + * @author Louis Wasserman + */ +abstract class AbstractRangeSet implements RangeSet { + AbstractRangeSet() { + } + + @Override + public boolean contains(C value) { + return rangeContaining(value) != null; + } + + @Override + public abstract Range rangeContaining(C value); + + @Override + public boolean isEmpty() { + return asRanges().isEmpty(); + } + + @Override + public void add(Range range) { + throw new UnsupportedOperationException(); + } + + @Override + public void remove(Range range) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + remove(Range.all()); + } + + @Override + public boolean enclosesAll(RangeSet other) { + for (Range range : other.asRanges()) { + if (!encloses(range)) { + return false; + } + } + return true; + } + + @Override + public void addAll(RangeSet other) { + for (Range range : other.asRanges()) { + add(range); + } + } + + @Override + public void removeAll(RangeSet other) { + for (Range range : other.asRanges()) { + remove(range); + } + } + + @Override + public abstract boolean encloses(Range otherRange); + + @Override + public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof RangeSet) { + RangeSet other = (RangeSet) obj; + return this.asRanges().equals(other.asRanges()); + } + return false; + } + + @Override + public final int hashCode() { + return asRanges().hashCode(); + } + + @Override + public final String toString() { + return asRanges().toString(); + } +} diff --git a/sources/main/java/com/google/common/collect/AbstractSequentialIterator.java b/sources/main/java/com/google/common/collect/AbstractSequentialIterator.java new file mode 100644 index 0000000..2c1f0c1 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractSequentialIterator.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.NoSuchElementException; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * This class provides a skeletal implementation of the {@code Iterator} + * interface for sequences whose next element can always be derived from the + * previous element. Null elements are not supported, nor is the + * {@link #remove()} method. + * + *

+ * Example: + * + *

+ * {
+ * 	@code
+ *
+ * 	Iterator powersOfTwo = new AbstractSequentialIterator(1) {
+ * 		protected Integer computeNext(Integer previous) {
+ * 			return (previous == 1 << 30) ? null : previous * 2;
+ * 		}
+ * 	};
+ * }
+ * 
+ * + * @author Chris Povirk + * @since 12.0 (in Guava as {@code AbstractLinkedIterator} since 8.0) + */ +@GwtCompatible +public abstract class AbstractSequentialIterator extends UnmodifiableIterator { + private T nextOrNull; + + /** + * Creates a new iterator with the given first element, or, if {@code + * firstOrNull} is null, creates a new empty iterator. + */ + protected AbstractSequentialIterator(@Nullable T firstOrNull) { + this.nextOrNull = firstOrNull; + } + + /** + * Returns the element that follows {@code previous}, or returns {@code null} if + * no elements remain. This method is invoked during each call to + * {@link #next()} in order to compute the result of a future call to + * {@code next()}. + */ + protected abstract T computeNext(T previous); + + @Override + public final boolean hasNext() { + return nextOrNull != null; + } + + @Override + public final T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + try { + return nextOrNull; + } finally { + nextOrNull = computeNext(nextOrNull); + } + } +} diff --git a/sources/main/java/com/google/common/collect/AbstractSetMultimap.java b/sources/main/java/com/google/common/collect/AbstractSetMultimap.java new file mode 100644 index 0000000..fd07264 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractSetMultimap.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Basic implementation of the {@link SetMultimap} interface. It's a wrapper + * around {@link AbstractMapBasedMultimap} that converts the returned + * collections into {@code Sets}. The {@link #createCollection} method must + * return a {@code Set}. + * + * @author Jared Levy + */ +@GwtCompatible +abstract class AbstractSetMultimap extends AbstractMapBasedMultimap implements SetMultimap { + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + */ + protected AbstractSetMultimap(Map> map) { + super(map); + } + + @Override + abstract Set createCollection(); + + @Override + Set createUnmodifiableEmptyCollection() { + return ImmutableSet.of(); + } + + // Following Javadoc copied from SetMultimap. + + /** + * {@inheritDoc} + * + *

+ * Because a {@code SetMultimap} has unique values for a given key, this method + * returns a {@link Set}, instead of the {@link Collection} specified in the + * {@link Multimap} interface. + */ + @Override + public Set get(@Nullable K key) { + return (Set) super.get(key); + } + + /** + * {@inheritDoc} + * + *

+ * Because a {@code SetMultimap} has unique values for a given key, this method + * returns a {@link Set}, instead of the {@link Collection} specified in the + * {@link Multimap} interface. + */ + @Override + public Set> entries() { + return (Set>) super.entries(); + } + + /** + * {@inheritDoc} + * + *

+ * Because a {@code SetMultimap} has unique values for a given key, this method + * returns a {@link Set}, instead of the {@link Collection} specified in the + * {@link Multimap} interface. + */ + @Override + public Set removeAll(@Nullable Object key) { + return (Set) super.removeAll(key); + } + + /** + * {@inheritDoc} + * + *

+ * Because a {@code SetMultimap} has unique values for a given key, this method + * returns a {@link Set}, instead of the {@link Collection} specified in the + * {@link Multimap} interface. + * + *

+ * Any duplicates in {@code values} will be stored in the multimap once. + */ + @Override + public Set replaceValues(@Nullable K key, Iterable values) { + return (Set) super.replaceValues(key, values); + } + + /** + * {@inheritDoc} + * + *

+ * Though the method signature doesn't say so explicitly, the returned map has + * {@link Set} values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * Stores a key-value pair in the multimap. + * + * @param key key to store in the multimap + * @param value value to store in the multimap + * @return {@code true} if the method increased the size of the multimap, or + * {@code false} if the multimap already contained the key-value pair + */ + @Override + public boolean put(@Nullable K key, @Nullable V value) { + return super.put(key, value); + } + + /** + * Compares the specified object to this multimap for equality. + * + *

+ * Two {@code SetMultimap} instances are equal if, for each key, they contain + * the same values. Equality does not depend on the ordering of keys or values. + */ + @Override + public boolean equals(@Nullable Object object) { + return super.equals(object); + } + + private static final long serialVersionUID = 7431625294878419160L; +} diff --git a/sources/main/java/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java b/sources/main/java/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java new file mode 100644 index 0000000..f1d5b67 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.SortedMap; +import java.util.SortedSet; + +import com.google.common.annotations.GwtCompatible; + +/** + * Basic implementation of a {@link SortedSetMultimap} with a sorted key set. + * + * This superclass allows {@code TreeMultimap} to override methods to return + * navigable set and map types in non-GWT only, while GWT code will inherit the + * SortedMap/SortedSet overrides. + * + * @author Louis Wasserman + */ +@GwtCompatible +abstract class AbstractSortedKeySortedSetMultimap extends AbstractSortedSetMultimap { + + AbstractSortedKeySortedSetMultimap(SortedMap> map) { + super(map); + } + + @Override + public SortedMap> asMap() { + return (SortedMap>) super.asMap(); + } + + @Override + SortedMap> backingMap() { + return (SortedMap>) super.backingMap(); + } + + @Override + public SortedSet keySet() { + return (SortedSet) super.keySet(); + } + +} diff --git a/sources/main/java/com/google/common/collect/AbstractSortedMultiset.java b/sources/main/java/com/google/common/collect/AbstractSortedMultiset.java new file mode 100644 index 0000000..4f66689 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractSortedMultiset.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * This class provides a skeletal implementation of the {@link SortedMultiset} + * interface. + * + *

+ * The {@link #count} and {@link #size} implementations all iterate across the + * set returned by {@link Multiset#entrySet()}, as do many methods acting on the + * set returned by {@link #elementSet()}. Override those methods for better + * performance. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +abstract class AbstractSortedMultiset extends AbstractMultiset implements SortedMultiset { + @GwtTransient + final Comparator comparator; + + // needed for serialization + @SuppressWarnings("unchecked") + AbstractSortedMultiset() { + this((Comparator) Ordering.natural()); + } + + AbstractSortedMultiset(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + @Override + public NavigableSet elementSet() { + return (NavigableSet) super.elementSet(); + } + + @Override + NavigableSet createElementSet() { + return new SortedMultisets.NavigableElementSet(this); + } + + @Override + public Comparator comparator() { + return comparator; + } + + @Override + public Entry firstEntry() { + Iterator> entryIterator = entryIterator(); + return entryIterator.hasNext() ? entryIterator.next() : null; + } + + @Override + public Entry lastEntry() { + Iterator> entryIterator = descendingEntryIterator(); + return entryIterator.hasNext() ? entryIterator.next() : null; + } + + @Override + public Entry pollFirstEntry() { + Iterator> entryIterator = entryIterator(); + if (entryIterator.hasNext()) { + Entry result = entryIterator.next(); + result = Multisets.immutableEntry(result.getElement(), result.getCount()); + entryIterator.remove(); + return result; + } + return null; + } + + @Override + public Entry pollLastEntry() { + Iterator> entryIterator = descendingEntryIterator(); + if (entryIterator.hasNext()) { + Entry result = entryIterator.next(); + result = Multisets.immutableEntry(result.getElement(), result.getCount()); + entryIterator.remove(); + return result; + } + return null; + } + + @Override + public SortedMultiset subMultiset(@Nullable E fromElement, BoundType fromBoundType, @Nullable E toElement, + BoundType toBoundType) { + // These are checked elsewhere, but NullPointerTester wants them checked + // eagerly. + checkNotNull(fromBoundType); + checkNotNull(toBoundType); + return tailMultiset(fromElement, fromBoundType).headMultiset(toElement, toBoundType); + } + + abstract Iterator> descendingEntryIterator(); + + Iterator descendingIterator() { + return Multisets.iteratorImpl(descendingMultiset()); + } + + private transient SortedMultiset descendingMultiset; + + @Override + public SortedMultiset descendingMultiset() { + SortedMultiset result = descendingMultiset; + return (result == null) ? descendingMultiset = createDescendingMultiset() : result; + } + + SortedMultiset createDescendingMultiset() { + return new DescendingMultiset() { + @Override + SortedMultiset forwardMultiset() { + return AbstractSortedMultiset.this; + } + + @Override + Iterator> entryIterator() { + return descendingEntryIterator(); + } + + @Override + public Iterator iterator() { + return descendingIterator(); + } + }; + } +} diff --git a/sources/main/java/com/google/common/collect/AbstractSortedSetMultimap.java b/sources/main/java/com/google/common/collect/AbstractSortedSetMultimap.java new file mode 100644 index 0000000..52a2217 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractSortedSetMultimap.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Map; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Basic implementation of the {@link SortedSetMultimap} interface. It's a + * wrapper around {@link AbstractMapBasedMultimap} that converts the returned + * collections into sorted sets. The {@link #createCollection} method must + * return a {@code SortedSet}. + * + * @author Jared Levy + */ +@GwtCompatible +abstract class AbstractSortedSetMultimap extends AbstractSetMultimap implements SortedSetMultimap { + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + */ + protected AbstractSortedSetMultimap(Map> map) { + super(map); + } + + @Override + abstract SortedSet createCollection(); + + @Override + SortedSet createUnmodifiableEmptyCollection() { + Comparator comparator = valueComparator(); + if (comparator == null) { + return Collections.unmodifiableSortedSet(createCollection()); + } else { + return ImmutableSortedSet.emptySet(valueComparator()); + } + } + + // Following Javadoc copied from Multimap and SortedSetMultimap. + + /** + * Returns a collection view of all values associated with a key. If no mappings + * in the multimap have the provided key, an empty collection is returned. + * + *

+ * Changes to the returned collection will update the underlying multimap, and + * vice versa. + * + *

+ * Because a {@code SortedSetMultimap} has unique sorted values for a given key, + * this method returns a {@link SortedSet}, instead of the {@link Collection} + * specified in the {@link Multimap} interface. + */ + @Override + public SortedSet get(@Nullable K key) { + return (SortedSet) super.get(key); + } + + /** + * Removes all values associated with a given key. The returned collection is + * immutable. + * + *

+ * Because a {@code SortedSetMultimap} has unique sorted values for a given key, + * this method returns a {@link SortedSet}, instead of the {@link Collection} + * specified in the {@link Multimap} interface. + */ + @Override + public SortedSet removeAll(@Nullable Object key) { + return (SortedSet) super.removeAll(key); + } + + /** + * Stores a collection of values with the same key, replacing any existing + * values for that key. The returned collection is immutable. + * + *

+ * Because a {@code SortedSetMultimap} has unique sorted values for a given key, + * this method returns a {@link SortedSet}, instead of the {@link Collection} + * specified in the {@link Multimap} interface. + * + *

+ * Any duplicates in {@code values} will be stored in the multimap once. + */ + @Override + public SortedSet replaceValues(@Nullable K key, Iterable values) { + return (SortedSet) super.replaceValues(key, values); + } + + /** + * Returns a map view that associates each key with the corresponding values in + * the multimap. Changes to the returned map, such as element removal, will + * update the underlying multimap. The map does not support {@code setValue} on + * its entries, {@code put}, or {@code putAll}. + * + *

+ * When passed a key that is present in the map, {@code + * asMap().get(Object)} has the same behavior as {@link #get}, returning a live + * collection. When passed a key that is not present, however, {@code + * asMap().get(Object)} returns {@code null} instead of an empty collection. + * + *

+ * Though the method signature doesn't say so explicitly, the returned map has + * {@link SortedSet} values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * {@inheritDoc} + * + * Consequently, the values do not follow their natural ordering or the ordering + * of the value comparator. + */ + @Override + public Collection values() { + return super.values(); + } + + private static final long serialVersionUID = 430848587173315748L; +} diff --git a/sources/main/java/com/google/common/collect/AbstractTable.java b/sources/main/java/com/google/common/collect/AbstractTable.java new file mode 100644 index 0000000..b7a6d2c --- /dev/null +++ b/sources/main/java/com/google/common/collect/AbstractTable.java @@ -0,0 +1,220 @@ +/* + * 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 com.google.common.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; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Skeletal, implementation-agnostic implementation of the {@link Table} + * interface. + * + * @author Louis Wasserman + */ +@GwtCompatible +abstract class AbstractTable implements Table { + + @Override + public boolean containsRow(@Nullable Object rowKey) { + return Maps.safeContainsKey(rowMap(), rowKey); + } + + @Override + public boolean containsColumn(@Nullable 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(@Nullable Object value) { + for (Map row : rowMap().values()) { + if (row.containsValue(value)) { + return true; + } + } + return false; + } + + @Override + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return row != null && Maps.safeContainsKey(row, columnKey); + } + + @Override + public V get(@Nullable Object rowKey, @Nullable 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(@Nullable Object rowKey, @Nullable 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 && Collections2.safeContains(row.entrySet(), + Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + return false; + } + + @Override + public boolean remove(@Nullable Object o) { + if (o instanceof Cell) { + Cell cell = (Cell) o; + Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + return row != null && Collections2.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(@Nullable Object obj) { + return Tables.equalsImpl(this, obj); + } + + @Override + public int hashCode() { + return cellSet().hashCode(); + } + + /** + * Returns the string representation {@code rowMap().toString()}. + */ + @Override + public String toString() { + return rowMap().toString(); + } +} diff --git a/sources/main/java/com/google/common/collect/AllEqualOrdering.java b/sources/main/java/com/google/common/collect/AllEqualOrdering.java new file mode 100644 index 0000000..de8a925 --- /dev/null +++ b/sources/main/java/com/google/common/collect/AllEqualOrdering.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An ordering that treats all references as equals, even nulls. + * + * @author Emily Soldal + */ +@GwtCompatible(serializable = true) +final class AllEqualOrdering extends Ordering implements Serializable { + static final AllEqualOrdering INSTANCE = new AllEqualOrdering(); + + @Override + public int compare(@Nullable Object left, @Nullable Object right) { + return 0; + } + + @Override + public List sortedCopy(Iterable iterable) { + return Lists.newArrayList(iterable); + } + + @Override + public ImmutableList immutableSortedCopy(Iterable iterable) { + return ImmutableList.copyOf(iterable); + } + + @SuppressWarnings("unchecked") + @Override + public Ordering reverse() { + return (Ordering) this; + } + + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.allEqual()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ArrayListMultimap.java b/sources/main/java/com/google/common/collect/ArrayListMultimap.java new file mode 100644 index 0000000..8fbef2f --- /dev/null +++ b/sources/main/java/com/google/common/collect/ArrayListMultimap.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.CollectPreconditions.checkNonnegative; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; + +/** + * Implementation of {@code Multimap} that uses an {@code ArrayList} to store + * the values for a given key. A {@link HashMap} associates each key with an + * {@link ArrayList} of values. + * + *

+ * When iterating through the collections supplied by this class, the ordering + * of values for a given key agrees with the order in which the values were + * added. + * + *

+ * This multimap allows duplicate key-value pairs. After adding a new key-value + * pair equal to an existing key-value pair, the {@code + * ArrayListMultimap} will contain entries for both the new value and the old + * value. + * + *

+ * Keys and values may be null. All optional multimap methods are supported, and + * all returned views are modifiable. + * + *

+ * The lists returned by {@link #get}, {@link #removeAll}, and + * {@link #replaceValues} all implement {@link net.lax1dude.eaglercraft.v1_8.RandomAccess}. + * + *

+ * This class is not threadsafe when any concurrent operations update the + * multimap. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap your multimap with a call to + * {@link Multimaps#synchronizedListMultimap}. + * + *

+ * See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public final class ArrayListMultimap extends AbstractListMultimap { + // Default from ArrayList + private static final int DEFAULT_VALUES_PER_KEY = 3; + + @VisibleForTesting + transient int expectedValuesPerKey; + + /** + * Creates a new, empty {@code ArrayListMultimap} with the default initial + * capacities. + */ + public static ArrayListMultimap create() { + return new ArrayListMultimap(); + } + + /** + * Constructs an empty {@code ArrayListMultimap} with enough capacity to hold + * the specified numbers of keys and values without resizing. + * + * @param expectedKeys the expected number of distinct keys + * @param expectedValuesPerKey the expected average number of values per key + * @throws IllegalArgumentException if {@code expectedKeys} or {@code + * expectedValuesPerKey} is negative + */ + public static ArrayListMultimap create(int expectedKeys, int expectedValuesPerKey) { + return new ArrayListMultimap(expectedKeys, expectedValuesPerKey); + } + + /** + * Constructs an {@code ArrayListMultimap} with the same mappings as the + * specified multimap. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static ArrayListMultimap create(Multimap multimap) { + return new ArrayListMultimap(multimap); + } + + private ArrayListMultimap() { + super(new HashMap>()); + expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; + } + + private ArrayListMultimap(int expectedKeys, int expectedValuesPerKey) { + super(Maps.>newHashMapWithExpectedSize(expectedKeys)); + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + this.expectedValuesPerKey = expectedValuesPerKey; + } + + private ArrayListMultimap(Multimap multimap) { + this(multimap.keySet().size(), + (multimap instanceof ArrayListMultimap) ? ((ArrayListMultimap) multimap).expectedValuesPerKey + : DEFAULT_VALUES_PER_KEY); + putAll(multimap); + } + + /** + * Creates a new, empty {@code ArrayList} to hold the collection of values for + * an arbitrary key. + */ + @Override + List createCollection() { + return new ArrayList(expectedValuesPerKey); + } + + /** + * Reduces the memory used by this {@code ArrayListMultimap}, if feasible. + */ + public void trimToSize() { + for (Collection collection : backingMap().values()) { + ArrayList arrayList = (ArrayList) collection; + arrayList.trimToSize(); + } + } + + /** + * @serialData expectedValuesPerKey, number of distinct keys, and then for each + * distinct key: the key, number of values for that key, and the + * key's values + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(expectedValuesPerKey); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + expectedValuesPerKey = stream.readInt(); + int distinctKeys = Serialization.readCount(stream); + Map> map = Maps.newHashMapWithExpectedSize(distinctKeys); + setMap(map); + Serialization.populateMultimap(this, stream, distinctKeys); + } + + @GwtIncompatible("Not needed in emulated source.") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ArrayTable.java b/sources/main/java/com/google/common/collect/ArrayTable.java new file mode 100644 index 0000000..2f5dcbe --- /dev/null +++ b/sources/main/java/com/google/common/collect/ArrayTable.java @@ -0,0 +1,793 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkElementIndex; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Objects; + +/** + * Fixed-size {@link Table} implementation backed by a two-dimensional array. + * + *

+ * The allowed row and column keys must be supplied when the table is created. + * The table always contains a mapping for every row key / column pair. The + * value corresponding to a given row and column is null unless another value is + * provided. + * + *

+ * The table's size is constant: the product of the number of supplied row keys + * and the number of supplied column keys. The {@code remove} and {@code + * clear} methods are not supported by the table or its views. The + * {@link #erase} and {@link #eraseAll} methods may be used instead. + * + *

+ * The ordering of the row and column keys provided when the table is + * constructed determines the iteration ordering across rows and columns in the + * table's views. None of the view iterators support {@link Iterator#remove}. If + * the table is modified after an iterator is created, the iterator remains + * valid. + * + *

+ * This class requires less memory than the {@link HashBasedTable} and + * {@link TreeBasedTable} implementations, except when the table is sparse. + * + *

+ * Null row keys or column keys are not permitted. + * + *

+ * This class provides methods involving the underlying array structure, where + * the array indices correspond to the position of a row or column in the lists + * of allowed keys and values. See the {@link #at}, {@link #set}, + * {@link #toArray}, {@link #rowKeyList}, and {@link #columnKeyList} methods for + * more details. + * + *

+ * Note that this implementation is not synchronized. If multiple threads access + * the same cell of an {@code ArrayTable} concurrently and one of the threads + * modifies its value, there is no guarantee that the new value will be fully + * visible to the other threads. To guarantee that modifications are visible, + * synchronize access to the table. Unlike other {@code Table} implementations, + * synchronization is unnecessary between a thread that writes to one cell and a + * thread that reads from another. + * + *

+ * See the Guava User Guide article on + * {@code Table}. + * + * @author Jared Levy + * @since 10.0 + */ +@Beta +@GwtCompatible(emulated = true) +public final class ArrayTable extends AbstractTable implements Serializable { + + /** + * Creates an empty {@code ArrayTable}. + * + * @param rowKeys row keys that may be stored in the generated table + * @param columnKeys column keys that may be stored in the generated table + * @throws NullPointerException if any of the provided keys is null + * @throws IllegalArgumentException if {@code rowKeys} or {@code columnKeys} + * contains duplicates or is empty + */ + public static ArrayTable create(Iterable rowKeys, + Iterable columnKeys) { + return new ArrayTable(rowKeys, columnKeys); + } + + /* + * TODO(jlevy): Add factory methods taking an Enum class, instead of an + * iterable, to specify the allowed row keys and/or column keys. Note that + * custom serialization logic is needed to support different enum sizes during + * serialization and deserialization. + */ + + /** + * Creates an {@code ArrayTable} with the mappings in the provided table. + * + *

+ * If {@code table} includes a mapping with row key {@code r} and a separate + * mapping with column key {@code c}, the returned table contains a mapping with + * row key {@code r} and column key {@code c}. If that row key / column key pair + * in not in {@code table}, the pair maps to {@code null} in the generated + * table. + * + *

+ * The returned table allows subsequent {@code put} calls with the row keys in + * {@code table.rowKeySet()} and the column keys in {@code + * table.columnKeySet()}. Calling {@link #put} with other keys leads to an + * {@code IllegalArgumentException}. + * + *

+ * The ordering of {@code table.rowKeySet()} and {@code + * table.columnKeySet()} determines the row and column iteration ordering of the + * returned table. + * + * @throws NullPointerException if {@code table} has a null key + * @throws IllegalArgumentException if the provided table is empty + */ + public static ArrayTable create(Table table) { + return (table instanceof ArrayTable) ? new ArrayTable((ArrayTable) table) + : new ArrayTable(table); + } + + private final ImmutableList rowList; + private final ImmutableList columnList; + + // TODO(jlevy): Add getters returning rowKeyToIndex and columnKeyToIndex? + private final ImmutableMap rowKeyToIndex; + private final ImmutableMap columnKeyToIndex; + private final V[][] array; + + private ArrayTable(Iterable rowKeys, Iterable columnKeys) { + this.rowList = ImmutableList.copyOf(rowKeys); + this.columnList = ImmutableList.copyOf(columnKeys); + checkArgument(!rowList.isEmpty()); + checkArgument(!columnList.isEmpty()); + + /* + * TODO(jlevy): Support empty rowKeys or columnKeys? If we do, when columnKeys + * is empty but rowKeys isn't, the table is empty but containsRow() can return + * true and rowKeySet() isn't empty. + */ + rowKeyToIndex = index(rowList); + columnKeyToIndex = index(columnList); + + @SuppressWarnings("unchecked") + V[][] tmpArray = (V[][]) new Object[rowList.size()][columnList.size()]; + array = tmpArray; + // Necessary because in GWT the arrays are initialized with "undefined" instead + // of null. + eraseAll(); + } + + private static ImmutableMap index(List list) { + ImmutableMap.Builder columnBuilder = ImmutableMap.builder(); + for (int i = 0; i < list.size(); i++) { + columnBuilder.put(list.get(i), i); + } + return columnBuilder.build(); + } + + private ArrayTable(Table table) { + this(table.rowKeySet(), table.columnKeySet()); + putAll(table); + } + + private ArrayTable(ArrayTable table) { + rowList = table.rowList; + columnList = table.columnList; + rowKeyToIndex = table.rowKeyToIndex; + columnKeyToIndex = table.columnKeyToIndex; + @SuppressWarnings("unchecked") + V[][] copy = (V[][]) new Object[rowList.size()][columnList.size()]; + array = copy; + // Necessary because in GWT the arrays are initialized with "undefined" instead + // of null. + eraseAll(); + for (int i = 0; i < rowList.size(); i++) { + System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length); + } + } + + private abstract static class ArrayMap extends Maps.ImprovedAbstractMap { + private final ImmutableMap keyIndex; + + private ArrayMap(ImmutableMap keyIndex) { + this.keyIndex = keyIndex; + } + + @Override + public Set keySet() { + return keyIndex.keySet(); + } + + K getKey(int index) { + return keyIndex.keySet().asList().get(index); + } + + abstract String getKeyRole(); + + @Nullable + abstract V getValue(int index); + + @Nullable + abstract V setValue(int index, V newValue); + + @Override + public int size() { + return keyIndex.size(); + } + + @Override + public boolean isEmpty() { + return keyIndex.isEmpty(); + } + + @Override + protected Set> createEntrySet() { + return new Maps.EntrySet() { + @Override + Map map() { + return ArrayMap.this; + } + + @Override + public Iterator> iterator() { + return new AbstractIndexedListIterator>(size()) { + @Override + protected Entry get(final int index) { + return new AbstractMapEntry() { + @Override + public K getKey() { + return ArrayMap.this.getKey(index); + } + + @Override + public V getValue() { + return ArrayMap.this.getValue(index); + } + + @Override + public V setValue(V value) { + return ArrayMap.this.setValue(index, value); + } + }; + } + }; + } + }; + } + + // TODO(user): consider an optimized values() implementation + + @Override + public boolean containsKey(@Nullable Object key) { + return keyIndex.containsKey(key); + } + + @Override + public V get(@Nullable Object key) { + Integer index = keyIndex.get(key); + if (index == null) { + return null; + } else { + return getValue(index); + } + } + + @Override + public V put(K key, V value) { + Integer index = keyIndex.get(key); + if (index == null) { + throw new IllegalArgumentException(getKeyRole() + " " + key + " not in " + keyIndex.keySet()); + } + return setValue(index, value); + } + + @Override + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns, as an immutable list, the row keys provided when the table was + * constructed, including those that are mapped to null values only. + */ + public ImmutableList rowKeyList() { + return rowList; + } + + /** + * Returns, as an immutable list, the column keys provided when the table was + * constructed, including those that are mapped to null values only. + */ + public ImmutableList columnKeyList() { + return columnList; + } + + /** + * Returns the value corresponding to the specified row and column indices. The + * same value is returned by {@code + * get(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex))}, but this + * method runs more quickly. + * + * @param rowIndex position of the row key in {@link #rowKeyList()} + * @param columnIndex position of the row key in {@link #columnKeyList()} + * @return the value with the specified row and column + * @throws IndexOutOfBoundsException if either index is negative, {@code + * rowIndex} is greater then or equal to the number of + * allowed row keys, or {@code columnIndex} is + * greater then or equal to the number of + * allowed column keys + */ + public V at(int rowIndex, int columnIndex) { + // In GWT array access never throws IndexOutOfBoundsException. + checkElementIndex(rowIndex, rowList.size()); + checkElementIndex(columnIndex, columnList.size()); + return array[rowIndex][columnIndex]; + } + + /** + * Associates {@code value} with the specified row and column indices. The logic + * {@code + * put(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex), value)} has + * the same behavior, but this method runs more quickly. + * + * @param rowIndex position of the row key in {@link #rowKeyList()} + * @param columnIndex position of the row key in {@link #columnKeyList()} + * @param value value to store in the table + * @return the previous value with the specified row and column + * @throws IndexOutOfBoundsException if either index is negative, {@code + * rowIndex} is greater then or equal to the number of + * allowed row keys, or {@code columnIndex} is + * greater then or equal to the number of + * allowed column keys + */ + public V set(int rowIndex, int columnIndex, @Nullable V value) { + // In GWT array access never throws IndexOutOfBoundsException. + checkElementIndex(rowIndex, rowList.size()); + checkElementIndex(columnIndex, columnList.size()); + V oldValue = array[rowIndex][columnIndex]; + array[rowIndex][columnIndex] = value; + return oldValue; + } + + /** + * Returns a two-dimensional array with the table contents. The row and column + * indices correspond to the positions of the row and column in the iterables + * provided during table construction. If the table lacks a mapping for a given + * row and column, the corresponding array element is null. + * + *

+ * Subsequent table changes will not modify the array, and vice versa. + * + * @param valueClass class of values stored in the returned array + */ + @GwtIncompatible("reflection") + public V[][] toArray(Class valueClass) { + // Can change to use varargs in JDK 1.6 if we want + @SuppressWarnings("unchecked") // TODO: safe? + V[][] copy = (V[][]) Array.newInstance(valueClass, new int[] { rowList.size(), columnList.size() }); + for (int i = 0; i < rowList.size(); i++) { + System.arraycopy(array[i], 0, copy[i], 0, array[i].length); + } + return copy; + } + + /** + * Not supported. Use {@link #eraseAll} instead. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link #eraseAll} + */ + @Override + @Deprecated + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Associates the value {@code null} with every pair of allowed row and column + * keys. + */ + public void eraseAll() { + for (V[] row : array) { + Arrays.fill(row, null); + } + } + + /** + * Returns {@code true} if the provided keys are among the keys provided when + * the table was constructed. + */ + @Override + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return containsRow(rowKey) && containsColumn(columnKey); + } + + /** + * Returns {@code true} if the provided column key is among the column keys + * provided when the table was constructed. + */ + @Override + public boolean containsColumn(@Nullable Object columnKey) { + return columnKeyToIndex.containsKey(columnKey); + } + + /** + * Returns {@code true} if the provided row key is among the row keys provided + * when the table was constructed. + */ + @Override + public boolean containsRow(@Nullable Object rowKey) { + return rowKeyToIndex.containsKey(rowKey); + } + + @Override + public boolean containsValue(@Nullable Object value) { + for (V[] row : array) { + for (V element : row) { + if (Objects.equal(value, element)) { + return true; + } + } + } + return false; + } + + @Override + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + return (rowIndex == null || columnIndex == null) ? null : at(rowIndex, columnIndex); + } + + /** + * Always returns {@code false}. + */ + @Override + public boolean isEmpty() { + return false; + } + + /** + * {@inheritDoc} + * + * @throws IllegalArgumentException if {@code rowKey} is not in + * {@link #rowKeySet()} or {@code columnKey} is + * not in {@link #columnKeySet()}. + */ + @Override + public V put(R rowKey, C columnKey, @Nullable V value) { + checkNotNull(rowKey); + checkNotNull(columnKey); + Integer rowIndex = rowKeyToIndex.get(rowKey); + checkArgument(rowIndex != null, "Row %s not in %s", rowKey, rowList); + Integer columnIndex = columnKeyToIndex.get(columnKey); + checkArgument(columnIndex != null, "Column %s not in %s", columnKey, columnList); + return set(rowIndex, columnIndex, value); + } + + /* + * TODO(jlevy): Consider creating a merge() method, similar to putAll() but + * copying non-null values only. + */ + + /** + * {@inheritDoc} + * + *

+ * If {@code table} is an {@code ArrayTable}, its null values will be stored in + * this table, possibly replacing values that were previously non-null. + * + * @throws NullPointerException if {@code table} has a null key + * @throws IllegalArgumentException if any of the provided table's row keys or + * column keys is not in {@link #rowKeySet()} + * or {@link #columnKeySet()} + */ + @Override + public void putAll(Table table) { + super.putAll(table); + } + + /** + * Not supported. Use {@link #erase} instead. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link #erase} + */ + @Override + @Deprecated + public V remove(Object rowKey, Object columnKey) { + throw new UnsupportedOperationException(); + } + + /** + * Associates the value {@code null} with the specified keys, assuming both keys + * are valid. If either key is null or isn't among the keys provided during + * construction, this method has no effect. + * + *

+ * This method is equivalent to {@code put(rowKey, columnKey, null)} when both + * provided keys are valid. + * + * @param rowKey row key of mapping to be erased + * @param columnKey column key of mapping to be erased + * @return the value previously associated with the keys, or {@code null} if no + * mapping existed for the keys + */ + public V erase(@Nullable Object rowKey, @Nullable Object columnKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + if (rowIndex == null || columnIndex == null) { + return null; + } + return set(rowIndex, columnIndex, null); + } + + // TODO(jlevy): Add eraseRow and eraseColumn methods? + + @Override + public int size() { + return rowList.size() * columnList.size(); + } + + /** + * Returns an unmodifiable set of all row key / column key / value triplets. + * Changes to the table will update the returned set. + * + *

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

+ * The value in the returned cells may change if the table subsequently changes. + * + * @return set of table cells consisting of row key / column key / value + * triplets + */ + @Override + public Set> cellSet() { + return super.cellSet(); + } + + @Override + Iterator> cellIterator() { + return new AbstractIndexedListIterator>(size()) { + @Override + protected Cell get(final int index) { + return new Tables.AbstractCell() { + final int rowIndex = index / columnList.size(); + final int columnIndex = index % columnList.size(); + + @Override + public R getRowKey() { + return rowList.get(rowIndex); + } + + @Override + public C getColumnKey() { + return columnList.get(columnIndex); + } + + @Override + public V getValue() { + return at(rowIndex, columnIndex); + } + }; + } + }; + } + + /** + * Returns a view of all mappings that have the given column key. If the column + * key isn't in {@link #columnKeySet()}, an empty immutable map is returned. + * + *

+ * Otherwise, for each row key in {@link #rowKeySet()}, the returned map + * associates the row key with the corresponding value in the table. Changes to + * the returned map will update the underlying table, and vice versa. + * + * @param columnKey key of column to search for in the table + * @return the corresponding map from row keys to values + */ + @Override + public Map column(C columnKey) { + checkNotNull(columnKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + return (columnIndex == null) ? ImmutableMap.of() : new Column(columnIndex); + } + + private class Column extends ArrayMap { + final int columnIndex; + + Column(int columnIndex) { + super(rowKeyToIndex); + this.columnIndex = columnIndex; + } + + @Override + String getKeyRole() { + return "Row"; + } + + @Override + V getValue(int index) { + return at(index, columnIndex); + } + + @Override + V setValue(int index, V newValue) { + return set(index, columnIndex, newValue); + } + } + + /** + * Returns an immutable set of the valid column keys, including those that are + * associated with null values only. + * + * @return immutable set of column keys + */ + @Override + public ImmutableSet columnKeySet() { + return columnKeyToIndex.keySet(); + } + + private transient ColumnMap columnMap; + + @Override + public Map> columnMap() { + ColumnMap map = columnMap; + return (map == null) ? columnMap = new ColumnMap() : map; + } + + private class ColumnMap extends ArrayMap> { + private ColumnMap() { + super(columnKeyToIndex); + } + + @Override + String getKeyRole() { + return "Column"; + } + + @Override + Map getValue(int index) { + return new Column(index); + } + + @Override + Map setValue(int index, Map newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public Map put(C key, Map value) { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns a view of all mappings that have the given row key. If the row key + * isn't in {@link #rowKeySet()}, an empty immutable map is returned. + * + *

+ * Otherwise, for each column key in {@link #columnKeySet()}, the returned map + * associates the column key with the corresponding value in the table. Changes + * to the returned map will update the underlying table, and vice versa. + * + * @param rowKey key of row to search for in the table + * @return the corresponding map from column keys to values + */ + @Override + public Map row(R rowKey) { + checkNotNull(rowKey); + Integer rowIndex = rowKeyToIndex.get(rowKey); + return (rowIndex == null) ? ImmutableMap.of() : new Row(rowIndex); + } + + private class Row extends ArrayMap { + final int rowIndex; + + Row(int rowIndex) { + super(columnKeyToIndex); + this.rowIndex = rowIndex; + } + + @Override + String getKeyRole() { + return "Column"; + } + + @Override + V getValue(int index) { + return at(rowIndex, index); + } + + @Override + V setValue(int index, V newValue) { + return set(rowIndex, index, newValue); + } + } + + /** + * Returns an immutable set of the valid row keys, including those that are + * associated with null values only. + * + * @return immutable set of row keys + */ + @Override + public ImmutableSet rowKeySet() { + return rowKeyToIndex.keySet(); + } + + private transient RowMap rowMap; + + @Override + public Map> rowMap() { + RowMap map = rowMap; + return (map == null) ? rowMap = new RowMap() : map; + } + + private class RowMap extends ArrayMap> { + private RowMap() { + super(rowKeyToIndex); + } + + @Override + String getKeyRole() { + return "Row"; + } + + @Override + Map getValue(int index) { + return new Row(index); + } + + @Override + Map setValue(int index, Map newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public Map put(R key, Map value) { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns an unmodifiable collection of all values, which may contain + * duplicates. Changes to the table will update the returned collection. + * + *

+ * The returned collection's iterator traverses the values of the first row key, + * the values of the second row key, and so on. + * + * @return collection of values + */ + @Override + public Collection values() { + return super.values(); + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/BiMap.java b/sources/main/java/com/google/common/collect/BiMap.java new file mode 100644 index 0000000..c34fa39 --- /dev/null +++ b/sources/main/java/com/google/common/collect/BiMap.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * 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) + */ +@GwtCompatible +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(@Nullable K key, @Nullable 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(@Nullable K key, @Nullable 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/sources/main/java/com/google/common/collect/BinaryTreeTraverser.java b/sources/main/java/com/google/common/collect/BinaryTreeTraverser.java new file mode 100644 index 0000000..b3249b2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/BinaryTreeTraverser.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayDeque; +import java.util.BitSet; +import java.util.Deque; +import java.util.Iterator; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Optional; + +/** + * A variant of {@link TreeTraverser} for binary trees, providing additional + * traversals specific to binary trees. + * + * @author Louis Wasserman + * @since 15.0 + */ +@Beta +@GwtCompatible(emulated = true) +public abstract class BinaryTreeTraverser extends TreeTraverser { + // TODO(user): make this GWT-compatible when we've checked in ArrayDeque and + // BitSet emulation + + /** + * Returns the left child of the specified node, or {@link Optional#absent()} if + * the specified node has no left child. + */ + public abstract Optional leftChild(T root); + + /** + * Returns the right child of the specified node, or {@link Optional#absent()} + * if the specified node has no right child. + */ + public abstract Optional rightChild(T root); + + /** + * Returns the children of this node, in left-to-right order. + */ + @Override + public final Iterable children(final T root) { + checkNotNull(root); + return new FluentIterable() { + @Override + public Iterator iterator() { + return new AbstractIterator() { + boolean doneLeft; + boolean doneRight; + + @Override + protected T computeNext() { + if (!doneLeft) { + doneLeft = true; + Optional left = leftChild(root); + if (left.isPresent()) { + return left.get(); + } + } + if (!doneRight) { + doneRight = true; + Optional right = rightChild(root); + if (right.isPresent()) { + return right.get(); + } + } + return endOfData(); + } + }; + } + }; + } + + @Override + UnmodifiableIterator preOrderIterator(T root) { + return new PreOrderIterator(root); + } + + /* + * Optimized implementation of preOrderIterator for binary trees. + */ + private final class PreOrderIterator extends UnmodifiableIterator implements PeekingIterator { + private final Deque stack; + + PreOrderIterator(T root) { + this.stack = new ArrayDeque(); + stack.addLast(root); + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } + + @Override + public T next() { + T result = stack.removeLast(); + pushIfPresent(stack, rightChild(result)); + pushIfPresent(stack, leftChild(result)); + return result; + } + + @Override + public T peek() { + return stack.getLast(); + } + } + + @Override + UnmodifiableIterator postOrderIterator(T root) { + return new PostOrderIterator(root); + } + + /* + * Optimized implementation of postOrderIterator for binary trees. + */ + private final class PostOrderIterator extends UnmodifiableIterator { + private final Deque stack; + private final BitSet hasExpanded; + + PostOrderIterator(T root) { + this.stack = new ArrayDeque(); + stack.addLast(root); + this.hasExpanded = new BitSet(); + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } + + @Override + public T next() { + while (true) { + T node = stack.getLast(); + boolean expandedNode = hasExpanded.get(stack.size() - 1); + if (expandedNode) { + stack.removeLast(); + hasExpanded.clear(stack.size()); + return node; + } else { + hasExpanded.set(stack.size() - 1); + pushIfPresent(stack, rightChild(node)); + pushIfPresent(stack, leftChild(node)); + } + } + } + } + + // TODO(user): see if any significant optimizations are possible for + // breadthFirstIterator + + public final FluentIterable inOrderTraversal(final T root) { + checkNotNull(root); + return new FluentIterable() { + @Override + public UnmodifiableIterator iterator() { + return new InOrderIterator(root); + } + }; + } + + private final class InOrderIterator extends AbstractIterator { + private final Deque stack; + private final BitSet hasExpandedLeft; + + InOrderIterator(T root) { + this.stack = new ArrayDeque(); + this.hasExpandedLeft = new BitSet(); + stack.addLast(root); + } + + @Override + protected T computeNext() { + while (!stack.isEmpty()) { + T node = stack.getLast(); + if (hasExpandedLeft.get(stack.size() - 1)) { + stack.removeLast(); + hasExpandedLeft.clear(stack.size()); + pushIfPresent(stack, rightChild(node)); + return node; + } else { + hasExpandedLeft.set(stack.size() - 1); + pushIfPresent(stack, leftChild(node)); + } + } + return endOfData(); + } + } + + private static void pushIfPresent(Deque stack, Optional node) { + if (node.isPresent()) { + stack.addLast(node.get()); + } + } +} diff --git a/sources/main/java/com/google/common/collect/BoundType.java b/sources/main/java/com/google/common/collect/BoundType.java new file mode 100644 index 0000000..25770a1 --- /dev/null +++ b/sources/main/java/com/google/common/collect/BoundType.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * Indicates whether an endpoint of some range is contained in the range itself + * ("closed") or not ("open"). If a range is unbounded on a side, it is neither + * open nor closed on that side; the bound simply does not exist. + * + * @since 10.0 + */ +@GwtCompatible +public enum BoundType { + /** + * The endpoint value is not considered part of the set ("exclusive"). + */ + OPEN { + @Override + BoundType flip() { + return CLOSED; + } + }, + /** + * The endpoint value is considered part of the set ("inclusive"). + */ + CLOSED { + @Override + BoundType flip() { + return OPEN; + } + }; + + /** + * Returns the bound type corresponding to a boolean value for inclusivity. + */ + static BoundType forBoolean(boolean inclusive) { + return inclusive ? CLOSED : OPEN; + } + + abstract BoundType flip(); +} diff --git a/sources/main/java/com/google/common/collect/ByFunctionOrdering.java b/sources/main/java/com/google/common/collect/ByFunctionOrdering.java new file mode 100644 index 0000000..7094e0a --- /dev/null +++ b/sources/main/java/com/google/common/collect/ByFunctionOrdering.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Objects; + +/** + * An ordering that orders elements by applying an order to the result of a + * function on those elements. + */ +@GwtCompatible(serializable = true) +final class ByFunctionOrdering extends Ordering implements Serializable { + final Function function; + final Ordering ordering; + + ByFunctionOrdering(Function function, Ordering ordering) { + this.function = checkNotNull(function); + this.ordering = checkNotNull(ordering); + } + + @Override + public int compare(F left, F right) { + return ordering.compare(function.apply(left), function.apply(right)); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof ByFunctionOrdering) { + ByFunctionOrdering that = (ByFunctionOrdering) object; + return this.function.equals(that.function) && this.ordering.equals(that.ordering); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(function, ordering); + } + + @Override + public String toString() { + return ordering + ".onResultOf(" + function + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/CartesianList.java b/sources/main/java/com/google/common/collect/CartesianList.java new file mode 100644 index 0000000..a7f5139 --- /dev/null +++ b/sources/main/java/com/google/common/collect/CartesianList.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkElementIndex; + +import java.util.AbstractList; +import java.util.List; +import java.util.ListIterator; +import java.util.RandomAccess; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.math.IntMath; + +/** + * Implementation of {@link Lists#cartesianProduct(List)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class CartesianList extends AbstractList> implements RandomAccess { + + private transient final ImmutableList> axes; + private transient final int[] axesSizeProduct; + + static List> create(List> lists) { + ImmutableList.Builder> axesBuilder = new ImmutableList.Builder>(lists.size()); + for (List list : lists) { + List copy = ImmutableList.copyOf(list); + if (copy.isEmpty()) { + return ImmutableList.of(); + } + axesBuilder.add(copy); + } + return new CartesianList(axesBuilder.build()); + } + + CartesianList(ImmutableList> axes) { + this.axes = axes; + int[] axesSizeProduct = new int[axes.size() + 1]; + axesSizeProduct[axes.size()] = 1; + try { + for (int i = axes.size() - 1; i >= 0; i--) { + axesSizeProduct[i] = IntMath.checkedMultiply(axesSizeProduct[i + 1], axes.get(i).size()); + } + } catch (ArithmeticException e) { + throw new IllegalArgumentException("Cartesian product too large; must have size at most Integer.MAX_VALUE"); + } + this.axesSizeProduct = axesSizeProduct; + } + + private int getAxisIndexForProductIndex(int index, int axis) { + return (index / axesSizeProduct[axis + 1]) % axes.get(axis).size(); + } + + @Override + public ImmutableList get(final int index) { + checkElementIndex(index, size()); + return new ImmutableList() { + + @Override + public int size() { + return axes.size(); + } + + @Override + public E get(int axis) { + checkElementIndex(axis, size()); + int axisIndex = getAxisIndexForProductIndex(index, axis); + return axes.get(axis).get(axisIndex); + } + + @Override + boolean isPartialView() { + return true; + } + }; + } + + @Override + public int size() { + return axesSizeProduct[0]; + } + + @Override + public boolean contains(@Nullable Object o) { + if (!(o instanceof List)) { + return false; + } + List list = (List) o; + if (list.size() != axes.size()) { + return false; + } + ListIterator itr = list.listIterator(); + while (itr.hasNext()) { + int index = itr.nextIndex(); + if (!axes.get(index).contains(itr.next())) { + return false; + } + } + return true; + } +} diff --git a/sources/main/java/com/google/common/collect/ClassToInstanceMap.java b/sources/main/java/com/google/common/collect/ClassToInstanceMap.java new file mode 100644 index 0000000..3945edd --- /dev/null +++ b/sources/main/java/com/google/common/collect/ClassToInstanceMap.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A map, each entry of which maps a Java + * raw type to an instance of that type. + * In addition to implementing {@code Map}, the additional type-safe operations + * {@link #putInstance} and {@link #getInstance} are available. + * + *

+ * Like any other {@code Map}, this map may contain entries for + * primitive types, and a primitive type and its corresponding wrapper type may + * map to different values. + * + *

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

+ * To map a generic type to an instance of that type, use + * {@link com.google.common.reflect.TypeToInstanceMap} instead. + * + * @param the common supertype that all entries must share; often this is + * simply {@link Object} + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface ClassToInstanceMap extends Map, B> { + /** + * Returns the value the specified class is mapped to, or {@code null} if no + * entry for this class is present. This will only return a value that was bound + * to this specific class, not a value that may have been bound to a subtype. + */ + T getInstance(Class type); + + /** + * Maps the specified class to the specified value. Does not associate + * this value with any of the class's supertypes. + * + * @return the value previously associated with this class (possibly {@code + * null}), or {@code null} if there was no previous entry. + */ + T putInstance(Class type, @Nullable T value); +} diff --git a/sources/main/java/com/google/common/collect/CollectPreconditions.java b/sources/main/java/com/google/common/collect/CollectPreconditions.java new file mode 100644 index 0000000..65526e2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/CollectPreconditions.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.GwtCompatible; + +/** + * Precondition checks useful in collection implementations. + */ +@GwtCompatible +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/sources/main/java/com/google/common/collect/Collections2.java b/sources/main/java/com/google/common/collect/Collections2.java new file mode 100644 index 0000000..6ef373d --- /dev/null +++ b/sources/main/java/com/google/common/collect/Collections2.java @@ -0,0 +1,695 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.and; +import static com.google.common.base.Predicates.in; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.math.LongMath.binomial; + +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.math.IntMath; +import com.google.common.primitives.Ints; + +/** + * 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) + */ +@GwtCompatible +public final class Collections2 { + private Collections2() { + } + + /** + * 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, @Nullable 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, @Nullable 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.apply(element)); + return unfiltered.add(element); + } + + @Override + public boolean addAll(Collection collection) { + for (E element : collection) { + checkArgument(predicate.apply(element)); + } + return unfiltered.addAll(collection); + } + + @Override + public void clear() { + Iterables.removeIf(unfiltered, predicate); + } + + @Override + public boolean contains(@Nullable Object element) { + if (safeContains(unfiltered, element)) { + @SuppressWarnings("unchecked") // element is in unfiltered, so it must be an E + E e = (E) element; + return predicate.apply(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, (Predicate)in(collection))); + } + + @Override + public boolean retainAll(final Collection collection) { + return Iterables.removeIf(unfiltered, and(predicate, (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 Collections2.orderedPermutations(list, Ordering.natural())}. + * + * @param elements the original iterable whose elements have to be permuted. + * @return an immutable {@link Collection} containing all the different + * permutations of the original iterable. + * @throws NullPointerException if the specified iterable is null or has any + * null elements. + * @since 12.0 + */ + @Beta + public static > 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(@Nullable Object obj) { + if (obj instanceof List) { + List list = (List) obj; + return isPermutation(inputList, list); + } + return false; + } + + @Override + public String toString() { + return "orderedPermutationCollection(" + inputList + ")"; + } + } + + private static final class OrderedPermutationIterator 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(@Nullable Object obj) { + if (obj instanceof List) { + List list = (List) obj; + return isPermutation(inputList, list); + } + return false; + } + + @Override + public String toString() { + return "permutations(" + inputList + ")"; + } + } + + private static class PermutationIterator 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/sources/main/java/com/google/common/collect/ComparatorOrdering.java b/sources/main/java/com/google/common/collect/ComparatorOrdering.java new file mode 100644 index 0000000..810586c --- /dev/null +++ b/sources/main/java/com/google/common/collect/ComparatorOrdering.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Comparator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** An ordering for a pre-existing comparator. */ +@GwtCompatible(serializable = true) +final class ComparatorOrdering extends Ordering implements Serializable { + final Comparator comparator; + + ComparatorOrdering(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + @Override + public int compare(T a, T b) { + return comparator.compare(a, b); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof ComparatorOrdering) { + ComparatorOrdering that = (ComparatorOrdering) object; + return this.comparator.equals(that.comparator); + } + return false; + } + + @Override + public int hashCode() { + return comparator.hashCode(); + } + + @Override + public String toString() { + return comparator.toString(); + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ComparisonChain.java b/sources/main/java/com/google/common/collect/ComparisonChain.java new file mode 100644 index 0000000..4209e0d --- /dev/null +++ b/sources/main/java/com/google/common/collect/ComparisonChain.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Comparator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.primitives.Booleans; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; + +/** + * A utility for performing a chained comparison statement. For example: + * + *

+ *    {@code
+ *
+ *   public int compareTo(Foo that) {
+ *     return ComparisonChain.start()
+ *         .compare(this.aString, that.aString)
+ *         .compare(this.anInt, that.anInt)
+ *         .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
+ *         .result();
+ *   }}
+ * 
+ * + *

+ * The value of this expression will have the same sign as the first + * nonzero comparison result in the chain, or will be zero if every + * comparison result was zero. + * + *

+ * Performance note: Even though the {@code ComparisonChain} caller always + * invokes its {@code compare} methods unconditionally, the {@code + * ComparisonChain} implementation stops calling its inputs' + * {@link Comparable#compareTo compareTo} and {@link Comparator#compare compare} + * methods as soon as one of them returns a nonzero result. This optimization is + * typically important only in the presence of expensive {@code compareTo} and + * {@code compare} implementations. + * + *

+ * See the Guava User Guide article on + * {@code ComparisonChain}. + * + * @author Mark Davis + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +public abstract class ComparisonChain { + private ComparisonChain() { + } + + /** + * Begins a new chained comparison statement. See example in the class + * documentation. + */ + public static ComparisonChain start() { + return ACTIVE; + } + + private static final ComparisonChain ACTIVE = new ComparisonChain() { + @SuppressWarnings("unchecked") + @Override + public ComparisonChain compare(Comparable left, Comparable right) { + return classify(left.compareTo(right)); + } + + @Override + public ComparisonChain compare(@Nullable T left, @Nullable T right, Comparator comparator) { + return classify(comparator.compare(left, right)); + } + + @Override + public ComparisonChain compare(int left, int right) { + return classify(Ints.compare(left, right)); + } + + @Override + public ComparisonChain compare(long left, long right) { + return classify(Longs.compare(left, right)); + } + + @Override + public ComparisonChain compare(float left, float right) { + return classify(Float.compare(left, right)); + } + + @Override + public ComparisonChain compare(double left, double right) { + return classify(Double.compare(left, right)); + } + + @Override + public ComparisonChain compareTrueFirst(boolean left, boolean right) { + return classify(Booleans.compare(right, left)); // reversed + } + + @Override + public ComparisonChain compareFalseFirst(boolean left, boolean right) { + return classify(Booleans.compare(left, right)); + } + + ComparisonChain classify(int result) { + return (result < 0) ? LESS : (result > 0) ? GREATER : ACTIVE; + } + + @Override + public int result() { + return 0; + } + }; + + private static final ComparisonChain LESS = new InactiveComparisonChain(-1); + + private static final ComparisonChain GREATER = new InactiveComparisonChain(1); + + private static final class InactiveComparisonChain extends ComparisonChain { + final int result; + + InactiveComparisonChain(int result) { + this.result = result; + } + + @Override + public ComparisonChain compare(@Nullable Comparable left, @Nullable Comparable right) { + return this; + } + + @Override + public ComparisonChain compare(@Nullable T left, @Nullable T right, @Nullable Comparator comparator) { + return this; + } + + @Override + public ComparisonChain compare(int left, int right) { + return this; + } + + @Override + public ComparisonChain compare(long left, long right) { + return this; + } + + @Override + public ComparisonChain compare(float left, float right) { + return this; + } + + @Override + public ComparisonChain compare(double left, double right) { + return this; + } + + @Override + public ComparisonChain compareTrueFirst(boolean left, boolean right) { + return this; + } + + @Override + public ComparisonChain compareFalseFirst(boolean left, boolean right) { + return this; + } + + @Override + public int result() { + return result; + } + } + + /** + * Compares two comparable objects as specified by {@link Comparable#compareTo}, + * if the result of this comparison chain has not already been + * determined. + */ + public abstract ComparisonChain compare(Comparable left, Comparable right); + + /** + * Compares two objects using a comparator, if the result of this + * comparison chain has not already been determined. + */ + public abstract ComparisonChain compare(@Nullable T left, @Nullable T right, Comparator comparator); + + /** + * Compares two {@code int} values as specified by {@link Ints#compare}, + * if the result of this comparison chain has not already been + * determined. + */ + public abstract ComparisonChain compare(int left, int right); + + /** + * Compares two {@code long} values as specified by {@link Longs#compare}, + * if the result of this comparison chain has not already been + * determined. + */ + public abstract ComparisonChain compare(long left, long right); + + /** + * Compares two {@code float} values as specified by {@link Float#compare}, + * if the result of this comparison chain has not already been + * determined. + */ + public abstract ComparisonChain compare(float left, float right); + + /** + * Compares two {@code double} values as specified by {@link Double#compare}, + * if the result of this comparison chain has not already been + * determined. + */ + public abstract ComparisonChain compare(double left, double right); + + /** + * Compares two {@code boolean} values, considering {@code true} to be less than + * {@code false}, if the result of this comparison chain has not already + * been determined. + * + * @since 12.0 + */ + public abstract ComparisonChain compareTrueFirst(boolean left, boolean right); + + /** + * Compares two {@code boolean} values, considering {@code false} to be less + * than {@code true}, if the result of this comparison chain has not + * already been determined. + * + * @since 12.0 (present as {@code compare} since 2.0) + */ + public abstract ComparisonChain compareFalseFirst(boolean left, boolean right); + + /** + * Ends this comparison chain and returns its result: a value having the same + * sign as the first nonzero comparison result in the chain, or zero if every + * result was zero. + */ + public abstract int result(); +} diff --git a/sources/main/java/com/google/common/collect/CompoundOrdering.java b/sources/main/java/com/google/common/collect/CompoundOrdering.java new file mode 100644 index 0000000..483eaf9 --- /dev/null +++ b/sources/main/java/com/google/common/collect/CompoundOrdering.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.util.Comparator; + +import com.google.common.annotations.GwtCompatible; + +/** An ordering that tries several comparators in order. */ +@GwtCompatible(serializable = true) +final class CompoundOrdering extends Ordering implements Serializable { + final ImmutableList> comparators; + + CompoundOrdering(Comparator primary, Comparator secondary) { + this.comparators = ImmutableList.>of(primary, secondary); + } + + CompoundOrdering(Iterable> comparators) { + this.comparators = ImmutableList.copyOf(comparators); + } + + @Override + public int compare(T left, T right) { + // Avoid using the Iterator to avoid generating garbage (issue 979). + int size = comparators.size(); + for (int i = 0; i < size; i++) { + int result = comparators.get(i).compare(left, right); + if (result != 0) { + return result; + } + } + return 0; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof CompoundOrdering) { + CompoundOrdering that = (CompoundOrdering) object; + return this.comparators.equals(that.comparators); + } + return false; + } + + @Override + public int hashCode() { + return comparators.hashCode(); + } + + @Override + public String toString() { + return "Ordering.compound(" + comparators + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ComputationException.java b/sources/main/java/com/google/common/collect/ComputationException.java new file mode 100644 index 0000000..d0d12f2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ComputationException.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Wraps an exception that occurred during a computation. + * + * @author Bob Lee + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public class ComputationException extends RuntimeException { + /** + * Creates a new instance with the given cause. + */ + public ComputationException(@Nullable Throwable cause) { + super(cause); + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/Constraint.java b/sources/main/java/com/google/common/collect/Constraint.java new file mode 100644 index 0000000..2b8e276 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Constraint.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * A constraint that an element must satisfy in order to be added to a + * collection. For example, {@link Constraints#notNull()}, which prevents a + * collection from including any null elements, could be implemented like this: + * + *

+ *    {@code
+ *
+ *   public Object checkElement(Object element) {
+ *     if (element == null) {
+ *       throw new NullPointerException();
+ *     }
+ *     return element;
+ *   }}
+ * 
+ * + *

+ * In order to be effective, constraints should be deterministic; that is, they + * should not depend on state that can change (such as external state, random + * variables, and time) and should only depend on the value of the passed-in + * element. A non-deterministic constraint cannot reliably enforce that all the + * collection's elements meet the constraint, since the constraint is only + * enforced when elements are added. + * + * @author Mike Bostock + */ +@GwtCompatible +interface Constraint { + /** + * Throws a suitable {@code RuntimeException} if the specified element is + * illegal. Typically this is either a {@link NullPointerException}, an + * {@link IllegalArgumentException}, or a {@link ClassCastException}, though an + * application-specific exception class may be used if appropriate. + * + * @param element the element to check + * @return the provided element + */ + E checkElement(E element); + + /** + * Returns a brief human readable description of this constraint, such as "Not + * null" or "Positive number". + */ + @Override + String toString(); +} diff --git a/sources/main/java/com/google/common/collect/Constraints.java b/sources/main/java/com/google/common/collect/Constraints.java new file mode 100644 index 0000000..f141cad --- /dev/null +++ b/sources/main/java/com/google/common/collect/Constraints.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; +import java.util.RandomAccess; +import java.util.Set; +import java.util.SortedSet; + +import com.google.common.annotations.GwtCompatible; + +/** + * Factories and utilities pertaining to the {@link Constraint} interface. + * + * @author Mike Bostock + * @author Jared Levy + */ +@GwtCompatible +final class Constraints { + private Constraints() { + } + + /** + * Returns a constrained view of the specified collection, using the specified + * constraint. Any operations that add new elements to the collection will call + * the provided constraint. However, this method does not verify that existing + * elements satisfy the constraint. + * + *

+ * The returned collection is not serializable. + * + * @param collection the collection to constrain + * @param constraint the constraint that validates added elements + * @return a constrained view of the collection + */ + public static Collection constrainedCollection(Collection collection, Constraint constraint) { + return new ConstrainedCollection(collection, constraint); + } + + /** @see Constraints#constrainedCollection */ + static class ConstrainedCollection extends ForwardingCollection { + private final Collection delegate; + private final Constraint constraint; + + public ConstrainedCollection(Collection delegate, Constraint constraint) { + this.delegate = checkNotNull(delegate); + this.constraint = checkNotNull(constraint); + } + + @Override + protected Collection delegate() { + return delegate; + } + + @Override + public boolean add(E element) { + constraint.checkElement(element); + return delegate.add(element); + } + + @Override + public boolean addAll(Collection elements) { + return delegate.addAll(checkElements(elements, constraint)); + } + } + + /** + * Returns a constrained view of the specified set, using the specified + * constraint. Any operations that add new elements to the set will call the + * provided constraint. However, this method does not verify that existing + * elements satisfy the constraint. + * + *

+ * The returned set is not serializable. + * + * @param set the set to constrain + * @param constraint the constraint that validates added elements + * @return a constrained view of the set + */ + public static Set constrainedSet(Set set, Constraint constraint) { + return new ConstrainedSet(set, constraint); + } + + /** @see Constraints#constrainedSet */ + static class ConstrainedSet extends ForwardingSet { + private final Set delegate; + private final Constraint constraint; + + public ConstrainedSet(Set delegate, Constraint constraint) { + this.delegate = checkNotNull(delegate); + this.constraint = checkNotNull(constraint); + } + + @Override + protected Set delegate() { + return delegate; + } + + @Override + public boolean add(E element) { + constraint.checkElement(element); + return delegate.add(element); + } + + @Override + public boolean addAll(Collection elements) { + return delegate.addAll(checkElements(elements, constraint)); + } + } + + /** + * Returns a constrained view of the specified sorted set, using the specified + * constraint. Any operations that add new elements to the sorted set will call + * the provided constraint. However, this method does not verify that existing + * elements satisfy the constraint. + * + *

+ * The returned set is not serializable. + * + * @param sortedSet the sorted set to constrain + * @param constraint the constraint that validates added elements + * @return a constrained view of the sorted set + */ + public static SortedSet constrainedSortedSet(SortedSet sortedSet, Constraint constraint) { + return new ConstrainedSortedSet(sortedSet, constraint); + } + + /** @see Constraints#constrainedSortedSet */ + private static class ConstrainedSortedSet extends ForwardingSortedSet { + final SortedSet delegate; + final Constraint constraint; + + ConstrainedSortedSet(SortedSet delegate, Constraint constraint) { + this.delegate = checkNotNull(delegate); + this.constraint = checkNotNull(constraint); + } + + @Override + protected SortedSet delegate() { + return delegate; + } + + @Override + public SortedSet headSet(E toElement) { + return constrainedSortedSet(delegate.headSet(toElement), constraint); + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + return constrainedSortedSet(delegate.subSet(fromElement, toElement), constraint); + } + + @Override + public SortedSet tailSet(E fromElement) { + return constrainedSortedSet(delegate.tailSet(fromElement), constraint); + } + + @Override + public boolean add(E element) { + constraint.checkElement(element); + return delegate.add(element); + } + + @Override + public boolean addAll(Collection elements) { + return delegate.addAll(checkElements(elements, constraint)); + } + } + + /** + * Returns a constrained view of the specified list, using the specified + * constraint. Any operations that add new elements to the list will call the + * provided constraint. However, this method does not verify that existing + * elements satisfy the constraint. + * + *

+ * If {@code list} implements {@link RandomAccess}, so will the returned list. + * The returned list is not serializable. + * + * @param list the list to constrain + * @param constraint the constraint that validates added elements + * @return a constrained view of the list + */ + public static List constrainedList(List list, Constraint constraint) { + return (list instanceof RandomAccess) ? new ConstrainedRandomAccessList(list, constraint) + : new ConstrainedList(list, constraint); + } + + /** @see Constraints#constrainedList */ + @GwtCompatible + private static class ConstrainedList extends ForwardingList { + final List delegate; + final Constraint constraint; + + ConstrainedList(List delegate, Constraint constraint) { + this.delegate = checkNotNull(delegate); + this.constraint = checkNotNull(constraint); + } + + @Override + protected List delegate() { + return delegate; + } + + @Override + public boolean add(E element) { + constraint.checkElement(element); + return delegate.add(element); + } + + @Override + public void add(int index, E element) { + constraint.checkElement(element); + delegate.add(index, element); + } + + @Override + public boolean addAll(Collection elements) { + return delegate.addAll(checkElements(elements, constraint)); + } + + @Override + public boolean addAll(int index, Collection elements) { + return delegate.addAll(index, checkElements(elements, constraint)); + } + + @Override + public ListIterator listIterator() { + return constrainedListIterator(delegate.listIterator(), constraint); + } + + @Override + public ListIterator listIterator(int index) { + return constrainedListIterator(delegate.listIterator(index), constraint); + } + + @Override + public E set(int index, E element) { + constraint.checkElement(element); + return delegate.set(index, element); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return constrainedList(delegate.subList(fromIndex, toIndex), constraint); + } + } + + /** @see Constraints#constrainedList */ + static class ConstrainedRandomAccessList extends ConstrainedList implements RandomAccess { + ConstrainedRandomAccessList(List delegate, Constraint constraint) { + super(delegate, constraint); + } + } + + /** + * Returns a constrained view of the specified list iterator, using the + * specified constraint. Any operations that would add new elements to the + * underlying list will be verified by the constraint. + * + * @param listIterator the iterator for which to return a constrained view + * @param constraint the constraint for elements in the list + * @return a constrained view of the specified iterator + */ + private static ListIterator constrainedListIterator(ListIterator listIterator, + Constraint constraint) { + return new ConstrainedListIterator(listIterator, constraint); + } + + /** @see Constraints#constrainedListIterator */ + static class ConstrainedListIterator extends ForwardingListIterator { + private final ListIterator delegate; + private final Constraint constraint; + + public ConstrainedListIterator(ListIterator delegate, Constraint constraint) { + this.delegate = delegate; + this.constraint = constraint; + } + + @Override + protected ListIterator delegate() { + return delegate; + } + + @Override + public void add(E element) { + constraint.checkElement(element); + delegate.add(element); + } + + @Override + public void set(E element) { + constraint.checkElement(element); + delegate.set(element); + } + } + + static Collection constrainedTypePreservingCollection(Collection collection, Constraint constraint) { + if (collection instanceof SortedSet) { + return constrainedSortedSet((SortedSet) collection, constraint); + } else if (collection instanceof Set) { + return constrainedSet((Set) collection, constraint); + } else if (collection instanceof List) { + return constrainedList((List) collection, constraint); + } else { + return constrainedCollection(collection, constraint); + } + } + + /* + * TODO(kevinb): For better performance, avoid making a copy of the elements by + * having addAll() call add() repeatedly instead. + */ + + private static Collection checkElements(Collection elements, Constraint constraint) { + Collection copy = Lists.newArrayList(elements); + for (E element : copy) { + constraint.checkElement(element); + } + return copy; + } +} diff --git a/sources/main/java/com/google/common/collect/ContiguousSet.java b/sources/main/java/com/google/common/collect/ContiguousSet.java new file mode 100644 index 0000000..b3b91ff --- /dev/null +++ b/sources/main/java/com/google/common/collect/ContiguousSet.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.NoSuchElementException; +import java.util.Set; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * A sorted set of contiguous values in a given {@link DiscreteDomain}. + * + *

+ * Warning: Be extremely careful what you do with conceptually large + * instances (such as + * {@code ContiguousSet.create(Range.greaterThan(0), DiscreteDomain.integers()}). + * Certain operations on such a set can be performed efficiently, but others + * (such as {@link Set#hashCode} or {@link Collections#frequency}) can cause + * major performance problems. + * + * @author Gregory Kick + * @since 10.0 + */ +@Beta +@GwtCompatible(emulated = true) +@SuppressWarnings("rawtypes") // allow ungenerified Comparable types +public abstract class ContiguousSet extends ImmutableSortedSet { + /** + * Returns a {@code ContiguousSet} containing the same values in the given + * domain {@linkplain Range#contains contained} by the range. + * + * @throws IllegalArgumentException if neither range nor the domain has a lower + * bound, or if neither has an upper bound + * + * @since 13.0 + */ + public static ContiguousSet create(Range range, DiscreteDomain domain) { + checkNotNull(range); + checkNotNull(domain); + Range effectiveRange = range; + try { + if (!range.hasLowerBound()) { + effectiveRange = effectiveRange.intersection(Range.atLeast(domain.minValue())); + } + if (!range.hasUpperBound()) { + effectiveRange = effectiveRange.intersection(Range.atMost(domain.maxValue())); + } + } catch (NoSuchElementException e) { + throw new IllegalArgumentException(e); + } + + // Per class spec, we are allowed to throw CCE if necessary + boolean empty = effectiveRange.isEmpty() || Range.compareOrThrow(range.lowerBound.leastValueAbove(domain), + range.upperBound.greatestValueBelow(domain)) > 0; + + return empty ? new EmptyContiguousSet(domain) : new RegularContiguousSet(effectiveRange, domain); + } + + final DiscreteDomain domain; + + ContiguousSet(DiscreteDomain domain) { + super(Ordering.natural()); + this.domain = domain; + } + + @Override + public ContiguousSet headSet(C toElement) { + return headSetImpl(checkNotNull(toElement), false); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public ContiguousSet headSet(C toElement, boolean inclusive) { + return headSetImpl(checkNotNull(toElement), inclusive); + } + + @Override + public ContiguousSet subSet(C fromElement, C toElement) { + checkNotNull(fromElement); + checkNotNull(toElement); + checkArgument(comparator().compare(fromElement, toElement) <= 0); + return subSetImpl(fromElement, true, toElement, false); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public ContiguousSet subSet(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + checkNotNull(fromElement); + checkNotNull(toElement); + checkArgument(comparator().compare(fromElement, toElement) <= 0); + return subSetImpl(fromElement, fromInclusive, toElement, toInclusive); + } + + @Override + public ContiguousSet tailSet(C fromElement) { + return tailSetImpl(checkNotNull(fromElement), true); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public ContiguousSet tailSet(C fromElement, boolean inclusive) { + return tailSetImpl(checkNotNull(fromElement), inclusive); + } + + /* + * These methods perform most headSet, subSet, and tailSet logic, besides + * parameter validation. + */ + /* @Override */ abstract ContiguousSet headSetImpl(C toElement, boolean inclusive); + + /* @Override */ abstract ContiguousSet subSetImpl(C fromElement, boolean fromInclusive, C toElement, + boolean toInclusive); + + /* @Override */ abstract ContiguousSet tailSetImpl(C fromElement, boolean inclusive); + + /** + * Returns the set of values that are contained in both this set and the other. + * + *

+ * This method should always be used instead of {@link Sets#intersection} for + * {@link ContiguousSet} instances. + */ + public abstract ContiguousSet intersection(ContiguousSet other); + + /** + * Returns a range, closed on both ends, whose endpoints are the minimum and + * maximum values contained in this set. This is equivalent to + * {@code range(CLOSED, CLOSED)}. + * + * @throws NoSuchElementException if this set is empty + */ + public abstract Range range(); + + /** + * Returns the minimal range with the given boundary types for which all values + * in this set are {@linkplain Range#contains(Comparable) contained} within the + * range. + * + *

+ * Note that this method will return ranges with unbounded endpoints if + * {@link BoundType#OPEN} is requested for a domain minimum or maximum. For + * example, if {@code set} was created from the range + * {@code [1..Integer.MAX_VALUE]} then {@code set.range(CLOSED, OPEN)} must + * return {@code [1..∞)}. + * + * @throws NoSuchElementException if this set is empty + */ + public abstract Range range(BoundType lowerBoundType, BoundType upperBoundType); + + /** + * Returns a short-hand representation of the contents such as + * {@code "[1..100]"}. + */ + @Override + public String toString() { + return range().toString(); + } + + /** + * Not supported. {@code ContiguousSet} instances are constructed with + * {@link #create}. This method exists only to hide {@link ImmutableSet#builder} + * from consumers of {@code + * ContiguousSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link #create}. + */ + @Deprecated + public static ImmutableSortedSet.Builder builder() { + throw new UnsupportedOperationException(); + } +} diff --git a/sources/main/java/com/google/common/collect/Count.java b/sources/main/java/com/google/common/collect/Count.java new file mode 100644 index 0000000..ce9a3d3 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Count.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A mutable value of type {@code int}, for multisets to use in tracking counts + * of values. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class Count implements Serializable { + private int value; + + Count(int value) { + this.value = value; + } + + public int get() { + return value; + } + + public int getAndAdd(int delta) { + int result = value; + value = result + delta; + return result; + } + + public int addAndGet(int delta) { + return value += delta; + } + + public void set(int newValue) { + value = newValue; + } + + public int getAndSet(int newValue) { + int result = value; + value = newValue; + return result; + } + + @Override + public int hashCode() { + return value; + } + + @Override + public boolean equals(@Nullable Object obj) { + return obj instanceof Count && ((Count) obj).value == value; + } + + @Override + public String toString() { + return Integer.toString(value); + } +} diff --git a/sources/main/java/com/google/common/collect/Cut.java b/sources/main/java/com/google/common/collect/Cut.java new file mode 100644 index 0000000..d45811b --- /dev/null +++ b/sources/main/java/com/google/common/collect/Cut.java @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.NoSuchElementException; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.primitives.Booleans; + +/** + * Implementation detail for the internal structure of {@link Range} instances. + * Represents a unique way of "cutting" a "number line" (actually of instances + * of type {@code C}, not necessarily "numbers") into two sections; this can be + * done below a certain value, above a certain value, below all values or above + * all values. With this object defined in this way, an interval can always be + * represented by a pair of {@code Cut} instances. + * + * @author Kevin Bourrillion + */ +@GwtCompatible +abstract class Cut implements Comparable>, Serializable { + final C endpoint; + + Cut(@Nullable C endpoint) { + this.endpoint = endpoint; + } + + abstract boolean isLessThan(C value); + + abstract BoundType typeAsLowerBound(); + + abstract BoundType typeAsUpperBound(); + + abstract Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain); + + abstract Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain); + + abstract void describeAsLowerBound(StringBuilder sb); + + abstract void describeAsUpperBound(StringBuilder sb); + + abstract C leastValueAbove(DiscreteDomain domain); + + abstract C greatestValueBelow(DiscreteDomain domain); + + /* + * The canonical form is a BelowValue cut whenever possible, otherwise + * ABOVE_ALL, or (only in the case of types that are unbounded below) BELOW_ALL. + */ + Cut canonical(DiscreteDomain domain) { + return this; + } + + // note: overriden by {BELOW,ABOVE}_ALL + @Override + public int compareTo(Cut that) { + if (that == belowAll()) { + return 1; + } + if (that == aboveAll()) { + return -1; + } + int result = Range.compareOrThrow(endpoint, that.endpoint); + if (result != 0) { + return result; + } + // same value. below comes before above + return Booleans.compare(this instanceof AboveValue, that instanceof AboveValue); + } + + C endpoint() { + return endpoint; + } + + @SuppressWarnings("unchecked") // catching CCE + @Override + public boolean equals(Object obj) { + if (obj instanceof Cut) { + // It might not really be a Cut, but we'll catch a CCE if it's not + Cut that = (Cut) obj; + try { + int compareResult = compareTo(that); + return compareResult == 0; + } catch (ClassCastException ignored) { + } + } + return false; + } + + /* + * The implementation neither produces nor consumes any non-null instance of + * type C, so casting the type parameter is safe. + */ + @SuppressWarnings("unchecked") + static Cut belowAll() { + return (Cut) BelowAll.INSTANCE; + } + + private static final long serialVersionUID = 0; + + private static final class BelowAll extends Cut> { + private static final BelowAll INSTANCE = new BelowAll(); + + private BelowAll() { + super(null); + } + + @Override + Comparable endpoint() { + throw new IllegalStateException("range unbounded on this side"); + } + + @Override + boolean isLessThan(Comparable value) { + return true; + } + + @Override + BoundType typeAsLowerBound() { + throw new IllegalStateException(); + } + + @Override + BoundType typeAsUpperBound() { + throw new AssertionError("this statement should be unreachable"); + } + + @Override + Cut> withLowerBoundType(BoundType boundType, DiscreteDomain> domain) { + throw new IllegalStateException(); + } + + @Override + Cut> withUpperBoundType(BoundType boundType, DiscreteDomain> domain) { + throw new AssertionError("this statement should be unreachable"); + } + + @Override + void describeAsLowerBound(StringBuilder sb) { + sb.append("(-\u221e"); + } + + @Override + void describeAsUpperBound(StringBuilder sb) { + throw new AssertionError(); + } + + @Override + Comparable leastValueAbove(DiscreteDomain> domain) { + return domain.minValue(); + } + + @Override + Comparable greatestValueBelow(DiscreteDomain> domain) { + throw new AssertionError(); + } + + @Override + Cut> canonical(DiscreteDomain> domain) { + try { + return Cut.>belowValue(domain.minValue()); + } catch (NoSuchElementException e) { + return this; + } + } + + @Override + public int compareTo(Cut> o) { + return (o == this) ? 0 : -1; + } + + @Override + public String toString() { + return "-\u221e"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; + } + + /* + * The implementation neither produces nor consumes any non-null instance of + * type C, so casting the type parameter is safe. + */ + @SuppressWarnings("unchecked") + static Cut aboveAll() { + return (Cut) AboveAll.INSTANCE; + } + + private static final class AboveAll extends Cut> { + private static final AboveAll INSTANCE = new AboveAll(); + + private AboveAll() { + super(null); + } + + @Override + Comparable endpoint() { + throw new IllegalStateException("range unbounded on this side"); + } + + @Override + boolean isLessThan(Comparable value) { + return false; + } + + @Override + BoundType typeAsLowerBound() { + throw new AssertionError("this statement should be unreachable"); + } + + @Override + BoundType typeAsUpperBound() { + throw new IllegalStateException(); + } + + @Override + Cut> withLowerBoundType(BoundType boundType, DiscreteDomain> domain) { + throw new AssertionError("this statement should be unreachable"); + } + + @Override + Cut> withUpperBoundType(BoundType boundType, DiscreteDomain> domain) { + throw new IllegalStateException(); + } + + @Override + void describeAsLowerBound(StringBuilder sb) { + throw new AssertionError(); + } + + @Override + void describeAsUpperBound(StringBuilder sb) { + sb.append("+\u221e)"); + } + + @Override + Comparable leastValueAbove(DiscreteDomain> domain) { + throw new AssertionError(); + } + + @Override + Comparable greatestValueBelow(DiscreteDomain> domain) { + return domain.maxValue(); + } + + @Override + public int compareTo(Cut> o) { + return (o == this) ? 0 : 1; + } + + @Override + public String toString() { + return "+\u221e"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; + } + + static Cut belowValue(C endpoint) { + return new BelowValue(endpoint); + } + + private static final class BelowValue extends Cut { + BelowValue(C endpoint) { + super(checkNotNull(endpoint)); + } + + @Override + boolean isLessThan(C value) { + return Range.compareOrThrow(endpoint, value) <= 0; + } + + @Override + BoundType typeAsLowerBound() { + return BoundType.CLOSED; + } + + @Override + BoundType typeAsUpperBound() { + return BoundType.OPEN; + } + + @Override + Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain) { + switch (boundType) { + case CLOSED: + return this; + case OPEN: + @Nullable + C previous = domain.previous(endpoint); + return (previous == null) ? Cut.belowAll() : new AboveValue(previous); + default: + throw new AssertionError(); + } + } + + @Override + Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { + switch (boundType) { + case CLOSED: + @Nullable + C previous = domain.previous(endpoint); + return (previous == null) ? Cut.aboveAll() : new AboveValue(previous); + case OPEN: + return this; + default: + throw new AssertionError(); + } + } + + @Override + void describeAsLowerBound(StringBuilder sb) { + sb.append('[').append(endpoint); + } + + @Override + void describeAsUpperBound(StringBuilder sb) { + sb.append(endpoint).append(')'); + } + + @Override + C leastValueAbove(DiscreteDomain domain) { + return endpoint; + } + + @Override + C greatestValueBelow(DiscreteDomain domain) { + return domain.previous(endpoint); + } + + @Override + public int hashCode() { + return endpoint.hashCode(); + } + + @Override + public String toString() { + return "\\" + endpoint + "/"; + } + + private static final long serialVersionUID = 0; + } + + static Cut aboveValue(C endpoint) { + return new AboveValue(endpoint); + } + + private static final class AboveValue extends Cut { + AboveValue(C endpoint) { + super(checkNotNull(endpoint)); + } + + @Override + boolean isLessThan(C value) { + return Range.compareOrThrow(endpoint, value) < 0; + } + + @Override + BoundType typeAsLowerBound() { + return BoundType.OPEN; + } + + @Override + BoundType typeAsUpperBound() { + return BoundType.CLOSED; + } + + @Override + Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain) { + switch (boundType) { + case OPEN: + return this; + case CLOSED: + @Nullable + C next = domain.next(endpoint); + return (next == null) ? Cut.belowAll() : belowValue(next); + default: + throw new AssertionError(); + } + } + + @Override + Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { + switch (boundType) { + case OPEN: + @Nullable + C next = domain.next(endpoint); + return (next == null) ? Cut.aboveAll() : belowValue(next); + case CLOSED: + return this; + default: + throw new AssertionError(); + } + } + + @Override + void describeAsLowerBound(StringBuilder sb) { + sb.append('(').append(endpoint); + } + + @Override + void describeAsUpperBound(StringBuilder sb) { + sb.append(endpoint).append(']'); + } + + @Override + C leastValueAbove(DiscreteDomain domain) { + return domain.next(endpoint); + } + + @Override + C greatestValueBelow(DiscreteDomain domain) { + return endpoint; + } + + @Override + Cut canonical(DiscreteDomain domain) { + C next = leastValueAbove(domain); + return (next != null) ? belowValue(next) : Cut.aboveAll(); + } + + @Override + public int hashCode() { + return ~endpoint.hashCode(); + } + + @Override + public String toString() { + return "/" + endpoint + "\\"; + } + + private static final long serialVersionUID = 0; + } +} diff --git a/sources/main/java/com/google/common/collect/DenseImmutableTable.java b/sources/main/java/com/google/common/collect/DenseImmutableTable.java new file mode 100644 index 0000000..8e171a9 --- /dev/null +++ b/sources/main/java/com/google/common/collect/DenseImmutableTable.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.Map; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A {@code RegularImmutableTable} optimized for dense data. + */ +@GwtCompatible +@Immutable +final class DenseImmutableTable extends RegularImmutableTable { + private final ImmutableMap rowKeyToIndex; + private final ImmutableMap columnKeyToIndex; + private final ImmutableMap> rowMap; + private final ImmutableMap> columnMap; + private final int[] rowCounts; + private final int[] columnCounts; + private final V[][] values; + private final int[] iterationOrderRow; + private final int[] iterationOrderColumn; + + private static ImmutableMap makeIndex(ImmutableSet set) { + ImmutableMap.Builder indexBuilder = ImmutableMap.builder(); + int i = 0; + for (E key : set) { + indexBuilder.put(key, i); + i++; + } + return indexBuilder.build(); + } + + DenseImmutableTable(ImmutableList> cellList, ImmutableSet rowSpace, ImmutableSet columnSpace) { + @SuppressWarnings("unchecked") + V[][] array = (V[][]) new Object[rowSpace.size()][columnSpace.size()]; + this.values = array; + this.rowKeyToIndex = makeIndex(rowSpace); + this.columnKeyToIndex = makeIndex(columnSpace); + rowCounts = new int[rowKeyToIndex.size()]; + columnCounts = new int[columnKeyToIndex.size()]; + int[] iterationOrderRow = new int[cellList.size()]; + int[] iterationOrderColumn = new int[cellList.size()]; + for (int i = 0; i < cellList.size(); i++) { + Cell 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); + } + + @Nullable + abstract V getValue(int keyIndex); + + @Override + ImmutableSet createKeySet() { + return isFull() ? keyToIndex().keySet() : super.createKeySet(); + } + + @Override + public int size() { + return size; + } + + @Override + public V get(@Nullable Object key) { + Integer keyIndex = keyToIndex().get(key); + return (keyIndex == null) ? null : getValue(keyIndex); + } + + @Override + ImmutableSet> 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(@Nullable Object rowKey, @Nullable 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/sources/main/java/com/google/common/collect/DescendingImmutableSortedMultiset.java b/sources/main/java/com/google/common/collect/DescendingImmutableSortedMultiset.java new file mode 100644 index 0000000..da2e226 --- /dev/null +++ b/sources/main/java/com/google/common/collect/DescendingImmutableSortedMultiset.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import javax.annotation.Nullable; + +/** + * A descending wrapper around an {@code ImmutableSortedMultiset} + * + * @author Louis Wasserman + */ +@SuppressWarnings("serial") // uses writeReplace, not default serialization +final class DescendingImmutableSortedMultiset extends ImmutableSortedMultiset { + private final transient ImmutableSortedMultiset forward; + + DescendingImmutableSortedMultiset(ImmutableSortedMultiset forward) { + this.forward = forward; + } + + @Override + public int count(@Nullable Object element) { + return forward.count(element); + } + + @Override + public Entry firstEntry() { + return forward.lastEntry(); + } + + @Override + public Entry lastEntry() { + return forward.firstEntry(); + } + + @Override + public int size() { + return forward.size(); + } + + @Override + public ImmutableSortedSet elementSet() { + return forward.elementSet().descendingSet(); + } + + @Override + Entry getEntry(int index) { + return forward.entrySet().asList().reverse().get(index); + } + + @Override + public ImmutableSortedMultiset descendingMultiset() { + return forward; + } + + @Override + public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { + return forward.tailMultiset(upperBound, boundType).descendingMultiset(); + } + + @Override + public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return forward.headMultiset(lowerBound, boundType).descendingMultiset(); + } + + @Override + boolean isPartialView() { + return forward.isPartialView(); + } +} diff --git a/sources/main/java/com/google/common/collect/DescendingImmutableSortedSet.java b/sources/main/java/com/google/common/collect/DescendingImmutableSortedSet.java new file mode 100644 index 0000000..003f31f --- /dev/null +++ b/sources/main/java/com/google/common/collect/DescendingImmutableSortedSet.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtIncompatible; + +/** + * Skeletal implementation of {@link ImmutableSortedSet#descendingSet()}. + * + * @author Louis Wasserman + */ +class DescendingImmutableSortedSet extends ImmutableSortedSet { + private final ImmutableSortedSet forward; + + DescendingImmutableSortedSet(ImmutableSortedSet forward) { + super(Ordering.from(forward.comparator()).reverse()); + this.forward = forward; + } + + @Override + public int size() { + return forward.size(); + } + + @Override + public UnmodifiableIterator iterator() { + return forward.descendingIterator(); + } + + @Override + ImmutableSortedSet headSetImpl(E toElement, boolean inclusive) { + return forward.tailSet(toElement, inclusive).descendingSet(); + } + + @Override + ImmutableSortedSet subSetImpl(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); + } + + @Override + ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive) { + return forward.headSet(fromElement, inclusive).descendingSet(); + } + + @Override + @GwtIncompatible("NavigableSet") + public ImmutableSortedSet descendingSet() { + return forward; + } + + @Override + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return forward.iterator(); + } + + @Override + @GwtIncompatible("NavigableSet") + ImmutableSortedSet createDescendingSet() { + throw new AssertionError("should never be called"); + } + + @Override + public E lower(E element) { + return forward.higher(element); + } + + @Override + public E floor(E element) { + return forward.ceiling(element); + } + + @Override + public E ceiling(E element) { + return forward.floor(element); + } + + @Override + public E higher(E element) { + return forward.lower(element); + } + + @Override + int indexOf(@Nullable Object target) { + int index = forward.indexOf(target); + if (index == -1) { + return index; + } else { + return size() - 1 - index; + } + } + + @Override + boolean isPartialView() { + return forward.isPartialView(); + } +} diff --git a/sources/main/java/com/google/common/collect/DescendingMultiset.java b/sources/main/java/com/google/common/collect/DescendingMultiset.java new file mode 100644 index 0000000..449eb6f --- /dev/null +++ b/sources/main/java/com/google/common/collect/DescendingMultiset.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.Set; + +import com.google.common.annotations.GwtCompatible; + +/** + * A skeleton implementation of a descending multiset. Only needs + * {@code forwardMultiset()} and {@code entryIterator()}. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +abstract class DescendingMultiset extends ForwardingMultiset implements SortedMultiset { + abstract SortedMultiset forwardMultiset(); + + private transient Comparator comparator; + + @Override + public Comparator comparator() { + Comparator result = comparator; + if (result == null) { + return comparator = Ordering.from(forwardMultiset().comparator()).reverse(); + } + return result; + } + + private transient NavigableSet elementSet; + + @Override + public NavigableSet elementSet() { + NavigableSet result = elementSet; + if (result == null) { + return elementSet = new SortedMultisets.NavigableElementSet(this); + } + return result; + } + + @Override + public Entry pollFirstEntry() { + return forwardMultiset().pollLastEntry(); + } + + @Override + public Entry pollLastEntry() { + return forwardMultiset().pollFirstEntry(); + } + + @Override + public SortedMultiset headMultiset(E toElement, BoundType boundType) { + return forwardMultiset().tailMultiset(toElement, boundType).descendingMultiset(); + } + + @Override + public SortedMultiset subMultiset(E fromElement, BoundType fromBoundType, E toElement, BoundType toBoundType) { + return forwardMultiset().subMultiset(toElement, toBoundType, fromElement, fromBoundType).descendingMultiset(); + } + + @Override + public SortedMultiset tailMultiset(E fromElement, BoundType boundType) { + return forwardMultiset().headMultiset(fromElement, boundType).descendingMultiset(); + } + + @Override + protected Multiset delegate() { + return forwardMultiset(); + } + + @Override + public SortedMultiset descendingMultiset() { + return forwardMultiset(); + } + + @Override + public Entry firstEntry() { + return forwardMultiset().lastEntry(); + } + + @Override + public Entry lastEntry() { + return forwardMultiset().firstEntry(); + } + + abstract Iterator> entryIterator(); + + private transient Set> entrySet; + + @Override + public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + Set> createEntrySet() { + return new Multisets.EntrySet() { + @Override + Multiset multiset() { + return DescendingMultiset.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + + @Override + public int size() { + return forwardMultiset().entrySet().size(); + } + }; + } + + @Override + public Iterator iterator() { + return Multisets.iteratorImpl(this); + } + + @Override + public Object[] toArray() { + return standardToArray(); + } + + @Override + public T[] toArray(T[] array) { + return standardToArray(array); + } + + @Override + public String toString() { + return entrySet().toString(); + } +} \ No newline at end of file diff --git a/sources/main/java/com/google/common/collect/DiscreteDomain.java b/sources/main/java/com/google/common/collect/DiscreteDomain.java new file mode 100644 index 0000000..33c3bc3 --- /dev/null +++ b/sources/main/java/com/google/common/collect/DiscreteDomain.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.NoSuchElementException; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A descriptor for a discrete {@code Comparable} domain such as all + * {@link Integer} instances. A discrete domain is one that supports the three + * basic operations: {@link #next}, {@link #previous} and {@link #distance}, + * according to their specifications. The methods {@link #minValue} and + * {@link #maxValue} should also be overridden for bounded types. + * + *

+ * A discrete domain always represents the entire set of values of its + * type; it cannot represent partial domains such as "prime integers" or + * "strings of length 5." + * + *

+ * See the Guava User Guide section on + * {@code DiscreteDomain}. + * + * @author Kevin Bourrillion + * @since 10.0 + */ +@GwtCompatible +@Beta +public abstract class DiscreteDomain { + + /** + * Returns the discrete domain for values of type {@code Integer}. + * + * @since 14.0 (since 10.0 as {@code DiscreteDomains.integers()}) + */ + public static DiscreteDomain integers() { + return IntegerDomain.INSTANCE; + } + + private static final class IntegerDomain extends DiscreteDomain implements Serializable { + private static final IntegerDomain INSTANCE = new IntegerDomain(); + + @Override + public Integer next(Integer value) { + int i = value; + return (i == Integer.MAX_VALUE) ? null : i + 1; + } + + @Override + public Integer previous(Integer value) { + int i = value; + return (i == Integer.MIN_VALUE) ? null : i - 1; + } + + @Override + public long distance(Integer start, Integer end) { + return (long) end - start; + } + + @Override + public Integer minValue() { + return Integer.MIN_VALUE; + } + + @Override + public Integer maxValue() { + return Integer.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "DiscreteDomain.integers()"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the discrete domain for values of type {@code Long}. + * + * @since 14.0 (since 10.0 as {@code DiscreteDomains.longs()}) + */ + public static DiscreteDomain longs() { + return LongDomain.INSTANCE; + } + + private static final class LongDomain extends DiscreteDomain implements Serializable { + private static final LongDomain INSTANCE = new LongDomain(); + + @Override + public Long next(Long value) { + long l = value; + return (l == Long.MAX_VALUE) ? null : l + 1; + } + + @Override + public Long previous(Long value) { + long l = value; + return (l == Long.MIN_VALUE) ? null : l - 1; + } + + @Override + public long distance(Long start, Long end) { + long result = end - start; + if (end > start && result < 0) { // overflow + return Long.MAX_VALUE; + } + if (end < start && result > 0) { // underflow + return Long.MIN_VALUE; + } + return result; + } + + @Override + public Long minValue() { + return Long.MIN_VALUE; + } + + @Override + public Long maxValue() { + return Long.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "DiscreteDomain.longs()"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the discrete domain for values of type {@code BigInteger}. + * + * @since 15.0 + */ + public static DiscreteDomain bigIntegers() { + return BigIntegerDomain.INSTANCE; + } + + private static final class BigIntegerDomain extends DiscreteDomain implements Serializable { + private static final BigIntegerDomain INSTANCE = new BigIntegerDomain(); + + private static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE); + private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); + + @Override + public BigInteger next(BigInteger value) { + return value.add(BigInteger.ONE); + } + + @Override + public BigInteger previous(BigInteger value) { + return value.subtract(BigInteger.ONE); + } + + @Override + public long distance(BigInteger start, BigInteger end) { + return end.subtract(start).max(MIN_LONG).min(MAX_LONG).longValue(); + } + + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "DiscreteDomain.bigIntegers()"; + } + + private static final long serialVersionUID = 0; + } + + /** Constructor for use by subclasses. */ + protected DiscreteDomain() { + } + + /** + * Returns the unique least value of type {@code C} that is greater than + * {@code value}, or {@code null} if none exists. Inverse operation to + * {@link #previous}. + * + * @param value any value of type {@code C} + * @return the least value greater than {@code value}, or {@code null} if + * {@code value} is {@code maxValue()} + */ + public abstract C next(C value); + + /** + * Returns the unique greatest value of type {@code C} that is less than + * {@code value}, or {@code null} if none exists. Inverse operation to + * {@link #next}. + * + * @param value any value of type {@code C} + * @return the greatest value less than {@code value}, or {@code null} if + * {@code value} is {@code minValue()} + */ + public abstract C previous(C value); + + /** + * Returns a signed value indicating how many nested invocations of + * {@link #next} (if positive) or {@link #previous} (if negative) are needed to + * reach {@code end} starting from {@code start}. For example, if {@code end = + * next(next(next(start)))}, then {@code distance(start, end) == 3} and {@code + * distance(end, start) == -3}. As well, {@code distance(a, a)} is always zero. + * + *

+ * Note that this function is necessarily well-defined for any discrete type. + * + * @return the distance as described above, or {@link Long#MIN_VALUE} or + * {@link Long#MAX_VALUE} if the distance is too small or too large, + * respectively. + */ + public abstract long distance(C start, C end); + + /** + * Returns the minimum value of type {@code C}, if it has one. The minimum value + * is the unique value for which {@link Comparable#compareTo(Object)} never + * returns a positive value for any input of type {@code C}. + * + *

+ * The default implementation throws {@code NoSuchElementException}. + * + * @return the minimum value of type {@code C}; never null + * @throws NoSuchElementException if the type has no (practical) minimum value; + * for example, {@link java.math.BigInteger} + */ + public C minValue() { + throw new NoSuchElementException(); + } + + /** + * Returns the maximum value of type {@code C}, if it has one. The maximum value + * is the unique value for which {@link Comparable#compareTo(Object)} never + * returns a negative value for any input of type {@code C}. + * + *

+ * The default implementation throws {@code NoSuchElementException}. + * + * @return the maximum value of type {@code C}; never null + * @throws NoSuchElementException if the type has no (practical) maximum value; + * for example, {@link java.math.BigInteger} + */ + public C maxValue() { + throw new NoSuchElementException(); + } + +} diff --git a/sources/main/java/com/google/common/collect/EmptyContiguousSet.java b/sources/main/java/com/google/common/collect/EmptyContiguousSet.java new file mode 100644 index 0000000..c3fd961 --- /dev/null +++ b/sources/main/java/com/google/common/collect/EmptyContiguousSet.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. +*/ +package com.google.common.collect; + +import java.io.Serializable; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An empty contiguous set. + * + * @author Gregory Kick + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("unchecked") // allow ungenerified Comparable types +final class EmptyContiguousSet extends ContiguousSet { + EmptyContiguousSet(DiscreteDomain domain) { + super(domain); + } + + @Override + public C first() { + throw new NoSuchElementException(); + } + + @Override + public C last() { + throw new NoSuchElementException(); + } + + @Override + public int size() { + return 0; + } + + @Override + public ContiguousSet intersection(ContiguousSet other) { + return this; + } + + @Override + public Range range() { + throw new NoSuchElementException(); + } + + @Override + public Range range(BoundType lowerBoundType, BoundType upperBoundType) { + throw new NoSuchElementException(); + } + + @Override + ContiguousSet headSetImpl(C toElement, boolean inclusive) { + return this; + } + + @Override + ContiguousSet subSetImpl(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + return this; + } + + @Override + ContiguousSet tailSetImpl(C fromElement, boolean fromInclusive) { + return this; + } + + @GwtIncompatible("not used by GWT emulation") + @Override + int indexOf(Object target) { + return -1; + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + @GwtIncompatible("NavigableSet") + @Override + public UnmodifiableIterator descendingIterator() { + return Iterators.emptyIterator(); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public ImmutableList asList() { + return ImmutableList.of(); + } + + @Override + public String toString() { + return "[]"; + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof Set) { + Set that = (Set) object; + return that.isEmpty(); + } + return false; + } + + @Override + public int hashCode() { + return 0; + } + + @GwtIncompatible("serialization") + private static final class SerializedForm implements Serializable { + private final DiscreteDomain domain; + + private SerializedForm(DiscreteDomain domain) { + this.domain = domain; + } + + private Object readResolve() { + return new EmptyContiguousSet(domain); + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible("serialization") + @Override + Object writeReplace() { + return new SerializedForm(domain); + } + + @GwtIncompatible("NavigableSet") + ImmutableSortedSet createDescendingSet() { + return new EmptyImmutableSortedSet(Ordering.natural().reverse()); + } +} diff --git a/sources/main/java/com/google/common/collect/EmptyImmutableBiMap.java b/sources/main/java/com/google/common/collect/EmptyImmutableBiMap.java new file mode 100644 index 0000000..73604af --- /dev/null +++ b/sources/main/java/com/google/common/collect/EmptyImmutableBiMap.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Bimap with no mappings. + * + * @author Jared Levy + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +final class EmptyImmutableBiMap extends ImmutableBiMap { + static final EmptyImmutableBiMap INSTANCE = new EmptyImmutableBiMap(); + + private EmptyImmutableBiMap() { + } + + @Override + public ImmutableBiMap inverse() { + return this; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Object get(@Nullable Object key) { + return null; + } + + @Override + public ImmutableSet> 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 + } +} diff --git a/sources/main/java/com/google/common/collect/EmptyImmutableListMultimap.java b/sources/main/java/com/google/common/collect/EmptyImmutableListMultimap.java new file mode 100644 index 0000000..3cd20ef --- /dev/null +++ b/sources/main/java/com/google/common/collect/EmptyImmutableListMultimap.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * Implementation of {@link ImmutableListMultimap} with no entries. + * + * @author Jared Levy + */ +@GwtCompatible(serializable = true) +class EmptyImmutableListMultimap extends ImmutableListMultimap { + static final EmptyImmutableListMultimap INSTANCE = new EmptyImmutableListMultimap(); + + private EmptyImmutableListMultimap() { + super(ImmutableMap.>of(), 0); + } + + private Object readResolve() { + return INSTANCE; // preserve singleton property + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/EmptyImmutableSet.java b/sources/main/java/com/google/common/collect/EmptyImmutableSet.java new file mode 100644 index 0000000..d047cd0 --- /dev/null +++ b/sources/main/java/com/google/common/collect/EmptyImmutableSet.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An empty immutable set. + * + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +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(@Nullable 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(@Nullable 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/sources/main/java/com/google/common/collect/EmptyImmutableSetMultimap.java b/sources/main/java/com/google/common/collect/EmptyImmutableSetMultimap.java new file mode 100644 index 0000000..abafa5a --- /dev/null +++ b/sources/main/java/com/google/common/collect/EmptyImmutableSetMultimap.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * Implementation of {@link ImmutableListMultimap} with no entries. + * + * @author Mike Ward + */ +@GwtCompatible(serializable = true) +class EmptyImmutableSetMultimap extends ImmutableSetMultimap { + static final EmptyImmutableSetMultimap INSTANCE = new EmptyImmutableSetMultimap(); + + private EmptyImmutableSetMultimap() { + super(ImmutableMap.>of(), 0, null); + } + + private Object readResolve() { + return INSTANCE; // preserve singleton property + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/EmptyImmutableSortedMap.java b/sources/main/java/com/google/common/collect/EmptyImmutableSortedMap.java new file mode 100644 index 0000000..d25ff2c --- /dev/null +++ b/sources/main/java/com/google/common/collect/EmptyImmutableSortedMap.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Comparator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An empty immutable sorted map. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") // uses writeReplace, not default serialization +final class EmptyImmutableSortedMap extends ImmutableSortedMap { + private final transient ImmutableSortedSet keySet; + + EmptyImmutableSortedMap(Comparator comparator) { + this.keySet = ImmutableSortedSet.emptySet(comparator); + } + + EmptyImmutableSortedMap(Comparator comparator, ImmutableSortedMap descendingMap) { + super(descendingMap); + this.keySet = ImmutableSortedSet.emptySet(comparator); + } + + @Override + public V get(@Nullable Object key) { + return null; + } + + @Override + public ImmutableSortedSet keySet() { + return keySet; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public ImmutableCollection values() { + return ImmutableList.of(); + } + + @Override + public String toString() { + return "{}"; + } + + @Override + boolean isPartialView() { + return false; + } + + @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 ImmutableSortedMap headMap(K toKey, boolean inclusive) { + checkNotNull(toKey); + return this; + } + + @Override + public ImmutableSortedMap tailMap(K fromKey, boolean inclusive) { + checkNotNull(fromKey); + return this; + } + + @Override + ImmutableSortedMap createDescendingMap() { + return new EmptyImmutableSortedMap(Ordering.from(comparator()).reverse(), this); + } +} diff --git a/sources/main/java/com/google/common/collect/EmptyImmutableSortedMultiset.java b/sources/main/java/com/google/common/collect/EmptyImmutableSortedMultiset.java new file mode 100644 index 0000000..df290cb --- /dev/null +++ b/sources/main/java/com/google/common/collect/EmptyImmutableSortedMultiset.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collection; +import java.util.Comparator; + +import javax.annotation.Nullable; + +/** + * An empty immutable sorted multiset. + * + * @author Louis Wasserman + */ +@SuppressWarnings("serial") // Uses writeReplace, not default serialization +final class EmptyImmutableSortedMultiset extends ImmutableSortedMultiset { + private final ImmutableSortedSet elementSet; + + EmptyImmutableSortedMultiset(Comparator comparator) { + this.elementSet = ImmutableSortedSet.emptySet(comparator); + } + + @Override + public Entry firstEntry() { + return null; + } + + @Override + public Entry lastEntry() { + return null; + } + + @Override + public int count(@Nullable Object element) { + return 0; + } + + @Override + public boolean containsAll(Collection targets) { + return targets.isEmpty(); + } + + @Override + public int size() { + return 0; + } + + @Override + public ImmutableSortedSet elementSet() { + return elementSet; + } + + @Override + Entry getEntry(int index) { + throw new AssertionError("should never be called"); + } + + @Override + public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { + checkNotNull(upperBound); + checkNotNull(boundType); + return this; + } + + @Override + public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + checkNotNull(lowerBound); + checkNotNull(boundType); + return this; + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof Multiset) { + Multiset other = (Multiset) object; + return other.isEmpty(); + } + return false; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + return offset; + } + + @Override + public ImmutableList asList() { + return ImmutableList.of(); + } +} diff --git a/sources/main/java/com/google/common/collect/EmptyImmutableSortedSet.java b/sources/main/java/com/google/common/collect/EmptyImmutableSortedSet.java new file mode 100644 index 0000000..1d383e4 --- /dev/null +++ b/sources/main/java/com/google/common/collect/EmptyImmutableSortedSet.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Comparator; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An empty immutable sorted set. + * + * @author Jared Levy + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +class EmptyImmutableSortedSet extends ImmutableSortedSet { + EmptyImmutableSortedSet(Comparator comparator) { + super(comparator); + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public boolean contains(@Nullable Object target) { + return false; + } + + @Override + public boolean containsAll(Collection targets) { + return targets.isEmpty(); + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + @GwtIncompatible("NavigableSet") + @Override + public UnmodifiableIterator descendingIterator() { + return Iterators.emptyIterator(); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public ImmutableList asList() { + return ImmutableList.of(); + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + return offset; + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof Set) { + Set that = (Set) object; + return that.isEmpty(); + } + return false; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public String toString() { + return "[]"; + } + + @Override + public E first() { + throw new NoSuchElementException(); + } + + @Override + public E last() { + throw new NoSuchElementException(); + } + + @Override + ImmutableSortedSet headSetImpl(E toElement, boolean inclusive) { + return this; + } + + @Override + ImmutableSortedSet subSetImpl(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return this; + } + + @Override + ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive) { + return this; + } + + @Override + int indexOf(@Nullable Object target) { + return -1; + } + + @Override + ImmutableSortedSet createDescendingSet() { + return new EmptyImmutableSortedSet(Ordering.from(comparator).reverse()); + } +} diff --git a/sources/main/java/com/google/common/collect/EnumBiMap.java b/sources/main/java/com/google/common/collect/EnumBiMap.java new file mode 100644 index 0000000..ffec2ea --- /dev/null +++ b/sources/main/java/com/google/common/collect/EnumBiMap.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumMap; +import java.util.Map; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values + * are not permitted. An {@code EnumBiMap} and its inverse are both + * serializable. + * + *

+ * See the Guava User Guide article on + * {@code BiMap}. + * + * @author Mike Bostock + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public final class EnumBiMap, V extends Enum> extends AbstractBiMap { + private transient Class keyType; + private transient Class valueType; + + /** + * Returns a new, empty {@code EnumBiMap} using the specified key and value + * types. + * + * @param keyType the key type + * @param valueType the value type + */ + public static , V extends Enum> EnumBiMap create(Class keyType, Class valueType) { + return new EnumBiMap(keyType, valueType); + } + + /** + * Returns a new bimap with the same mappings as the specified map. If the + * specified map is an {@code EnumBiMap}, the new bimap has the same types as + * the provided map. Otherwise, the specified map must contain at least one + * mapping, in order to determine the key and value types. + * + * @param map the map whose mappings are to be placed in this map + * @throws IllegalArgumentException if map is not an {@code EnumBiMap} instance + * and contains no mappings + */ + public static , V extends Enum> EnumBiMap create(Map map) { + EnumBiMap bimap = create(inferKeyType(map), inferValueType(map)); + bimap.putAll(map); + return bimap; + } + + private EnumBiMap(Class keyType, Class valueType) { + super(WellBehavedMap.wrap(new EnumMap(keyType)), WellBehavedMap.wrap(new EnumMap(valueType))); + this.keyType = keyType; + this.valueType = valueType; + } + + static > Class inferKeyType(Map map) { + if (map instanceof EnumBiMap) { + return ((EnumBiMap) map).keyType(); + } + if (map instanceof EnumHashBiMap) { + return ((EnumHashBiMap) map).keyType(); + } + checkArgument(!map.isEmpty()); + return map.keySet().iterator().next().getDeclaringClass(); + } + + private static > Class inferValueType(Map map) { + if (map instanceof EnumBiMap) { + return ((EnumBiMap) map).valueType; + } + checkArgument(!map.isEmpty()); + return map.values().iterator().next().getDeclaringClass(); + } + + /** Returns the associated key type. */ + public Class keyType() { + return keyType; + } + + /** Returns the associated value type. */ + public Class valueType() { + return valueType; + } + + @Override + K checkKey(K key) { + return checkNotNull(key); + } + + @Override + V checkValue(V value) { + return checkNotNull(value); + } + + /** + * @serialData the key class, value class, number of entries, first key, first + * value, second key, second value, and so on. + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(keyType); + stream.writeObject(valueType); + Serialization.writeMap(this, stream); + } + + @SuppressWarnings("unchecked") // reading fields populated by writeObject + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyType = (Class) stream.readObject(); + valueType = (Class) stream.readObject(); + setDelegates(WellBehavedMap.wrap(new EnumMap(keyType)), + WellBehavedMap.wrap(new EnumMap(valueType))); + Serialization.populateMap(this, stream); + } + + @GwtIncompatible("not needed in emulated source.") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/EnumHashBiMap.java b/sources/main/java/com/google/common/collect/EnumHashBiMap.java new file mode 100644 index 0000000..8ebb4fc --- /dev/null +++ b/sources/main/java/com/google/common/collect/EnumHashBiMap.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * A {@code BiMap} backed by an {@code EnumMap} instance for keys-to-values, and + * a {@code HashMap} instance for values-to-keys. Null keys are not permitted, + * but null values are. An {@code EnumHashBiMap} and its inverse are both + * serializable. + * + *

+ * See the Guava User Guide article on + * {@code BiMap}. + * + * @author Mike Bostock + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public final class EnumHashBiMap, V> extends AbstractBiMap { + private transient Class keyType; + + /** + * Returns a new, empty {@code EnumHashBiMap} using the specified key type. + * + * @param keyType the key type + */ + public static , V> EnumHashBiMap create(Class keyType) { + return new EnumHashBiMap(keyType); + } + + /** + * Constructs a new bimap with the same mappings as the specified map. If the + * specified map is an {@code EnumHashBiMap} or an {@link EnumBiMap}, the new + * bimap has the same key type as the input bimap. Otherwise, the specified map + * must contain at least one mapping, in order to determine the key type. + * + * @param map the map whose mappings are to be placed in this map + * @throws IllegalArgumentException if map is not an {@code EnumBiMap} or an + * {@code EnumHashBiMap} instance and contains + * no mappings + */ + public static , V> EnumHashBiMap create(Map map) { + EnumHashBiMap bimap = create(EnumBiMap.inferKeyType(map)); + bimap.putAll(map); + return bimap; + } + + private EnumHashBiMap(Class keyType) { + super(WellBehavedMap.wrap(new EnumMap(keyType)), + Maps.newHashMapWithExpectedSize(keyType.getEnumConstants().length)); + this.keyType = keyType; + } + + // Overriding these 3 methods to show that values may be null (but not keys) + + @Override + K checkKey(K key) { + return checkNotNull(key); + } + + @Override + public V put(K key, @Nullable V value) { + return super.put(key, value); + } + + @Override + public V forcePut(K key, @Nullable V value) { + return super.forcePut(key, value); + } + + /** Returns the associated key type. */ + public Class keyType() { + return keyType; + } + + /** + * @serialData the key class, number of entries, first key, first value, second + * key, second value, and so on. + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(keyType); + Serialization.writeMap(this, stream); + } + + @SuppressWarnings("unchecked") // reading field populated by writeObject + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyType = (Class) stream.readObject(); + setDelegates(WellBehavedMap.wrap(new EnumMap(keyType)), + new HashMap(keyType.getEnumConstants().length * 3 / 2)); + Serialization.populateMap(this, stream); + } + + @GwtIncompatible("only needed in emulated source.") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/EnumMultiset.java b/sources/main/java/com/google/common/collect/EnumMultiset.java new file mode 100644 index 0000000..94f6b7f --- /dev/null +++ b/sources/main/java/com/google/common/collect/EnumMultiset.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumMap; +import java.util.Iterator; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * Multiset implementation backed by an {@link EnumMap}. + * + *

+ * See the Guava User Guide article on + * {@code Multiset}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public final class EnumMultiset> extends AbstractMapBasedMultiset { + /** Creates an empty {@code EnumMultiset}. */ + public static > EnumMultiset create(Class type) { + return new EnumMultiset(type); + } + + /** + * Creates a new {@code EnumMultiset} containing the specified elements. + * + *

+ * This implementation is highly efficient when {@code elements} is itself a + * {@link Multiset}. + * + * @param elements the elements that the multiset should contain + * @throws IllegalArgumentException if {@code elements} is empty + */ + public static > EnumMultiset create(Iterable elements) { + Iterator iterator = elements.iterator(); + checkArgument(iterator.hasNext(), "EnumMultiset constructor passed empty Iterable"); + EnumMultiset multiset = new EnumMultiset(iterator.next().getDeclaringClass()); + Iterables.addAll(multiset, elements); + return multiset; + } + + /** + * Returns a new {@code EnumMultiset} instance containing the given elements. + * Unlike {@link EnumMultiset#create(Iterable)}, this method does not produce an + * exception on an empty iterable. + * + * @since 14.0 + */ + public static > EnumMultiset create(Iterable elements, Class type) { + EnumMultiset result = create(type); + Iterables.addAll(result, elements); + return result; + } + + private transient Class type; + + /** Creates an empty {@code EnumMultiset}. */ + private EnumMultiset(Class type) { + super(WellBehavedMap.wrap(new EnumMap(type))); + this.type = type; + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(type); + Serialization.writeMultiset(this, stream); + } + + /** + * @serialData the {@code Class} for the enum type, the number of distinct + * elements, the first element, its count, the second element, its + * count, and so on + */ + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + @SuppressWarnings("unchecked") // reading data stored by writeObject + Class localType = (Class) stream.readObject(); + type = localType; + setBackingMap(WellBehavedMap.wrap(new EnumMap(type))); + Serialization.populateMultiset(this, stream); + } + + @GwtIncompatible("Not needed in emulated source") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/EvictingQueue.java b/sources/main/java/com/google/common/collect/EvictingQueue.java new file mode 100644 index 0000000..dfd4094 --- /dev/null +++ b/sources/main/java/com/google/common/collect/EvictingQueue.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Queue; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; + +/** + * A non-blocking queue which automatically evicts elements from the head of the + * queue when attempting to add new elements onto the queue and it is full. + * + *

+ * An evicting queue must be configured with a maximum size. Each time an + * element is added to a full queue, the queue automatically removes its head + * element. This is different from conventional bounded queues, which either + * block or reject new elements when full. + * + *

+ * This class is not thread-safe, and does not accept null elements. + * + * @author Kurt Alfred Kluever + * @since 15.0 + */ +@Beta +@GwtIncompatible("java.util.ArrayDeque") +public final class EvictingQueue extends ForwardingQueue implements Serializable { + + private final Queue delegate; + + @VisibleForTesting + final int maxSize; + + private EvictingQueue(int maxSize) { + checkArgument(maxSize >= 0, "maxSize (%s) must >= 0", maxSize); + this.delegate = new ArrayDeque(maxSize); + this.maxSize = maxSize; + } + + /** + * Creates and returns a new evicting queue that will hold up to {@code maxSize} + * elements. + * + *

+ * When {@code maxSize} is zero, elements will be evicted immediately after + * being added to the queue. + */ + public static EvictingQueue create(int maxSize) { + return new EvictingQueue(maxSize); + } + + /** + * Returns the number of additional elements that this queue can accept without + * evicting; zero if the queue is currently full. + * + * @since 16.0 + */ + public int remainingCapacity() { + return maxSize - size(); + } + + @Override + protected Queue delegate() { + return delegate; + } + + /** + * Adds the given element to this queue. If the queue is currently full, the + * element at the head of the queue is evicted to make room. + * + * @return {@code true} always + */ + @Override + public boolean offer(E e) { + return add(e); + } + + /** + * Adds the given element to this queue. If the queue is currently full, the + * element at the head of the queue is evicted to make room. + * + * @return {@code true} always + */ + @Override + public boolean add(E e) { + checkNotNull(e); // check before removing + if (maxSize == 0) { + return true; + } + if (size() == maxSize) { + delegate.remove(); + } + delegate.add(e); + return true; + } + + @Override + public boolean addAll(Collection collection) { + return standardAddAll(collection); + } + + @Override + public boolean contains(Object object) { + return delegate().contains(checkNotNull(object)); + } + + @Override + public boolean remove(Object object) { + return delegate().remove(checkNotNull(object)); + } + + // TODO(user): Do we want to checkNotNull each element in containsAll, + // removeAll, and retainAll? + + private static final long serialVersionUID = 0L; +} diff --git a/sources/main/java/com/google/common/collect/ExplicitOrdering.java b/sources/main/java/com/google/common/collect/ExplicitOrdering.java new file mode 100644 index 0000000..07e99f0 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ExplicitOrdering.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** An ordering that compares objects according to a given order. */ +@GwtCompatible(serializable = true) +final class ExplicitOrdering extends Ordering implements Serializable { + final ImmutableMap rankMap; + + ExplicitOrdering(List valuesInOrder) { + this(buildRankMap(valuesInOrder)); + } + + ExplicitOrdering(ImmutableMap rankMap) { + this.rankMap = rankMap; + } + + @Override + public int compare(T left, T right) { + return rank(left) - rank(right); // safe because both are nonnegative + } + + private int rank(T value) { + Integer rank = rankMap.get(value); + if (rank == null) { + throw new IncomparableValueException(value); + } + return rank; + } + + private static ImmutableMap buildRankMap(List valuesInOrder) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + int rank = 0; + for (T value : valuesInOrder) { + builder.put(value, rank++); + } + return builder.build(); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof ExplicitOrdering) { + ExplicitOrdering that = (ExplicitOrdering) object; + return this.rankMap.equals(that.rankMap); + } + return false; + } + + @Override + public int hashCode() { + return rankMap.hashCode(); + } + + @Override + public String toString() { + return "Ordering.explicit(" + rankMap.keySet() + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/FilteredEntryMultimap.java b/sources/main/java/com/google/common/collect/FilteredEntryMultimap.java new file mode 100644 index 0000000..677d5b7 --- /dev/null +++ b/sources/main/java/com/google/common/collect/FilteredEntryMultimap.java @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.in; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import com.google.common.base.Predicate; +import com.google.common.collect.Maps.ImprovedAbstractMap; + +/** + * Implementation of {@link Multimaps#filterEntries(Multimap, Predicate)}. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible +class FilteredEntryMultimap extends AbstractMultimap implements FilteredMultimap { + final Multimap unfiltered; + final Predicate> predicate; + + FilteredEntryMultimap(Multimap unfiltered, Predicate> predicate) { + this.unfiltered = checkNotNull(unfiltered); + this.predicate = checkNotNull(predicate); + } + + @Override + public Multimap unfiltered() { + return unfiltered; + } + + @Override + public Predicate> entryPredicate() { + return predicate; + } + + @Override + public int size() { + return entries().size(); + } + + private boolean satisfies(K key, V value) { + return predicate.apply(Maps.immutableEntry(key, value)); + } + + final class ValuePredicate implements Predicate { + private final K key; + + ValuePredicate(K key) { + this.key = key; + } + + @Override + public boolean apply(@Nullable V value) { + return satisfies(key, value); + } + } + + static Collection filterCollection(Collection collection, Predicate predicate) { + if (collection instanceof Set) { + return Sets.filter((Set) collection, predicate); + } else { + return Collections2.filter(collection, predicate); + } + } + + @Override + public boolean containsKey(@Nullable Object key) { + return asMap().get(key) != null; + } + + @Override + public Collection removeAll(@Nullable Object key) { + return Objects.firstNonNull(asMap().remove(key), unmodifiableEmptyCollection()); + } + + Collection unmodifiableEmptyCollection() { + // These return false, rather than throwing a UOE, on remove calls. + return (unfiltered instanceof SetMultimap) ? Collections.emptySet() : Collections.emptyList(); + } + + @Override + public void clear() { + entries().clear(); + } + + @Override + public Collection get(final K key) { + return filterCollection(unfiltered.get(key), new ValuePredicate(key)); + } + + @Override + Collection> createEntries() { + return filterCollection(unfiltered.entries(), predicate); + } + + @Override + Collection createValues() { + return new FilteredMultimapValues(this); + } + + @Override + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Map> createAsMap() { + return new AsMap(); + } + + @Override + public Set keySet() { + return asMap().keySet(); + } + + boolean removeEntriesIf(Predicate>> predicate) { + Iterator>> entryIterator = unfiltered.asMap().entrySet().iterator(); + boolean changed = false; + while (entryIterator.hasNext()) { + Entry> entry = entryIterator.next(); + K key = entry.getKey(); + Collection collection = filterCollection(entry.getValue(), new ValuePredicate(key)); + if (!collection.isEmpty() && predicate.apply(Maps.immutableEntry(key, collection))) { + if (collection.size() == entry.getValue().size()) { + entryIterator.remove(); + } else { + collection.clear(); + } + changed = true; + } + } + return changed; + } + + class AsMap extends ImprovedAbstractMap> { + @Override + public boolean containsKey(@Nullable Object key) { + return get(key) != null; + } + + @Override + public void clear() { + FilteredEntryMultimap.this.clear(); + } + + @Override + public Collection get(@Nullable Object key) { + Collection result = unfiltered.asMap().get(key); + if (result == null) { + return null; + } + @SuppressWarnings("unchecked") // key is equal to a K, if not a K itself + K k = (K) key; + result = filterCollection(result, new ValuePredicate(k)); + return result.isEmpty() ? null : result; + } + + @Override + public Collection remove(@Nullable Object key) { + Collection collection = unfiltered.asMap().get(key); + if (collection == null) { + return null; + } + @SuppressWarnings("unchecked") // it's definitely equal to a K + K k = (K) key; + List result = Lists.newArrayList(); + Iterator itr = collection.iterator(); + while (itr.hasNext()) { + V v = itr.next(); + if (satisfies(k, v)) { + itr.remove(); + result.add(v); + } + } + if (result.isEmpty()) { + return null; + } else if (unfiltered instanceof SetMultimap) { + return Collections.unmodifiableSet(Sets.newLinkedHashSet(result)); + } else { + return Collections.unmodifiableList(result); + } + } + + @Override + Set createKeySet() { + return new Maps.KeySet>(this) { + @Override + public boolean removeAll(Collection c) { + return removeEntriesIf(Maps.keyPredicateOnEntries(in(c))); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntriesIf(Maps.keyPredicateOnEntries(not(in(c)))); + } + + @Override + public boolean remove(@Nullable Object o) { + return AsMap.this.remove(o) != null; + } + }; + } + + @Override + Set>> createEntrySet() { + return new Maps.EntrySet>() { + @Override + Map> map() { + return AsMap.this; + } + + @Override + public Iterator>> iterator() { + return new AbstractIterator>>() { + final Iterator>> backingIterator = unfiltered.asMap().entrySet() + .iterator(); + + @Override + protected Entry> computeNext() { + while (backingIterator.hasNext()) { + Entry> entry = backingIterator.next(); + K key = entry.getKey(); + Collection collection = filterCollection(entry.getValue(), new ValuePredicate(key)); + if (!collection.isEmpty()) { + return Maps.immutableEntry(key, collection); + } + } + return endOfData(); + } + }; + } + + @Override + public boolean removeAll(Collection c) { + return removeEntriesIf(in(c)); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntriesIf(not(in(c))); + } + + @Override + public int size() { + return Iterators.size(iterator()); + } + }; + } + + @Override + Collection> createValues() { + return new Maps.Values>(AsMap.this) { + @Override + public boolean remove(@Nullable Object o) { + if (o instanceof Collection) { + Collection c = (Collection) o; + Iterator>> entryIterator = unfiltered.asMap().entrySet().iterator(); + while (entryIterator.hasNext()) { + Entry> entry = entryIterator.next(); + K key = entry.getKey(); + Collection collection = filterCollection(entry.getValue(), new ValuePredicate(key)); + if (!collection.isEmpty() && c.equals(collection)) { + if (collection.size() == entry.getValue().size()) { + entryIterator.remove(); + } else { + collection.clear(); + } + return true; + } + } + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + return removeEntriesIf(Maps.>valuePredicateOnEntries(in(c))); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntriesIf(Maps.>valuePredicateOnEntries(not(in(c)))); + } + }; + } + } + + @Override + Multiset createKeys() { + return new Keys(); + } + + class Keys extends Multimaps.Keys { + Keys() { + super(FilteredEntryMultimap.this); + } + + @Override + public int remove(@Nullable Object key, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(key); + } + Collection collection = unfiltered.asMap().get(key); + if (collection == null) { + return 0; + } + @SuppressWarnings("unchecked") // key is equal to a K, if not a K itself + K k = (K) key; + int oldCount = 0; + Iterator itr = collection.iterator(); + while (itr.hasNext()) { + V v = itr.next(); + if (satisfies(k, v)) { + oldCount++; + if (oldCount <= occurrences) { + itr.remove(); + } + } + } + return oldCount; + } + + @Override + public Set> entrySet() { + return new Multisets.EntrySet() { + + @Override + Multiset multiset() { + return Keys.this; + } + + @Override + public Iterator> iterator() { + return Keys.this.entryIterator(); + } + + @Override + public int size() { + return FilteredEntryMultimap.this.keySet().size(); + } + + private boolean removeEntriesIf(final Predicate> predicate) { + return FilteredEntryMultimap.this.removeEntriesIf(new Predicate>>() { + @Override + public boolean apply(Map.Entry> entry) { + return predicate.apply(Multisets.immutableEntry(entry.getKey(), entry.getValue().size())); + } + }); + } + + @Override + public boolean removeAll(Collection c) { + return removeEntriesIf(in(c)); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntriesIf(not(in(c))); + } + }; + } + } +} diff --git a/sources/main/java/com/google/common/collect/FilteredEntrySetMultimap.java b/sources/main/java/com/google/common/collect/FilteredEntrySetMultimap.java new file mode 100644 index 0000000..9bc9016 --- /dev/null +++ b/sources/main/java/com/google/common/collect/FilteredEntrySetMultimap.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map.Entry; +import java.util.Set; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; + +/** + * Implementation of {@link Multimaps#filterEntries(SetMultimap, Predicate)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class FilteredEntrySetMultimap extends FilteredEntryMultimap implements FilteredSetMultimap { + + FilteredEntrySetMultimap(SetMultimap unfiltered, Predicate> predicate) { + super(unfiltered, predicate); + } + + @Override + public SetMultimap unfiltered() { + return (SetMultimap) unfiltered; + } + + @Override + public Set get(K key) { + return (Set) super.get(key); + } + + @Override + public Set removeAll(Object key) { + return (Set) super.removeAll(key); + } + + @Override + public Set replaceValues(K key, Iterable values) { + return (Set) super.replaceValues(key, values); + } + + @Override + Set> createEntries() { + return Sets.filter(unfiltered().entries(), entryPredicate()); + } + + @Override + public Set> entries() { + return (Set>) super.entries(); + } +} diff --git a/sources/main/java/com/google/common/collect/FilteredKeyListMultimap.java b/sources/main/java/com/google/common/collect/FilteredKeyListMultimap.java new file mode 100644 index 0000000..5742c42 --- /dev/null +++ b/sources/main/java/com/google/common/collect/FilteredKeyListMultimap.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; + +/** + * Implementation of {@link Multimaps#filterKeys(ListMultimap, Predicate)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class FilteredKeyListMultimap extends FilteredKeyMultimap implements ListMultimap { + FilteredKeyListMultimap(ListMultimap unfiltered, Predicate keyPredicate) { + super(unfiltered, keyPredicate); + } + + @Override + public ListMultimap unfiltered() { + return (ListMultimap) super.unfiltered(); + } + + @Override + public List get(K key) { + return (List) super.get(key); + } + + @Override + public List removeAll(@Nullable Object key) { + return (List) super.removeAll(key); + } + + @Override + public List replaceValues(K key, Iterable values) { + return (List) super.replaceValues(key, values); + } +} diff --git a/sources/main/java/com/google/common/collect/FilteredKeyMultimap.java b/sources/main/java/com/google/common/collect/FilteredKeyMultimap.java new file mode 100644 index 0000000..c8fa782 --- /dev/null +++ b/sources/main/java/com/google/common/collect/FilteredKeyMultimap.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkPositionIndex; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; + +/** + * Implementation of {@link Multimaps#filterKeys(Multimap, Predicate)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +class FilteredKeyMultimap extends AbstractMultimap implements FilteredMultimap { + final Multimap unfiltered; + final Predicate keyPredicate; + + FilteredKeyMultimap(Multimap unfiltered, Predicate keyPredicate) { + this.unfiltered = checkNotNull(unfiltered); + this.keyPredicate = checkNotNull(keyPredicate); + } + + @Override + public Multimap unfiltered() { + return unfiltered; + } + + @Override + public Predicate> entryPredicate() { + return Maps.keyPredicateOnEntries(keyPredicate); + } + + @Override + public int size() { + int size = 0; + for (Collection collection : asMap().values()) { + size += collection.size(); + } + return size; + } + + @Override + public boolean containsKey(@Nullable Object key) { + if (unfiltered.containsKey(key)) { + @SuppressWarnings("unchecked") // k is equal to a K, if not one itself + K k = (K) key; + return keyPredicate.apply(k); + } + return false; + } + + @Override + public Collection removeAll(Object key) { + return containsKey(key) ? unfiltered.removeAll(key) : unmodifiableEmptyCollection(); + } + + Collection unmodifiableEmptyCollection() { + if (unfiltered instanceof SetMultimap) { + return ImmutableSet.of(); + } else { + return ImmutableList.of(); + } + } + + @Override + public void clear() { + keySet().clear(); + } + + @Override + Set createKeySet() { + return Sets.filter(unfiltered.keySet(), keyPredicate); + } + + @Override + public Collection get(K key) { + if (keyPredicate.apply(key)) { + return unfiltered.get(key); + } else if (unfiltered instanceof SetMultimap) { + return new AddRejectingSet(key); + } else { + return new AddRejectingList(key); + } + } + + static class AddRejectingSet extends ForwardingSet { + final K key; + + AddRejectingSet(K key) { + this.key = key; + } + + @Override + public boolean add(V element) { + throw new IllegalArgumentException("Key does not satisfy predicate: " + key); + } + + @Override + public boolean addAll(Collection collection) { + checkNotNull(collection); + throw new IllegalArgumentException("Key does not satisfy predicate: " + key); + } + + @Override + protected Set delegate() { + return Collections.emptySet(); + } + } + + static class AddRejectingList extends ForwardingList { + final K key; + + AddRejectingList(K key) { + this.key = key; + } + + @Override + public boolean add(V v) { + add(0, v); + return true; + } + + @Override + public boolean addAll(Collection collection) { + addAll(0, collection); + return true; + } + + @Override + public void add(int index, V element) { + checkPositionIndex(index, 0); + throw new IllegalArgumentException("Key does not satisfy predicate: " + key); + } + + @Override + public boolean addAll(int index, Collection elements) { + checkNotNull(elements); + checkPositionIndex(index, 0); + throw new IllegalArgumentException("Key does not satisfy predicate: " + key); + } + + @Override + protected List delegate() { + return Collections.emptyList(); + } + } + + @Override + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Collection> createEntries() { + return new Entries(); + } + + class Entries extends ForwardingCollection> { + @Override + protected Collection> delegate() { + return Collections2.filter(unfiltered.entries(), entryPredicate()); + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(@Nullable Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + if (unfiltered.containsKey(entry.getKey()) + // if this holds, then we know entry.getKey() is a K + && keyPredicate.apply((K) entry.getKey())) { + return unfiltered.remove(entry.getKey(), entry.getValue()); + } + } + return false; + } + } + + @Override + Collection createValues() { + return new FilteredMultimapValues(this); + } + + @Override + Map> createAsMap() { + return Maps.filterKeys(unfiltered.asMap(), keyPredicate); + } + + @Override + Multiset createKeys() { + return Multisets.filter(unfiltered.keys(), keyPredicate); + } +} diff --git a/sources/main/java/com/google/common/collect/FilteredKeySetMultimap.java b/sources/main/java/com/google/common/collect/FilteredKeySetMultimap.java new file mode 100644 index 0000000..8913ac2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/FilteredKeySetMultimap.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; + +/** + * Implementation of {@link Multimaps#filterKeys(SetMultimap, Predicate)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class FilteredKeySetMultimap extends FilteredKeyMultimap implements FilteredSetMultimap { + + FilteredKeySetMultimap(SetMultimap unfiltered, Predicate keyPredicate) { + super(unfiltered, keyPredicate); + } + + @Override + public SetMultimap unfiltered() { + return (SetMultimap) unfiltered; + } + + @Override + public Set get(K key) { + return (Set) super.get(key); + } + + @Override + public Set removeAll(Object key) { + return (Set) super.removeAll(key); + } + + @Override + public Set replaceValues(K key, Iterable values) { + return (Set) super.replaceValues(key, values); + } + + @Override + public Set> entries() { + return (Set>) super.entries(); + } + + @Override + Set> createEntries() { + return new EntrySet(); + } + + class EntrySet extends Entries implements Set> { + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @Override + public boolean equals(@Nullable Object o) { + return Sets.equalsImpl(this, o); + } + } +} diff --git a/sources/main/java/com/google/common/collect/FilteredMultimap.java b/sources/main/java/com/google/common/collect/FilteredMultimap.java new file mode 100644 index 0000000..17a6abd --- /dev/null +++ b/sources/main/java/com/google/common/collect/FilteredMultimap.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map.Entry; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; + +/** + * An interface for all filtered multimap types. + * + * @author Louis Wasserman + */ +@GwtCompatible +interface FilteredMultimap extends Multimap { + Multimap unfiltered(); + + Predicate> entryPredicate(); +} diff --git a/sources/main/java/com/google/common/collect/FilteredMultimapValues.java b/sources/main/java/com/google/common/collect/FilteredMultimapValues.java new file mode 100644 index 0000000..8e8af6b --- /dev/null +++ b/sources/main/java/com/google/common/collect/FilteredMultimapValues.java @@ -0,0 +1,94 @@ +/* + * 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 com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +/** + * Implementation for {@link FilteredMultimap#values()}. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class FilteredMultimapValues extends AbstractCollection { + private final FilteredMultimap multimap; + + FilteredMultimapValues(FilteredMultimap multimap) { + this.multimap = checkNotNull(multimap); + } + + @Override + public Iterator iterator() { + return Maps.valueIterator(multimap.entries().iterator()); + } + + @Override + public boolean contains(@Nullable Object o) { + return multimap.containsValue(o); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + public boolean remove(@Nullable Object o) { + Predicate> entryPredicate = multimap.entryPredicate(); + for (Iterator> unfilteredItr = multimap.unfiltered().entries().iterator(); unfilteredItr + .hasNext();) { + Map.Entry entry = unfilteredItr.next(); + if (entryPredicate.apply(entry) && Objects.equal(entry.getValue(), o)) { + unfilteredItr.remove(); + return true; + } + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + return Iterables.removeIf(multimap.unfiltered().entries(), + // explicit > is required to build with JDK6 + Predicates.>and(multimap.entryPredicate(), + Maps.valuePredicateOnEntries(Predicates.in(c)))); + } + + @Override + public boolean retainAll(Collection c) { + return Iterables.removeIf(multimap.unfiltered().entries(), + // explicit > is required to build with JDK6 + Predicates.>and(multimap.entryPredicate(), + Maps.valuePredicateOnEntries(Predicates.not(Predicates.in(c))))); + } + + @Override + public void clear() { + multimap.clear(); + } +} diff --git a/sources/main/java/com/google/common/collect/FilteredSetMultimap.java b/sources/main/java/com/google/common/collect/FilteredSetMultimap.java new file mode 100644 index 0000000..cfa921e --- /dev/null +++ b/sources/main/java/com/google/common/collect/FilteredSetMultimap.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * A supertype for filtered {@link SetMultimap} implementations. + * + * @author Louis Wasserman + */ +@GwtCompatible +interface FilteredSetMultimap extends FilteredMultimap, SetMultimap { + @Override + SetMultimap unfiltered(); +} diff --git a/sources/main/java/com/google/common/collect/FluentIterable.java b/sources/main/java/com/google/common/collect/FluentIterable.java new file mode 100644 index 0000000..12860b9 --- /dev/null +++ b/sources/main/java/com/google/common/collect/FluentIterable.java @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; + +/** + * {@code FluentIterable} provides a rich interface for manipulating + * {@code Iterable} instances in a chained fashion. A {@code FluentIterable} can + * be created from an {@code Iterable}, or from a set of elements. The following + * types of methods are provided on {@code FluentIterable}: + *

    + *
  • chained methods which return a new {@code FluentIterable} based in some + * way on the contents of the current one (for example {@link #transform}) + *
  • conversion methods which copy the {@code FluentIterable}'s contents into + * a new collection or array (for example {@link #toList}) + *
  • element extraction methods which facilitate the retrieval of certain + * elements (for example {@link #last}) + *
  • query methods which answer questions about the {@code FluentIterable}'s + * contents (for example {@link #anyMatch}) + *
+ * + *

+ * Here is an example that merges the lists returned by two separate database + * calls, transforms it by invoking {@code toString()} on each element, and + * returns the first 10 elements as an {@code ImmutableList}: + * + *

+ *    {@code
+ *
+ *   FluentIterable
+ *       .from(database.getClientList())
+ *       .filter(activeInLastMonth())
+ *       .transform(Functions.toStringFunction())
+ *       .limit(10)
+ *       .toList();}
+ * 
+ * + *

+ * Anything which can be done using {@code FluentIterable} could be done in a + * different fashion (often with {@link Iterables}), however the use of + * {@code FluentIterable} makes many sets of operations significantly more + * concise. + * + * @author Marcin Mikosik + * @since 12.0 + */ +@GwtCompatible(emulated = true) +public abstract class FluentIterable implements Iterable { + // We store 'iterable' and use it instead of 'this' to allow Iterables to + // perform instanceof + // checks on the _original_ iterable when FluentIterable.from is used. + private final Iterable iterable; + + /** Constructor for use by subclasses. */ + protected FluentIterable() { + this.iterable = this; + } + + FluentIterable(Iterable iterable) { + this.iterable = checkNotNull(iterable); + } + + /** + * Returns a fluent iterable that wraps {@code iterable}, or {@code iterable} + * itself if it is already a {@code FluentIterable}. + */ + public static FluentIterable from(final Iterable iterable) { + return (iterable instanceof FluentIterable) ? (FluentIterable) iterable : new FluentIterable(iterable) { + @Override + public Iterator iterator() { + return iterable.iterator(); + } + }; + } + + /** + * Construct a fluent iterable from another fluent iterable. This is obviously + * never necessary, but is intended to help call out cases where one migration + * from {@code Iterable} to {@code FluentIterable} has obviated the need to + * explicitly convert to a {@code FluentIterable}. + * + * @deprecated instances of {@code FluentIterable} don't need to be converted to + * {@code FluentIterable} + */ + @Deprecated + public static FluentIterable from(FluentIterable iterable) { + return checkNotNull(iterable); + } + + /** + * Returns a string representation of this fluent iterable, with the format + * {@code [e1, e2, ..., en]}. + */ + @Override + public String toString() { + return Iterables.toString(iterable); + } + + /** + * Returns the number of elements in this fluent iterable. + */ + public final int size() { + return Iterables.size(iterable); + } + + /** + * Returns {@code true} if this fluent iterable contains any object for which + * {@code equals(element)} is true. + */ + public final boolean contains(@Nullable Object element) { + return Iterables.contains(iterable, element); + } + + /** + * Returns a fluent iterable whose {@code Iterator} cycles indefinitely over the + * elements of this fluent iterable. + * + *

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

+ * 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. + */ + @CheckReturnValue + public final FluentIterable cycle() { + return from(Iterables.cycle(iterable)); + } + + /** + * Returns the elements from this fluent iterable that satisfy a predicate. The + * resulting fluent iterable's iterator does not support {@code remove()}. + */ + @CheckReturnValue + public final FluentIterable filter(Predicate predicate) { + return from(Iterables.filter(iterable, predicate)); + } + + /** + * Returns the elements from this fluent iterable that are instances of class + * {@code type}. + * + * @param type the type of elements desired + */ + @GwtIncompatible("Class.isInstance") + @CheckReturnValue + public final FluentIterable filter(Class type) { + return from(Iterables.filter(iterable, type)); + } + + /** + * Returns {@code true} if any element in this fluent iterable satisfies the + * predicate. + */ + public final boolean anyMatch(Predicate predicate) { + return Iterables.any(iterable, predicate); + } + + /** + * Returns {@code true} if every element in this fluent iterable satisfies the + * predicate. If this fluent iterable is empty, {@code true} is returned. + */ + public final boolean allMatch(Predicate predicate) { + return Iterables.all(iterable, predicate); + } + + /** + * Returns an {@link Optional} containing the first element in this fluent + * iterable that satisfies the given predicate, if such an element exists. + * + *

+ * Warning: avoid using a {@code predicate} that matches {@code null}. If + * {@code null} is matched in this fluent iterable, a + * {@link NullPointerException} will be thrown. + */ + public final Optional firstMatch(Predicate predicate) { + return Iterables.tryFind(iterable, predicate); + } + + /** + * Returns a fluent iterable that applies {@code function} to each element of + * this fluent iterable. + * + *

+ * The returned fluent iterable's iterator supports {@code remove()} if this + * iterable's iterator does. After a successful {@code remove()} call, this + * fluent iterable no longer contains the corresponding element. + */ + public final FluentIterable transform(Function function) { + return from(Iterables.transform(iterable, function)); + } + + /** + * Applies {@code function} to each element of this fluent iterable and returns + * a fluent iterable with the concatenated combination of results. + * {@code function} returns an Iterable of results. + * + *

+ * The returned fluent iterable's iterator supports {@code remove()} if this + * function-returned iterables' iterator does. After a successful + * {@code remove()} call, the returned fluent iterable no longer contains the + * corresponding element. + * + * @since 13.0 (required {@code Function>} until 14.0) + */ + public FluentIterable transformAndConcat(Function> function) { + return from(Iterables.concat(transform(function))); + } + + /** + * Returns an {@link Optional} containing the first element in this fluent + * iterable. If the iterable is empty, {@code Optional.absent()} is returned. + * + * @throws NullPointerException if the first element is null; if this is a + * possibility, use {@code iterator().next()} or + * {@link Iterables#getFirst} instead. + */ + public final Optional first() { + Iterator iterator = iterable.iterator(); + return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.absent(); + } + + /** + * Returns an {@link Optional} containing the last element in this fluent + * iterable. If the iterable is empty, {@code Optional.absent()} is returned. + * + * @throws NullPointerException if the last element is null; if this is a + * possibility, use {@link Iterables#getLast} + * instead. + */ + public final Optional last() { + // Iterables#getLast was inlined here so we don't have to throw/catch a NSEE + + // TODO(kevinb): Support a concurrently modified collection? + if (iterable instanceof List) { + List list = (List) iterable; + if (list.isEmpty()) { + return Optional.absent(); + } + return Optional.of(list.get(list.size() - 1)); + } + Iterator iterator = iterable.iterator(); + if (!iterator.hasNext()) { + return Optional.absent(); + } + + /* + * TODO(kevinb): consider whether this "optimization" is worthwhile. Users with + * SortedSets tend to know they are SortedSets and probably would not call this + * method. + */ + if (iterable instanceof SortedSet) { + SortedSet sortedSet = (SortedSet) iterable; + return Optional.of(sortedSet.last()); + } + + while (true) { + E current = iterator.next(); + if (!iterator.hasNext()) { + return Optional.of(current); + } + } + } + + /** + * Returns a view of this fluent iterable that skips its first + * {@code numberToSkip} elements. If this fluent iterable contains fewer than + * {@code numberToSkip} elements, the returned fluent iterable skips all of its + * elements. + * + *

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

+ * The returned fluent iterable's iterator supports {@code remove()} if the + * {@code Iterator} of this fluent iterable supports it. Note that it is + * not possible to delete the last skipped element by immediately calling + * {@code remove()} on the returned fluent iterable's iterator, as the + * {@code Iterator} contract states that a call to {@code * remove()} before a + * call to {@code next()} will throw an {@link IllegalStateException}. + */ + @CheckReturnValue + public final FluentIterable skip(int numberToSkip) { + return from(Iterables.skip(iterable, numberToSkip)); + } + + /** + * Creates a fluent iterable with the first {@code size} elements of this fluent + * iterable. If this fluent iterable does not contain that many elements, the + * returned fluent iterable will have the same behavior as this fluent iterable. + * The returned fluent iterable's iterator supports {@code remove()} if this + * fluent iterable's iterator does. + * + * @param size the maximum number of elements in the returned fluent iterable + * @throws IllegalArgumentException if {@code size} is negative + */ + @CheckReturnValue + public final FluentIterable limit(int size) { + return from(Iterables.limit(iterable, size)); + } + + /** + * Determines whether this fluent iterable is empty. + */ + public final boolean isEmpty() { + return !iterable.iterator().hasNext(); + } + + /** + * Returns an {@code ImmutableList} containing all of the elements from this + * fluent iterable in proper sequence. + * + * @since 14.0 (since 12.0 as {@code toImmutableList()}). + */ + public final ImmutableList toList() { + return ImmutableList.copyOf(iterable); + } + + /** + * Returns an {@code ImmutableList} containing all of the elements from this + * {@code + * FluentIterable} in the order specified by {@code comparator}. To produce an + * {@code + * ImmutableList} sorted by its natural ordering, use + * {@code toSortedList(Ordering.natural())}. + * + * @param comparator the function by which to sort list elements + * @throws NullPointerException if any element is null + * @since 14.0 (since 13.0 as {@code toSortedImmutableList()}). + */ + @Beta + public final ImmutableList toSortedList(Comparator comparator) { + return Ordering.from(comparator).immutableSortedCopy(iterable); + } + + /** + * Returns an {@code ImmutableSet} containing all of the elements from this + * fluent iterable with duplicates removed. + * + * @since 14.0 (since 12.0 as {@code toImmutableSet()}). + */ + public final ImmutableSet toSet() { + return ImmutableSet.copyOf(iterable); + } + + /** + * Returns an {@code ImmutableSortedSet} containing all of the elements from + * this {@code + * FluentIterable} in the order specified by {@code comparator}, with duplicates + * (determined by {@code comparator.compare(x, y) == 0}) removed. To produce an + * {@code ImmutableSortedSet} sorted by its natural ordering, use + * {@code toSortedSet(Ordering.natural())}. + * + * @param comparator the function by which to sort set elements + * @throws NullPointerException if any element is null + * @since 14.0 (since 12.0 as {@code toImmutableSortedSet()}). + */ + public final ImmutableSortedSet toSortedSet(Comparator comparator) { + return ImmutableSortedSet.copyOf(comparator, iterable); + } + + /** + * Returns an immutable map for which the elements of this + * {@code FluentIterable} are the keys in the same order, mapped to values by + * the given function. If this iterable contains duplicate elements, the + * returned map will contain each distinct element once in the order it first + * appears. + * + * @throws NullPointerException if any element of this iterable is {@code null}, + * or if {@code + * valueFunction} produces {@code null} for any key + * @since 14.0 + */ + public final ImmutableMap toMap(Function valueFunction) { + return Maps.toMap(iterable, valueFunction); + } + + /** + * Creates an index {@code ImmutableListMultimap} that contains the results of + * applying a specified function to each item in this {@code FluentIterable} of + * values. Each element of this iterable will be stored as a value in the + * resulting multimap, yielding a multimap with the same size as this iterable. + * The key used to store that value in the multimap will be the result of + * calling the function on that value. The resulting multimap is created as an + * immutable snapshot. In the returned multimap, keys appear in the order they + * are first encountered, and the values corresponding to each key appear in the + * same order as they are encountered. + * + * @param keyFunction the function used to produce the key for each value + * @throws NullPointerException if any of the following cases is true: + *

    + *
  • {@code keyFunction} is null + *
  • An element in this fluent iterable is null + *
  • {@code keyFunction} returns {@code null} for + * any element of this iterable + *
+ * @since 14.0 + */ + public final ImmutableListMultimap index(Function keyFunction) { + return Multimaps.index(iterable, keyFunction); + } + + /** + * Returns an immutable map for which the {@link java.util.Map#values} are the + * elements of this {@code FluentIterable} in the given order, and each key is + * the product of invoking a supplied function on its corresponding value. + * + * @param keyFunction the function used to produce the key for each value + * @throws IllegalArgumentException if {@code keyFunction} produces the same key + * for more than one value in this fluent + * iterable + * @throws NullPointerException if any element of this fluent iterable is + * null, or if {@code keyFunction} produces + * {@code null} for any value + * @since 14.0 + */ + public final ImmutableMap uniqueIndex(Function keyFunction) { + return Maps.uniqueIndex(iterable, keyFunction); + } + + /** + * Returns an array containing all of the elements from this fluent iterable in + * iteration order. + * + * @param type the type of the elements + * @return a newly-allocated array into which all the elements of this fluent + * iterable have been copied + */ + @GwtIncompatible("Array.newArray(Class, int)") + public final E[] toArray(Class type) { + return Iterables.toArray(iterable, type); + } + + /** + * Copies all the elements from this fluent iterable to {@code collection}. This + * is equivalent to calling {@code Iterables.addAll(collection, this)}. + * + * @param collection the collection to copy elements to + * @return {@code collection}, for convenience + * @since 14.0 + */ + public final > C copyInto(C collection) { + checkNotNull(collection); + if (iterable instanceof Collection) { + collection.addAll(Collections2.cast(iterable)); + } else { + for (E item : iterable) { + collection.add(item); + } + } + return collection; + } + + /** + * Returns the element at the specified position in this fluent iterable. + * + * @param position position of the element to return + * @return the element at the specified position in this fluent iterable + * @throws IndexOutOfBoundsException if {@code position} is negative or greater + * than or equal to the size of this fluent + * iterable + */ + public final E get(int position) { + return Iterables.get(iterable, position); + } + + /** + * Function that transforms {@code Iterable} into a fluent iterable. + */ + private static class FromIterableFunction implements Function, FluentIterable> { + @Override + public FluentIterable apply(Iterable fromObject) { + return FluentIterable.from(fromObject); + } + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingCollection.java b/sources/main/java/com/google/common/collect/ForwardingCollection.java new file mode 100644 index 0000000..7377087 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingCollection.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Iterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * 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) + */ +@GwtCompatible +public 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(@Nullable 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 Collections2.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(@Nullable Object object) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + if (Objects.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 Collections2.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/sources/main/java/com/google/common/collect/ForwardingDeque.java b/sources/main/java/com/google/common/collect/ForwardingDeque.java new file mode 100644 index 0000000..3145c1d --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingDeque.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Deque; +import java.util.Iterator; + +/** + * A deque which forwards all its method calls to another deque. Subclasses + * should override one or more methods to modify the behavior of the backing + * deque as desired per the + * decorator + * pattern. + * + *

+ * Warning: The methods of {@code ForwardingDeque} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #add} alone will not change the behavior of + * {@link #offer} which can lead to unexpected behavior. In this case, you + * should override {@code offer} as well. + * + * @author Kurt Alfred Kluever + * @since 12.0 + */ +public abstract class ForwardingDeque extends ForwardingQueue implements Deque { + + /** Constructor for use by subclasses. */ + protected ForwardingDeque() { + } + + @Override + protected abstract Deque delegate(); + + @Override + public void addFirst(E e) { + delegate().addFirst(e); + } + + @Override + public void addLast(E e) { + delegate().addLast(e); + } + + @Override + public Iterator descendingIterator() { + return delegate().descendingIterator(); + } + + @Override + public E getFirst() { + return delegate().getFirst(); + } + + @Override + public E getLast() { + return delegate().getLast(); + } + + @Override + public boolean offerFirst(E e) { + return delegate().offerFirst(e); + } + + @Override + public boolean offerLast(E e) { + return delegate().offerLast(e); + } + + @Override + public E peekFirst() { + return delegate().peekFirst(); + } + + @Override + public E peekLast() { + return delegate().peekLast(); + } + + @Override + public E pollFirst() { + return delegate().pollFirst(); + } + + @Override + public E pollLast() { + return delegate().pollLast(); + } + + @Override + public E pop() { + return delegate().pop(); + } + + @Override + public void push(E e) { + delegate().push(e); + } + + @Override + public E removeFirst() { + return delegate().removeFirst(); + } + + @Override + public E removeLast() { + return delegate().removeLast(); + } + + @Override + public boolean removeFirstOccurrence(Object o) { + return delegate().removeFirstOccurrence(o); + } + + @Override + public boolean removeLastOccurrence(Object o) { + return delegate().removeLastOccurrence(o); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingImmutableCollection.java b/sources/main/java/com/google/common/collect/ForwardingImmutableCollection.java new file mode 100644 index 0000000..e48fcbc --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingImmutableCollection.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * Dummy class that makes the GWT serialization policy happy. It isn't used on + * the server-side. + * + * @author Hayward Chan + */ +@GwtCompatible(emulated = true) +class ForwardingImmutableCollection { + private ForwardingImmutableCollection() { + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingImmutableList.java b/sources/main/java/com/google/common/collect/ForwardingImmutableList.java new file mode 100644 index 0000000..209e2f0 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingImmutableList.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * Unused stub class, unreferenced under Java and manually emulated under GWT. + * + * @author Chris Povirk + */ +@GwtCompatible(emulated = true) +abstract class ForwardingImmutableList { + private ForwardingImmutableList() { + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingImmutableMap.java b/sources/main/java/com/google/common/collect/ForwardingImmutableMap.java new file mode 100644 index 0000000..8c30710 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingImmutableMap.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * Unused stub class, unreferenced under Java and manually emulated under GWT. + * + * @author Chris Povirk + */ +@GwtCompatible(emulated = true) +abstract class ForwardingImmutableMap { + private ForwardingImmutableMap() { + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingImmutableSet.java b/sources/main/java/com/google/common/collect/ForwardingImmutableSet.java new file mode 100644 index 0000000..f0366a9 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingImmutableSet.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +/** + * Unused stub class, unreferenced under Java and manually emulated under GWT. + * + * @author Chris Povirk + */ +@GwtCompatible(emulated = true) +abstract class ForwardingImmutableSet { + private ForwardingImmutableSet() { + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingIterator.java b/sources/main/java/com/google/common/collect/ForwardingIterator.java new file mode 100644 index 0000000..f2e6d14 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingIterator.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Iterator; + +import com.google.common.annotations.GwtCompatible; + +/** + * An iterator which forwards all its method calls to another iterator. + * Subclasses should override one or more methods to modify the behavior of the + * backing iterator as desired per the + * decorator + * pattern. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class ForwardingIterator extends ForwardingObject implements Iterator { + + /** Constructor for use by subclasses. */ + protected ForwardingIterator() { + } + + @Override + protected abstract Iterator delegate(); + + @Override + public boolean hasNext() { + return delegate().hasNext(); + } + + @Override + public T next() { + return delegate().next(); + } + + @Override + public void remove() { + delegate().remove(); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingList.java b/sources/main/java/com/google/common/collect/ForwardingList.java new file mode 100644 index 0000000..e1cdd3f --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingList.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A list which forwards all its method calls to another list. Subclasses should + * override one or more methods to modify the behavior of the backing list as + * desired per the + * decorator + * pattern. + * + *

+ * This class does not implement {@link net.lax1dude.eaglercraft.v1_8.RandomAccess}. If the delegate + * supports random access, the {@code ForwardingList} subclass should implement + * the {@code RandomAccess} interface. + * + *

+ * Warning: The methods of {@code ForwardingList} 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 and any 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 Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class ForwardingList extends ForwardingCollection implements List { + // TODO(user): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingList() { + } + + @Override + protected abstract List delegate(); + + @Override + public void add(int index, E element) { + delegate().add(index, element); + } + + @Override + public boolean addAll(int index, Collection elements) { + return delegate().addAll(index, elements); + } + + @Override + public E get(int index) { + return delegate().get(index); + } + + @Override + public int indexOf(Object element) { + return delegate().indexOf(element); + } + + @Override + public int lastIndexOf(Object element) { + return delegate().lastIndexOf(element); + } + + @Override + public ListIterator listIterator() { + return delegate().listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + return delegate().listIterator(index); + } + + @Override + public E remove(int index) { + return delegate().remove(index); + } + + @Override + public E set(int index, E element) { + return delegate().set(index, element); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return delegate().subList(fromIndex, toIndex); + } + + @Override + public boolean equals(@Nullable Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible default implementation of {@link #add(Object)}, in terms of + * {@link #add(int, Object)}. If you override {@link #add(int, Object)}, you may + * wish to override {@link #add(Object)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardAdd(E element) { + add(size(), element); + return true; + } + + /** + * A sensible default implementation of {@link #addAll(int, Collection)}, in + * terms of the {@code add} method of {@link #listIterator(int)}. If you + * override {@link #listIterator(int)}, you may wish to override + * {@link #addAll(int, Collection)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardAddAll(int index, Iterable elements) { + return Lists.addAllImpl(this, index, elements); + } + + /** + * A sensible default implementation of {@link #indexOf}, in terms of + * {@link #listIterator()}. If you override {@link #listIterator()}, you may + * wish to override {@link #indexOf} to forward to this implementation. + * + * @since 7.0 + */ + protected int standardIndexOf(@Nullable Object element) { + return Lists.indexOfImpl(this, element); + } + + /** + * A sensible default implementation of {@link #lastIndexOf}, in terms of + * {@link #listIterator(int)}. If you override {@link #listIterator(int)}, you + * may wish to override {@link #lastIndexOf} to forward to this implementation. + * + * @since 7.0 + */ + protected int standardLastIndexOf(@Nullable Object element) { + return Lists.lastIndexOfImpl(this, element); + } + + /** + * A sensible default implementation of {@link #iterator}, in terms of + * {@link #listIterator()}. If you override {@link #listIterator()}, you may + * wish to override {@link #iterator} to forward to this implementation. + * + * @since 7.0 + */ + protected Iterator standardIterator() { + return listIterator(); + } + + /** + * A sensible default implementation of {@link #listIterator()}, in terms of + * {@link #listIterator(int)}. If you override {@link #listIterator(int)}, you + * may wish to override {@link #listIterator()} to forward to this + * implementation. + * + * @since 7.0 + */ + protected ListIterator standardListIterator() { + return listIterator(0); + } + + /** + * A sensible default implementation of {@link #listIterator(int)}, in terms of + * {@link #size}, {@link #get(int)}, {@link #set(int, Object)}, + * {@link #add(int, Object)}, and {@link #remove(int)}. If you override any of + * these methods, you may wish to override {@link #listIterator(int)} to forward + * to this implementation. + * + * @since 7.0 + */ + @Beta + protected ListIterator standardListIterator(int start) { + return Lists.listIteratorImpl(this, start); + } + + /** + * A sensible default implementation of {@link #subList(int, int)}. If you + * override any other methods, you may wish to override + * {@link #subList(int, int)} to forward to this implementation. + * + * @since 7.0 + */ + @Beta + protected List standardSubList(int fromIndex, int toIndex) { + return Lists.subListImpl(this, fromIndex, toIndex); + } + + /** + * A sensible definition of {@link #equals(Object)} in terms of {@link #size} + * and {@link #iterator}. If you override either of those methods, you may wish + * to override {@link #equals(Object)} to forward to this implementation. + * + * @since 7.0 + */ + @Beta + protected boolean standardEquals(@Nullable Object object) { + return Lists.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 #hashCode} to + * forward to this implementation. + * + * @since 7.0 + */ + @Beta + protected int standardHashCode() { + return Lists.hashCodeImpl(this); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingListIterator.java b/sources/main/java/com/google/common/collect/ForwardingListIterator.java new file mode 100644 index 0000000..bde0cf6 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingListIterator.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 com.google.common.collect; + +import java.util.ListIterator; + +import com.google.common.annotations.GwtCompatible; + +/** + * A list iterator which forwards all its method calls to another list iterator. + * Subclasses should override one or more methods to modify the behavior of the + * backing iterator as desired per the + * decorator + * pattern. + * + * @author Mike Bostock + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class ForwardingListIterator extends ForwardingIterator implements ListIterator { + + /** Constructor for use by subclasses. */ + protected ForwardingListIterator() { + } + + @Override + protected abstract ListIterator delegate(); + + @Override + public void add(E element) { + delegate().add(element); + } + + @Override + public boolean hasPrevious() { + return delegate().hasPrevious(); + } + + @Override + public int nextIndex() { + return delegate().nextIndex(); + } + + @Override + public E previous() { + return delegate().previous(); + } + + @Override + public int previousIndex() { + return delegate().previousIndex(); + } + + @Override + public void set(E element) { + delegate().set(element); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingListMultimap.java b/sources/main/java/com/google/common/collect/ForwardingListMultimap.java new file mode 100644 index 0000000..e2427cb --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingListMultimap.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A list multimap which forwards all its method calls to another list multimap. + * Subclasses should override one or more methods to modify the behavior of the + * backing multimap as desired per the + * decorator + * pattern. + * + * @author Kurt Alfred Kluever + * @since 3.0 + */ +@GwtCompatible +public abstract class ForwardingListMultimap extends ForwardingMultimap implements ListMultimap { + + /** Constructor for use by subclasses. */ + protected ForwardingListMultimap() { + } + + @Override + protected abstract ListMultimap delegate(); + + @Override + public List get(@Nullable K key) { + return delegate().get(key); + } + + @Override + public List removeAll(@Nullable Object key) { + return delegate().removeAll(key); + } + + @Override + public List replaceValues(K key, Iterable values) { + return delegate().replaceValues(key, values); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingMap.java b/sources/main/java/com/google/common/collect/ForwardingMap.java new file mode 100644 index 0000000..9815653 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingMap.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * 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) + */ +@GwtCompatible +public 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(@Nullable Object key) { + return delegate().containsKey(key); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return delegate().containsValue(value); + } + + @Override + public V get(@Nullable 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(@Nullable 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(@Nullable 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(@Nullable 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(@Nullable 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(@Nullable 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/sources/main/java/com/google/common/collect/ForwardingMapEntry.java b/sources/main/java/com/google/common/collect/ForwardingMapEntry.java new file mode 100644 index 0000000..ecaba0b --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingMapEntry.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * 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) + */ +@GwtCompatible +public 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(@Nullable 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(@Nullable Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Objects.equal(this.getKey(), that.getKey()) && Objects.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/sources/main/java/com/google/common/collect/ForwardingMultimap.java b/sources/main/java/com/google/common/collect/ForwardingMultimap.java new file mode 100644 index 0000000..7dda3bf --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingMultimap.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A multimap which forwards all its method calls to another multimap. + * Subclasses should override one or more methods to modify the behavior of the + * backing multimap as desired per the + * decorator + * pattern. + * + * @author Robert Konigsberg + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class ForwardingMultimap extends ForwardingObject implements Multimap { + + /** Constructor for use by subclasses. */ + protected ForwardingMultimap() { + } + + @Override + protected abstract Multimap delegate(); + + @Override + public Map> asMap() { + return delegate().asMap(); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { + return delegate().containsEntry(key, value); + } + + @Override + public boolean containsKey(@Nullable Object key) { + return delegate().containsKey(key); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return delegate().containsValue(value); + } + + @Override + public Collection> entries() { + return delegate().entries(); + } + + @Override + public Collection get(@Nullable K key) { + return delegate().get(key); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public Multiset keys() { + return delegate().keys(); + } + + @Override + public Set keySet() { + return delegate().keySet(); + } + + @Override + public boolean put(K key, V value) { + return delegate().put(key, value); + } + + @Override + public boolean putAll(K key, Iterable values) { + return delegate().putAll(key, values); + } + + @Override + public boolean putAll(Multimap multimap) { + return delegate().putAll(multimap); + } + + @Override + public boolean remove(@Nullable Object key, @Nullable Object value) { + return delegate().remove(key, value); + } + + @Override + public Collection removeAll(@Nullable Object key) { + return delegate().removeAll(key); + } + + @Override + public Collection replaceValues(K key, Iterable values) { + return delegate().replaceValues(key, values); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override + public Collection values() { + return delegate().values(); + } + + @Override + public boolean equals(@Nullable Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingMultiset.java b/sources/main/java/com/google/common/collect/ForwardingMultiset.java new file mode 100644 index 0000000..70a2000 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingMultiset.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * A multiset which forwards all its method calls to another multiset. + * Subclasses should override one or more methods to modify the behavior of the + * backing multiset as desired per the + * decorator + * pattern. + * + *

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

+ * The {@code standard} methods and any 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 Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class ForwardingMultiset extends ForwardingCollection implements Multiset { + + /** Constructor for use by subclasses. */ + protected ForwardingMultiset() { + } + + @Override + protected abstract Multiset delegate(); + + @Override + public int count(Object element) { + return delegate().count(element); + } + + @Override + public int add(E element, int occurrences) { + return delegate().add(element, occurrences); + } + + @Override + public int remove(Object element, int occurrences) { + return delegate().remove(element, occurrences); + } + + @Override + public Set elementSet() { + return delegate().elementSet(); + } + + @Override + public Set> entrySet() { + return delegate().entrySet(); + } + + @Override + public boolean equals(@Nullable Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + + @Override + public int setCount(E element, int count) { + return delegate().setCount(element, count); + } + + @Override + public boolean setCount(E element, int oldCount, int newCount) { + return delegate().setCount(element, oldCount, newCount); + } + + /** + * A sensible definition of {@link #contains} in terms of {@link #count}. If you + * override {@link #count}, you may wish to override {@link #contains} to + * forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardContains(@Nullable Object object) { + return count(object) > 0; + } + + /** + * A sensible definition of {@link #clear} in terms of the {@code iterator} + * method of {@link #entrySet}. If you override {@link #entrySet}, you may wish + * to override {@link #clear} to forward to this implementation. + * + * @since 7.0 + */ + @Override + protected void standardClear() { + Iterators.clear(entrySet().iterator()); + } + + /** + * A sensible, albeit inefficient, definition of {@link #count} in terms of + * {@link #entrySet}. If you override {@link #entrySet}, you may wish to + * override {@link #count} to forward to this implementation. + * + * @since 7.0 + */ + @Beta + protected int standardCount(@Nullable Object object) { + for (Entry entry : this.entrySet()) { + if (Objects.equal(entry.getElement(), object)) { + return entry.getCount(); + } + } + return 0; + } + + /** + * A sensible definition of {@link #add(Object)} in terms of + * {@link #add(Object, int)}. If you override {@link #add(Object, int)}, you may + * wish to override {@link #add(Object)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardAdd(E element) { + add(element, 1); + return true; + } + + /** + * A sensible definition of {@link #addAll(Collection)} in terms of + * {@link #add(Object)} and {@link #add(Object, int)}. If you override either of + * these methods, you may wish to override {@link #addAll(Collection)} to + * forward to this implementation. + * + * @since 7.0 + */ + @Beta + @Override + protected boolean standardAddAll(Collection elementsToAdd) { + return Multisets.addAllImpl(this, elementsToAdd); + } + + /** + * A sensible definition of {@link #remove(Object)} in terms of + * {@link #remove(Object, int)}. If you override {@link #remove(Object, int)}, + * you may wish to override {@link #remove(Object)} to forward to this + * implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardRemove(Object element) { + return remove(element, 1) > 0; + } + + /** + * A sensible definition of {@link #removeAll} in terms of the {@code + * removeAll} method of {@link #elementSet}. If you override + * {@link #elementSet}, you may wish to override {@link #removeAll} to forward + * to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardRemoveAll(Collection elementsToRemove) { + return Multisets.removeAllImpl(this, elementsToRemove); + } + + /** + * A sensible definition of {@link #retainAll} in terms of the {@code + * retainAll} method of {@link #elementSet}. If you override + * {@link #elementSet}, you may wish to override {@link #retainAll} to forward + * to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardRetainAll(Collection elementsToRetain) { + return Multisets.retainAllImpl(this, elementsToRetain); + } + + /** + * A sensible definition of {@link #setCount(Object, int)} in terms of + * {@link #count(Object)}, {@link #add(Object, int)}, and + * {@link #remove(Object, int)}. {@link #entrySet()}. If you override any of + * these methods, you may wish to override {@link #setCount(Object, int)} to + * forward to this implementation. + * + * @since 7.0 + */ + protected int standardSetCount(E element, int count) { + return Multisets.setCountImpl(this, element, count); + } + + /** + * A sensible definition of {@link #setCount(Object, int, int)} in terms of + * {@link #count(Object)} and {@link #setCount(Object, int)}. If you override + * either of these methods, you may wish to override + * {@link #setCount(Object, int, int)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardSetCount(E element, int oldCount, int newCount) { + return Multisets.setCountImpl(this, element, oldCount, newCount); + } + + /** + * A sensible implementation of {@link Multiset#elementSet} in terms of the + * following methods: {@link ForwardingMultiset#clear}, + * {@link ForwardingMultiset#contains}, {@link ForwardingMultiset#containsAll}, + * {@link ForwardingMultiset#count}, {@link ForwardingMultiset#isEmpty}, the + * {@link Set#size} and {@link Set#iterator} methods of + * {@link ForwardingMultiset#entrySet}, and + * {@link ForwardingMultiset#remove(Object, int)}. In many situations, you may + * wish to override {@link ForwardingMultiset#elementSet} to forward to this + * implementation or a subclass thereof. + * + * @since 10.0 + */ + @Beta + protected class StandardElementSet extends Multisets.ElementSet { + /** Constructor for use by subclasses. */ + public StandardElementSet() { + } + + @Override + Multiset multiset() { + return ForwardingMultiset.this; + } + } + + /** + * A sensible definition of {@link #iterator} in terms of {@link #entrySet} and + * {@link #remove(Object)}. If you override either of these methods, you may + * wish to override {@link #iterator} to forward to this implementation. + * + * @since 7.0 + */ + protected Iterator standardIterator() { + return Multisets.iteratorImpl(this); + } + + /** + * A sensible, albeit inefficient, definition of {@link #size} in terms of + * {@link #entrySet}. If you override {@link #entrySet}, you may wish to + * override {@link #size} to forward to this implementation. + * + * @since 7.0 + */ + protected int standardSize() { + return Multisets.sizeImpl(this); + } + + /** + * A sensible, albeit inefficient, definition of {@link #size} in terms of + * {@code entrySet().size()} and {@link #count}. If you override either of these + * methods, you may wish to override {@link #size} to forward to this + * implementation. + * + * @since 7.0 + */ + protected boolean standardEquals(@Nullable Object object) { + return Multisets.equalsImpl(this, object); + } + + /** + * A sensible definition of {@link #hashCode} as {@code entrySet().hashCode()} . + * If you override {@link #entrySet}, you may wish to override {@link #hashCode} + * to forward to this implementation. + * + * @since 7.0 + */ + protected int standardHashCode() { + return entrySet().hashCode(); + } + + /** + * A sensible definition of {@link #toString} as {@code entrySet().toString()} . + * If you override {@link #entrySet}, you may wish to override {@link #toString} + * to forward to this implementation. + * + * @since 7.0 + */ + @Override + protected String standardToString() { + return entrySet().toString(); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingNavigableMap.java b/sources/main/java/com/google/common/collect/ForwardingNavigableMap.java new file mode 100644 index 0000000..0996692 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingNavigableMap.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.CollectPreconditions.checkRemove; +import static com.google.common.collect.Maps.keyOrNull; + +import java.util.Iterator; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.SortedMap; + +import com.google.common.annotations.Beta; + +/** + * A navigable map which forwards all its method calls to another navigable map. + * Subclasses should override one or more methods to modify the behavior of the + * backing map as desired per the + * decorator + * pattern. + * + *

+ * Warning: The methods of {@code ForwardingNavigableMap} 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 uses the map's comparator (or the + * natural ordering of the elements, if there is no comparator) to test element + * equality. As a result, if the comparator is not consistent with equals, some + * of the standard implementations may violate the {@code Map} contract. + * + *

+ * The {@code standard} methods and the collection views they return are not + * guaranteed to be thread-safe, even when all of the methods that they depend + * on are thread-safe. + * + * @author Louis Wasserman + * @since 12.0 + */ +public abstract class ForwardingNavigableMap extends ForwardingSortedMap implements NavigableMap { + + /** Constructor for use by subclasses. */ + protected ForwardingNavigableMap() { + } + + @Override + protected abstract NavigableMap delegate(); + + @Override + public Entry lowerEntry(K key) { + return delegate().lowerEntry(key); + } + + /** + * A sensible definition of {@link #lowerEntry} in terms of the + * {@code lastEntry()} of {@link #headMap(Object, boolean)}. If you override + * {@code headMap}, you may wish to override {@code lowerEntry} to forward to + * this implementation. + */ + protected Entry standardLowerEntry(K key) { + return headMap(key, false).lastEntry(); + } + + @Override + public K lowerKey(K key) { + return delegate().lowerKey(key); + } + + /** + * A sensible definition of {@link #lowerKey} in terms of {@code lowerEntry}. If + * you override {@link #lowerEntry}, you may wish to override {@code lowerKey} + * to forward to this implementation. + */ + protected K standardLowerKey(K key) { + return keyOrNull(lowerEntry(key)); + } + + @Override + public Entry floorEntry(K key) { + return delegate().floorEntry(key); + } + + /** + * A sensible definition of {@link #floorEntry} in terms of the + * {@code lastEntry()} of {@link #headMap(Object, boolean)}. If you override + * {@code headMap}, you may wish to override {@code floorEntry} to forward to + * this implementation. + */ + protected Entry standardFloorEntry(K key) { + return headMap(key, true).lastEntry(); + } + + @Override + public K floorKey(K key) { + return delegate().floorKey(key); + } + + /** + * A sensible definition of {@link #floorKey} in terms of {@code floorEntry}. If + * you override {@code floorEntry}, you may wish to override {@code floorKey} to + * forward to this implementation. + */ + protected K standardFloorKey(K key) { + return keyOrNull(floorEntry(key)); + } + + @Override + public Entry ceilingEntry(K key) { + return delegate().ceilingEntry(key); + } + + /** + * A sensible definition of {@link #ceilingEntry} in terms of the + * {@code firstEntry()} of {@link #tailMap(Object, boolean)}. If you override + * {@code tailMap}, you may wish to override {@code ceilingEntry} to forward to + * this implementation. + */ + protected Entry standardCeilingEntry(K key) { + return tailMap(key, true).firstEntry(); + } + + @Override + public K ceilingKey(K key) { + return delegate().ceilingKey(key); + } + + /** + * A sensible definition of {@link #ceilingKey} in terms of + * {@code ceilingEntry}. If you override {@code ceilingEntry}, you may wish to + * override {@code ceilingKey} to forward to this implementation. + */ + protected K standardCeilingKey(K key) { + return keyOrNull(ceilingEntry(key)); + } + + @Override + public Entry higherEntry(K key) { + return delegate().higherEntry(key); + } + + /** + * A sensible definition of {@link #higherEntry} in terms of the + * {@code firstEntry()} of {@link #tailMap(Object, boolean)}. If you override + * {@code tailMap}, you may wish to override {@code higherEntry} to forward to + * this implementation. + */ + protected Entry standardHigherEntry(K key) { + return tailMap(key, false).firstEntry(); + } + + @Override + public K higherKey(K key) { + return delegate().higherKey(key); + } + + /** + * A sensible definition of {@link #higherKey} in terms of {@code higherEntry}. + * If you override {@code higherEntry}, you may wish to override + * {@code higherKey} to forward to this implementation. + */ + protected K standardHigherKey(K key) { + return keyOrNull(higherEntry(key)); + } + + @Override + public Entry firstEntry() { + return delegate().firstEntry(); + } + + /** + * A sensible definition of {@link #firstEntry} in terms of the + * {@code iterator()} of {@link #entrySet}. If you override {@code entrySet}, + * you may wish to override {@code firstEntry} to forward to this + * implementation. + */ + protected Entry standardFirstEntry() { + return Iterables.getFirst(entrySet(), null); + } + + /** + * A sensible definition of {@link #firstKey} in terms of {@code firstEntry}. If + * you override {@code firstEntry}, you may wish to override {@code firstKey} to + * forward to this implementation. + */ + protected K standardFirstKey() { + Entry entry = firstEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Override + public Entry lastEntry() { + return delegate().lastEntry(); + } + + /** + * A sensible definition of {@link #lastEntry} in terms of the + * {@code iterator()} of the {@link #entrySet} of {@link #descendingMap}. If you + * override {@code descendingMap}, you may wish to override {@code lastEntry} to + * forward to this implementation. + */ + protected Entry standardLastEntry() { + return Iterables.getFirst(descendingMap().entrySet(), null); + } + + /** + * A sensible definition of {@link #lastKey} in terms of {@code lastEntry}. If + * you override {@code lastEntry}, you may wish to override {@code lastKey} to + * forward to this implementation. + */ + protected K standardLastKey() { + Entry entry = lastEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Override + public Entry pollFirstEntry() { + return delegate().pollFirstEntry(); + } + + /** + * A sensible definition of {@link #pollFirstEntry} in terms of the + * {@code iterator} of {@code entrySet}. If you override {@code entrySet}, you + * may wish to override {@code pollFirstEntry} to forward to this + * implementation. + */ + protected Entry standardPollFirstEntry() { + return Iterators.pollNext(entrySet().iterator()); + } + + @Override + public Entry pollLastEntry() { + return delegate().pollLastEntry(); + } + + /** + * A sensible definition of {@link #pollFirstEntry} in terms of the + * {@code iterator} of the {@code entrySet} of {@code descendingMap}. If you + * override {@code descendingMap}, you may wish to override + * {@code pollFirstEntry} to forward to this implementation. + */ + protected Entry standardPollLastEntry() { + return Iterators.pollNext(descendingMap().entrySet().iterator()); + } + + @Override + public NavigableMap descendingMap() { + return delegate().descendingMap(); + } + + /** + * A sensible implementation of {@link NavigableMap#descendingMap} in terms of + * the methods of this {@code NavigableMap}. In many cases, you may wish to + * override {@link ForwardingNavigableMap#descendingMap} to forward to this + * implementation or a subclass thereof. + * + *

+ * In particular, this map iterates over entries with repeated calls to + * {@link NavigableMap#lowerEntry}. If a more efficient means of iteration is + * available, you may wish to override the {@code entryIterator()} method of + * this class. + * + * @since 12.0 + */ + @Beta + protected class StandardDescendingMap extends Maps.DescendingMap { + /** Constructor for use by subclasses. */ + public StandardDescendingMap() { + } + + @Override + NavigableMap forward() { + return ForwardingNavigableMap.this; + } + + @Override + protected Iterator> entryIterator() { + return new Iterator>() { + private Entry toRemove = null; + private Entry nextOrNull = forward().lastEntry(); + + @Override + public boolean hasNext() { + return nextOrNull != null; + } + + @Override + public java.util.Map.Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + try { + return nextOrNull; + } finally { + toRemove = nextOrNull; + nextOrNull = forward().lowerEntry(nextOrNull.getKey()); + } + } + + @Override + public void remove() { + checkRemove(toRemove != null); + forward().remove(toRemove.getKey()); + toRemove = null; + } + }; + } + } + + @Override + public NavigableSet navigableKeySet() { + return delegate().navigableKeySet(); + } + + /** + * A sensible implementation of {@link NavigableMap#navigableKeySet} in terms of + * the methods of this {@code NavigableMap}. In many cases, you may wish to + * override {@link ForwardingNavigableMap#navigableKeySet} to forward to this + * implementation or a subclass thereof. + * + * @since 12.0 + */ + @Beta + protected class StandardNavigableKeySet extends Maps.NavigableKeySet { + /** Constructor for use by subclasses. */ + public StandardNavigableKeySet() { + super(ForwardingNavigableMap.this); + } + } + + @Override + public NavigableSet descendingKeySet() { + return delegate().descendingKeySet(); + } + + /** + * A sensible definition of {@link #descendingKeySet} as the + * {@code navigableKeySet} of {@link #descendingMap}. (The + * {@link StandardDescendingMap} implementation implements + * {@code navigableKeySet} on its own, so as not to cause an infinite loop.) If + * you override {@code descendingMap}, you may wish to override + * {@code descendingKeySet} to forward to this implementation. + */ + @Beta + protected NavigableSet standardDescendingKeySet() { + return descendingMap().navigableKeySet(); + } + + /** + * A sensible definition of {@link #subMap(Object, Object)} in terms of + * {@link #subMap(Object, boolean, Object, boolean)}. If you override + * {@code subMap(K, boolean, K, boolean)}, you may wish to override + * {@code subMap} to forward to this implementation. + */ + @Override + protected SortedMap standardSubMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return delegate().subMap(fromKey, fromInclusive, toKey, toInclusive); + } + + @Override + public NavigableMap headMap(K toKey, boolean inclusive) { + return delegate().headMap(toKey, inclusive); + } + + @Override + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return delegate().tailMap(fromKey, inclusive); + } + + /** + * A sensible definition of {@link #headMap(Object)} in terms of + * {@link #headMap(Object, boolean)}. If you override + * {@code headMap(K, boolean)}, you may wish to override {@code headMap} to + * forward to this implementation. + */ + protected SortedMap standardHeadMap(K toKey) { + return headMap(toKey, false); + } + + /** + * A sensible definition of {@link #tailMap(Object)} in terms of + * {@link #tailMap(Object, boolean)}. If you override + * {@code tailMap(K, boolean)}, you may wish to override {@code tailMap} to + * forward to this implementation. + */ + protected SortedMap standardTailMap(K fromKey) { + return tailMap(fromKey, true); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingNavigableSet.java b/sources/main/java/com/google/common/collect/ForwardingNavigableSet.java new file mode 100644 index 0000000..66a3b02 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingNavigableSet.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.SortedSet; + +import com.google.common.annotations.Beta; + +/** + * A navigable set which forwards all its method calls to another navigable set. + * Subclasses should override one or more methods to modify the behavior of the + * backing set as desired per the + * decorator + * pattern. + * + *

+ * Warning: The methods of {@code ForwardingNavigableSet} 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. + * + *

+ * Each of the {@code standard} methods uses the set's comparator (or the + * natural ordering of the elements, if there is no comparator) to test element + * equality. As a result, if the comparator is not consistent with equals, some + * of the standard implementations may violate the {@code Set} contract. + * + *

+ * The {@code standard} methods and the collection views they return are not + * guaranteed to be thread-safe, even when all of the methods that they depend + * on are thread-safe. + * + * @author Louis Wasserman + * @since 12.0 + */ +public abstract class ForwardingNavigableSet extends ForwardingSortedSet implements NavigableSet { + + /** Constructor for use by subclasses. */ + protected ForwardingNavigableSet() { + } + + @Override + protected abstract NavigableSet delegate(); + + @Override + public E lower(E e) { + return delegate().lower(e); + } + + /** + * A sensible definition of {@link #lower} in terms of the + * {@code descendingIterator} method of {@link #headSet(Object, boolean)}. If + * you override {@link #headSet(Object, boolean)}, you may wish to override + * {@link #lower} to forward to this implementation. + */ + protected E standardLower(E e) { + return Iterators.getNext(headSet(e, false).descendingIterator(), null); + } + + @Override + public E floor(E e) { + return delegate().floor(e); + } + + /** + * A sensible definition of {@link #floor} in terms of the + * {@code descendingIterator} method of {@link #headSet(Object, boolean)}. If + * you override {@link #headSet(Object, boolean)}, you may wish to override + * {@link #floor} to forward to this implementation. + */ + protected E standardFloor(E e) { + return Iterators.getNext(headSet(e, true).descendingIterator(), null); + } + + @Override + public E ceiling(E e) { + return delegate().ceiling(e); + } + + /** + * A sensible definition of {@link #ceiling} in terms of the {@code iterator} + * method of {@link #tailSet(Object, boolean)}. If you override + * {@link #tailSet(Object, boolean)}, you may wish to override {@link #ceiling} + * to forward to this implementation. + */ + protected E standardCeiling(E e) { + return Iterators.getNext(tailSet(e, true).iterator(), null); + } + + @Override + public E higher(E e) { + return delegate().higher(e); + } + + /** + * A sensible definition of {@link #higher} in terms of the {@code iterator} + * method of {@link #tailSet(Object, boolean)}. If you override + * {@link #tailSet(Object, boolean)}, you may wish to override {@link #higher} + * to forward to this implementation. + */ + protected E standardHigher(E e) { + return Iterators.getNext(tailSet(e, false).iterator(), null); + } + + @Override + public E pollFirst() { + return delegate().pollFirst(); + } + + /** + * A sensible definition of {@link #pollFirst} in terms of the {@code iterator} + * method. If you override {@link #iterator} you may wish to override + * {@link #pollFirst} to forward to this implementation. + */ + protected E standardPollFirst() { + return Iterators.pollNext(iterator()); + } + + @Override + public E pollLast() { + return delegate().pollLast(); + } + + /** + * A sensible definition of {@link #pollLast} in terms of the + * {@code descendingIterator} method. If you override + * {@link #descendingIterator} you may wish to override {@link #pollLast} to + * forward to this implementation. + */ + protected E standardPollLast() { + return Iterators.pollNext(descendingIterator()); + } + + protected E standardFirst() { + return iterator().next(); + } + + protected E standardLast() { + return descendingIterator().next(); + } + + @Override + public NavigableSet descendingSet() { + return delegate().descendingSet(); + } + + /** + * A sensible implementation of {@link NavigableSet#descendingSet} in terms of + * the other methods of {@link NavigableSet}, notably including + * {@link NavigableSet#descendingIterator}. + * + *

+ * In many cases, you may wish to override + * {@link ForwardingNavigableSet#descendingSet} to forward to this + * implementation or a subclass thereof. + * + * @since 12.0 + */ + @Beta + protected class StandardDescendingSet extends Sets.DescendingSet { + /** Constructor for use by subclasses. */ + public StandardDescendingSet() { + super(ForwardingNavigableSet.this); + } + } + + @Override + public Iterator descendingIterator() { + return delegate().descendingIterator(); + } + + @Override + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return delegate().subSet(fromElement, fromInclusive, toElement, toInclusive); + } + + /** + * A sensible definition of {@link #subSet(Object, boolean, Object, boolean)} in + * terms of the {@code headSet} and {@code tailSet} methods. In many cases, you + * may wish to override {@link #subSet(Object, boolean, Object, boolean)} to + * forward to this implementation. + */ + @Beta + protected NavigableSet standardSubSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return tailSet(fromElement, fromInclusive).headSet(toElement, toInclusive); + } + + /** + * A sensible definition of {@link #subSet(Object, Object)} in terms of the + * {@link #subSet(Object, boolean, Object, boolean)} method. If you override + * {@link #subSet(Object, boolean, Object, boolean)}, you may wish to override + * {@link #subSet(Object, Object)} to forward to this implementation. + */ + @Override + protected SortedSet standardSubSet(E fromElement, E toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public NavigableSet headSet(E toElement, boolean inclusive) { + return delegate().headSet(toElement, inclusive); + } + + /** + * A sensible definition of {@link #headSet(Object)} in terms of the + * {@link #headSet(Object, boolean)} method. If you override + * {@link #headSet(Object, boolean)}, you may wish to override + * {@link #headSet(Object)} to forward to this implementation. + */ + protected SortedSet standardHeadSet(E toElement) { + return headSet(toElement, false); + } + + @Override + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return delegate().tailSet(fromElement, inclusive); + } + + /** + * A sensible definition of {@link #tailSet(Object)} in terms of the + * {@link #tailSet(Object, boolean)} method. If you override + * {@link #tailSet(Object, boolean)}, you may wish to override + * {@link #tailSet(Object)} to forward to this implementation. + */ + protected SortedSet standardTailSet(E fromElement) { + return tailSet(fromElement, true); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingObject.java b/sources/main/java/com/google/common/collect/ForwardingObject.java new file mode 100644 index 0000000..8e8d76c --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingObject.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; + +import com.google.common.annotations.GwtCompatible; + +/** + * 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) + */ +@GwtCompatible +public 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/sources/main/java/com/google/common/collect/ForwardingQueue.java b/sources/main/java/com/google/common/collect/ForwardingQueue.java new file mode 100644 index 0000000..440c964 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingQueue.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.NoSuchElementException; +import java.util.Queue; + +import com.google.common.annotations.GwtCompatible; + +/** + * A queue which forwards all its method calls to another queue. Subclasses + * should override one or more methods to modify the behavior of the backing + * queue as desired per the + * decorator + * pattern. + * + *

+ * Warning: The methods of {@code ForwardingQueue} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #add} alone will not change the behavior of + * {@link #offer} which can lead to unexpected behavior. In this case, you + * should override {@code offer} as well, either providing your own + * implementation, or delegating to the provided {@code standardOffer} 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 Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class ForwardingQueue extends ForwardingCollection implements Queue { + + /** Constructor for use by subclasses. */ + protected ForwardingQueue() { + } + + @Override + protected abstract Queue delegate(); + + @Override + public boolean offer(E o) { + return delegate().offer(o); + } + + @Override + public E poll() { + return delegate().poll(); + } + + @Override + public E remove() { + return delegate().remove(); + } + + @Override + public E peek() { + return delegate().peek(); + } + + @Override + public E element() { + return delegate().element(); + } + + /** + * A sensible definition of {@link #offer} in terms of {@link #add}. If you + * override {@link #add}, you may wish to override {@link #offer} to forward to + * this implementation. + * + * @since 7.0 + */ + protected boolean standardOffer(E e) { + try { + return add(e); + } catch (IllegalStateException caught) { + return false; + } + } + + /** + * A sensible definition of {@link #peek} in terms of {@link #element}. If you + * override {@link #element}, you may wish to override {@link #peek} to forward + * to this implementation. + * + * @since 7.0 + */ + protected E standardPeek() { + try { + return element(); + } catch (NoSuchElementException caught) { + return null; + } + } + + /** + * A sensible definition of {@link #poll} in terms of {@link #remove}. If you + * override {@link #remove}, you may wish to override {@link #poll} to forward + * to this implementation. + * + * @since 7.0 + */ + protected E standardPoll() { + try { + return remove(); + } catch (NoSuchElementException caught) { + return null; + } + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingSet.java b/sources/main/java/com/google/common/collect/ForwardingSet.java new file mode 100644 index 0000000..49e66c7 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingSet.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 com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collection; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * 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) + */ +@GwtCompatible +public 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(@Nullable 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(@Nullable 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/sources/main/java/com/google/common/collect/ForwardingSetMultimap.java b/sources/main/java/com/google/common/collect/ForwardingSetMultimap.java new file mode 100644 index 0000000..fe9393d --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingSetMultimap.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A set multimap which forwards all its method calls to another set multimap. + * Subclasses should override one or more methods to modify the behavior of the + * backing multimap as desired per the + * decorator + * pattern. + * + * @author Kurt Alfred Kluever + * @since 3.0 + */ +@GwtCompatible +public abstract class ForwardingSetMultimap extends ForwardingMultimap implements SetMultimap { + + @Override + protected abstract SetMultimap delegate(); + + @Override + public Set> entries() { + return delegate().entries(); + } + + @Override + public Set get(@Nullable K key) { + return delegate().get(key); + } + + @Override + public Set removeAll(@Nullable Object key) { + return delegate().removeAll(key); + } + + @Override + public Set replaceValues(K key, Iterable values) { + return delegate().replaceValues(key, values); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingSortedMap.java b/sources/main/java/com/google/common/collect/ForwardingSortedMap.java new file mode 100644 index 0000000..269c596 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingSortedMap.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.Comparator; +import java.util.NoSuchElementException; +import java.util.SortedMap; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A sorted map which forwards all its method calls to another sorted map. + * Subclasses should override one or more methods to modify the behavior of the + * backing sorted map as desired per the + * decorator + * pattern. + * + *

+ * Warning: The methods of {@code ForwardingSortedMap} 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 the comparator + * of the map to test equality for both keys and values, unlike + * {@code ForwardingMap}. + * + *

+ * 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 Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class ForwardingSortedMap extends ForwardingMap implements SortedMap { + // TODO(user): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingSortedMap() { + } + + @Override + protected abstract SortedMap delegate(); + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + public K firstKey() { + return delegate().firstKey(); + } + + @Override + public SortedMap headMap(K toKey) { + return delegate().headMap(toKey); + } + + @Override + public K lastKey() { + return delegate().lastKey(); + } + + @Override + public SortedMap subMap(K fromKey, K toKey) { + return delegate().subMap(fromKey, toKey); + } + + @Override + public SortedMap tailMap(K fromKey) { + return delegate().tailMap(fromKey); + } + + /** + * A sensible implementation of {@link SortedMap#keySet} in terms of the methods + * of {@code ForwardingSortedMap}. In many cases, you may wish to override + * {@link ForwardingSortedMap#keySet} to forward to this implementation or a + * subclass thereof. + * + * @since 15.0 + */ + @Beta + protected class StandardKeySet extends Maps.SortedKeySet { + /** Constructor for use by subclasses. */ + public StandardKeySet() { + super(ForwardingSortedMap.this); + } + } + + // unsafe, but worst case is a CCE is thrown, which callers will be expecting + @SuppressWarnings("unchecked") + private int unsafeCompare(Object k1, Object k2) { + Comparator comparator = comparator(); + if (comparator == null) { + return ((Comparable) k1).compareTo(k2); + } else { + return ((Comparator) comparator).compare(k1, k2); + } + } + + /** + * A sensible definition of {@link #containsKey} in terms of the {@code + * firstKey()} method of {@link #tailMap}. If you override {@link #tailMap}, you + * may wish to override {@link #containsKey} to forward to this implementation. + * + * @since 7.0 + */ + @Override + @Beta + protected boolean standardContainsKey(@Nullable Object key) { + try { + // any CCE will be caught + @SuppressWarnings("unchecked") + SortedMap self = (SortedMap) this; + Object ceilingKey = self.tailMap(key).firstKey(); + return unsafeCompare(ceilingKey, key) == 0; + } catch (ClassCastException e) { + return false; + } catch (NoSuchElementException e) { + return false; + } catch (NullPointerException e) { + return false; + } + } + + /** + * A sensible default implementation of {@link #subMap(Object, Object)} in terms + * of {@link #headMap(Object)} and {@link #tailMap(Object)}. In some situations, + * you may wish to override {@link #subMap(Object, Object)} to forward to this + * implementation. + * + * @since 7.0 + */ + @Beta + protected SortedMap standardSubMap(K fromKey, K toKey) { + checkArgument(unsafeCompare(fromKey, toKey) <= 0, "fromKey must be <= toKey"); + return tailMap(fromKey).headMap(toKey); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingSortedMultiset.java b/sources/main/java/com/google/common/collect/ForwardingSortedMultiset.java new file mode 100644 index 0000000..2ce1991 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingSortedMultiset.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A sorted multiset which forwards all its method calls to another sorted + * multiset. Subclasses should override one or more methods to modify the + * behavior of the backing multiset as desired per the + * decorator + * pattern. + * + *

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

+ * The {@code standard} methods and any collection views they return are not + * guaranteed to be thread-safe, even when all of the methods that they depend + * on are thread-safe. + * + * @author Louis Wasserman + * @since 15.0 + */ +@Beta +@GwtCompatible(emulated = true) +public abstract class ForwardingSortedMultiset extends ForwardingMultiset implements SortedMultiset { + /** Constructor for use by subclasses. */ + protected ForwardingSortedMultiset() { + } + + @Override + protected abstract SortedMultiset delegate(); + + @Override + public NavigableSet elementSet() { + return (NavigableSet) super.elementSet(); + } + + /** + * A sensible implementation of {@link SortedMultiset#elementSet} in terms of + * the following methods: {@link SortedMultiset#clear}, + * {@link SortedMultiset#comparator}, {@link SortedMultiset#contains}, + * {@link SortedMultiset#containsAll}, {@link SortedMultiset#count}, + * {@link SortedMultiset#firstEntry} {@link SortedMultiset#headMultiset}, + * {@link SortedMultiset#isEmpty}, {@link SortedMultiset#lastEntry}, + * {@link SortedMultiset#subMultiset}, {@link SortedMultiset#tailMultiset}, the + * {@code size()} and {@code iterator()} methods of + * {@link SortedMultiset#entrySet}, and + * {@link SortedMultiset#remove(Object, int)}. In many situations, you may wish + * to override {@link SortedMultiset#elementSet} to forward to this + * implementation or a subclass thereof. + */ + protected class StandardElementSet extends SortedMultisets.NavigableElementSet { + /** Constructor for use by subclasses. */ + public StandardElementSet() { + super(ForwardingSortedMultiset.this); + } + } + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + public SortedMultiset descendingMultiset() { + return delegate().descendingMultiset(); + } + + /** + * A skeleton implementation of a descending multiset view. Normally, + * {@link #descendingMultiset()} will not reflect any changes you make to the + * behavior of methods such as {@link #add(Object)} or {@link #pollFirstEntry}. + * This skeleton implementation correctly delegates each of its operations to + * the appropriate methods of this {@code + * ForwardingSortedMultiset}. + * + * In many cases, you may wish to override {@link #descendingMultiset()} to + * return an instance of a subclass of {@code StandardDescendingMultiset}. + */ + protected abstract class StandardDescendingMultiset extends DescendingMultiset { + /** Constructor for use by subclasses. */ + public StandardDescendingMultiset() { + } + + @Override + SortedMultiset forwardMultiset() { + return ForwardingSortedMultiset.this; + } + } + + @Override + public Entry firstEntry() { + return delegate().firstEntry(); + } + + /** + * A sensible definition of {@link #firstEntry()} in terms of + * {@code entrySet().iterator()}. + * + * If you override {@link #entrySet()}, you may wish to override + * {@link #firstEntry()} to forward to this implementation. + */ + protected Entry standardFirstEntry() { + Iterator> entryIterator = entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } + Entry entry = entryIterator.next(); + return Multisets.immutableEntry(entry.getElement(), entry.getCount()); + } + + @Override + public Entry lastEntry() { + return delegate().lastEntry(); + } + + /** + * A sensible definition of {@link #lastEntry()} in terms of {@code + * descendingMultiset().entrySet().iterator()}. + * + * If you override {@link #descendingMultiset} or {@link #entrySet()}, you may + * wish to override {@link #firstEntry()} to forward to this implementation. + */ + protected Entry standardLastEntry() { + Iterator> entryIterator = descendingMultiset().entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } + Entry entry = entryIterator.next(); + return Multisets.immutableEntry(entry.getElement(), entry.getCount()); + } + + @Override + public Entry pollFirstEntry() { + return delegate().pollFirstEntry(); + } + + /** + * A sensible definition of {@link #pollFirstEntry()} in terms of + * {@code entrySet().iterator()}. + * + * If you override {@link #entrySet()}, you may wish to override + * {@link #pollFirstEntry()} to forward to this implementation. + */ + protected Entry standardPollFirstEntry() { + Iterator> entryIterator = entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } + Entry entry = entryIterator.next(); + entry = Multisets.immutableEntry(entry.getElement(), entry.getCount()); + entryIterator.remove(); + return entry; + } + + @Override + public Entry pollLastEntry() { + return delegate().pollLastEntry(); + } + + /** + * A sensible definition of {@link #pollLastEntry()} in terms of {@code + * descendingMultiset().entrySet().iterator()}. + * + * If you override {@link #descendingMultiset()} or {@link #entrySet()}, you may + * wish to override {@link #pollLastEntry()} to forward to this implementation. + */ + protected Entry standardPollLastEntry() { + Iterator> entryIterator = descendingMultiset().entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } + Entry entry = entryIterator.next(); + entry = Multisets.immutableEntry(entry.getElement(), entry.getCount()); + entryIterator.remove(); + return entry; + } + + @Override + public SortedMultiset headMultiset(E upperBound, BoundType boundType) { + return delegate().headMultiset(upperBound, boundType); + } + + @Override + public SortedMultiset subMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, + BoundType upperBoundType) { + return delegate().subMultiset(lowerBound, lowerBoundType, upperBound, upperBoundType); + } + + /** + * A sensible definition of + * {@link #subMultiset(Object, BoundType, Object, BoundType)} in terms of + * {@link #headMultiset(Object, BoundType) headMultiset} and + * {@link #tailMultiset(Object, BoundType) tailMultiset}. + * + * If you override either of these methods, you may wish to override + * {@link #subMultiset(Object, BoundType, Object, BoundType)} to forward to this + * implementation. + */ + protected SortedMultiset standardSubMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, + BoundType upperBoundType) { + return tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); + } + + @Override + public SortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return delegate().tailMultiset(lowerBound, boundType); + } + +} diff --git a/sources/main/java/com/google/common/collect/ForwardingSortedSet.java b/sources/main/java/com/google/common/collect/ForwardingSortedSet.java new file mode 100644 index 0000000..b9e53ec --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingSortedSet.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A sorted set which forwards all its method calls to another sorted set. + * Subclasses should override one or more methods to modify the behavior of the + * backing sorted set as desired per the + * decorator + * pattern. + * + *

+ * Warning: The methods of {@code ForwardingSortedSet} 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. + * + *

+ * Each of the {@code standard} methods, where appropriate, uses the set's + * comparator (or the natural ordering of the elements, if there is no + * comparator) to test element equality. As a result, if the comparator is not + * consistent with equals, some of the standard implementations may violate the + * {@code Set} contract. + * + *

+ * 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 Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class ForwardingSortedSet extends ForwardingSet implements SortedSet { + + /** Constructor for use by subclasses. */ + protected ForwardingSortedSet() { + } + + @Override + protected abstract SortedSet delegate(); + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + public E first() { + return delegate().first(); + } + + @Override + public SortedSet headSet(E toElement) { + return delegate().headSet(toElement); + } + + @Override + public E last() { + return delegate().last(); + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + return delegate().subSet(fromElement, toElement); + } + + @Override + public SortedSet tailSet(E fromElement) { + return delegate().tailSet(fromElement); + } + + // unsafe, but worst case is a CCE is thrown, which callers will be expecting + @SuppressWarnings("unchecked") + private int unsafeCompare(Object o1, Object o2) { + Comparator comparator = comparator(); + return (comparator == null) ? ((Comparable) o1).compareTo(o2) + : ((Comparator) comparator).compare(o1, o2); + } + + /** + * A sensible definition of {@link #contains} in terms of the {@code first()} + * method of {@link #tailSet}. If you override {@link #tailSet}, you may wish to + * override {@link #contains} to forward to this implementation. + * + * @since 7.0 + */ + @Override + @Beta + protected boolean standardContains(@Nullable Object object) { + try { + // any ClassCastExceptions are caught + @SuppressWarnings("unchecked") + SortedSet self = (SortedSet) this; + Object ceiling = self.tailSet(object).first(); + return unsafeCompare(ceiling, object) == 0; + } catch (ClassCastException e) { + return false; + } catch (NoSuchElementException e) { + return false; + } catch (NullPointerException e) { + return false; + } + } + + /** + * A sensible definition of {@link #remove} in terms of the {@code iterator()} + * method of {@link #tailSet}. If you override {@link #tailSet}, you may wish to + * override {@link #remove} to forward to this implementation. + * + * @since 7.0 + */ + @Override + @Beta + protected boolean standardRemove(@Nullable Object object) { + try { + // any ClassCastExceptions are caught + @SuppressWarnings("unchecked") + SortedSet self = (SortedSet) this; + Iterator iterator = self.tailSet(object).iterator(); + if (iterator.hasNext()) { + Object ceiling = iterator.next(); + if (unsafeCompare(ceiling, object) == 0) { + iterator.remove(); + return true; + } + } + } catch (ClassCastException e) { + return false; + } catch (NullPointerException e) { + return false; + } + return false; + } + + /** + * A sensible default implementation of {@link #subSet(Object, Object)} in terms + * of {@link #headSet(Object)} and {@link #tailSet(Object)}. In some situations, + * you may wish to override {@link #subSet(Object, Object)} to forward to this + * implementation. + * + * @since 7.0 + */ + @Beta + protected SortedSet standardSubSet(E fromElement, E toElement) { + return tailSet(fromElement).headSet(toElement); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingSortedSetMultimap.java b/sources/main/java/com/google/common/collect/ForwardingSortedSetMultimap.java new file mode 100644 index 0000000..9cf18aa --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingSortedSetMultimap.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Comparator; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A sorted set multimap which forwards all its method calls to another sorted + * set multimap. Subclasses should override one or more methods to modify the + * behavior of the backing multimap as desired per the + * decorator + * pattern. + * + * @author Kurt Alfred Kluever + * @since 3.0 + */ +@GwtCompatible +public abstract class ForwardingSortedSetMultimap extends ForwardingSetMultimap + implements SortedSetMultimap { + + /** Constructor for use by subclasses. */ + protected ForwardingSortedSetMultimap() { + } + + @Override + protected abstract SortedSetMultimap delegate(); + + @Override + public SortedSet get(@Nullable K key) { + return delegate().get(key); + } + + @Override + public SortedSet removeAll(@Nullable Object key) { + return delegate().removeAll(key); + } + + @Override + public SortedSet replaceValues(K key, Iterable values) { + return delegate().replaceValues(key, values); + } + + @Override + public Comparator valueComparator() { + return delegate().valueComparator(); + } +} diff --git a/sources/main/java/com/google/common/collect/ForwardingTable.java b/sources/main/java/com/google/common/collect/ForwardingTable.java new file mode 100644 index 0000000..a4d5f72 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ForwardingTable.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import com.google.common.annotations.GwtCompatible; + +/** + * A table which forwards all its method calls to another table. Subclasses + * should override one or more methods to modify the behavior of the backing map + * as desired per the + * decorator + * pattern. + * + * @author Gregory Kick + * @since 7.0 + */ +@GwtCompatible +public abstract class ForwardingTable extends ForwardingObject implements Table { + /** Constructor for use by subclasses. */ + protected ForwardingTable() { + } + + @Override + protected abstract Table delegate(); + + @Override + public Set> cellSet() { + return delegate().cellSet(); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public Map column(C columnKey) { + return delegate().column(columnKey); + } + + @Override + public Set columnKeySet() { + return delegate().columnKeySet(); + } + + @Override + public Map> columnMap() { + return delegate().columnMap(); + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + return delegate().contains(rowKey, columnKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + return delegate().containsColumn(columnKey); + } + + @Override + public boolean containsRow(Object rowKey) { + return delegate().containsRow(rowKey); + } + + @Override + public boolean containsValue(Object value) { + return delegate().containsValue(value); + } + + @Override + public V get(Object rowKey, Object columnKey) { + return delegate().get(rowKey, columnKey); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public V put(R rowKey, C columnKey, V value) { + return delegate().put(rowKey, columnKey, value); + } + + @Override + public void putAll(Table table) { + delegate().putAll(table); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + return delegate().remove(rowKey, columnKey); + } + + @Override + public Map row(R rowKey) { + return delegate().row(rowKey); + } + + @Override + public Set rowKeySet() { + return delegate().rowKeySet(); + } + + @Override + public Map> rowMap() { + return delegate().rowMap(); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override + public Collection values() { + return delegate().values(); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) || delegate().equals(obj); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } +} diff --git a/sources/main/java/com/google/common/collect/GeneralRange.java b/sources/main/java/com/google/common/collect/GeneralRange.java new file mode 100644 index 0000000..6e2ff0d --- /dev/null +++ b/sources/main/java/com/google/common/collect/GeneralRange.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.BoundType.CLOSED; +import static com.google.common.collect.BoundType.OPEN; + +import java.io.Serializable; +import java.util.Comparator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * A generalized interval on any ordering, for internal use. Supports + * {@code null}. Unlike {@link Range}, this allows the use of an arbitrary + * comparator. This is designed for use in the implementation of subcollections + * of sorted collection types. + * + *

+ * Whenever possible, use {@code Range} instead, which is better supported. + * + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true) +final class GeneralRange implements Serializable { + /** + * Converts a Range to a GeneralRange. + */ + static GeneralRange from(Range range) { + @Nullable + T lowerEndpoint = range.hasLowerBound() ? range.lowerEndpoint() : null; + BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : OPEN; + + @Nullable + T upperEndpoint = range.hasUpperBound() ? range.upperEndpoint() : null; + BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : OPEN; + return new GeneralRange(Ordering.natural(), range.hasLowerBound(), lowerEndpoint, lowerBoundType, + range.hasUpperBound(), upperEndpoint, upperBoundType); + } + + /** + * Returns the whole range relative to the specified comparator. + */ + static GeneralRange all(Comparator comparator) { + return new GeneralRange(comparator, false, null, OPEN, false, null, OPEN); + } + + /** + * Returns everything above the endpoint relative to the specified comparator, + * with the specified endpoint behavior. + */ + static GeneralRange downTo(Comparator comparator, @Nullable T endpoint, BoundType boundType) { + return new GeneralRange(comparator, true, endpoint, boundType, false, null, OPEN); + } + + /** + * Returns everything below the endpoint relative to the specified comparator, + * with the specified endpoint behavior. + */ + static GeneralRange upTo(Comparator comparator, @Nullable T endpoint, BoundType boundType) { + return new GeneralRange(comparator, false, null, OPEN, true, endpoint, boundType); + } + + /** + * Returns everything between the endpoints relative to the specified + * comparator, with the specified endpoint behavior. + */ + static GeneralRange range(Comparator comparator, @Nullable T lower, BoundType lowerType, + @Nullable T upper, BoundType upperType) { + return new GeneralRange(comparator, true, lower, lowerType, true, upper, upperType); + } + + private final Comparator comparator; + private final boolean hasLowerBound; + @Nullable + private final T lowerEndpoint; + private final BoundType lowerBoundType; + private final boolean hasUpperBound; + @Nullable + private final T upperEndpoint; + private final BoundType upperBoundType; + + private GeneralRange(Comparator comparator, boolean hasLowerBound, @Nullable T lowerEndpoint, + BoundType lowerBoundType, boolean hasUpperBound, @Nullable T upperEndpoint, BoundType upperBoundType) { + this.comparator = checkNotNull(comparator); + this.hasLowerBound = hasLowerBound; + this.hasUpperBound = hasUpperBound; + this.lowerEndpoint = lowerEndpoint; + this.lowerBoundType = checkNotNull(lowerBoundType); + this.upperEndpoint = upperEndpoint; + this.upperBoundType = checkNotNull(upperBoundType); + + if (hasLowerBound) { + comparator.compare(lowerEndpoint, lowerEndpoint); + } + if (hasUpperBound) { + comparator.compare(upperEndpoint, upperEndpoint); + } + if (hasLowerBound && hasUpperBound) { + int cmp = comparator.compare(lowerEndpoint, upperEndpoint); + // be consistent with Range + checkArgument(cmp <= 0, "lowerEndpoint (%s) > upperEndpoint (%s)", lowerEndpoint, upperEndpoint); + if (cmp == 0) { + checkArgument(lowerBoundType != OPEN | upperBoundType != OPEN); + } + } + } + + Comparator comparator() { + return comparator; + } + + boolean hasLowerBound() { + return hasLowerBound; + } + + boolean hasUpperBound() { + return hasUpperBound; + } + + boolean isEmpty() { + return (hasUpperBound() && tooLow(getUpperEndpoint())) || (hasLowerBound() && tooHigh(getLowerEndpoint())); + } + + boolean tooLow(@Nullable T t) { + if (!hasLowerBound()) { + return false; + } + T lbound = getLowerEndpoint(); + int cmp = comparator.compare(t, lbound); + return cmp < 0 | (cmp == 0 & getLowerBoundType() == OPEN); + } + + boolean tooHigh(@Nullable T t) { + if (!hasUpperBound()) { + return false; + } + T ubound = getUpperEndpoint(); + int cmp = comparator.compare(t, ubound); + return cmp > 0 | (cmp == 0 & getUpperBoundType() == OPEN); + } + + boolean contains(@Nullable T t) { + return !tooLow(t) && !tooHigh(t); + } + + /** + * Returns the intersection of the two ranges, or an empty range if their + * intersection is empty. + */ + GeneralRange intersect(GeneralRange other) { + checkNotNull(other); + checkArgument(comparator.equals(other.comparator)); + + boolean hasLowBound = this.hasLowerBound; + @Nullable + T lowEnd = getLowerEndpoint(); + BoundType lowType = getLowerBoundType(); + if (!hasLowerBound()) { + hasLowBound = other.hasLowerBound; + lowEnd = other.getLowerEndpoint(); + lowType = other.getLowerBoundType(); + } else if (other.hasLowerBound()) { + int cmp = comparator.compare(getLowerEndpoint(), other.getLowerEndpoint()); + if (cmp < 0 || (cmp == 0 && other.getLowerBoundType() == OPEN)) { + lowEnd = other.getLowerEndpoint(); + lowType = other.getLowerBoundType(); + } + } + + boolean hasUpBound = this.hasUpperBound; + @Nullable + T upEnd = getUpperEndpoint(); + BoundType upType = getUpperBoundType(); + if (!hasUpperBound()) { + hasUpBound = other.hasUpperBound; + upEnd = other.getUpperEndpoint(); + upType = other.getUpperBoundType(); + } else if (other.hasUpperBound()) { + int cmp = comparator.compare(getUpperEndpoint(), other.getUpperEndpoint()); + if (cmp > 0 || (cmp == 0 && other.getUpperBoundType() == OPEN)) { + upEnd = other.getUpperEndpoint(); + upType = other.getUpperBoundType(); + } + } + + if (hasLowBound && hasUpBound) { + int cmp = comparator.compare(lowEnd, upEnd); + if (cmp > 0 || (cmp == 0 && lowType == OPEN && upType == OPEN)) { + // force allowed empty range + lowEnd = upEnd; + lowType = OPEN; + upType = CLOSED; + } + } + + return new GeneralRange(comparator, hasLowBound, lowEnd, lowType, hasUpBound, upEnd, upType); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof GeneralRange) { + GeneralRange r = (GeneralRange) obj; + return comparator.equals(r.comparator) && hasLowerBound == r.hasLowerBound + && hasUpperBound == r.hasUpperBound && getLowerBoundType().equals(r.getLowerBoundType()) + && getUpperBoundType().equals(r.getUpperBoundType()) + && Objects.equal(getLowerEndpoint(), r.getLowerEndpoint()) + && Objects.equal(getUpperEndpoint(), r.getUpperEndpoint()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(comparator, getLowerEndpoint(), getLowerBoundType(), getUpperEndpoint(), + getUpperBoundType()); + } + + private transient GeneralRange reverse; + + /** + * Returns the same range relative to the reversed comparator. + */ + GeneralRange reverse() { + GeneralRange result = reverse; + if (result == null) { + result = new GeneralRange(Ordering.from(comparator).reverse(), hasUpperBound, getUpperEndpoint(), + getUpperBoundType(), hasLowerBound, getLowerEndpoint(), getLowerBoundType()); + result.reverse = this; + return this.reverse = result; + } + return result; + } + + @Override + public String toString() { + return new StringBuilder().append(comparator).append(":").append(lowerBoundType == CLOSED ? '[' : '(') + .append(hasLowerBound ? lowerEndpoint : "-\u221e").append(',') + .append(hasUpperBound ? upperEndpoint : "\u221e").append(upperBoundType == CLOSED ? ']' : ')') + .toString(); + } + + T getLowerEndpoint() { + return lowerEndpoint; + } + + BoundType getLowerBoundType() { + return lowerBoundType; + } + + T getUpperEndpoint() { + return upperEndpoint; + } + + BoundType getUpperBoundType() { + return upperBoundType; + } +} diff --git a/sources/main/java/com/google/common/collect/GwtTransient.java b/sources/main/java/com/google/common/collect/GwtTransient.java new file mode 100644 index 0000000..8184baf --- /dev/null +++ b/sources/main/java/com/google/common/collect/GwtTransient.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import com.google.common.annotations.GwtCompatible; + +/** + * Private replacement for {@link com.google.gwt.user.client.rpc.GwtTransient} + * to work around build-system quirks. This annotation should be used + * only in {@code com.google.common.collect}. + */ +@Documented +@GwtCompatible +@Retention(RUNTIME) +@Target(FIELD) +@interface GwtTransient { +} diff --git a/sources/main/java/com/google/common/collect/HashBasedTable.java b/sources/main/java/com/google/common/collect/HashBasedTable.java new file mode 100644 index 0000000..a5677a1 --- /dev/null +++ b/sources/main/java/com/google/common/collect/HashBasedTable.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.CollectPreconditions.checkNonnegative; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Supplier; + +/** + * Implementation of {@link Table} using hash tables. + * + *

+ * 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. + * + *

+ * See the Guava User Guide article on + * {@code Table}. + * + * @author Jared Levy + * @since 7.0 + */ +@GwtCompatible(serializable = true) +public class HashBasedTable extends StandardTable { + private static class Factory implements Supplier>, Serializable { + final int expectedSize; + + Factory(int expectedSize) { + this.expectedSize = expectedSize; + } + + @Override + public Map get() { + return Maps.newHashMapWithExpectedSize(expectedSize); + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates an empty {@code HashBasedTable}. + */ + public static HashBasedTable create() { + return new HashBasedTable(new HashMap>(), new Factory(0)); + } + + /** + * Creates an empty {@code HashBasedTable} with the specified map sizes. + * + * @param expectedRows the expected number of distinct row keys + * @param expectedCellsPerRow the expected number of column key / value mappings + * in each row + * @throws IllegalArgumentException if {@code expectedRows} or {@code + * expectedCellsPerRow} is negative + */ + public static HashBasedTable create(int expectedRows, int expectedCellsPerRow) { + checkNonnegative(expectedCellsPerRow, "expectedCellsPerRow"); + Map> backingMap = Maps.newHashMapWithExpectedSize(expectedRows); + return new HashBasedTable(backingMap, new Factory(expectedCellsPerRow)); + } + + /** + * Creates a {@code HashBasedTable} with the same mappings as the specified + * table. + * + * @param table the table to copy + * @throws NullPointerException if any of the row keys, column keys, or values + * in {@code table} is null + */ + public static HashBasedTable create(Table table) { + HashBasedTable result = create(); + result.putAll(table); + return result; + } + + HashBasedTable(Map> backingMap, Factory factory) { + super(backingMap, factory); + } + + // Overriding so NullPointerTester test passes. + + @Override + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return super.contains(rowKey, columnKey); + } + + @Override + public boolean containsColumn(@Nullable Object columnKey) { + return super.containsColumn(columnKey); + } + + @Override + public boolean containsRow(@Nullable Object rowKey) { + return super.containsRow(rowKey); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return super.containsValue(value); + } + + @Override + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + return super.get(rowKey, columnKey); + } + + @Override + public boolean equals(@Nullable Object obj) { + return super.equals(obj); + } + + @Override + public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + return super.remove(rowKey, columnKey); + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/HashBiMap.java b/sources/main/java/com/google/common/collect/HashBiMap.java new file mode 100644 index 0000000..83dcc84 --- /dev/null +++ b/sources/main/java/com/google/common/collect/HashBiMap.java @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Objects; + +/** + * 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) + */ +@GwtCompatible(emulated = true) +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; + + @Nullable + BiEntry nextInKToVBucket; + + @Nullable + 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(@Nullable Object o) { + return Hashing.smear((o == null) ? 0 : o.hashCode()); + } + + private BiEntry seekByKey(@Nullable Object key, int keyHash) { + for (BiEntry entry = hashTableKToV[keyHash & mask]; entry != null; entry = entry.nextInKToVBucket) { + if (keyHash == entry.keyHash && Objects.equal(key, entry.key)) { + return entry; + } + } + return null; + } + + private BiEntry seekByValue(@Nullable Object value, int valueHash) { + for (BiEntry entry = hashTableVToK[valueHash & mask]; entry != null; entry = entry.nextInVToKBucket) { + if (valueHash == entry.valueHash && Objects.equal(value, entry.value)) { + return entry; + } + } + return null; + } + + @Override + public boolean containsKey(@Nullable Object key) { + return seekByKey(key, hash(key)) != null; + } + + @Override + public boolean containsValue(@Nullable Object value) { + return seekByValue(value, hash(value)) != null; + } + + @Nullable + @Override + public V get(@Nullable Object key) { + BiEntry entry = seekByKey(key, hash(key)); + return (entry == null) ? null : entry.value; + } + + @Override + public V put(@Nullable K key, @Nullable V value) { + return put(key, value, false); + } + + @Override + public V forcePut(@Nullable K key, @Nullable V value) { + return put(key, value, true); + } + + private V put(@Nullable K key, @Nullable V value, boolean force) { + int keyHash = hash(key); + int valueHash = hash(value); + + BiEntry oldEntryForKey = seekByKey(key, keyHash); + if (oldEntryForKey != null && valueHash == oldEntryForKey.valueHash + && Objects.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; + } + + @Nullable + private K putInverse(@Nullable V value, @Nullable K key, boolean force) { + int valueHash = hash(value); + int keyHash = hash(key); + + BiEntry oldEntryForValue = seekByValue(value, valueHash); + if (oldEntryForValue != null && keyHash == oldEntryForValue.keyHash + && Objects.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++; + } + } + + @SuppressWarnings("unchecked") + private BiEntry[] createTable(int length) { + return new BiEntry[length]; + } + + @Override + public V remove(@Nullable 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(@Nullable 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 && Objects.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(@Nullable Object value) { + return forward().containsValue(value); + } + + @Override + public K get(@Nullable Object value) { + BiEntry entry = seekByValue(value, hash(value)); + return (entry == null) ? null : entry.key; + } + + @Override + public K put(@Nullable V value, @Nullable K key) { + return putInverse(value, key, false); + } + + @Override + public K forcePut(@Nullable V value, @Nullable K key) { + return putInverse(value, key, true); + } + + @Override + public K remove(@Nullable Object value) { + BiEntry 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(@Nullable 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 && Objects.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. + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int size = Serialization.readCount(stream); + init(size); + Serialization.populateMap(this, stream, size); + } + + @GwtIncompatible("Not needed in emulated source") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/HashMultimap.java b/sources/main/java/com/google/common/collect/HashMultimap.java new file mode 100644 index 0000000..5b3fec3 --- /dev/null +++ b/sources/main/java/com/google/common/collect/HashMultimap.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; + +/** + * Implementation of {@link Multimap} using hash tables. + * + *

+ * The multimap does not store duplicate key-value pairs. Adding a new key-value + * pair equal to an existing key-value pair has no effect. + * + *

+ * Keys and values may be null. All optional multimap methods are supported, and + * all returned views are modifiable. + * + *

+ * This class is not threadsafe when any concurrent operations update the + * multimap. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap your multimap with a call to + * {@link Multimaps#synchronizedSetMultimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public final class HashMultimap extends AbstractSetMultimap { + private static final int DEFAULT_VALUES_PER_KEY = 2; + + @VisibleForTesting + transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; + + /** + * Creates a new, empty {@code HashMultimap} with the default initial + * capacities. + */ + public static HashMultimap create() { + return new HashMultimap(); + } + + /** + * Constructs an empty {@code HashMultimap} with enough capacity to hold the + * specified numbers of keys and values without rehashing. + * + * @param expectedKeys the expected number of distinct keys + * @param expectedValuesPerKey the expected average number of values per key + * @throws IllegalArgumentException if {@code expectedKeys} or {@code + * expectedValuesPerKey} is negative + */ + public static HashMultimap create(int expectedKeys, int expectedValuesPerKey) { + return new HashMultimap(expectedKeys, expectedValuesPerKey); + } + + /** + * Constructs a {@code HashMultimap} with the same mappings as the specified + * multimap. If a key-value mapping appears multiple times in the input + * multimap, it only appears once in the constructed multimap. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static HashMultimap create(Multimap multimap) { + return new HashMultimap(multimap); + } + + private HashMultimap() { + super(new HashMap>()); + } + + private HashMultimap(int expectedKeys, int expectedValuesPerKey) { + super(Maps.>newHashMapWithExpectedSize(expectedKeys)); + Preconditions.checkArgument(expectedValuesPerKey >= 0); + this.expectedValuesPerKey = expectedValuesPerKey; + } + + private HashMultimap(Multimap multimap) { + super(Maps.>newHashMapWithExpectedSize(multimap.keySet().size())); + putAll(multimap); + } + + /** + * {@inheritDoc} + * + *

+ * Creates an empty {@code HashSet} for a collection of values for one key. + * + * @return a new {@code HashSet} containing a collection of values for one key + */ + @Override + Set createCollection() { + return Sets.newHashSetWithExpectedSize(expectedValuesPerKey); + } + + /** + * @serialData expectedValuesPerKey, number of distinct keys, and then for each + * distinct key: the key, number of values for that key, and the + * key's values + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(expectedValuesPerKey); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + expectedValuesPerKey = stream.readInt(); + int distinctKeys = Serialization.readCount(stream); + Map> map = Maps.newHashMapWithExpectedSize(distinctKeys); + setMap(map); + Serialization.populateMultimap(this, stream, distinctKeys); + } + + @GwtIncompatible("Not needed in emulated source") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/HashMultiset.java b/sources/main/java/com/google/common/collect/HashMultiset.java new file mode 100644 index 0000000..fe5b7b8 --- /dev/null +++ b/sources/main/java/com/google/common/collect/HashMultiset.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * Multiset implementation backed by a {@link HashMap}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public final class HashMultiset extends AbstractMapBasedMultiset { + + /** + * Creates a new, empty {@code HashMultiset} using the default initial capacity. + */ + public static HashMultiset create() { + return new HashMultiset(); + } + + /** + * Creates a new, empty {@code HashMultiset} with the specified expected number + * of distinct elements. + * + * @param distinctElements the expected number of distinct elements + * @throws IllegalArgumentException if {@code distinctElements} is negative + */ + public static HashMultiset create(int distinctElements) { + return new HashMultiset(distinctElements); + } + + /** + * Creates a new {@code HashMultiset} containing the specified elements. + * + *

+ * This implementation is highly efficient when {@code elements} is itself a + * {@link Multiset}. + * + * @param elements the elements that the multiset should contain + */ + public static HashMultiset create(Iterable elements) { + HashMultiset multiset = create(Multisets.inferDistinctElements(elements)); + Iterables.addAll(multiset, elements); + return multiset; + } + + private HashMultiset() { + super(new HashMap()); + } + + private HashMultiset(int distinctElements) { + super(Maps.newHashMapWithExpectedSize(distinctElements)); + } + + /** + * @serialData the number of distinct elements, the first element, its count, + * the second element, its count, and so on + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int distinctElements = Serialization.readCount(stream); + setBackingMap(Maps.newHashMapWithExpectedSize(distinctElements)); + Serialization.populateMultiset(this, stream, distinctElements); + } + + @GwtIncompatible("Not needed in emulated source.") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/Hashing.java b/sources/main/java/com/google/common/collect/Hashing.java new file mode 100644 index 0000000..8ae5f49 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Hashing.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.primitives.Ints; + +/** + * Static methods for implementing hash-based collections. + * + * @author Kevin Bourrillion + * @author Jesse Wilson + * @author Austin Appleby + */ +@GwtCompatible +final class Hashing { + private Hashing() { + } + + private static final int C1 = 0xcc9e2d51; + private static final int C2 = 0x1b873593; + + /* + * This method was rewritten in Java from an intermediate step of the Murmur + * hash function in + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which + * contained the following header: + * + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. The author hereby disclaims copyright to this source code. + */ + static int smear(int hashCode) { + return C2 * Integer.rotateLeft(hashCode * C1, 15); + } + + static int smearedHash(@Nullable Object o) { + return smear((o == null) ? 0 : o.hashCode()); + } + + private static int MAX_TABLE_SIZE = Ints.MAX_POWER_OF_TWO; + + static int closedTableSize(int expectedEntries, double loadFactor) { + // Get the recommended table size. + // Round down to the nearest power of 2. + expectedEntries = Math.max(expectedEntries, 2); + int tableSize = Integer.highestOneBit(expectedEntries); + // Check to make sure that we will not exceed the maximum load factor. + if (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/sources/main/java/com/google/common/collect/ImmutableAsList.java b/sources/main/java/com/google/common/collect/ImmutableAsList.java new file mode 100644 index 0000000..f36dbfd --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableAsList.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * List returned by {@link ImmutableCollection#asList} that delegates + * {@code contains} checks to the backing collection. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") +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. + */ + @GwtIncompatible("serialization") + 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; + } + + @GwtIncompatible("serialization") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @GwtIncompatible("serialization") + @Override + Object writeReplace() { + return new SerializedForm(delegateCollection()); + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableBiMap.java b/sources/main/java/com/google/common/collect/ImmutableBiMap.java new file mode 100644 index 0000000..29b7dd7 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableBiMap.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map; + +import com.google.common.annotations.GwtCompatible; + +/** + * An immutable {@link BiMap} with reliable user-specified iteration order. Does + * not permit null keys or values. An {@code ImmutableBiMap} and its inverse + * have the same iteration ordering. + * + *

+ * An instance of {@code ImmutableBiMap} contains its own data and will + * never change. {@code ImmutableBiMap} is convenient for + * {@code public static final} maps ("constant maps") and also lets you easily + * make a "defensive copy" of a bimap 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 class are + * guaranteed to be immutable. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public abstract class ImmutableBiMap extends ImmutableMap implements BiMap { + + /** + * Returns the empty bimap. + */ + // Casting to any type is safe because the set will never hold any elements. + @SuppressWarnings("unchecked") + 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) { + @SuppressWarnings("unchecked") // 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: + @SuppressWarnings("unchecked") // 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 inverse().keySet(); + } + + /** + * 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/sources/main/java/com/google/common/collect/ImmutableClassToInstanceMap.java b/sources/main/java/com/google/common/collect/ImmutableClassToInstanceMap.java new file mode 100644 index 0000000..ab5aa17 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableClassToInstanceMap.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.primitives.Primitives; + +/** + * A class-to-instance map backed by an {@link ImmutableMap}. See also + * {@link MutableClassToInstanceMap}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +public final class ImmutableClassToInstanceMap extends ForwardingMap, B> + implements ClassToInstanceMap, Serializable { + + /** + * 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 class-to-instance maps. Example: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	static final ImmutableClassToInstanceMap HANDLERS = new ImmutableClassToInstanceMap.Builder()
+	 * 			.put(FooHandler.class, new FooHandler()).put(BarHandler.class, new SubBarHandler())
+	 * 			.put(Handler.class, new QuuxHandler()).build();
+	 * }
+	 * 
+ * + *

+ * After invoking {@link #build()} it is still possible to add more entries and + * build again. Thus each map generated by this builder will be a superset of + * any map generated before it. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static final class Builder { + private final ImmutableMap.Builder, B> mapBuilder = ImmutableMap.builder(); + + /** + * 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(Class key, T value) { + mapBuilder.put(key, value); + return this; + } + + /** + * Associates all of {@code 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 + * @throws ClassCastException if any value is not an instance of the type + * specified by its key + */ + public Builder putAll(Map, ? extends T> map) { + for (Entry, ? extends T> entry : map.entrySet()) { + Class type = entry.getKey(); + T value = entry.getValue(); + mapBuilder.put(type, cast(type, value)); + } + return this; + } + + private static T cast(Class type, B value) { + return Primitives.wrap(type).cast(value); + } + + /** + * Returns a new immutable class-to-instance map containing the entries provided + * to this builder. + * + * @throws IllegalArgumentException if duplicate keys were added + */ + public ImmutableClassToInstanceMap build() { + return new ImmutableClassToInstanceMap(mapBuilder.build()); + } + } + + /** + * 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. + * + *

+ * Note: Despite what the method name suggests, if {@code map} is an + * {@code ImmutableClassToInstanceMap}, no copy will actually be performed. + * + * @throws NullPointerException if any key or value in {@code map} is null + * @throws ClassCastException if any value is not an instance of the type + * specified by its key + */ + public static ImmutableClassToInstanceMap copyOf( + Map, ? extends S> map) { + if (map instanceof ImmutableClassToInstanceMap) { + @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable) + // Eclipse won't compile if we cast to the parameterized type. + ImmutableClassToInstanceMap cast = (ImmutableClassToInstanceMap) map; + return cast; + } + return new Builder().putAll(map).build(); + } + + private final ImmutableMap, B> delegate; + + private ImmutableClassToInstanceMap(ImmutableMap, B> delegate) { + this.delegate = delegate; + } + + @Override + protected Map, B> delegate() { + return delegate; + } + + @Override + @SuppressWarnings("unchecked") // value could not get in if not a T + @Nullable + public T getInstance(Class type) { + return (T) delegate.get(checkNotNull(type)); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public T putInstance(Class type, T value) { + throw new UnsupportedOperationException(); + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableCollection.java b/sources/main/java/com/google/common/collect/ImmutableCollection.java new file mode 100644 index 0000000..3f674cf --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableCollection.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.ObjectArrays.checkElementsNotNull; + +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * 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) + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +public 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(@Nullable 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/sources/main/java/com/google/common/collect/ImmutableEntry.java b/sources/main/java/com/google/common/collect/ImmutableEntry.java new file mode 100644 index 0000000..577e89f --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableEntry.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * @see com.google.common.collect.Maps#immutableEntry(Object, Object) + */ +@GwtCompatible(serializable = true) +class ImmutableEntry extends AbstractMapEntry implements Serializable { + final K key; + final V value; + + ImmutableEntry(@Nullable K key, @Nullable V value) { + this.key = key; + this.value = value; + } + + @Nullable + @Override + public final K getKey() { + return key; + } + + @Nullable + @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/sources/main/java/com/google/common/collect/ImmutableEnumMap.java b/sources/main/java/com/google/common/collect/ImmutableEnumMap.java new file mode 100644 index 0000000..a153291 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableEnumMap.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.io.Serializable; +import java.util.EnumMap; +import java.util.Iterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Implementation of {@link ImmutableMap} backed by a non-empty + * {@link java.util.EnumMap}. + * + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +final class ImmutableEnumMap, 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(@Nullable 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/sources/main/java/com/google/common/collect/ImmutableEnumSet.java b/sources/main/java/com/google/common/collect/ImmutableEnumSet.java new file mode 100644 index 0000000..84381e4 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableEnumSet.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.util.Collection; +import java.util.EnumSet; + +import com.google.common.annotations.GwtCompatible; + +/** + * Implementation of {@link ImmutableSet} backed by a non-empty + * {@link java.util.EnumSet}. + * + * @author Jared Levy + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // 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/sources/main/java/com/google/common/collect/ImmutableList.java b/sources/main/java/com/google/common/collect/ImmutableList.java new file mode 100644 index 0000000..0e00986 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableList.java @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkElementIndex; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkPositionIndexes; +import static com.google.common.collect.ObjectArrays.arraysCopyOf; +import static com.google.common.collect.ObjectArrays.checkElementsNotNull; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.RandomAccess; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * 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) + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // 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. + @SuppressWarnings("unchecked") + 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(Collections2.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) { + @SuppressWarnings("unchecked") // 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: + @SuppressWarnings("unchecked") // 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(@Nullable Object object) { + return (object == null) ? -1 : Lists.indexOfImpl(this, object); + } + + @Override + public int lastIndexOf(@Nullable Object object) { + return (object == null) ? -1 : Lists.lastIndexOfImpl(this, object); + } + + @Override + public boolean contains(@Nullable 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(@Nullable Object object) { + return forwardList.contains(object); + } + + @Override + public int indexOf(@Nullable Object object) { + int index = forwardList.lastIndexOf(object); + return (index >= 0) ? reverseIndex(index) : -1; + } + + @Override + public int lastIndexOf(@Nullable 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(); + } + } + + @Override + public boolean equals(@Nullable Object obj) { + return Lists.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/sources/main/java/com/google/common/collect/ImmutableListMultimap.java b/sources/main/java/com/google/common/collect/ImmutableListMultimap.java new file mode 100644 index 0000000..727b0c4 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableListMultimap.java @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An immutable {@link ListMultimap} with reliable user-specified key and value + * iteration order. Does not permit null keys or values. + * + *

+ * Unlike {@link Multimaps#unmodifiableListMultimap(ListMultimap)}, which is a + * view of a separate multimap which can still change, an instance of + * {@code ImmutableListMultimap} contains its own data and will never + * change. {@code ImmutableListMultimap} is convenient for + * {@code public static final} multimaps ("constant multimaps") and also lets + * you easily make a "defensive copy" of a multimap 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 class are + * guaranteed to be immutable. + * + *

+ * See the Guava User Guide article on + * immutable collections. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public class ImmutableListMultimap extends ImmutableMultimap implements ListMultimap { + + /** Returns the empty multimap. */ + // Casting is safe because the multimap will never hold any elements. + @SuppressWarnings("unchecked") + public static ImmutableListMultimap of() { + return (ImmutableListMultimap) EmptyImmutableListMultimap.INSTANCE; + } + + /** + * Returns an immutable multimap containing a single entry. + */ + public static ImmutableListMultimap of(K k1, V v1) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + builder.put(k5, v5); + return builder.build(); + } + + // 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 {@code ListMultimap} instances, especially + * {@code public static final} multimaps ("constant multimaps"). Example: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	static final Multimap STRING_TO_INTEGER_MULTIMAP = new ImmutableListMultimap.Builder()
+	 * 			.put("one", 1).putAll("several", 1, 2, 3).putAll("many", 1, 2, 3, 4, 5).build();
+	 * }
+	 * 
+ * + *

+ * Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple multimaps in series. Each multimap contains the + * key-value mappings in the previously created multimaps. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static final class Builder extends ImmutableMultimap.Builder { + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableListMultimap#builder}. + */ + public Builder() { + } + + @Override + public Builder put(K key, V value) { + super.put(key, value); + return this; + } + + /** + * {@inheritDoc} + * + * @since 11.0 + */ + @Override + public Builder put(Entry entry) { + super.put(entry); + return this; + } + + @Override + public Builder putAll(K key, Iterable values) { + super.putAll(key, values); + return this; + } + + @Override + public Builder putAll(K key, V... values) { + super.putAll(key, values); + return this; + } + + @Override + public Builder putAll(Multimap multimap) { + super.putAll(multimap); + return this; + } + + /** + * {@inheritDoc} + * + * @since 8.0 + */ + @Override + public Builder orderKeysBy(Comparator keyComparator) { + super.orderKeysBy(keyComparator); + return this; + } + + /** + * {@inheritDoc} + * + * @since 8.0 + */ + @Override + public Builder orderValuesBy(Comparator valueComparator) { + super.orderValuesBy(valueComparator); + return this; + } + + /** + * Returns a newly-created immutable list multimap. + */ + @Override + public ImmutableListMultimap build() { + return (ImmutableListMultimap) super.build(); + } + } + + /** + * Returns an immutable multimap containing the same mappings as {@code + * multimap}. The generated multimap's key and value orderings correspond to the + * iteration ordering of the {@code multimap.asMap()} view. + * + *

+ * 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 multimap} is null + */ + public static ImmutableListMultimap copyOf(Multimap multimap) { + if (multimap.isEmpty()) { + return of(); + } + + // TODO(user): copy ImmutableSetMultimap by using asList() on the sets + if (multimap instanceof ImmutableListMultimap) { + @SuppressWarnings("unchecked") // safe since multimap is not writable + ImmutableListMultimap kvMultimap = (ImmutableListMultimap) multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int size = 0; + + for (Entry> entry : multimap.asMap().entrySet()) { + ImmutableList list = ImmutableList.copyOf(entry.getValue()); + if (!list.isEmpty()) { + builder.put(entry.getKey(), list); + size += list.size(); + } + } + + return new ImmutableListMultimap(builder.build(), size); + } + + ImmutableListMultimap(ImmutableMap> map, int size) { + super(map, size); + } + + // views + + /** + * Returns an immutable list of the values for the given key. If no mappings in + * the multimap have the provided key, an empty immutable list is returned. The + * values are in the same order as the parameters used to build this multimap. + */ + @Override + public ImmutableList get(@Nullable K key) { + // This cast is safe as its type is known in constructor. + ImmutableList list = (ImmutableList) map.get(key); + return (list == null) ? ImmutableList.of() : list; + } + + private transient ImmutableListMultimap inverse; + + /** + * {@inheritDoc} + * + *

+ * Because an inverse of a list multimap can contain multiple pairs with the + * same key and value, this method returns an {@code + * ImmutableListMultimap} rather than the {@code ImmutableMultimap} specified in + * the {@code ImmutableMultimap} class. + * + * @since 11.0 + */ + @Override + public ImmutableListMultimap inverse() { + ImmutableListMultimap result = inverse; + return (result == null) ? (inverse = invert()) : result; + } + + private ImmutableListMultimap invert() { + Builder builder = builder(); + for (Entry entry : entries()) { + builder.put(entry.getValue(), entry.getKey()); + } + ImmutableListMultimap invertedMultimap = builder.build(); + invertedMultimap.inverse = this; + return invertedMultimap; + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public ImmutableList removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public ImmutableList replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** + * @serialData number of distinct keys, and then for each distinct key: the key, + * the number of values for that key, and the key's values + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int keyCount = stream.readInt(); + if (keyCount < 0) { + throw new InvalidObjectException("Invalid key count " + keyCount); + } + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int tmpSize = 0; + + for (int i = 0; i < keyCount; i++) { + Object key = stream.readObject(); + int valueCount = stream.readInt(); + if (valueCount <= 0) { + throw new InvalidObjectException("Invalid value count " + valueCount); + } + + Object[] array = new Object[valueCount]; + for (int j = 0; j < valueCount; j++) { + array[j] = stream.readObject(); + } + builder.put(key, ImmutableList.copyOf(array)); + tmpSize += valueCount; + } + + ImmutableMap> tmpMap; + try { + tmpMap = builder.build(); + } catch (IllegalArgumentException e) { + throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); + } + + FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); + FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); + } + + @GwtIncompatible("Not needed in emulated source") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ImmutableMap.java b/sources/main/java/com/google/common/collect/ImmutableMap.java new file mode 100644 index 0000000..139938b --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableMap.java @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; + +import java.io.Serializable; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.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) + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // 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 ImmutableBiMap.of(); + } + + /** + * 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); + } + + @SuppressWarnings("unchecked") + 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 + + @SuppressWarnings("unchecked") // 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: + @SuppressWarnings("unchecked") // 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 >. + @SuppressWarnings({ "unchecked", "rawtypes" }) + 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(@Nullable Object key) { + return get(key) != null; + } + + @Override + public boolean containsValue(@Nullable Object value) { + return values().contains(value); + } + + // Overriding to mark it Nullable + @Override + public abstract V get(@Nullable 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(@Nullable Object key) { + return delegate.containsKey(key); + } + + @Override + public ImmutableSet get(@Nullable 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(@Nullable 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/sources/main/java/com/google/common/collect/ImmutableMapEntry.java b/sources/main/java/com/google/common/collect/ImmutableMapEntry.java new file mode 100644 index 0000000..aad5174 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableMapEntry.java @@ -0,0 +1,72 @@ +/* + * 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 com.google.common.collect; + +import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtIncompatible; + +/** + * 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 + */ +@GwtIncompatible("unnecessary") +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 + } + + @Nullable + abstract ImmutableMapEntry getNextInKeyBucket(); + + @Nullable + abstract ImmutableMapEntry getNextInValueBucket(); + + static final class TerminalEntry extends ImmutableMapEntry { + TerminalEntry(ImmutableMapEntry contents) { + super(contents); + } + + TerminalEntry(K key, V value) { + super(key, value); + } + + @Override + @Nullable + ImmutableMapEntry getNextInKeyBucket() { + return null; + } + + @Override + @Nullable + ImmutableMapEntry getNextInValueBucket() { + return null; + } + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableMapEntrySet.java b/sources/main/java/com/google/common/collect/ImmutableMapEntrySet.java new file mode 100644 index 0000000..01610ae --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableMapEntrySet.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * {@code entrySet()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(emulated = true) +abstract class ImmutableMapEntrySet extends ImmutableSet> { + ImmutableMapEntrySet() { + } + + abstract ImmutableMap map(); + + @Override + public int size() { + return map().size(); + } + + @Override + public boolean contains(@Nullable Object object) { + if (object instanceof Entry) { + Entry entry = (Entry) object; + V value = map().get(entry.getKey()); + return value != null && value.equals(entry.getValue()); + } + return false; + } + + @Override + boolean isPartialView() { + return map().isPartialView(); + } + + @GwtIncompatible("serialization") + @Override + Object writeReplace() { + return new EntrySetSerializedForm(map()); + } + + @GwtIncompatible("serialization") + 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/sources/main/java/com/google/common/collect/ImmutableMapKeySet.java b/sources/main/java/com/google/common/collect/ImmutableMapKeySet.java new file mode 100644 index 0000000..aeecfd5 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableMapKeySet.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * {@code keySet()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(emulated = true) +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(@Nullable 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; + } + + @GwtIncompatible("serialization") + @Override + Object writeReplace() { + return new KeySetSerializedForm(map); + } + + @GwtIncompatible("serialization") + 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/sources/main/java/com/google/common/collect/ImmutableMapValues.java b/sources/main/java/com/google/common/collect/ImmutableMapValues.java new file mode 100644 index 0000000..c33fee2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableMapValues.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * {@code values()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(emulated = true) +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(@Nullable 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; + } + }; + } + + @GwtIncompatible("serialization") + @Override + Object writeReplace() { + return new SerializedForm(map); + } + + @GwtIncompatible("serialization") + 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/sources/main/java/com/google/common/collect/ImmutableMultimap.java b/sources/main/java/com/google/common/collect/ImmutableMultimap.java new file mode 100644 index 0000000..d7c7fe6 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableMultimap.java @@ -0,0 +1,676 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An immutable {@link Multimap}. Does not permit null keys or values. + * + *

+ * Unlike {@link Multimaps#unmodifiableMultimap(Multimap)}, which is a + * view of a separate multimap which can still change, an instance of + * {@code ImmutableMultimap} contains its own data and will never change. + * {@code ImmutableMultimap} is convenient for {@code public static final} + * multimaps ("constant multimaps") and also lets you easily make a "defensive + * copy" of a multimap 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 class are + * guaranteed to be immutable. + * + *

+ * In addition to methods defined by {@link Multimap}, an {@link #inverse} + * method is also supported. + * + *

+ * See the Guava User Guide article on + * immutable collections. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public abstract class ImmutableMultimap extends AbstractMultimap implements Serializable { + + /** Returns an empty multimap. */ + public static ImmutableMultimap of() { + return ImmutableListMultimap.of(); + } + + /** + * Returns an immutable multimap containing a single entry. + */ + public static ImmutableMultimap of(K k1, V v1) { + return ImmutableListMultimap.of(k1, v1); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableMultimap of(K k1, V v1, K k2, V v2) { + return ImmutableListMultimap.of(k1, v1, k2, v2); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + */ + public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); + } + + // 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(); + } + + /** + * Multimap for {@link ImmutableMultimap.Builder} that maintains key and value + * orderings, allows duplicate values, and performs better than + * {@link LinkedListMultimap}. + */ + private static class BuilderMultimap extends AbstractMapBasedMultimap { + BuilderMultimap() { + super(new LinkedHashMap>()); + } + + @Override + Collection createCollection() { + return Lists.newArrayList(); + } + + private static final long serialVersionUID = 0; + } + + /** + * A builder for creating immutable multimap instances, especially + * {@code public static final} multimaps ("constant multimaps"). Example: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	static final Multimap STRING_TO_INTEGER_MULTIMAP = new ImmutableMultimap.Builder()
+	 * 			.put("one", 1).putAll("several", 1, 2, 3).putAll("many", 1, 2, 3, 4, 5).build();
+	 * }
+	 * 
+ * + *

+ * Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple multimaps in series. Each multimap contains the + * key-value mappings in the previously created multimaps. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static class Builder { + Multimap builderMultimap = new BuilderMultimap(); + Comparator keyComparator; + Comparator valueComparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableMultimap#builder}. + */ + public Builder() { + } + + /** + * Adds a key-value mapping to the built multimap. + */ + public Builder put(K key, V value) { + checkEntryNotNull(key, value); + builderMultimap.put(key, value); + return this; + } + + /** + * Adds an entry to the built multimap. + * + * @since 11.0 + */ + public Builder put(Entry entry) { + return put(entry.getKey(), entry.getValue()); + } + + /** + * Stores a collection of values with the same key in the built multimap. + * + * @throws NullPointerException if {@code key}, {@code values}, or any element + * in {@code values} is null. The builder is left + * in an invalid state. + */ + public Builder putAll(K key, Iterable values) { + if (key == null) { + throw new NullPointerException("null key in entry: null=" + Iterables.toString(values)); + } + Collection valueList = builderMultimap.get(key); + for (V value : values) { + checkEntryNotNull(key, value); + valueList.add(value); + } + return this; + } + + /** + * Stores an array of values with the same key in the built multimap. + * + * @throws NullPointerException if the key or any value is null. The builder is + * left in an invalid state. + */ + public Builder putAll(K key, V... values) { + return putAll(key, Arrays.asList(values)); + } + + /** + * Stores another multimap's entries in the built multimap. The generated + * multimap's key and value orderings correspond to the iteration ordering of + * the {@code multimap.asMap()} view, with new keys and values following any + * existing keys and values. + * + * @throws NullPointerException if any key or value in {@code multimap} is null. + * The builder is left in an invalid state. + */ + public Builder putAll(Multimap multimap) { + for (Entry> entry : multimap.asMap().entrySet()) { + putAll(entry.getKey(), entry.getValue()); + } + return this; + } + + /** + * Specifies the ordering of the generated multimap's keys. + * + * @since 8.0 + */ + public Builder orderKeysBy(Comparator keyComparator) { + this.keyComparator = checkNotNull(keyComparator); + return this; + } + + /** + * Specifies the ordering of the generated multimap's values for each key. + * + * @since 8.0 + */ + public Builder orderValuesBy(Comparator valueComparator) { + this.valueComparator = checkNotNull(valueComparator); + return this; + } + + /** + * Returns a newly-created immutable multimap. + */ + public ImmutableMultimap build() { + if (valueComparator != null) { + for (Collection values : builderMultimap.asMap().values()) { + List list = (List) values; + Collections.sort(list, valueComparator); + } + } + if (keyComparator != null) { + Multimap sortedCopy = new BuilderMultimap(); + List>> entries = Lists.newArrayList(builderMultimap.asMap().entrySet()); + Collections.sort(entries, Ordering.from(keyComparator).onKeys()); + for (Map.Entry> entry : entries) { + sortedCopy.putAll(entry.getKey(), entry.getValue()); + } + builderMultimap = sortedCopy; + } + return copyOf(builderMultimap); + } + } + + /** + * Returns an immutable multimap containing the same mappings as {@code + * multimap}. The generated multimap's key and value orderings correspond to the + * iteration ordering of the {@code multimap.asMap()} view. + * + *

+ * 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 multimap} is null + */ + public static ImmutableMultimap copyOf(Multimap multimap) { + if (multimap instanceof ImmutableMultimap) { + @SuppressWarnings("unchecked") // safe since multimap is not writable + ImmutableMultimap kvMultimap = (ImmutableMultimap) multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + return ImmutableListMultimap.copyOf(multimap); + } + + final transient ImmutableMap> map; + final transient int size; + + // These constants allow the deserialization code to set final fields. This + // holder class makes sure they are not initialized unless an instance is + // deserialized. + @GwtIncompatible("java serialization is not supported") + static class FieldSettersHolder { + static final Serialization.FieldSetter MAP_FIELD_SETTER = Serialization + .getFieldSetter(ImmutableMultimap.class, "map"); + static final Serialization.FieldSetter SIZE_FIELD_SETTER = Serialization + .getFieldSetter(ImmutableMultimap.class, "size"); + static final Serialization.FieldSetter EMPTY_SET_FIELD_SETTER = Serialization + .getFieldSetter(ImmutableSetMultimap.class, "emptySet"); + } + + ImmutableMultimap(ImmutableMap> map, int size) { + this.map = map; + this.size = size; + } + + // mutators (not supported) + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public ImmutableCollection removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public ImmutableCollection replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Returns an immutable collection of the values for the given key. If no + * mappings in the multimap have the provided key, an empty immutable collection + * is returned. The values are in the same order as the parameters used to build + * this multimap. + */ + @Override + public abstract ImmutableCollection get(K key); + + /** + * Returns an immutable multimap which is the inverse of this one. For every + * key-value mapping in the original, the result will have a mapping with key + * and value reversed. + * + * @since 11.0 + */ + public abstract ImmutableMultimap inverse(); + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + /** + * Returns {@code true} if this immutable multimap's implementation contains + * references to user-created objects that aren't accessible via this multimap's + * methods. This is generally used to determine whether {@code copyOf} + * implementations should make an explicit copy to avoid memory leaks. + */ + boolean isPartialView() { + return map.isPartialView(); + } + + // accessors + + @Override + public boolean containsKey(@Nullable Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return value != null && super.containsValue(value); + } + + @Override + public int size() { + return size; + } + + // views + + /** + * Returns an immutable set of the distinct keys in this multimap. These keys + * are ordered according to when they first appeared during the construction of + * this multimap. + */ + @Override + public ImmutableSet keySet() { + return map.keySet(); + } + + /** + * Returns an immutable map that associates each key with its corresponding + * values in the multimap. + */ + @Override + @SuppressWarnings("unchecked") // a widening cast + public ImmutableMap> asMap() { + return (ImmutableMap) map; + } + + @Override + Map> createAsMap() { + throw new AssertionError("should never be called"); + } + + /** + * Returns an immutable collection of all key-value pairs in the multimap. Its + * iterator traverses the values for the first key, the values for the second + * key, and so on. + */ + @Override + public ImmutableCollection> entries() { + return (ImmutableCollection>) super.entries(); + } + + @Override + ImmutableCollection> createEntries() { + return new EntryCollection(this); + } + + private static class EntryCollection extends ImmutableCollection> { + final ImmutableMultimap multimap; + + EntryCollection(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + @Override + public UnmodifiableIterator> iterator() { + return multimap.entryIterator(); + } + + @Override + boolean isPartialView() { + return multimap.isPartialView(); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + public boolean contains(Object object) { + if (object instanceof Entry) { + Entry entry = (Entry) object; + return multimap.containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + private static final long serialVersionUID = 0; + } + + private abstract class Itr extends UnmodifiableIterator { + final Iterator>> mapIterator = asMap().entrySet().iterator(); + K key = null; + Iterator valueIterator = Iterators.emptyIterator(); + + abstract T output(K key, V value); + + @Override + public boolean hasNext() { + return mapIterator.hasNext() || valueIterator.hasNext(); + } + + @Override + public T next() { + if (!valueIterator.hasNext()) { + Entry> mapEntry = mapIterator.next(); + key = mapEntry.getKey(); + valueIterator = mapEntry.getValue().iterator(); + } + return output(key, valueIterator.next()); + } + } + + @Override + UnmodifiableIterator> entryIterator() { + return new Itr>() { + @Override + Entry output(K key, V value) { + return Maps.immutableEntry(key, value); + } + }; + } + + /** + * Returns a collection, which may contain duplicates, of all keys. The number + * of times a key appears in the returned multiset equals the number of mappings + * the key has in the multimap. Duplicate keys appear consecutively in the + * multiset's iteration order. + */ + @Override + public ImmutableMultiset keys() { + return (ImmutableMultiset) super.keys(); + } + + @Override + ImmutableMultiset createKeys() { + return new Keys(); + } + + @SuppressWarnings("serial") // Uses writeReplace, not default serialization + class Keys extends ImmutableMultiset { + @Override + public boolean contains(@Nullable Object object) { + return containsKey(object); + } + + @Override + public int count(@Nullable Object element) { + Collection values = map.get(element); + return (values == null) ? 0 : values.size(); + } + + @Override + public Set elementSet() { + return keySet(); + } + + @Override + public int size() { + return ImmutableMultimap.this.size(); + } + + @Override + Multiset.Entry getEntry(int index) { + Map.Entry> entry = map.entrySet().asList().get(index); + return Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); + } + + @Override + boolean isPartialView() { + return true; + } + } + + /** + * Returns an immutable collection of the values in this multimap. Its iterator + * traverses the values for the first key, the values for the second key, and so + * on. + */ + @Override + public ImmutableCollection values() { + return (ImmutableCollection) super.values(); + } + + @Override + ImmutableCollection createValues() { + return new Values(this); + } + + @Override + UnmodifiableIterator valueIterator() { + return new Itr() { + @Override + V output(K key, V value) { + return value; + } + }; + } + + private static final class Values extends ImmutableCollection { + private transient final ImmutableMultimap multimap; + + Values(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + @Override + public boolean contains(@Nullable Object object) { + return multimap.containsValue(object); + } + + @Override + public UnmodifiableIterator iterator() { + return multimap.valueIterator(); + } + + @GwtIncompatible("not present in emulated superclass") + @Override + int copyIntoArray(Object[] dst, int offset) { + for (ImmutableCollection valueCollection : multimap.map.values()) { + offset = valueCollection.copyIntoArray(dst, offset); + } + return offset; + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + boolean isPartialView() { + return true; + } + + private static final long serialVersionUID = 0; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ImmutableMultiset.java b/sources/main/java/com/google/common/collect/ImmutableMultiset.java new file mode 100644 index 0000000..7f08336 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableMultiset.java @@ -0,0 +1,603 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.primitives.Ints; + +/** + * An immutable hash-based multiset. Does not permit null elements. + * + *

+ * Its iterator orders elements according to the first appearance of the element + * among the items passed to the factory method or builder. When the multiset + * contains multiple instances of an element, those instances are consecutive in + * the iteration order. + * + *

+ * See the Guava User Guide article on + * immutable collections. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +// TODO(user): write an efficient asList() implementation +public abstract class ImmutableMultiset extends ImmutableCollection implements Multiset { + + private static final ImmutableMultiset EMPTY = new RegularImmutableMultiset( + ImmutableMap.of(), 0); + + /** + * Returns the empty immutable multiset. + */ + @SuppressWarnings("unchecked") // all supported methods are covariant + public static ImmutableMultiset of() { + return (ImmutableMultiset) EMPTY; + } + + /** + * Returns an immutable multiset containing a single element. + * + * @throws NullPointerException if {@code element} is null + * @since 6.0 (source-compatible since 2.0) + */ + @SuppressWarnings("unchecked") // generic array created but never written + public static ImmutableMultiset of(E element) { + return copyOfInternal(element); + } + + /** + * Returns an immutable multiset containing the given elements, in order. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + @SuppressWarnings("unchecked") // + public static ImmutableMultiset of(E e1, E e2) { + return copyOfInternal(e1, e2); + } + + /** + * Returns an immutable multiset containing the given elements, in order. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + @SuppressWarnings("unchecked") // + public static ImmutableMultiset of(E e1, E e2, E e3) { + return copyOfInternal(e1, e2, e3); + } + + /** + * Returns an immutable multiset containing the given elements, in order. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + @SuppressWarnings("unchecked") // + public static ImmutableMultiset of(E e1, E e2, E e3, E e4) { + return copyOfInternal(e1, e2, e3, e4); + } + + /** + * Returns an immutable multiset containing the given elements, in order. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + @SuppressWarnings("unchecked") // + public static ImmutableMultiset of(E e1, E e2, E e3, E e4, E e5) { + return copyOfInternal(e1, e2, e3, e4, e5); + } + + /** + * Returns an immutable multiset containing the given elements, in order. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + @SuppressWarnings("unchecked") // + public static ImmutableMultiset of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) { + return new Builder().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build(); + } + + /** + * Returns an immutable multiset containing the given elements. + * + *

+ * The multiset is ordered by the first occurrence of each element. For example, + * {@code ImmutableMultiset.copyOf([2, 3, 1, 3])} yields a multiset with + * elements in the order {@code 2, 3, 3, 1}. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 6.0 + */ + public static ImmutableMultiset copyOf(E[] elements) { + return copyOf(Arrays.asList(elements)); + } + + /** + * Returns an immutable multiset containing the given elements. + * + *

+ * The multiset is ordered by the first occurrence of each element. For example, + * {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3))} yields a multiset + * with elements in the order {@code 2, 3, 3, 1}. + * + *

+ * 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: Despite what the method name suggests, if {@code elements} is an + * {@code ImmutableMultiset}, no copy will actually be performed, and the given + * multiset itself will be returned. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableMultiset copyOf(Iterable elements) { + if (elements instanceof ImmutableMultiset) { + @SuppressWarnings("unchecked") // all supported methods are covariant + ImmutableMultiset result = (ImmutableMultiset) elements; + if (!result.isPartialView()) { + return result; + } + } + + Multiset multiset = (elements instanceof Multiset) ? Multisets.cast(elements) + : LinkedHashMultiset.create(elements); + + return copyOfInternal(multiset); + } + + private static ImmutableMultiset copyOfInternal(E... elements) { + return copyOf(Arrays.asList(elements)); + } + + private static ImmutableMultiset copyOfInternal(Multiset multiset) { + return copyFromEntries(multiset.entrySet()); + } + + static ImmutableMultiset copyFromEntries(Collection> entries) { + long size = 0; + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (Entry entry : entries) { + int count = entry.getCount(); + if (count > 0) { + // Since ImmutableMap.Builder throws an NPE if an element is null, no + // other null checks are needed. + builder.put(entry.getElement(), count); + size += count; + } + } + + if (size == 0) { + return of(); + } + return new RegularImmutableMultiset(builder.build(), Ints.saturatedCast(size)); + } + + /** + * Returns an immutable multiset containing the given elements. + * + *

+ * The multiset is ordered by the first occurrence of each element. For example, + * {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3).iterator())} yields + * a multiset with elements in the order {@code 2, 3, 3, 1}. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableMultiset copyOf(Iterator elements) { + Multiset multiset = LinkedHashMultiset.create(); + Iterators.addAll(multiset, elements); + return copyOfInternal(multiset); + } + + ImmutableMultiset() { + } + + @Override + public UnmodifiableIterator iterator() { + final Iterator> entryIterator = entrySet().iterator(); + return new UnmodifiableIterator() { + int remaining; + E element; + + @Override + public boolean hasNext() { + return (remaining > 0) || entryIterator.hasNext(); + } + + @Override + public E next() { + if (remaining <= 0) { + Entry entry = entryIterator.next(); + element = entry.getElement(); + remaining = entry.getCount(); + } + remaining--; + return element; + } + }; + } + + @Override + public boolean contains(@Nullable Object object) { + return count(object) > 0; + } + + @Override + public boolean containsAll(Collection targets) { + return elementSet().containsAll(targets); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final int add(E element, int occurrences) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final int remove(Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final int setCount(E element, int count) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean setCount(E element, int oldCount, int newCount) { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible("not present in emulated superclass") + @Override + int copyIntoArray(Object[] dst, int offset) { + for (Multiset.Entry entry : entrySet()) { + Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement()); + offset += entry.getCount(); + } + return offset; + } + + @Override + public boolean equals(@Nullable Object object) { + return Multisets.equalsImpl(this, object); + } + + @Override + public int hashCode() { + return Sets.hashCodeImpl(entrySet()); + } + + @Override + public String toString() { + return entrySet().toString(); + } + + private transient ImmutableSet> entrySet; + + @Override + public ImmutableSet> entrySet() { + ImmutableSet> es = entrySet; + return (es == null) ? (entrySet = createEntrySet()) : es; + } + + private final ImmutableSet> createEntrySet() { + return isEmpty() ? ImmutableSet.>of() : new EntrySet(); + } + + abstract Entry getEntry(int index); + + private final class EntrySet extends ImmutableSet> { + @Override + boolean isPartialView() { + return ImmutableMultiset.this.isPartialView(); + } + + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + ImmutableList> createAsList() { + return new ImmutableAsList>() { + @Override + public Entry get(int index) { + return getEntry(index); + } + + @Override + ImmutableCollection> delegateCollection() { + return EntrySet.this; + } + }; + } + + @Override + public int size() { + return elementSet().size(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + if (entry.getCount() <= 0) { + return false; + } + int count = count(entry.getElement()); + return count == entry.getCount(); + } + return false; + } + + @Override + public int hashCode() { + return ImmutableMultiset.this.hashCode(); + } + + // We can't label this with @Override, because it doesn't override anything + // in the GWT emulated version. + // TODO(cpovirk): try making all copies of this method @GwtIncompatible instead + Object writeReplace() { + return new EntrySetSerializedForm(ImmutableMultiset.this); + } + + private static final long serialVersionUID = 0; + } + + static class EntrySetSerializedForm implements Serializable { + final ImmutableMultiset multiset; + + EntrySetSerializedForm(ImmutableMultiset multiset) { + this.multiset = multiset; + } + + Object readResolve() { + return multiset.entrySet(); + } + } + + private static class SerializedForm implements Serializable { + final Object[] elements; + final int[] counts; + + SerializedForm(Multiset multiset) { + int distinct = multiset.entrySet().size(); + elements = new Object[distinct]; + counts = new int[distinct]; + int i = 0; + for (Entry entry : multiset.entrySet()) { + elements[i] = entry.getElement(); + counts[i] = entry.getCount(); + i++; + } + } + + Object readResolve() { + LinkedHashMultiset multiset = LinkedHashMultiset.create(elements.length); + for (int i = 0; i < elements.length; i++) { + multiset.add(elements[i], counts[i]); + } + return ImmutableMultiset.copyOf(multiset); + } + + private static final long serialVersionUID = 0; + } + + // We can't label this with @Override, because it doesn't override anything + // in the GWT emulated version. + Object writeReplace() { + return new SerializedForm(this); + } + + /** + * 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 multiset instances, especially {@code + * public static final} multisets ("constant multisets"). Example: + * + *
+	 * {
+	 * 	@code
+	 *
+	 * 	public static final ImmutableMultiset BEANS = new ImmutableMultiset.Builder()
+	 * 			.addCopies(Bean.COCOA, 4).addCopies(Bean.GARDEN, 6).addCopies(Bean.RED, 8)
+	 * 			.addCopies(Bean.BLACK_EYED, 10).build();
+	 * }
+	 * 
+ * + *

+ * Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple multisets in series. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static class Builder extends ImmutableCollection.Builder { + final Multiset contents; + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableMultiset#builder}. + */ + public Builder() { + this(LinkedHashMultiset.create()); + } + + Builder(Multiset contents) { + this.contents = contents; + } + + /** + * Adds {@code element} to the {@code ImmutableMultiset}. + * + * @param element the element to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + @Override + public Builder add(E element) { + contents.add(checkNotNull(element)); + return this; + } + + /** + * Adds a number of occurrences of an element to this {@code + * ImmutableMultiset}. + * + * @param element the element to add + * @param occurrences the number of occurrences of the element to add. May be + * zero, in which case no change will be made. + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + * @throws IllegalArgumentException if {@code occurrences} is negative, or if + * this operation would result in more than + * {@link Integer#MAX_VALUE} occurrences of the + * element + */ + public Builder addCopies(E element, int occurrences) { + contents.add(checkNotNull(element), occurrences); + return this; + } + + /** + * Adds or removes the necessary occurrences of an element such that the element + * attains the desired count. + * + * @param element the element to add or remove occurrences of + * @param count the desired count of the element in this multiset + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + * @throws IllegalArgumentException if {@code count} is negative + */ + public Builder setCount(E element, int count) { + contents.setCount(checkNotNull(element), count); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableMultiset}. + * + * @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 ImmutableMultiset}. + * + * @param elements the {@code Iterable} to add to the {@code + * ImmutableMultiset} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null + * element + */ + @Override + public Builder addAll(Iterable elements) { + if (elements instanceof Multiset) { + Multiset multiset = Multisets.cast(elements); + for (Entry entry : multiset.entrySet()) { + addCopies(entry.getElement(), entry.getCount()); + } + } else { + super.addAll(elements); + } + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableMultiset}. + * + * @param elements the elements to add to the {@code ImmutableMultiset} + * @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 ImmutableMultiset} based on the contents of + * the {@code Builder}. + */ + @Override + public ImmutableMultiset build() { + return copyOf(contents); + } + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableRangeMap.java b/sources/main/java/com/google/common/collect/ImmutableRangeMap.java new file mode 100644 index 0000000..d938ca7 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableRangeMap.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkElementIndex; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.SortedLists.KeyAbsentBehavior; +import com.google.common.collect.SortedLists.KeyPresentBehavior; + +/** + * An immutable implementation of {@code RangeMap}, supporting all query + * operations efficiently. + * + *

+ * Like all {@code RangeMap} implementations, this supports neither null keys + * nor null values. + * + * @author Louis Wasserman + * @since 14.0 + */ +@Beta +@GwtIncompatible("NavigableMap") +public class ImmutableRangeMap, V> implements RangeMap { + + private static final ImmutableRangeMap, Object> EMPTY = new ImmutableRangeMap, Object>( + ImmutableList.>>of(), ImmutableList.of()); + + /** + * Returns an empty immutable range map. + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableRangeMap of() { + return (ImmutableRangeMap) EMPTY; + } + + /** + * Returns an immutable range map mapping a single range to a single value. + */ + public static , V> ImmutableRangeMap of(Range range, V value) { + return new ImmutableRangeMap(ImmutableList.of(range), ImmutableList.of(value)); + } + + @SuppressWarnings("unchecked") + public static , V> ImmutableRangeMap copyOf(RangeMap rangeMap) { + if (rangeMap instanceof ImmutableRangeMap) { + return (ImmutableRangeMap) rangeMap; + } + Map, ? extends V> map = rangeMap.asMapOfRanges(); + ImmutableList.Builder> rangesBuilder = new ImmutableList.Builder>(map.size()); + ImmutableList.Builder valuesBuilder = new ImmutableList.Builder(map.size()); + for (Entry, ? extends V> entry : map.entrySet()) { + rangesBuilder.add(entry.getKey()); + valuesBuilder.add(entry.getValue()); + } + return new ImmutableRangeMap(rangesBuilder.build(), valuesBuilder.build()); + } + + /** + * Returns a new builder for an immutable range map. + */ + public static , V> Builder builder() { + return new Builder(); + } + + /** + * A builder for immutable range maps. Overlapping ranges are prohibited. + */ + public static final class Builder, V> { + private final RangeSet keyRanges; + private final RangeMap rangeMap; + + public Builder() { + this.keyRanges = TreeRangeSet.create(); + this.rangeMap = TreeRangeMap.create(); + } + + /** + * Associates the specified range with the specified value. + * + * @throws IllegalArgumentException if {@code range} overlaps with any other + * ranges inserted into this builder, or if + * {@code range} is empty + */ + public Builder put(Range range, V value) { + checkNotNull(range); + checkNotNull(value); + checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range); + if (!keyRanges.complement().encloses(range)) { + // it's an error case; we can afford an expensive lookup + for (Entry, V> entry : rangeMap.asMapOfRanges().entrySet()) { + Range key = entry.getKey(); + if (key.isConnected(range) && !key.intersection(range).isEmpty()) { + throw new IllegalArgumentException( + "Overlapping ranges: range " + range + " overlaps with entry " + entry); + } + } + } + keyRanges.add(range); + rangeMap.put(range, value); + return this; + } + + /** + * Copies all associations from the specified range map into this builder. + * + * @throws IllegalArgumentException if any of the ranges in {@code rangeMap} + * overlap with ranges already in this builder + */ + public Builder putAll(RangeMap rangeMap) { + for (Entry, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + /** + * Returns an {@code ImmutableRangeMap} containing the associations previously + * added to this builder. + */ + public ImmutableRangeMap build() { + Map, V> map = rangeMap.asMapOfRanges(); + ImmutableList.Builder> rangesBuilder = new ImmutableList.Builder>(map.size()); + ImmutableList.Builder valuesBuilder = new ImmutableList.Builder(map.size()); + for (Entry, V> entry : map.entrySet()) { + rangesBuilder.add(entry.getKey()); + valuesBuilder.add(entry.getValue()); + } + return new ImmutableRangeMap(rangesBuilder.build(), valuesBuilder.build()); + } + } + + private final ImmutableList> ranges; + private final ImmutableList values; + + ImmutableRangeMap(ImmutableList> ranges, ImmutableList values) { + this.ranges = ranges; + this.values = values; + } + + @Override + @Nullable + public V get(K key) { + int index = SortedLists.binarySearch(ranges, Range.lowerBoundFn(), Cut.belowValue(key), + KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_LOWER); + if (index == -1) { + return null; + } else { + Range range = ranges.get(index); + return range.contains(key) ? values.get(index) : null; + } + } + + @Override + @Nullable + public Map.Entry, V> getEntry(K key) { + int index = SortedLists.binarySearch(ranges, Range.lowerBoundFn(), Cut.belowValue(key), + KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_LOWER); + if (index == -1) { + return null; + } else { + Range range = ranges.get(index); + return range.contains(key) ? Maps.immutableEntry(range, values.get(index)) : null; + } + } + + @Override + public Range span() { + if (ranges.isEmpty()) { + throw new NoSuchElementException(); + } + Range firstRange = ranges.get(0); + Range lastRange = ranges.get(ranges.size() - 1); + return Range.create(firstRange.lowerBound, lastRange.upperBound); + } + + @Override + public void put(Range range, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(RangeMap rangeMap) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public void remove(Range range) { + throw new UnsupportedOperationException(); + } + + @Override + public ImmutableMap, V> asMapOfRanges() { + if (ranges.isEmpty()) { + return ImmutableMap.of(); + } + RegularImmutableSortedSet> rangeSet = new RegularImmutableSortedSet>(ranges, + Range.RANGE_LEX_ORDERING); + return new RegularImmutableSortedMap, V>(rangeSet, values); + } + + @Override + public ImmutableRangeMap subRangeMap(final Range range) { + if (checkNotNull(range).isEmpty()) { + return ImmutableRangeMap.of(); + } else if (ranges.isEmpty() || range.encloses(span())) { + return this; + } + int lowerIndex = SortedLists.binarySearch(ranges, Range.upperBoundFn(), range.lowerBound, + KeyPresentBehavior.FIRST_AFTER, KeyAbsentBehavior.NEXT_HIGHER); + int upperIndex = SortedLists.binarySearch(ranges, Range.lowerBoundFn(), range.upperBound, + KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_HIGHER); + if (lowerIndex >= upperIndex) { + return ImmutableRangeMap.of(); + } + final int off = lowerIndex; + final int len = upperIndex - lowerIndex; + ImmutableList> subRanges = new ImmutableList>() { + @Override + public int size() { + return len; + } + + @Override + public Range get(int index) { + checkElementIndex(index, len); + if (index == 0 || index == len - 1) { + return ranges.get(index + off).intersection(range); + } else { + return ranges.get(index + off); + } + } + + @Override + boolean isPartialView() { + return true; + } + }; + final ImmutableRangeMap outer = this; + return new ImmutableRangeMap(subRanges, values.subList(lowerIndex, upperIndex)) { + @Override + public ImmutableRangeMap subRangeMap(Range subRange) { + if (range.isConnected(subRange)) { + return outer.subRangeMap(subRange.intersection(range)); + } else { + return ImmutableRangeMap.of(); + } + } + }; + } + + @Override + public int hashCode() { + return asMapOfRanges().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap) o; + return asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } + return false; + } + + @Override + public String toString() { + return asMapOfRanges().toString(); + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableRangeSet.java b/sources/main/java/com/google/common/collect/ImmutableRangeSet.java new file mode 100644 index 0000000..54ccadc --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableRangeSet.java @@ -0,0 +1,608 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkElementIndex; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER; +import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.SortedLists.KeyAbsentBehavior; +import com.google.common.collect.SortedLists.KeyPresentBehavior; +import com.google.common.primitives.Ints; + +/** + * An efficient immutable implementation of a {@link RangeSet}. + * + * @author Louis Wasserman + * @since 14.0 + */ +@Beta +public final class ImmutableRangeSet extends AbstractRangeSet implements Serializable { + + private static final ImmutableRangeSet> EMPTY = new ImmutableRangeSet>( + ImmutableList.>>of()); + + private static final ImmutableRangeSet> ALL = new ImmutableRangeSet>( + ImmutableList.of(Range.>all())); + + /** + * Returns an empty immutable range set. + */ + @SuppressWarnings("unchecked") + public static ImmutableRangeSet of() { + return (ImmutableRangeSet) EMPTY; + } + + /** + * Returns an immutable range set containing the single range + * {@link Range#all()}. + */ + @SuppressWarnings("unchecked") + static ImmutableRangeSet all() { + return (ImmutableRangeSet) ALL; + } + + /** + * Returns an immutable range set containing the specified single range. If + * {@link Range#isEmpty() range.isEmpty()}, this is equivalent to + * {@link ImmutableRangeSet#of()}. + */ + public static ImmutableRangeSet of(Range range) { + checkNotNull(range); + if (range.isEmpty()) { + return of(); + } else if (range.equals(Range.all())) { + return all(); + } else { + return new ImmutableRangeSet(ImmutableList.of(range)); + } + } + + /** + * Returns an immutable copy of the specified {@code RangeSet}. + */ + public static ImmutableRangeSet copyOf(RangeSet rangeSet) { + checkNotNull(rangeSet); + if (rangeSet.isEmpty()) { + return of(); + } else if (rangeSet.encloses(Range.all())) { + return all(); + } + + if (rangeSet instanceof ImmutableRangeSet) { + ImmutableRangeSet immutableRangeSet = (ImmutableRangeSet) rangeSet; + if (!immutableRangeSet.isPartialView()) { + return immutableRangeSet; + } + } + return new ImmutableRangeSet(ImmutableList.copyOf(rangeSet.asRanges())); + } + + ImmutableRangeSet(ImmutableList> ranges) { + this.ranges = ranges; + } + + private ImmutableRangeSet(ImmutableList> ranges, ImmutableRangeSet complement) { + this.ranges = ranges; + this.complement = complement; + } + + private transient final ImmutableList> ranges; + + @Override + public boolean encloses(Range otherRange) { + int index = SortedLists.binarySearch(ranges, Range.lowerBoundFn(), otherRange.lowerBound, Ordering.natural(), + ANY_PRESENT, NEXT_LOWER); + return index != -1 && ranges.get(index).encloses(otherRange); + } + + @Override + public Range rangeContaining(C value) { + int index = SortedLists.binarySearch(ranges, Range.lowerBoundFn(), Cut.belowValue(value), Ordering.natural(), + ANY_PRESENT, NEXT_LOWER); + if (index != -1) { + Range range = ranges.get(index); + return range.contains(value) ? range : null; + } + return null; + } + + @Override + public Range span() { + if (ranges.isEmpty()) { + throw new NoSuchElementException(); + } + return Range.create(ranges.get(0).lowerBound, ranges.get(ranges.size() - 1).upperBound); + } + + @Override + public boolean isEmpty() { + return ranges.isEmpty(); + } + + @Override + public void add(Range range) { + throw new UnsupportedOperationException(); + } + + @Override + public void addAll(RangeSet other) { + throw new UnsupportedOperationException(); + } + + @Override + public void remove(Range range) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeAll(RangeSet other) { + throw new UnsupportedOperationException(); + } + + @Override + public ImmutableSet> asRanges() { + if (ranges.isEmpty()) { + return ImmutableSet.of(); + } + return new RegularImmutableSortedSet>(ranges, Range.RANGE_LEX_ORDERING); + } + + private transient ImmutableRangeSet complement; + + private final class ComplementRanges extends ImmutableList> { + // True if the "positive" range set is empty or bounded below. + private final boolean positiveBoundedBelow; + + // True if the "positive" range set is empty or bounded above. + private final boolean positiveBoundedAbove; + + private final int size; + + ComplementRanges() { + this.positiveBoundedBelow = ranges.get(0).hasLowerBound(); + this.positiveBoundedAbove = Iterables.getLast(ranges).hasUpperBound(); + + int size = ranges.size() - 1; + if (positiveBoundedBelow) { + size++; + } + if (positiveBoundedAbove) { + size++; + } + this.size = size; + } + + @Override + public int size() { + return size; + } + + @Override + public Range get(int index) { + checkElementIndex(index, size); + + Cut lowerBound; + if (positiveBoundedBelow) { + lowerBound = (index == 0) ? Cut.belowAll() : ranges.get(index - 1).upperBound; + } else { + lowerBound = ranges.get(index).upperBound; + } + + Cut upperBound; + if (positiveBoundedAbove && index == size - 1) { + upperBound = Cut.aboveAll(); + } else { + upperBound = ranges.get(index + (positiveBoundedBelow ? 0 : 1)).lowerBound; + } + + return Range.create(lowerBound, upperBound); + } + + @Override + boolean isPartialView() { + return true; + } + } + + @Override + public ImmutableRangeSet complement() { + ImmutableRangeSet result = complement; + if (result != null) { + return result; + } else if (ranges.isEmpty()) { + return complement = all(); + } else if (ranges.size() == 1 && ranges.get(0).equals(Range.all())) { + return complement = of(); + } else { + ImmutableList> complementRanges = new ComplementRanges(); + result = complement = new ImmutableRangeSet(complementRanges, this); + } + return result; + } + + /** + * Returns a list containing the nonempty intersections of {@code range} with + * the ranges in this range set. + */ + private ImmutableList> intersectRanges(final Range range) { + if (ranges.isEmpty() || range.isEmpty()) { + return ImmutableList.of(); + } else if (range.encloses(span())) { + return ranges; + } + + final int fromIndex; + if (range.hasLowerBound()) { + fromIndex = SortedLists.binarySearch(ranges, Range.upperBoundFn(), range.lowerBound, + KeyPresentBehavior.FIRST_AFTER, KeyAbsentBehavior.NEXT_HIGHER); + } else { + fromIndex = 0; + } + + int toIndex; + if (range.hasUpperBound()) { + toIndex = SortedLists.binarySearch(ranges, Range.lowerBoundFn(), range.upperBound, + KeyPresentBehavior.FIRST_PRESENT, KeyAbsentBehavior.NEXT_HIGHER); + } else { + toIndex = ranges.size(); + } + final int length = toIndex - fromIndex; + if (length == 0) { + return ImmutableList.of(); + } else { + return new ImmutableList>() { + @Override + public int size() { + return length; + } + + @Override + public Range get(int index) { + checkElementIndex(index, length); + if (index == 0 || index == length - 1) { + return ranges.get(index + fromIndex).intersection(range); + } else { + return ranges.get(index + fromIndex); + } + } + + @Override + boolean isPartialView() { + return true; + } + }; + } + } + + /** + * Returns a view of the intersection of this range set with the given range. + */ + @Override + public ImmutableRangeSet subRangeSet(Range range) { + if (!isEmpty()) { + Range span = span(); + if (range.encloses(span)) { + return this; + } else if (range.isConnected(span)) { + return new ImmutableRangeSet(intersectRanges(range)); + } + } + return of(); + } + + /** + * Returns an {@link ImmutableSortedSet} containing the same values in the given + * domain {@linkplain RangeSet#contains contained} by this range set. + * + *

+ * Note: {@code a.asSet(d).equals(b.asSet(d))} does not imply + * {@code a.equals(b)}! For example, {@code a} and {@code b} could be + * {@code [2..4]} and {@code (1..5)}, or the empty ranges {@code [3..3)} and + * {@code [4..4)}. + * + *

+ * Warning: Be extremely careful what you do with the {@code asSet} view + * of a large range set (such as + * {@code ImmutableRangeSet.of(Range.greaterThan(0))}). Certain operations on + * such a set can be performed efficiently, but others (such as + * {@link Set#hashCode} or {@link Collections#frequency}) can cause major + * performance problems. + * + *

+ * The returned set's {@link Object#toString} method returns a short-hand form + * of the set's contents, such as {@code "[1..100]}"}. + * + * @throws IllegalArgumentException if neither this range nor the domain has a + * lower bound, or if neither has an upper + * bound + */ + public ImmutableSortedSet asSet(DiscreteDomain domain) { + checkNotNull(domain); + if (isEmpty()) { + return ImmutableSortedSet.of(); + } + Range span = span().canonical(domain); + if (!span.hasLowerBound()) { + // according to the spec of canonical, neither this ImmutableRangeSet nor + // the range have a lower bound + throw new IllegalArgumentException("Neither the DiscreteDomain nor this range set are bounded below"); + } else if (!span.hasUpperBound()) { + try { + domain.maxValue(); + } catch (NoSuchElementException e) { + throw new IllegalArgumentException("Neither the DiscreteDomain nor this range set are bounded above"); + } + } + + return new AsSet(domain); + } + + private final class AsSet extends ImmutableSortedSet { + private final DiscreteDomain domain; + + AsSet(DiscreteDomain domain) { + super(Ordering.natural()); + this.domain = domain; + } + + private transient Integer size; + + @Override + public int size() { + // racy single-check idiom + Integer result = size; + if (result == null) { + long total = 0; + for (Range range : ranges) { + total += ContiguousSet.create(range, domain).size(); + if (total >= Integer.MAX_VALUE) { + break; + } + } + result = size = Ints.saturatedCast(total); + } + return result.intValue(); + } + + @Override + public UnmodifiableIterator iterator() { + return new AbstractIterator() { + final Iterator> rangeItr = ranges.iterator(); + Iterator elemItr = Iterators.emptyIterator(); + + @Override + protected C computeNext() { + while (!elemItr.hasNext()) { + if (rangeItr.hasNext()) { + elemItr = ContiguousSet.create(rangeItr.next(), domain).iterator(); + } else { + return endOfData(); + } + } + return elemItr.next(); + } + }; + } + + @Override + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return new AbstractIterator() { + final Iterator> rangeItr = ranges.reverse().iterator(); + Iterator elemItr = Iterators.emptyIterator(); + + @Override + protected C computeNext() { + while (!elemItr.hasNext()) { + if (rangeItr.hasNext()) { + elemItr = ContiguousSet.create(rangeItr.next(), domain).descendingIterator(); + } else { + return endOfData(); + } + } + return elemItr.next(); + } + }; + } + + ImmutableSortedSet subSet(Range range) { + return subRangeSet(range).asSet(domain); + } + + @Override + ImmutableSortedSet headSetImpl(C toElement, boolean inclusive) { + return subSet(Range.upTo(toElement, BoundType.forBoolean(inclusive))); + } + + @Override + ImmutableSortedSet subSetImpl(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + if (!fromInclusive && !toInclusive && Range.compareOrThrow(fromElement, toElement) == 0) { + return ImmutableSortedSet.of(); + } + return subSet(Range.range(fromElement, BoundType.forBoolean(fromInclusive), toElement, + BoundType.forBoolean(toInclusive))); + } + + @Override + ImmutableSortedSet tailSetImpl(C fromElement, boolean inclusive) { + return subSet(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); + } + + @Override + public boolean contains(@Nullable Object o) { + if (o == null) { + return false; + } + try { + @SuppressWarnings("unchecked") // we catch CCE's + C c = (C) o; + return ImmutableRangeSet.this.contains(c); + } catch (ClassCastException e) { + return false; + } + } + + @Override + int indexOf(Object target) { + if (contains(target)) { + @SuppressWarnings("unchecked") // if it's contained, it's definitely a C + C c = (C) target; + long total = 0; + for (Range range : ranges) { + if (range.contains(c)) { + return Ints.saturatedCast(total + ContiguousSet.create(range, domain).indexOf(c)); + } else { + total += ContiguousSet.create(range, domain).size(); + } + } + throw new AssertionError("impossible"); + } + return -1; + } + + @Override + boolean isPartialView() { + return ranges.isPartialView(); + } + + @Override + public String toString() { + return ranges.toString(); + } + + @Override + Object writeReplace() { + return new AsSetSerializedForm(ranges, domain); + } + } + + private static class AsSetSerializedForm implements Serializable { + private final ImmutableList> ranges; + private final DiscreteDomain domain; + + AsSetSerializedForm(ImmutableList> ranges, DiscreteDomain domain) { + this.ranges = ranges; + this.domain = domain; + } + + Object readResolve() { + return new ImmutableRangeSet(ranges).asSet(domain); + } + } + + /** + * Returns {@code true} if this immutable range set's implementation contains + * references to user-created objects that aren't accessible via this range + * set's methods. This is generally used to determine whether {@code copyOf} + * implementations should make an explicit copy to avoid memory leaks. + */ + boolean isPartialView() { + return ranges.isPartialView(); + } + + /** + * Returns a new builder for an immutable range set. + */ + public static > Builder builder() { + return new Builder(); + } + + /** + * A builder for immutable range sets. + */ + public static class Builder> { + private final RangeSet rangeSet; + + public Builder() { + this.rangeSet = TreeRangeSet.create(); + } + + /** + * Add the specified range to this builder. Adjacent/abutting ranges are + * permitted, but empty ranges, or ranges with nonempty overlap, are forbidden. + * + * @throws IllegalArgumentException if {@code range} is empty or has nonempty + * intersection with any ranges already added + * to the builder + */ + public Builder add(Range range) { + if (range.isEmpty()) { + throw new IllegalArgumentException("range must not be empty, but was " + range); + } else if (!rangeSet.complement().encloses(range)) { + for (Range currentRange : rangeSet.asRanges()) { + checkArgument(!currentRange.isConnected(range) || currentRange.intersection(range).isEmpty(), + "Ranges may not overlap, but received %s and %s", currentRange, range); + } + throw new AssertionError("should have thrown an IAE above"); + } + rangeSet.add(range); + return this; + } + + /** + * Add all ranges from the specified range set to this builder. Duplicate or + * connected ranges are permitted, and will be merged in the resulting immutable + * range set. + */ + public Builder addAll(RangeSet ranges) { + for (Range range : ranges.asRanges()) { + add(range); + } + return this; + } + + /** + * Returns an {@code ImmutableRangeSet} containing the ranges added to this + * builder. + */ + public ImmutableRangeSet build() { + return copyOf(rangeSet); + } + } + + private static final class SerializedForm implements Serializable { + private final ImmutableList> ranges; + + SerializedForm(ImmutableList> ranges) { + this.ranges = ranges; + } + + Object readResolve() { + if (ranges.isEmpty()) { + return of(); + } else if (ranges.equals(ImmutableList.of(Range.all()))) { + return all(); + } else { + return new ImmutableRangeSet(ranges); + } + } + } + + Object writeReplace() { + return new SerializedForm(ranges); + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableSet.java b/sources/main/java/com/google/common/collect/ImmutableSet.java new file mode 100644 index 0000000..8763eeb --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSet.java @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ObjectArrays.checkElementNotNull; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.primitives.Ints; + +/** + * 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) + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // 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. + @SuppressWarnings({ "unchecked" }) + 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: + @SuppressWarnings("unchecked") // safe; elements contains only E's + E elem = (E) elements[0]; + return of(elem); + default: + // continue below to handle the general case + } + int tableSize = chooseTableSize(n); + 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 + @SuppressWarnings("unchecked") // 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 = Ints.MAX_POWER_OF_TWO; + + // Represents how tightly we can pack things, as a maximum. + private static final double DESIRED_LOAD_FACTOR = 0.7; + + // If the set has this many elements, it will "max out" the table size + private static final int CUTOFF = (int) (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) { + // 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(Collections2.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)) { + @SuppressWarnings("unchecked") // 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(@Nullable 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/sources/main/java/com/google/common/collect/ImmutableSetMultimap.java b/sources/main/java/com/google/common/collect/ImmutableSetMultimap.java new file mode 100644 index 0000000..0050f97 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSetMultimap.java @@ -0,0 +1,527 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Objects.firstNonNull; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Arrays.asList; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An immutable {@link SetMultimap} with reliable user-specified key and value + * iteration order. Does not permit null keys or values. + * + *

+ * Unlike {@link Multimaps#unmodifiableSetMultimap(SetMultimap)}, which is a + * view of a separate multimap which can still change, an instance of + * {@code ImmutableSetMultimap} contains its own data and will never + * change. {@code ImmutableSetMultimap} is convenient for + * {@code public static final} multimaps ("constant multimaps") and also lets + * you easily make a "defensive copy" of a multimap 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 class are + * guaranteed to be immutable. + * + *

+ * See the Guava User Guide article on + * immutable collections. + * + * @author Mike Ward + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public class ImmutableSetMultimap extends ImmutableMultimap implements SetMultimap { + + /** Returns the empty multimap. */ + // Casting is safe because the multimap will never hold any elements. + @SuppressWarnings("unchecked") + public static ImmutableSetMultimap of() { + return (ImmutableSetMultimap) EmptyImmutableSetMultimap.INSTANCE; + } + + /** + * Returns an immutable multimap containing a single entry. + */ + public static ImmutableSetMultimap of(K k1, V v1) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + * Repeated occurrences of an entry (according to {@link Object#equals}) after + * the first are ignored. + */ + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + * Repeated occurrences of an entry (according to {@link Object#equals}) after + * the first are ignored. + */ + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + * Repeated occurrences of an entry (according to {@link Object#equals}) after + * the first are ignored. + */ + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. + * Repeated occurrences of an entry (according to {@link Object#equals}) after + * the first are ignored. + */ + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + builder.put(k5, v5); + return builder.build(); + } + + // looking for of() with > 5 entries? Use the builder instead. + + /** + * Returns a new {@link Builder}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Multimap for {@link ImmutableSetMultimap.Builder} that maintains key and + * value orderings and performs better than {@link LinkedHashMultimap}. + */ + private static class BuilderMultimap extends AbstractMapBasedMultimap { + BuilderMultimap() { + super(new LinkedHashMap>()); + } + + @Override + Collection createCollection() { + return Sets.newLinkedHashSet(); + } + + private static final long serialVersionUID = 0; + } + + /** + * A builder for creating immutable {@code SetMultimap} instances, especially + * {@code public static final} multimaps ("constant multimaps"). Example: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	static final Multimap STRING_TO_INTEGER_MULTIMAP = new ImmutableSetMultimap.Builder()
+	 * 			.put("one", 1).putAll("several", 1, 2, 3).putAll("many", 1, 2, 3, 4, 5).build();
+	 * }
+	 * 
+ * + *

+ * Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple multimaps in series. Each multimap contains the + * key-value mappings in the previously created multimaps. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static final class Builder extends ImmutableMultimap.Builder { + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableSetMultimap#builder}. + */ + public Builder() { + builderMultimap = new BuilderMultimap(); + } + + /** + * Adds a key-value mapping to the built multimap if it is not already present. + */ + @Override + public Builder put(K key, V value) { + builderMultimap.put(checkNotNull(key), checkNotNull(value)); + return this; + } + + /** + * Adds an entry to the built multimap if it is not already present. + * + * @since 11.0 + */ + @Override + public Builder put(Entry entry) { + builderMultimap.put(checkNotNull(entry.getKey()), checkNotNull(entry.getValue())); + return this; + } + + @Override + public Builder putAll(K key, Iterable values) { + Collection collection = builderMultimap.get(checkNotNull(key)); + for (V value : values) { + collection.add(checkNotNull(value)); + } + return this; + } + + @Override + public Builder putAll(K key, V... values) { + return putAll(key, Arrays.asList(values)); + } + + @Override + public Builder putAll(Multimap multimap) { + for (Entry> entry : multimap.asMap().entrySet()) { + putAll(entry.getKey(), entry.getValue()); + } + return this; + } + + /** + * {@inheritDoc} + * + * @since 8.0 + */ + @Override + public Builder orderKeysBy(Comparator keyComparator) { + this.keyComparator = checkNotNull(keyComparator); + return this; + } + + /** + * Specifies the ordering of the generated multimap's values for each key. + * + *

+ * If this method is called, the sets returned by the {@code get()} method of + * the generated multimap and its {@link Multimap#asMap()} view are + * {@link ImmutableSortedSet} instances. However, serialization does not + * preserve that property, though it does maintain the key and value ordering. + * + * @since 8.0 + */ + // TODO: Make serialization behavior consistent. + @Override + public Builder orderValuesBy(Comparator valueComparator) { + super.orderValuesBy(valueComparator); + return this; + } + + /** + * Returns a newly-created immutable set multimap. + */ + @Override + public ImmutableSetMultimap build() { + if (keyComparator != null) { + Multimap sortedCopy = new BuilderMultimap(); + List>> entries = Lists.newArrayList(builderMultimap.asMap().entrySet()); + Collections.sort(entries, Ordering.from(keyComparator).onKeys()); + for (Map.Entry> entry : entries) { + sortedCopy.putAll(entry.getKey(), entry.getValue()); + } + builderMultimap = sortedCopy; + } + return copyOf(builderMultimap, valueComparator); + } + } + + /** + * Returns an immutable set multimap containing the same mappings as + * {@code multimap}. The generated multimap's key and value orderings correspond + * to the iteration ordering of the {@code multimap.asMap()} view. Repeated + * occurrences of an entry in the multimap after the first are ignored. + * + *

+ * 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 multimap} is null + */ + public static ImmutableSetMultimap copyOf(Multimap multimap) { + return copyOf(multimap, null); + } + + private static ImmutableSetMultimap copyOf(Multimap multimap, + Comparator valueComparator) { + checkNotNull(multimap); // eager for GWT + if (multimap.isEmpty() && valueComparator == null) { + return of(); + } + + if (multimap instanceof ImmutableSetMultimap) { + @SuppressWarnings("unchecked") // safe since multimap is not writable + ImmutableSetMultimap kvMultimap = (ImmutableSetMultimap) multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int size = 0; + + for (Entry> entry : multimap.asMap().entrySet()) { + K key = entry.getKey(); + Collection values = entry.getValue(); + ImmutableSet set = valueSet(valueComparator, values); + if (!set.isEmpty()) { + builder.put(key, set); + size += set.size(); + } + } + + return new ImmutableSetMultimap(builder.build(), size, valueComparator); + } + + /** + * Returned by get() when a missing key is provided. Also holds the comparator, + * if any, used for values. + */ + private final transient ImmutableSet emptySet; + + ImmutableSetMultimap(ImmutableMap> map, int size, + @Nullable Comparator valueComparator) { + super(map, size); + this.emptySet = emptySet(valueComparator); + } + + // views + + /** + * Returns an immutable set of the values for the given key. If no mappings in + * the multimap have the provided key, an empty immutable set is returned. The + * values are in the same order as the parameters used to build this multimap. + */ + @Override + public ImmutableSet get(@Nullable K key) { + // This cast is safe as its type is known in constructor. + ImmutableSet set = (ImmutableSet) map.get(key); + return firstNonNull(set, emptySet); + } + + private transient ImmutableSetMultimap inverse; + + /** + * {@inheritDoc} + * + *

+ * Because an inverse of a set multimap cannot contain multiple pairs with the + * same key and value, this method returns an {@code ImmutableSetMultimap} + * rather than the {@code ImmutableMultimap} specified in the {@code + * ImmutableMultimap} class. + * + * @since 11.0 + */ + public ImmutableSetMultimap inverse() { + ImmutableSetMultimap result = inverse; + return (result == null) ? (inverse = invert()) : result; + } + + private ImmutableSetMultimap invert() { + Builder builder = builder(); + for (Entry entry : entries()) { + builder.put(entry.getValue(), entry.getKey()); + } + ImmutableSetMultimap invertedMultimap = builder.build(); + invertedMultimap.inverse = this; + return invertedMultimap; + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public ImmutableSet removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public ImmutableSet replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + private transient ImmutableSet> entries; + + /** + * Returns an immutable collection of all key-value pairs in the multimap. Its + * iterator traverses the values for the first key, the values for the second + * key, and so on. + */ + @Override + public ImmutableSet> entries() { + ImmutableSet> result = entries; + return (result == null) ? (entries = new EntrySet(this)) : result; + } + + private static final class EntrySet extends ImmutableSet> { + private transient final ImmutableSetMultimap multimap; + + EntrySet(ImmutableSetMultimap multimap) { + this.multimap = multimap; + } + + @Override + public boolean contains(@Nullable Object object) { + if (object instanceof Entry) { + Entry entry = (Entry) object; + return multimap.containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + public UnmodifiableIterator> iterator() { + return multimap.entryIterator(); + } + + @Override + boolean isPartialView() { + return false; + } + } + + private static ImmutableSet valueSet(@Nullable Comparator valueComparator, + Collection values) { + return (valueComparator == null) ? ImmutableSet.copyOf(values) + : ImmutableSortedSet.copyOf(valueComparator, values); + } + + private static ImmutableSet emptySet(@Nullable Comparator valueComparator) { + return (valueComparator == null) ? ImmutableSet.of() : ImmutableSortedSet.emptySet(valueComparator); + } + + /** + * @serialData number of distinct keys, and then for each distinct key: the key, + * the number of values for that key, and the key's values + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(valueComparator()); + Serialization.writeMultimap(this, stream); + } + + @Nullable + Comparator valueComparator() { + return emptySet instanceof ImmutableSortedSet ? ((ImmutableSortedSet) emptySet).comparator() : null; + } + + @GwtIncompatible("java.io.ObjectInputStream") + // Serialization type safety is at the caller's mercy. + @SuppressWarnings("unchecked") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + Comparator valueComparator = (Comparator) stream.readObject(); + int keyCount = stream.readInt(); + if (keyCount < 0) { + throw new InvalidObjectException("Invalid key count " + keyCount); + } + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int tmpSize = 0; + + for (int i = 0; i < keyCount; i++) { + Object key = stream.readObject(); + int valueCount = stream.readInt(); + if (valueCount <= 0) { + throw new InvalidObjectException("Invalid value count " + valueCount); + } + + Object[] array = new Object[valueCount]; + for (int j = 0; j < valueCount; j++) { + array[j] = stream.readObject(); + } + ImmutableSet valueSet = valueSet(valueComparator, asList(array)); + if (valueSet.size() != array.length) { + throw new InvalidObjectException("Duplicate key-value pairs exist for key " + key); + } + builder.put(key, valueSet); + tmpSize += valueCount; + } + + ImmutableMap> tmpMap; + try { + tmpMap = builder.build(); + } catch (IllegalArgumentException e) { + throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); + } + + FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); + FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); + FieldSettersHolder.EMPTY_SET_FIELD_SETTER.set(this, emptySet(valueComparator)); + } + + @GwtIncompatible("not needed in emulated source.") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ImmutableSortedAsList.java b/sources/main/java/com/google/common/collect/ImmutableSortedAsList.java new file mode 100644 index 0000000..62603a1 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSortedAsList.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Comparator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * List returned by {@code ImmutableSortedSet.asList()} when the set isn't + * empty. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") +final class ImmutableSortedAsList extends RegularImmutableAsList implements SortedIterable { + ImmutableSortedAsList(ImmutableSortedSet backingSet, ImmutableList backingList) { + super(backingSet, backingList); + } + + @Override + ImmutableSortedSet delegateCollection() { + return (ImmutableSortedSet) super.delegateCollection(); + } + + @Override + public Comparator comparator() { + return delegateCollection().comparator(); + } + + // Override indexOf() and lastIndexOf() to be O(log N) instead of O(N). + + @GwtIncompatible("ImmutableSortedSet.indexOf") + // TODO(cpovirk): consider manual binary search under GWT to preserve O(log N) + // lookup + @Override + public int indexOf(@Nullable Object target) { + int index = delegateCollection().indexOf(target); + + // TODO(kevinb): reconsider if it's really worth making feeble attempts at + // sanity for inconsistent comparators. + + // The equals() check is needed when the comparator isn't compatible with + // equals(). + return (index >= 0 && get(index).equals(target)) ? index : -1; + } + + @GwtIncompatible("ImmutableSortedSet.indexOf") + @Override + public int lastIndexOf(@Nullable Object target) { + return indexOf(target); + } + + @Override + public boolean contains(Object target) { + // Necessary for ISS's with comparators inconsistent with equals. + return indexOf(target) >= 0; + } + + @GwtIncompatible("super.subListUnchecked does not exist; inherited subList is valid if slow") + /* + * TODO(cpovirk): if we start to override indexOf/lastIndexOf under GWT, we'll + * want some way to override subList to return an ImmutableSortedAsList for + * better performance. Right now, I'm not sure there's any performance hit from + * our failure to override subListUnchecked under GWT + */ + @Override + ImmutableList subListUnchecked(int fromIndex, int toIndex) { + return new RegularImmutableSortedSet(super.subListUnchecked(fromIndex, toIndex), comparator()).asList(); + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableSortedMap.java b/sources/main/java/com/google/common/collect/ImmutableSortedMap.java new file mode 100644 index 0000000..6908186 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSortedMap.java @@ -0,0 +1,713 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Maps.keyOrNull; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An immutable {@link SortedMap}. Does not permit null keys or values. + * + *

+ * Unlike {@link Collections#unmodifiableSortedMap}, which is a view of a + * separate map which can still change, an instance of {@code + * ImmutableSortedMap} contains its own data and will never change. + * {@code ImmutableSortedMap} 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. + * + *

+ * 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 Jared Levy + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library; implements {@code + * NavigableMap} since 12.0) + */ +@GwtCompatible(serializable = true, emulated = true) +public abstract class ImmutableSortedMap extends ImmutableSortedMapFauxverideShim + implements NavigableMap { + /* + * TODO(kevinb): Confirm that ImmutableSortedMap is faster to construct and uses + * less memory than TreeMap; then say so in the class Javadoc. + */ + private static final Comparator NATURAL_ORDER = Ordering.natural(); + + private static final ImmutableSortedMap NATURAL_EMPTY_MAP = new EmptyImmutableSortedMap( + NATURAL_ORDER); + + static ImmutableSortedMap emptyMap(Comparator comparator) { + if (Ordering.natural().equals(comparator)) { + return of(); + } else { + return new EmptyImmutableSortedMap(comparator); + } + } + + static ImmutableSortedMap fromSortedEntries(Comparator comparator, int size, + Entry[] entries) { + if (size == 0) { + return emptyMap(comparator); + } + + ImmutableList.Builder keyBuilder = ImmutableList.builder(); + ImmutableList.Builder valueBuilder = ImmutableList.builder(); + for (int i = 0; i < size; i++) { + Entry entry = entries[i]; + keyBuilder.add(entry.getKey()); + valueBuilder.add(entry.getValue()); + } + + return new RegularImmutableSortedMap(new RegularImmutableSortedSet(keyBuilder.build(), comparator), + valueBuilder.build()); + } + + static ImmutableSortedMap from(ImmutableSortedSet keySet, ImmutableList valueList) { + if (keySet.isEmpty()) { + return emptyMap(keySet.comparator()); + } else { + return new RegularImmutableSortedMap((RegularImmutableSortedSet) keySet, valueList); + } + } + + /** + * Returns the empty sorted map. + */ + @SuppressWarnings("unchecked") + // unsafe, comparator() returns a comparator on the specified type + // TODO(kevinb): evaluate whether or not of().comparator() should return null + public static ImmutableSortedMap of() { + return (ImmutableSortedMap) NATURAL_EMPTY_MAP; + } + + /** + * Returns an immutable map containing a single entry. + */ + public static , V> ImmutableSortedMap of(K k1, V v1) { + return from(ImmutableSortedSet.of(k1), ImmutableList.of(v1)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the + * natural ordering of their keys. + * + * @throws IllegalArgumentException if the two keys are equal according to their + * natural ordering + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of(K k1, V v1, K k2, V v2) { + return fromEntries(Ordering.natural(), false, 2, entryOf(k1, v1), entryOf(k2, v2)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the + * natural ordering of their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their + * natural ordering + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return fromEntries(Ordering.natural(), false, 3, entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the + * natural ordering of their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their + * natural ordering + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, + K k4, V v4) { + return fromEntries(Ordering.natural(), false, 4, entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), + entryOf(k4, v4)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the + * natural ordering of their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their + * natural ordering + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, + K k4, V v4, K k5, V v5) { + return fromEntries(Ordering.natural(), false, 5, entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), + entryOf(k4, v4), entryOf(k5, v5)); + } + + /** + * Returns an immutable map containing the same entries as {@code map}, sorted + * by the natural ordering of the keys. + * + *

+ * 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. + * + *

+ * This method is not type-safe, as it may be called on a map with keys that are + * not mutually comparable. + * + * @throws ClassCastException if the keys in {@code map} are not mutually + * comparable + * @throws NullPointerException if any key or value in {@code map} is null + * @throws IllegalArgumentException if any two keys are equal according to their + * natural ordering + */ + public static ImmutableSortedMap copyOf(Map map) { + // Hack around K not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOfInternal(map, naturalOrder); + } + + /** + * Returns an immutable map containing the same entries as {@code map}, with + * keys sorted by the provided comparator. + * + *

+ * 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 + * @throws IllegalArgumentException if any two keys are equal according to the + * comparator + */ + public static ImmutableSortedMap copyOf(Map map, + Comparator comparator) { + return copyOfInternal(map, checkNotNull(comparator)); + } + + /** + * Returns an immutable map containing the same entries as the provided sorted + * map, with the same ordering. + * + *

+ * 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 + */ + @SuppressWarnings("unchecked") + public static ImmutableSortedMap copyOfSorted(SortedMap map) { + Comparator comparator = map.comparator(); + if (comparator == null) { + // If map has a null comparator, the keys should have a natural ordering, + // even though K doesn't explicitly implement Comparable. + comparator = (Comparator) NATURAL_ORDER; + } + return copyOfInternal(map, comparator); + } + + private static ImmutableSortedMap copyOfInternal(Map map, + Comparator comparator) { + boolean sameComparator = false; + if (map instanceof SortedMap) { + SortedMap sortedMap = (SortedMap) map; + Comparator comparator2 = sortedMap.comparator(); + sameComparator = (comparator2 == null) ? comparator == NATURAL_ORDER : comparator.equals(comparator2); + } + + if (sameComparator && (map instanceof ImmutableSortedMap)) { + // TODO(kevinb): Prove that this cast is safe, even though + // Collections.unmodifiableSortedMap requires the same key type. + @SuppressWarnings("unchecked") + ImmutableSortedMap kvMap = (ImmutableSortedMap) map; + if (!kvMap.isPartialView()) { + return kvMap; + } + } + + // "adding" type params to an array of a raw type should be safe as + // long as no one can ever cast that same array instance back to a + // raw type. + @SuppressWarnings("unchecked") + Entry[] entries = map.entrySet().toArray(new Entry[0]); + + return fromEntries(comparator, sameComparator, entries.length, entries); + } + + static ImmutableSortedMap fromEntries(Comparator comparator, boolean sameComparator, + int size, Entry... entries) { + for (int i = 0; i < size; i++) { + Entry entry = entries[i]; + entries[i] = entryOf(entry.getKey(), entry.getValue()); + } + if (!sameComparator) { + sortEntries(comparator, size, entries); + validateEntries(size, entries, comparator); + } + + return fromSortedEntries(comparator, size, entries); + } + + private static void sortEntries(final Comparator comparator, int size, Entry[] entries) { + Arrays.sort(entries, 0, size, Ordering.from(comparator).onKeys()); + } + + private static void validateEntries(int size, Entry[] entries, Comparator comparator) { + for (int i = 1; i < size; i++) { + checkNoConflict(comparator.compare(entries[i - 1].getKey(), entries[i].getKey()) != 0, "key", + entries[i - 1], entries[i]); + } + } + + /** + * Returns a builder that creates immutable sorted maps whose keys are ordered + * by their natural ordering. The sorted maps use {@link Ordering#natural()} as + * the comparator. + */ + public static , V> Builder naturalOrder() { + return new Builder(Ordering.natural()); + } + + /** + * Returns a builder that creates immutable sorted maps with an explicit + * comparator. If the comparator has a more general type than the map's keys, + * such as creating a {@code SortedMap} with a {@code + * Comparator}, use the {@link Builder} constructor instead. + * + * @throws NullPointerException if {@code comparator} is null + */ + public static Builder orderedBy(Comparator comparator) { + return new Builder(comparator); + } + + /** + * Returns a builder that creates immutable sorted maps whose keys are ordered + * by the reverse of their natural ordering. + */ + public static , V> Builder reverseOrder() { + return new Builder(Ordering.natural().reverse()); + } + + /** + * A builder for creating immutable sorted map instances, especially {@code + * public static final} maps ("constant maps"). Example: + * + *

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

+ * For small immutable sorted maps, the {@code ImmutableSortedMap.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 extends ImmutableMap.Builder { + private final Comparator comparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableSortedMap#orderedBy}. + */ + @SuppressWarnings("unchecked") + public Builder(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + /** + * Associates {@code key} with {@code value} in the built map. Duplicate keys, + * according to the comparator (which might be the keys' natural order), are not + * allowed, and will cause {@link #build} to fail. + */ + @Override + public Builder put(K key, V value) { + super.put(key, value); + return this; + } + + /** + * Adds the given {@code entry} to the map, making it immutable if necessary. + * Duplicate keys, according to the comparator (which might be the keys' natural + * order), are not allowed, and will cause {@link #build} to fail. + * + * @since 11.0 + */ + @Override + public Builder put(Entry entry) { + super.put(entry); + return this; + } + + /** + * Associates all of the given map's keys and values in the built map. Duplicate + * keys, according to the comparator (which might be the keys' natural order), + * 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 sorted map. + * + * @throws IllegalArgumentException if any two keys are equal according to the + * comparator (which might be the keys' natural + * order) + */ + @Override + public ImmutableSortedMap build() { + return fromEntries(comparator, false, size, entries); + } + } + + ImmutableSortedMap() { + } + + ImmutableSortedMap(ImmutableSortedMap descendingMap) { + this.descendingMap = descendingMap; + } + + @Override + public int size() { + return values().size(); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return values().contains(value); + } + + @Override + boolean isPartialView() { + return keySet().isPartialView() || values().isPartialView(); + } + + /** + * Returns an immutable set of the mappings in this map, sorted by the key + * ordering. + */ + @Override + public ImmutableSet> entrySet() { + return super.entrySet(); + } + + /** + * Returns an immutable sorted set of the keys in this map. + */ + @Override + public abstract ImmutableSortedSet keySet(); + + /** + * Returns an immutable collection of the values in this map, sorted by the + * ordering of the corresponding keys. + */ + @Override + public abstract ImmutableCollection values(); + + /** + * Returns the comparator that orders the keys, which is + * {@link Ordering#natural()} when the natural ordering of the keys is used. + * Note that its behavior is not consistent with {@link TreeMap#comparator()}, + * which returns {@code null} to indicate natural ordering. + */ + @Override + public Comparator comparator() { + return keySet().comparator(); + } + + @Override + public K firstKey() { + return keySet().first(); + } + + @Override + public K lastKey() { + return keySet().last(); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries + * whose keys are less than {@code toKey}. + * + *

+ * The {@link SortedMap#headMap} documentation states that a submap of a submap + * throws an {@link IllegalArgumentException} if passed a {@code toKey} greater + * than an earlier {@code toKey}. However, this method doesn't throw an + * exception in that situation, but instead keeps the original {@code + * toKey}. + */ + @Override + public ImmutableSortedMap headMap(K toKey) { + return headMap(toKey, false); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries + * whose keys are less than (or equal to, if {@code inclusive}) {@code toKey}. + * + *

+ * The {@link SortedMap#headMap} documentation states that a submap of a submap + * throws an {@link IllegalArgumentException} if passed a {@code toKey} greater + * than an earlier {@code toKey}. However, this method doesn't throw an + * exception in that situation, but instead keeps the original {@code + * toKey}. + * + * @since 12.0 + */ + @Override + public abstract ImmutableSortedMap headMap(K toKey, boolean inclusive); + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries + * whose keys ranges from {@code fromKey}, inclusive, to {@code toKey}, + * exclusive. + * + *

+ * The {@link SortedMap#subMap} documentation states that a submap of a submap + * throws an {@link IllegalArgumentException} if passed a {@code + * fromKey} less than an earlier {@code fromKey}. However, this method doesn't + * throw an exception in that situation, but instead keeps the original {@code + * fromKey}. Similarly, this method keeps the original {@code toKey}, instead of + * throwing an exception, if passed a {@code toKey} greater than an earlier + * {@code toKey}. + */ + @Override + public ImmutableSortedMap subMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries + * whose keys ranges from {@code fromKey} to {@code toKey}, inclusive or + * exclusive as indicated by the boolean flags. + * + *

+ * The {@link SortedMap#subMap} documentation states that a submap of a submap + * throws an {@link IllegalArgumentException} if passed a {@code + * fromKey} less than an earlier {@code fromKey}. However, this method doesn't + * throw an exception in that situation, but instead keeps the original {@code + * fromKey}. Similarly, this method keeps the original {@code toKey}, instead of + * throwing an exception, if passed a {@code toKey} greater than an earlier + * {@code toKey}. + * + * @since 12.0 + */ + @Override + public ImmutableSortedMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + checkNotNull(fromKey); + checkNotNull(toKey); + checkArgument(comparator().compare(fromKey, toKey) <= 0, "expected fromKey <= toKey but %s > %s", fromKey, + toKey); + return headMap(toKey, toInclusive).tailMap(fromKey, fromInclusive); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries + * whose keys are greater than or equals to {@code fromKey}. + * + *

+ * The {@link SortedMap#tailMap} documentation states that a submap of a submap + * throws an {@link IllegalArgumentException} if passed a {@code + * fromKey} less than an earlier {@code fromKey}. However, this method doesn't + * throw an exception in that situation, but instead keeps the original {@code + * fromKey}. + */ + @Override + public ImmutableSortedMap tailMap(K fromKey) { + return tailMap(fromKey, true); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries + * whose keys are greater than (or equal to, if {@code inclusive}) + * {@code fromKey}. + * + *

+ * The {@link SortedMap#tailMap} documentation states that a submap of a submap + * throws an {@link IllegalArgumentException} if passed a {@code + * fromKey} less than an earlier {@code fromKey}. However, this method doesn't + * throw an exception in that situation, but instead keeps the original {@code + * fromKey}. + * + * @since 12.0 + */ + @Override + public abstract ImmutableSortedMap tailMap(K fromKey, boolean inclusive); + + @Override + public Entry lowerEntry(K key) { + return headMap(key, false).lastEntry(); + } + + @Override + public K lowerKey(K key) { + return keyOrNull(lowerEntry(key)); + } + + @Override + public Entry floorEntry(K key) { + return headMap(key, true).lastEntry(); + } + + @Override + public K floorKey(K key) { + return keyOrNull(floorEntry(key)); + } + + @Override + public Entry ceilingEntry(K key) { + return tailMap(key, true).firstEntry(); + } + + @Override + public K ceilingKey(K key) { + return keyOrNull(ceilingEntry(key)); + } + + @Override + public Entry higherEntry(K key) { + return tailMap(key, false).firstEntry(); + } + + @Override + public K higherKey(K key) { + return keyOrNull(higherEntry(key)); + } + + @Override + public Entry firstEntry() { + return isEmpty() ? null : entrySet().asList().get(0); + } + + @Override + public Entry lastEntry() { + return isEmpty() ? null : entrySet().asList().get(size() - 1); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + private transient ImmutableSortedMap descendingMap; + + @Override + public ImmutableSortedMap descendingMap() { + ImmutableSortedMap result = descendingMap; + if (result == null) { + result = descendingMap = createDescendingMap(); + } + return result; + } + + abstract ImmutableSortedMap createDescendingMap(); + + @Override + public ImmutableSortedSet navigableKeySet() { + return keySet(); + } + + @Override + public ImmutableSortedSet descendingKeySet() { + return keySet().descendingSet(); + } + + /** + * Serialized type for all ImmutableSortedMap instances. It captures the logical + * contents and they are reconstructed using public factory methods. This + * ensures that the implementation types remain as implementation details. + */ + private static class SerializedForm extends ImmutableMap.SerializedForm { + private final Comparator comparator; + + @SuppressWarnings("unchecked") + SerializedForm(ImmutableSortedMap sortedMap) { + super(sortedMap); + comparator = (Comparator) sortedMap.comparator(); + } + + @Override + Object readResolve() { + Builder builder = new Builder(comparator); + return createMap(builder); + } + + private static final long serialVersionUID = 0; + } + + @Override + Object writeReplace() { + return new SerializedForm(this); + } + + // 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/sources/main/java/com/google/common/collect/ImmutableSortedMapFauxverideShim.java b/sources/main/java/com/google/common/collect/ImmutableSortedMapFauxverideShim.java new file mode 100644 index 0000000..b5a1950 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSortedMapFauxverideShim.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +/** + * "Overrides" the {@link ImmutableMap} static methods that lack + * {@link ImmutableSortedMap} equivalents with deprecated, exception-throwing + * versions. See {@link ImmutableSortedSetFauxverideShim} for details. + * + * @author Chris Povirk + */ +abstract class ImmutableSortedMapFauxverideShim extends ImmutableMap { + /** + * Not supported. Use {@link ImmutableSortedMap#naturalOrder}, which offers + * better type-safety, instead. This method exists only to hide + * {@link ImmutableMap#builder} from consumers of {@code ImmutableSortedMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMap#naturalOrder}, which offers better + * type-safety. + */ + @Deprecated + public static ImmutableSortedMap.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain a + * non-{@code Comparable} key. Proper calls will resolve to the version in + * {@code ImmutableSortedMap}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a key of type {@code Comparable} to use + * {@link ImmutableSortedMap#of(Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain + * non-{@code Comparable} keys. Proper calls will resolve to the version in + * {@code ImmutableSortedMap}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use + * {@link ImmutableSortedMap#of(Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain + * non-{@code Comparable} keys. Proper calls to will resolve to the version + * in {@code ImmutableSortedMap}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use + * {@link ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain + * non-{@code Comparable} keys. Proper calls will resolve to the version in + * {@code ImmutableSortedMap}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use + * {@link ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain + * non-{@code Comparable} keys. Proper calls will resolve to the version in + * {@code ImmutableSortedMap}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use + * {@link ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + throw new UnsupportedOperationException(); + } + + // No copyOf() fauxveride; see ImmutableSortedSetFauxverideShim. +} diff --git a/sources/main/java/com/google/common/collect/ImmutableSortedMultiset.java b/sources/main/java/com/google/common/collect/ImmutableSortedMultiset.java new file mode 100644 index 0000000..08c092b --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSortedMultiset.java @@ -0,0 +1,622 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; + +/** + * An immutable {@code SortedMultiset} that stores its elements in a sorted + * array. Some instances are ordered by an explicit comparator, while others + * follow the natural sort ordering of their elements. Either way, null elements + * are not supported. + * + *

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

+ * The multisets returned by the {@link #headMultiset}, {@link #tailMultiset}, + * and {@link #subMultiset} methods share the same array as the original + * multiset, preventing that array from being garbage collected. If this is a + * concern, the data may be copied into a correctly-sized array by calling + * {@link #copyOfSorted}. + * + *

+ * Note on element equivalence: The {@link #contains(Object)}, + * {@link #containsAll(Collection)}, and {@link #equals(Object)} implementations + * must check whether a provided object is equivalent to an element in the + * collection. Unlike most collections, an {@code ImmutableSortedMultiset} + * doesn't use {@link Object#equals} to determine if two elements are + * equivalent. Instead, with an explicit comparator, the following relation + * determines whether elements {@code x} and {@code y} are equivalent: + * + *

+ *    {@code
+ *
+ *   {(x, y) | comparator.compare(x, y) == 0}}
+ * 
+ * + *

+ * With natural ordering of elements, the following relation determines whether + * two elements are equivalent: + * + *

+ *    {@code
+ *
+ *   {(x, y) | x.compareTo(y) == 0}}
+ * 
+ * + * Warning: Like most multisets, an {@code ImmutableSortedMultiset} will + * not function correctly if an element is modified after being placed in the + * multiset. For this reason, and to avoid general confusion, it is strongly + * recommended to place only immutable objects into this collection. + * + *

+ * 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. + * + * @author Louis Wasserman + * @since 12.0 + */ +@Beta +@GwtIncompatible("hasn't been tested yet") +public abstract class ImmutableSortedMultiset extends ImmutableSortedMultisetFauxverideShim + implements SortedMultiset { + // TODO(user): GWT compatibility + + private static final Comparator NATURAL_ORDER = Ordering.natural(); + + private static final ImmutableSortedMultiset NATURAL_EMPTY_MULTISET = new EmptyImmutableSortedMultiset( + NATURAL_ORDER); + + /** + * Returns the empty immutable sorted multiset. + */ + @SuppressWarnings("unchecked") + public static ImmutableSortedMultiset of() { + return (ImmutableSortedMultiset) NATURAL_EMPTY_MULTISET; + } + + /** + * Returns an immutable sorted multiset containing a single element. + */ + public static > ImmutableSortedMultiset of(E element) { + RegularImmutableSortedSet elementSet = (RegularImmutableSortedSet) ImmutableSortedSet.of(element); + int[] counts = { 1 }; + long[] cumulativeCounts = { 0, 1 }; + return new RegularImmutableSortedMultiset(elementSet, counts, cumulativeCounts, 0, 1); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * their natural ordering. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedMultiset of(E e1, E e2) { + return copyOf(Ordering.natural(), Arrays.asList(e1, e2)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * their natural ordering. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedMultiset of(E e1, E e2, E e3) { + return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * their natural ordering. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedMultiset of(E e1, E e2, E e3, E e4) { + return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * their natural ordering. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5) { + return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4, e5)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * their natural ordering. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5, E e6, + E... remaining) { + int size = remaining.length + 6; + List all = Lists.newArrayListWithCapacity(size); + Collections.addAll(all, e1, e2, e3, e4, e5, e6); + Collections.addAll(all, remaining); + return copyOf(Ordering.natural(), all); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * their natural ordering. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static > ImmutableSortedMultiset copyOf(E[] elements) { + return copyOf(Ordering.natural(), Arrays.asList(elements)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * their natural ordering. To create a copy of a {@code SortedMultiset} that + * preserves the comparator, call {@link #copyOfSorted} instead. This method + * iterates over {@code elements} at most once. + * + *

+ * Note that if {@code s} is a {@code multiset}, then {@code + * ImmutableSortedMultiset.copyOf(s)} returns an + * {@code ImmutableSortedMultiset} containing each of the strings in + * {@code s}, while {@code ImmutableSortedMultiset.of(s)} returns an + * {@code ImmutableSortedMultiset>} containing one element (the + * given multiset 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. + * + *

+ * This method is not type-safe, as it may be called on elements that are not + * mutually comparable. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSortedMultiset copyOf(Iterable elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedMultisetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * their natural ordering. + * + *

+ * This method is not type-safe, as it may be called on elements that are not + * mutually comparable. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSortedMultiset copyOf(Iterator elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedMultisetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * the given {@code + * Comparator}. + * + * @throws NullPointerException if {@code comparator} or any of {@code elements} + * is null + */ + public static ImmutableSortedMultiset copyOf(Comparator comparator, + Iterator elements) { + checkNotNull(comparator); + return new Builder(comparator).addAll(elements).build(); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by + * the given {@code + * Comparator}. This method iterates over {@code elements} at most once. + * + *

+ * 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 {@code comparator} or any of {@code elements} + * is null + */ + public static ImmutableSortedMultiset copyOf(Comparator comparator, + Iterable elements) { + if (elements instanceof ImmutableSortedMultiset) { + @SuppressWarnings("unchecked") // immutable collections are always safe for covariant casts + ImmutableSortedMultiset multiset = (ImmutableSortedMultiset) elements; + if (comparator.equals(multiset.comparator())) { + if (multiset.isPartialView()) { + return copyOfSortedEntries(comparator, multiset.entrySet().asList()); + } else { + return multiset; + } + } + } + elements = Lists.newArrayList(elements); // defensive copy + TreeMultiset sortedCopy = TreeMultiset.create(checkNotNull(comparator)); + Iterables.addAll(sortedCopy, elements); + return copyOfSortedEntries(comparator, sortedCopy.entrySet()); + } + + /** + * Returns an immutable sorted multiset containing the elements of a sorted + * multiset, sorted by the same {@code Comparator}. That behavior differs from + * {@link #copyOf(Iterable)}, which always uses the natural ordering of the + * elements. + * + *

+ * 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. + * + *

+ * This method is safe to use even when {@code sortedMultiset} is a synchronized + * or concurrent collection that is currently being modified by another thread. + * + * @throws NullPointerException if {@code sortedMultiset} or any of its elements + * is null + */ + public static ImmutableSortedMultiset copyOfSorted(SortedMultiset sortedMultiset) { + return copyOfSortedEntries(sortedMultiset.comparator(), Lists.newArrayList(sortedMultiset.entrySet())); + } + + private static ImmutableSortedMultiset copyOfSortedEntries(Comparator comparator, + Collection> entries) { + if (entries.isEmpty()) { + return emptyMultiset(comparator); + } + ImmutableList.Builder elementsBuilder = new ImmutableList.Builder(entries.size()); + int[] counts = new int[entries.size()]; + long[] cumulativeCounts = new long[entries.size() + 1]; + int i = 0; + for (Entry entry : entries) { + elementsBuilder.add(entry.getElement()); + counts[i] = entry.getCount(); + cumulativeCounts[i + 1] = cumulativeCounts[i] + counts[i]; + i++; + } + return new RegularImmutableSortedMultiset( + new RegularImmutableSortedSet(elementsBuilder.build(), comparator), counts, cumulativeCounts, 0, + entries.size()); + } + + @SuppressWarnings("unchecked") + static ImmutableSortedMultiset emptyMultiset(Comparator comparator) { + if (NATURAL_ORDER.equals(comparator)) { + return (ImmutableSortedMultiset) NATURAL_EMPTY_MULTISET; + } + return new EmptyImmutableSortedMultiset(comparator); + } + + ImmutableSortedMultiset() { + } + + @Override + public final Comparator comparator() { + return elementSet().comparator(); + } + + @Override + public abstract ImmutableSortedSet elementSet(); + + transient ImmutableSortedMultiset descendingMultiset; + + @Override + public ImmutableSortedMultiset descendingMultiset() { + ImmutableSortedMultiset result = descendingMultiset; + if (result == null) { + return descendingMultiset = new DescendingImmutableSortedMultiset(this); + } + return result; + } + + /** + * {@inheritDoc} + * + *

+ * This implementation is guaranteed to throw an + * {@link UnsupportedOperationException}. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + *

+ * This implementation is guaranteed to throw an + * {@link UnsupportedOperationException}. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public abstract ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType); + + @Override + public ImmutableSortedMultiset subMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, + BoundType upperBoundType) { + checkArgument(comparator().compare(lowerBound, upperBound) <= 0, + "Expected lowerBound <= upperBound but %s > %s", lowerBound, upperBound); + return tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); + } + + @Override + public abstract ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType); + + /** + * Returns a builder that creates immutable sorted multisets with an explicit + * comparator. If the comparator has a more general type than the set being + * generated, such as creating a {@code + * SortedMultiset} with a {@code Comparator}, use the + * {@link Builder} constructor instead. + * + * @throws NullPointerException if {@code comparator} is null + */ + public static Builder orderedBy(Comparator comparator) { + return new Builder(comparator); + } + + /** + * Returns a builder that creates immutable sorted multisets whose elements are + * ordered by the reverse of their natural ordering. + * + *

+ * Note: the type parameter {@code E} extends {@code Comparable} rather than + * {@code + * Comparable} as a workaround for javac + * bug + * 6468354. + */ + public static > Builder reverseOrder() { + return new Builder(Ordering.natural().reverse()); + } + + /** + * Returns a builder that creates immutable sorted multisets whose elements are + * ordered by their natural ordering. The sorted multisets use + * {@link Ordering#natural()} as the comparator. This method provides more + * type-safety than {@link #builder}, as it can be called only for classes that + * implement {@link Comparable}. + * + *

+ * Note: the type parameter {@code E} extends {@code Comparable} rather than + * {@code + * Comparable} as a workaround for javac + * bug + * 6468354. + */ + public static > Builder naturalOrder() { + return new Builder(Ordering.natural()); + } + + /** + * A builder for creating immutable multiset instances, especially + * {@code public static final} multisets ("constant multisets"). Example: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	public static final ImmutableSortedMultiset BEANS = new ImmutableSortedMultiset.Builder()
+	 * 			.addCopies(Bean.COCOA, 4).addCopies(Bean.GARDEN, 6).addCopies(Bean.RED, 8).addCopies(Bean.BLACK_EYED, 10)
+	 * 			.build();
+	 * }
+	 * 
+ * + *

+ * Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple multisets in series. + * + * @since 12.0 + */ + public static class Builder extends ImmutableMultiset.Builder { + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableSortedMultiset#orderedBy(Comparator)}. + */ + public Builder(Comparator comparator) { + super(TreeMultiset.create(checkNotNull(comparator))); + } + + /** + * Adds {@code element} to the {@code ImmutableSortedMultiset}. + * + * @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 a number of occurrences of an element to this + * {@code ImmutableSortedMultiset}. + * + * @param element the element to add + * @param occurrences the number of occurrences of the element to add. May be + * zero, in which case no change will be made. + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + * @throws IllegalArgumentException if {@code occurrences} is negative, or if + * this operation would result in more than + * {@link Integer#MAX_VALUE} occurrences of the + * element + */ + @Override + public Builder addCopies(E element, int occurrences) { + super.addCopies(element, occurrences); + return this; + } + + /** + * Adds or removes the necessary occurrences of an element such that the element + * attains the desired count. + * + * @param element the element to add or remove occurrences of + * @param count the desired count of the element in this multiset + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + * @throws IllegalArgumentException if {@code count} is negative + */ + @Override + public Builder setCount(E element, int count) { + super.setCount(element, count); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. + * + * @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 ImmutableSortedMultiset}. + * + * @param elements the {@code Iterable} to add to the + * {@code ImmutableSortedMultiset} + * @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 ImmutableSortedMultiset}. + * + * @param elements the elements to add to the {@code ImmutableSortedMultiset} + * @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 ImmutableSortedMultiset} based on the contents + * of the {@code + * Builder}. + */ + @Override + public ImmutableSortedMultiset build() { + return copyOfSorted((SortedMultiset) contents); + } + } + + private static final class SerializedForm implements Serializable { + Comparator comparator; + E[] elements; + int[] counts; + + @SuppressWarnings("unchecked") + SerializedForm(SortedMultiset multiset) { + this.comparator = multiset.comparator(); + int n = multiset.entrySet().size(); + elements = (E[]) new Object[n]; + counts = new int[n]; + int i = 0; + for (Entry entry : multiset.entrySet()) { + elements[i] = entry.getElement(); + counts[i] = entry.getCount(); + i++; + } + } + + Object readResolve() { + int n = elements.length; + Builder builder = new Builder(comparator); + for (int i = 0; i < n; i++) { + builder.addCopies(elements[i], counts[i]); + } + return builder.build(); + } + } + + @Override + Object writeReplace() { + return new SerializedForm(this); + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java b/sources/main/java/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java new file mode 100644 index 0000000..57a4346 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +/** + * "Overrides" the {@link ImmutableMultiset} static methods that lack + * {@link ImmutableSortedMultiset} equivalents with deprecated, + * exception-throwing versions. This prevents accidents like the following: + * + *

+ *    {@code
+ * 
+ *   List objects = ...;
+ *   // Sort them:
+ *   Set sorted = ImmutableSortedMultiset.copyOf(objects);
+ *   // BAD CODE! The returned multiset is actually an unsorted ImmutableMultiset!}
+ * 
+ *
+ * 

+ * While we could put the overrides in {@link ImmutableSortedMultiset} itself, + * it seems clearer to separate these "do not call" methods from those intended + * for normal use. + * + * @author Louis Wasserman + */ +abstract class ImmutableSortedMultisetFauxverideShim extends ImmutableMultiset { + /** + * Not supported. Use {@link ImmutableSortedMultiset#naturalOrder}, which offers + * better type-safety, instead. This method exists only to hide + * {@link ImmutableMultiset#builder} from consumers of + * {@code ImmutableSortedMultiset}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMultiset#naturalOrder}, which offers + * better type-safety. + */ + @Deprecated + public static ImmutableSortedMultiset.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a + * non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a parameter of type {@code Comparable} to use + * {@link ImmutableSortedMultiset#of(Comparable)}. + */ + @Deprecated + public static ImmutableSortedMultiset of(E element) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a + * non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedMultiset#of(Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a + * non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedMultiset#of(Comparable, Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a + * non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable)}. + * + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a + * non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable, Comparable)} + * . + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a + * non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable, Comparable, Comparable, Comparable...)} + * . + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain + * non-{@code + * Comparable} elements. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass parameters of type {@code Comparable} to use + * {@link ImmutableSortedMultiset#copyOf(Comparable[])}. + */ + @Deprecated + public static ImmutableSortedMultiset copyOf(E[] elements) { + throw new UnsupportedOperationException(); + } + + /* + * We would like to include an unsupported " copyOf(Iterable)" here, + * providing only the properly typed + * "> copyOf(Iterable)" in ImmutableSortedMultiset + * (and likewise for the Iterator equivalent). However, due to a change in Sun's + * interpretation of the JLS (as described at + * http://bugs.sun.com/view_bug.do?bug_id=6182950), the OpenJDK 7 compiler + * available as of this writing rejects our attempts. To maintain compatibility + * with that version and with any other compilers that interpret the JLS + * similarly, there is no definition of copyOf() here, and the definition in + * ImmutableSortedMultiset matches that in ImmutableMultiset. + * + * The result is that ImmutableSortedMultiset.copyOf() may be called on + * non-Comparable elements. We have not discovered a better solution. In + * retrospect, the static factory methods should have gone in a separate class + * so that ImmutableSortedMultiset wouldn't "inherit" too-permissive factory + * methods from ImmutableMultiset. + */ +} diff --git a/sources/main/java/com/google/common/collect/ImmutableSortedSet.java b/sources/main/java/com/google/common/collect/ImmutableSortedSet.java new file mode 100644 index 0000000..e90b199 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSortedSet.java @@ -0,0 +1,851 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.ObjectArrays.checkElementsNotNull; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An immutable {@code SortedSet} that stores its elements in a sorted array. + * Some instances are ordered by an explicit comparator, while others follow the + * natural sort ordering of their elements. Either way, null elements are not + * supported. + * + *

+ * Unlike {@link Collections#unmodifiableSortedSet}, which is a view of a + * separate collection that can still change, an instance of {@code + * ImmutableSortedSet} 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. + * + *

+ * The sets returned by the {@link #headSet}, {@link #tailSet}, and + * {@link #subSet} methods share the same array as the original set, preventing + * that array from being garbage collected. If this is a concern, the data may + * be copied into a correctly-sized array by calling {@link #copyOfSorted}. + * + *

+ * Note on element equivalence: The {@link #contains(Object)}, + * {@link #containsAll(Collection)}, and {@link #equals(Object)} implementations + * must check whether a provided object is equivalent to an element in the + * collection. Unlike most collections, an {@code ImmutableSortedSet} doesn't + * use {@link Object#equals} to determine if two elements are equivalent. + * Instead, with an explicit comparator, the following relation determines + * whether elements {@code x} and {@code y} are equivalent: + * + *

+ *    {@code
+ *
+ *   {(x, y) | comparator.compare(x, y) == 0}}
+ * 
+ * + *

+ * With natural ordering of elements, the following relation determines whether + * two elements are equivalent: + * + *

+ *    {@code
+ *
+ *   {(x, y) | x.compareTo(y) == 0}}
+ * 
+ * + * Warning: Like most sets, an {@code ImmutableSortedSet} 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. + * + *

+ * 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 ImmutableSet + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library; implements + * {@code NavigableSet} since 12.0) + */ +// TODO(benyu): benchmark and optimize all creation paths, which are a mess now +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +public abstract class ImmutableSortedSet extends ImmutableSortedSetFauxverideShim + implements NavigableSet, SortedIterable { + + private static final Comparator NATURAL_ORDER = Ordering.natural(); + + private static final ImmutableSortedSet NATURAL_EMPTY_SET = new EmptyImmutableSortedSet( + NATURAL_ORDER); + + @SuppressWarnings("unchecked") + private static ImmutableSortedSet emptySet() { + return (ImmutableSortedSet) NATURAL_EMPTY_SET; + } + + static ImmutableSortedSet emptySet(Comparator comparator) { + if (NATURAL_ORDER.equals(comparator)) { + return emptySet(); + } else { + return new EmptyImmutableSortedSet(comparator); + } + } + + /** + * Returns the empty immutable sorted set. + */ + public static ImmutableSortedSet of() { + return emptySet(); + } + + /** + * Returns an immutable sorted set containing a single element. + */ + public static > ImmutableSortedSet of(E element) { + return new RegularImmutableSortedSet(ImmutableList.of(element), Ordering.natural()); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@link Comparable#compareTo}, only the first one specified is included. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of(E e1, E e2) { + return construct(Ordering.natural(), 2, e1, e2); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@link Comparable#compareTo}, only the first one specified is included. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of(E e1, E e2, E e3) { + return construct(Ordering.natural(), 3, e1, e2, e3); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@link Comparable#compareTo}, only the first one specified is included. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of(E e1, E e2, E e3, E e4) { + return construct(Ordering.natural(), 4, e1, e2, e3, e4); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@link Comparable#compareTo}, only the first one specified is included. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5) { + return construct(Ordering.natural(), 5, e1, e2, e3, e4, e5); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@link Comparable#compareTo}, only the first one specified is included. + * + * @throws NullPointerException if any element is null + * @since 3.0 (source-compatible since 2.0) + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5, E e6, + E... remaining) { + Comparable[] contents = new Comparable[6 + remaining.length]; + contents[0] = e1; + contents[1] = e2; + contents[2] = e3; + contents[3] = e4; + contents[4] = e5; + contents[5] = e6; + System.arraycopy(remaining, 0, contents, 6, remaining.length); + return construct(Ordering.natural(), contents.length, (E[]) contents); + } + + // TODO(kevinb): Consider factory methods that reject duplicates + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@link Comparable#compareTo}, only the first one specified is included. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 3.0 + */ + public static > ImmutableSortedSet copyOf(E[] elements) { + return construct(Ordering.natural(), elements.length, elements.clone()); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@code compareTo()}, only the first one specified is included. To create a + * copy of a {@code SortedSet} that preserves the comparator, call + * {@link #copyOfSorted} instead. This method iterates over {@code elements} at + * most once. + * + * + *

+ * Note that if {@code s} is a {@code Set}, then {@code + * ImmutableSortedSet.copyOf(s)} returns an {@code ImmutableSortedSet} + * containing each of the strings in {@code s}, while {@code + * ImmutableSortedSet.of(s)} returns an {@code + * ImmutableSortedSet>} 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. + * + *

+ * This method is not type-safe, as it may be called on elements that are not + * mutually comparable. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSortedSet copyOf(Iterable elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@code compareTo()}, only the first one specified is included. To create a + * copy of a {@code SortedSet} that preserves the comparator, call + * {@link #copyOfSorted} instead. This method iterates over {@code elements} at + * most once. + * + *

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

+ * Note: Despite what the method name suggests, if {@code elements} is an + * {@code ImmutableSortedSet}, it may be returned instead of a copy. + * + *

+ * This method is not type-safe, as it may be called on elements that are not + * mutually comparable. + * + *

+ * 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 ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + * @since 7.0 (source-compatible since 2.0) + */ + public static ImmutableSortedSet copyOf(Collection elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their + * natural ordering. When multiple elements are equivalent according to + * {@code compareTo()}, only the first one specified is included. + * + *

+ * This method is not type-safe, as it may be called on elements that are not + * mutually comparable. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSortedSet copyOf(Iterator elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by the + * given {@code Comparator}. When multiple elements are equivalent according to + * {@code compareTo()}, only the first one specified is included. + * + * @throws NullPointerException if {@code comparator} or any of {@code elements} + * is null + */ + public static ImmutableSortedSet copyOf(Comparator comparator, Iterator elements) { + return new Builder(comparator).addAll(elements).build(); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by the + * given {@code Comparator}. When multiple elements are equivalent according to + * {@code compare()}, only the first one specified is included. This method + * iterates over {@code elements} at most once. + * + *

+ * 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 {@code comparator} or any of {@code + * elements} is null + */ + public static ImmutableSortedSet copyOf(Comparator comparator, Iterable elements) { + checkNotNull(comparator); + boolean hasSameComparator = SortedIterables.hasSameComparator(comparator, elements); + + if (hasSameComparator && (elements instanceof ImmutableSortedSet)) { + @SuppressWarnings("unchecked") + ImmutableSortedSet original = (ImmutableSortedSet) elements; + if (!original.isPartialView()) { + return original; + } + } + @SuppressWarnings("unchecked") // elements only contains E's; it's safe. + E[] array = (E[]) Iterables.toArray(elements); + return construct(comparator, array.length, array); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by the + * given {@code Comparator}. When multiple elements are equivalent according to + * {@code compareTo()}, only the first one specified is included. + * + *

+ * 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. + * + *

+ * 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 {@code comparator} or any of {@code elements} + * is null + * @since 7.0 (source-compatible since 2.0) + */ + public static ImmutableSortedSet copyOf(Comparator comparator, Collection elements) { + return copyOf(comparator, (Iterable) elements); + } + + /** + * Returns an immutable sorted set containing the elements of a sorted set, + * sorted by the same {@code Comparator}. That behavior differs from + * {@link #copyOf(Iterable)}, which always uses the natural ordering of the + * elements. + * + *

+ * 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. + * + *

+ * This method is safe to use even when {@code sortedSet} is a synchronized or + * concurrent collection that is currently being modified by another thread. + * + * @throws NullPointerException if {@code sortedSet} or any of its elements is + * null + */ + public static ImmutableSortedSet copyOfSorted(SortedSet sortedSet) { + Comparator comparator = SortedIterables.comparator(sortedSet); + ImmutableList list = ImmutableList.copyOf(sortedSet); + if (list.isEmpty()) { + return emptySet(comparator); + } else { + return new RegularImmutableSortedSet(list, comparator); + } + } + + /** + * Constructs an {@code ImmutableSortedSet} from the first {@code n} elements of + * {@code contents}. If {@code k} is the size of the returned + * {@code ImmutableSortedSet}, then the sorted unique elements are in the first + * {@code k} positions of {@code contents}, and {@code contents[i] == null} for + * {@code k <= i < n}. + * + *

+ * If {@code k == contents.length}, then {@code contents} may no longer be safe + * for modification. + * + * @throws NullPointerException if any of the first {@code n} elements of + * {@code contents} is null + */ + static ImmutableSortedSet construct(Comparator comparator, int n, E... contents) { + if (n == 0) { + return emptySet(comparator); + } + checkElementsNotNull(contents, n); + Arrays.sort(contents, 0, n, comparator); + int uniques = 1; + for (int i = 1; i < n; i++) { + E cur = contents[i]; + E prev = contents[uniques - 1]; + if (comparator.compare(cur, prev) != 0) { + contents[uniques++] = cur; + } + } + Arrays.fill(contents, uniques, n, null); + return new RegularImmutableSortedSet(ImmutableList.asImmutableList(contents, uniques), comparator); + } + + /** + * Returns a builder that creates immutable sorted sets with an explicit + * comparator. If the comparator has a more general type than the set being + * generated, such as creating a {@code SortedSet} with a + * {@code Comparator}, use the {@link Builder} constructor instead. + * + * @throws NullPointerException if {@code comparator} is null + */ + public static Builder orderedBy(Comparator comparator) { + return new Builder(comparator); + } + + /** + * Returns a builder that creates immutable sorted sets whose elements are + * ordered by the reverse of their natural ordering. + */ + public static > Builder reverseOrder() { + return new Builder(Ordering.natural().reverse()); + } + + /** + * Returns a builder that creates immutable sorted sets whose elements are + * ordered by their natural ordering. The sorted sets use + * {@link Ordering#natural()} as the comparator. This method provides more + * type-safety than {@link #builder}, as it can be called only for classes that + * implement {@link Comparable}. + */ + public static > Builder naturalOrder() { + return new Builder(Ordering.natural()); + } + + /** + * A builder for creating immutable sorted set instances, especially {@code + * public static final} sets ("constant sets"), with a given comparator. + * Example: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	public static final ImmutableSortedSet LUCKY_NUMBERS = new ImmutableSortedSet.Builder(
+	 * 			ODDS_FIRST_COMPARATOR).addAll(SINGLE_DIGIT_PRIMES).add(42).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 final class Builder extends ImmutableSet.Builder { + private final Comparator comparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableSortedSet#orderedBy}. + */ + public Builder(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + /** + * Adds {@code element} to the {@code ImmutableSortedSet}. If the + * {@code ImmutableSortedSet} 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 ImmutableSortedSet}, + * 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} contains a null element + */ + @Override + public Builder add(E... elements) { + super.add(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedSet}, + * ignoring duplicate elements (only the first duplicate element is added). + * + * @param elements the elements to add to the {@code ImmutableSortedSet} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} contains a null element + */ + @Override + public Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedSet}, + * ignoring duplicate elements (only the first duplicate element is added). + * + * @param elements the elements to add to the {@code ImmutableSortedSet} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} contains a null element + */ + @Override + public Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + /** + * Returns a newly-created {@code ImmutableSortedSet} based on the contents of + * the {@code Builder} and its comparator. + */ + @Override + public ImmutableSortedSet build() { + @SuppressWarnings("unchecked") // we're careful to put only E's in here + E[] contentsArray = (E[]) contents; + ImmutableSortedSet result = construct(comparator, size, contentsArray); + this.size = result.size(); // we eliminated duplicates in-place in contentsArray + return result; + } + } + + int unsafeCompare(Object a, Object b) { + return unsafeCompare(comparator, a, b); + } + + static int unsafeCompare(Comparator comparator, Object a, Object b) { + // Pretend the comparator can compare anything. If it turns out it can't + // compare a and b, we should get a CCE on the subsequent line. Only methods + // that are spec'd to throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator unsafeComparator = (Comparator) comparator; + return unsafeComparator.compare(a, b); + } + + final transient Comparator comparator; + + ImmutableSortedSet(Comparator comparator) { + this.comparator = comparator; + } + + /** + * Returns the comparator that orders the elements, which is + * {@link Ordering#natural()} when the natural ordering of the elements is used. + * Note that its behavior is not consistent with {@link SortedSet#comparator()}, + * which returns {@code null} to indicate natural ordering. + */ + @Override + public Comparator comparator() { + return comparator; + } + + @Override // needed to unify the iterator() methods in Collection and SortedIterable + public abstract UnmodifiableIterator iterator(); + + /** + * {@inheritDoc} + * + *

+ * This method returns a serializable {@code ImmutableSortedSet}. + * + *

+ * The {@link SortedSet#headSet} documentation states that a subset of a subset + * throws an {@link IllegalArgumentException} if passed a {@code toElement} + * greater than an earlier {@code toElement}. However, this method doesn't throw + * an exception in that situation, but instead keeps the original + * {@code toElement}. + */ + @Override + public ImmutableSortedSet headSet(E toElement) { + return headSet(toElement, false); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public ImmutableSortedSet headSet(E toElement, boolean inclusive) { + return headSetImpl(checkNotNull(toElement), inclusive); + } + + /** + * {@inheritDoc} + * + *

+ * This method returns a serializable {@code ImmutableSortedSet}. + * + *

+ * The {@link SortedSet#subSet} documentation states that a subset of a subset + * throws an {@link IllegalArgumentException} if passed a {@code fromElement} + * smaller than an earlier {@code fromElement}. However, this method doesn't + * throw an exception in that situation, but instead keeps the original + * {@code fromElement}. Similarly, this method keeps the original + * {@code toElement}, instead of throwing an exception, if passed a + * {@code toElement} greater than an earlier {@code toElement}. + */ + @Override + public ImmutableSortedSet subSet(E fromElement, E toElement) { + return subSet(fromElement, true, toElement, false); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public ImmutableSortedSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + checkNotNull(fromElement); + checkNotNull(toElement); + checkArgument(comparator.compare(fromElement, toElement) <= 0); + return subSetImpl(fromElement, fromInclusive, toElement, toInclusive); + } + + /** + * {@inheritDoc} + * + *

+ * This method returns a serializable {@code ImmutableSortedSet}. + * + *

+ * The {@link SortedSet#tailSet} documentation states that a subset of a subset + * throws an {@link IllegalArgumentException} if passed a {@code fromElement} + * smaller than an earlier {@code fromElement}. However, this method doesn't + * throw an exception in that situation, but instead keeps the original + * {@code fromElement}. + */ + @Override + public ImmutableSortedSet tailSet(E fromElement) { + return tailSet(fromElement, true); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public ImmutableSortedSet tailSet(E fromElement, boolean inclusive) { + return tailSetImpl(checkNotNull(fromElement), inclusive); + } + + /* + * These methods perform most headSet, subSet, and tailSet logic, besides + * parameter validation. + */ + abstract ImmutableSortedSet headSetImpl(E toElement, boolean inclusive); + + abstract ImmutableSortedSet subSetImpl(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive); + + abstract ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive); + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public E lower(E e) { + return Iterators.getNext(headSet(e, false).descendingIterator(), null); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public E floor(E e) { + return Iterators.getNext(headSet(e, true).descendingIterator(), null); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public E ceiling(E e) { + return Iterables.getFirst(tailSet(e, true), null); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public E higher(E e) { + return Iterables.getFirst(tailSet(e, false), null); + } + + @Override + public E first() { + return iterator().next(); + } + + @Override + public E last() { + return descendingIterator().next(); + } + + /** + * Guaranteed to throw an exception and leave the set unmodified. + * + * @since 12.0 + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @GwtIncompatible("NavigableSet") + @Override + public final E pollFirst() { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the set unmodified. + * + * @since 12.0 + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @GwtIncompatible("NavigableSet") + @Override + public final E pollLast() { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible("NavigableSet") + transient ImmutableSortedSet descendingSet; + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public ImmutableSortedSet descendingSet() { + // racy single-check idiom + ImmutableSortedSet result = descendingSet; + if (result == null) { + result = descendingSet = createDescendingSet(); + result.descendingSet = this; + } + return result; + } + + @GwtIncompatible("NavigableSet") + ImmutableSortedSet createDescendingSet() { + return new DescendingImmutableSortedSet(this); + } + + /** + * @since 12.0 + */ + @GwtIncompatible("NavigableSet") + @Override + public abstract UnmodifiableIterator descendingIterator(); + + /** + * Returns the position of an element within the set, or -1 if not present. + */ + abstract int indexOf(@Nullable Object target); + + /* + * This class is used to serialize all ImmutableSortedSet instances, regardless + * of implementation type. It captures their "logical contents" only. This is + * necessary to ensure that the existence of a particular implementation type is + * an implementation detail. + */ + private static class SerializedForm implements Serializable { + final Comparator comparator; + final Object[] elements; + + public SerializedForm(Comparator comparator, Object[] elements) { + this.comparator = comparator; + this.elements = elements; + } + + @SuppressWarnings("unchecked") + Object readResolve() { + return new Builder(comparator).add((E[]) elements).build(); + } + + private static final long serialVersionUID = 0; + } + + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @Override + Object writeReplace() { + return new SerializedForm(comparator, toArray()); + } +} diff --git a/sources/main/java/com/google/common/collect/ImmutableSortedSetFauxverideShim.java b/sources/main/java/com/google/common/collect/ImmutableSortedSetFauxverideShim.java new file mode 100644 index 0000000..f8ab7b3 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableSortedSetFauxverideShim.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +/** + * "Overrides" the {@link ImmutableSet} static methods that lack + * {@link ImmutableSortedSet} equivalents with deprecated, exception-throwing + * versions. This prevents accidents like the following: + * + *

+ *    {@code
+ *
+ *   List objects = ...;
+ *   // Sort them:
+ *   Set sorted = ImmutableSortedSet.copyOf(objects);
+ *   // BAD CODE! The returned set is actually an unsorted ImmutableSet!}
+ * 
+ *
+ * 

+ * While we could put the overrides in {@link ImmutableSortedSet} itself, it + * seems clearer to separate these "do not call" methods from those intended for + * normal use. + * + * @author Chris Povirk + */ +abstract class ImmutableSortedSetFauxverideShim extends ImmutableSet { + /** + * Not supported. Use {@link ImmutableSortedSet#naturalOrder}, which offers + * better type-safety, instead. This method exists only to hide + * {@link ImmutableSet#builder} from consumers of {@code ImmutableSortedSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedSet#naturalOrder}, which offers better + * type-safety. + */ + @Deprecated + public static ImmutableSortedSet.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a + * non-{@code Comparable} element. Proper calls will resolve to the version + * in {@code ImmutableSortedSet}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a parameter of type {@code Comparable} to use + * {@link ImmutableSortedSet#of(Comparable)}. + */ + @Deprecated + public static ImmutableSortedSet of(E element) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a + * non-{@code Comparable} element. Proper calls will resolve to the version + * in {@code ImmutableSortedSet}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedSet#of(Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a + * non-{@code Comparable} element. Proper calls will resolve to the version + * in {@code ImmutableSortedSet}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedSet#of(Comparable, Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a + * non-{@code Comparable} element. Proper calls will resolve to the version + * in {@code ImmutableSortedSet}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedSet#of(Comparable, Comparable, Comparable, Comparable)}. + * + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a + * non-{@code Comparable} element. Proper calls will resolve to the version + * in {@code ImmutableSortedSet}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedSet#of( Comparable, Comparable, Comparable, Comparable, Comparable)}. + * + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a + * non-{@code Comparable} element. Proper calls will resolve to the version + * in {@code ImmutableSortedSet}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use + * {@link ImmutableSortedSet#of(Comparable, Comparable, Comparable, Comparable, Comparable, Comparable, Comparable...)}. + * + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain + * non-{@code Comparable} elements. Proper calls will resolve to the version + * in {@code ImmutableSortedSet}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass parameters of type {@code Comparable} to use + * {@link ImmutableSortedSet#copyOf(Comparable[])}. + */ + @Deprecated + public static ImmutableSortedSet copyOf(E[] elements) { + throw new UnsupportedOperationException(); + } + + /* + * We would like to include an unsupported " copyOf(Iterable)" here, + * providing only the properly typed + * "> copyOf(Iterable)" in ImmutableSortedSet (and + * likewise for the Iterator equivalent). However, due to a change in Sun's + * interpretation of the JLS (as described at + * http://bugs.sun.com/view_bug.do?bug_id=6182950), the OpenJDK 7 compiler + * available as of this writing rejects our attempts. To maintain compatibility + * with that version and with any other compilers that interpret the JLS + * similarly, there is no definition of copyOf() here, and the definition in + * ImmutableSortedSet matches that in ImmutableSet. + * + * The result is that ImmutableSortedSet.copyOf() may be called on + * non-Comparable elements. We have not discovered a better solution. In + * retrospect, the static factory methods should have gone in a separate class + * so that ImmutableSortedSet wouldn't "inherit" too-permissive factory methods + * from ImmutableSet. + */ +} diff --git a/sources/main/java/com/google/common/collect/ImmutableTable.java b/sources/main/java/com/google/common/collect/ImmutableTable.java new file mode 100644 index 0000000..faf8bde --- /dev/null +++ b/sources/main/java/com/google/common/collect/ImmutableTable.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * 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 + */ +@GwtCompatible +// 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. */ + @SuppressWarnings("unchecked") + 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) { + @SuppressWarnings("unchecked") + 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(); + } + + /** + * 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 Tables.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 Tables.ImmutableCell) { + checkNotNull(cell.getRowKey()); + checkNotNull(cell.getColumnKey()); + checkNotNull(cell.getValue()); + @SuppressWarnings("unchecked") // 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"); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if {@code columnKey} is {@code null} + */ + @Override + public ImmutableMap column(C columnKey) { + checkNotNull(columnKey); + return Objects.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 Objects.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(@Nullable Object rowKey, @Nullable Object columnKey) { + return get(rowKey, columnKey) != null; + } + + @Override + public boolean containsValue(@Nullable 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/sources/main/java/com/google/common/collect/Interner.java b/sources/main/java/com/google/common/collect/Interner.java new file mode 100644 index 0000000..d1a6344 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Interner.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.Beta; + +/** + * Provides equivalent behavior to {@link String#intern} for other immutable + * types. + * + * @author Kevin Bourrillion + * @since 3.0 + */ +@Beta +public interface Interner { + /** + * Chooses and returns the representative instance for any of a collection of + * instances that are equal to each other. If two {@linkplain Object#equals + * equal} inputs are given to this method, both calls will return the same + * instance. That is, {@code intern(a).equals(a)} always holds, and {@code + * intern(a) == intern(b)} if and only if {@code a.equals(b)}. Note that + * {@code intern(a)} is permitted to return one instance now and a different + * instance later if the original interned instance was garbage-collected. + * + *

+ * Warning: do not use with mutable objects. + * + * @throws NullPointerException if {@code sample} is null + */ + E intern(E sample); +} diff --git a/sources/main/java/com/google/common/collect/Iterables.java b/sources/main/java/com/google/common/collect/Iterables.java new file mode 100644 index 0000000..e4bc5e8 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Iterables.java @@ -0,0 +1,1038 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.RandomAccess; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.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) + */ +@GwtCompatible(emulated = true) +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, @Nullable Object element) { + if (iterable instanceof Collection) { + Collection collection = (Collection) iterable; + return Collections2.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.apply(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.apply(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. + */ + @Nullable + 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 + */ + @Nullable + public static T getOnlyElement(Iterable iterable, @Nullable 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 + */ + @GwtIncompatible("Array.newInstance(Class, int)") + public static T[] toArray(Iterable iterable, Class type) { + Collection collection = toCollection(iterable); + T[] array = ObjectArrays.newArray(type, collection.size()); + return collection.toArray(array); + } + + /** + * 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 = Collections2.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, @Nullable 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 + */ + @GwtIncompatible("Class.isInstance") + 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 + */ + @Nullable + public static T find(Iterable iterable, Predicate predicate, @Nullable 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 Collections2#transform}. + */ + public static Iterable transform(final Iterable fromIterable, + final Function function) { + checkNotNull(fromIterable); + checkNotNull(function); + return new FluentIterable() { + @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 + */ + @Nullable + public static T get(Iterable iterable, int position, @Nullable 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 + */ + @Nullable + public static T getFirst(Iterable iterable, @Nullable 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 + */ + @Nullable + public static T getLast(Iterable iterable, @Nullable T defaultValue) { + if (iterable instanceof Collection) { + Collection c = Collections2.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/sources/main/java/com/google/common/collect/Iterators.java b/sources/main/java/com/google/common/collect/Iterators.java new file mode 100644 index 0000000..7a6609b --- /dev/null +++ b/sources/main/java/com/google/common/collect/Iterators.java @@ -0,0 +1,1333 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.in; +import static com.google.common.base.Predicates.instanceOf; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; +import java.util.Queue; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; + +/** + * 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) + */ +@GwtCompatible(emulated = true) +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. + @SuppressWarnings("unchecked") + 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. + @SuppressWarnings("unchecked") + 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, @Nullable 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.apply(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 (!Objects.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 Collections2.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. + */ + @Nullable + public static T getOnlyElement(Iterator iterator, @Nullable 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 + */ + @GwtIncompatible("Array.newInstance(Class, int)") + 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, @Nullable 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 net.lax1dude.eaglercraft.v1_8.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 net.lax1dude.eaglercraft.v1_8.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 + } + + @SuppressWarnings("unchecked") // 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.apply(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 + */ + @SuppressWarnings("unchecked") // can cast to because non-Ts are removed + @GwtIncompatible("Class.isInstance") + 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.apply(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 + */ + @Nullable + public static T find(Iterator iterator, Predicate predicate, @Nullable 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.apply(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 + */ + @Nullable + public static T get(Iterator iterator, int position, @Nullable 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 + */ + @Nullable + public static T getNext(Iterator iterator, @Nullable 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 + */ + @Nullable + public static T getLast(Iterator iterator, @Nullable 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. + */ + @Nullable + 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(@Nullable 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). + @SuppressWarnings("unchecked") + 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/sources/main/java/com/google/common/collect/LexicographicalOrdering.java b/sources/main/java/com/google/common/collect/LexicographicalOrdering.java new file mode 100644 index 0000000..bc294e3 --- /dev/null +++ b/sources/main/java/com/google/common/collect/LexicographicalOrdering.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; +import java.util.Iterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An ordering which sorts iterables by comparing corresponding elements + * pairwise. + */ +@GwtCompatible(serializable = true) +final class LexicographicalOrdering extends Ordering> implements Serializable { + final Ordering elementOrder; + + LexicographicalOrdering(Ordering elementOrder) { + this.elementOrder = elementOrder; + } + + @Override + public int compare(Iterable leftIterable, Iterable rightIterable) { + Iterator left = leftIterable.iterator(); + Iterator right = rightIterable.iterator(); + while (left.hasNext()) { + if (!right.hasNext()) { + return LEFT_IS_GREATER; // because it's longer + } + int result = elementOrder.compare(left.next(), right.next()); + if (result != 0) { + return result; + } + } + if (right.hasNext()) { + return RIGHT_IS_GREATER; // because it's longer + } + return 0; + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof LexicographicalOrdering) { + LexicographicalOrdering that = (LexicographicalOrdering) object; + return this.elementOrder.equals(that.elementOrder); + } + return false; + } + + @Override + public int hashCode() { + return elementOrder.hashCode() ^ 2075626741; // meaningless + } + + @Override + public String toString() { + return elementOrder + ".lexicographical()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/LinkedHashMultimap.java b/sources/main/java/com/google/common/collect/LinkedHashMultimap.java new file mode 100644 index 0000000..b9cac76 --- /dev/null +++ b/sources/main/java/com/google/common/collect/LinkedHashMultimap.java @@ -0,0 +1,590 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; + +/** + * Implementation of {@code Multimap} that does not allow duplicate key-value + * entries and that returns collections whose iterators follow the ordering in + * which the data was added to the multimap. + * + *

+ * The collections returned by {@code keySet}, {@code keys}, and {@code + * asMap} iterate through the keys in the order they were first added to the + * multimap. Similarly, {@code get}, {@code removeAll}, and {@code + * replaceValues} return collections that iterate through the values in the + * order they were added. The collections generated by {@code entries} and + * {@code values} iterate across the key-value mappings in the order they were + * added to the multimap. + * + *

+ * The iteration ordering of the collections generated by {@code keySet}, + * {@code keys}, and {@code asMap} has a few subtleties. As long as the set of + * keys remains unchanged, adding or removing mappings does not affect the key + * iteration order. However, if you remove all values associated with a key and + * then add the key back to the multimap, that key will come last in the key + * iteration order. + * + *

+ * The multimap does not store duplicate key-value pairs. Adding a new key-value + * pair equal to an existing key-value pair has no effect. + * + *

+ * Keys and values may be null. All optional multimap methods are supported, and + * all returned views are modifiable. + * + *

+ * This class is not threadsafe when any concurrent operations update the + * multimap. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap your multimap with a call to + * {@link Multimaps#synchronizedSetMultimap}. + * + *

+ * See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public final class LinkedHashMultimap extends AbstractSetMultimap { + + /** + * Creates a new, empty {@code LinkedHashMultimap} with the default initial + * capacities. + */ + public static LinkedHashMultimap create() { + return new LinkedHashMultimap(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY); + } + + /** + * Constructs an empty {@code LinkedHashMultimap} with enough capacity to hold + * the specified numbers of keys and values without rehashing. + * + * @param expectedKeys the expected number of distinct keys + * @param expectedValuesPerKey the expected average number of values per key + * @throws IllegalArgumentException if {@code expectedKeys} or {@code + * expectedValuesPerKey} is negative + */ + public static LinkedHashMultimap create(int expectedKeys, int expectedValuesPerKey) { + return new LinkedHashMultimap(Maps.capacity(expectedKeys), Maps.capacity(expectedValuesPerKey)); + } + + /** + * Constructs a {@code LinkedHashMultimap} with the same mappings as the + * specified multimap. If a key-value mapping appears multiple times in the + * input multimap, it only appears once in the constructed multimap. The new + * multimap has the same {@link Multimap#entries()} iteration order as the input + * multimap, except for excluding duplicate mappings. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static LinkedHashMultimap create(Multimap multimap) { + LinkedHashMultimap result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY); + result.putAll(multimap); + return result; + } + + private interface ValueSetLink { + ValueSetLink getPredecessorInValueSet(); + + ValueSetLink getSuccessorInValueSet(); + + void setPredecessorInValueSet(ValueSetLink entry); + + void setSuccessorInValueSet(ValueSetLink entry); + } + + private static void succeedsInValueSet(ValueSetLink pred, ValueSetLink succ) { + pred.setSuccessorInValueSet(succ); + succ.setPredecessorInValueSet(pred); + } + + private static void succeedsInMultimap(ValueEntry pred, ValueEntry succ) { + pred.setSuccessorInMultimap(succ); + succ.setPredecessorInMultimap(pred); + } + + private static void deleteFromValueSet(ValueSetLink entry) { + succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet()); + } + + private static void deleteFromMultimap(ValueEntry entry) { + succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap()); + } + + /** + * LinkedHashMultimap entries are in no less than three coexisting linked lists: + * a bucket in the hash table for a Set associated with a key, the linked + * list of insertion-ordered entries in that Set, and the linked list of + * entries in the LinkedHashMultimap as a whole. + */ + @VisibleForTesting + static final class ValueEntry extends ImmutableEntry implements ValueSetLink { + final int smearedValueHash; + + @Nullable + ValueEntry nextInValueBucket; + + ValueSetLink predecessorInValueSet; + ValueSetLink successorInValueSet; + + ValueEntry predecessorInMultimap; + ValueEntry successorInMultimap; + + ValueEntry(@Nullable K key, @Nullable V value, int smearedValueHash, + @Nullable ValueEntry nextInValueBucket) { + super(key, value); + this.smearedValueHash = smearedValueHash; + this.nextInValueBucket = nextInValueBucket; + } + + boolean matchesValue(@Nullable Object v, int smearedVHash) { + return smearedValueHash == smearedVHash && Objects.equal(getValue(), v); + } + + @Override + public ValueSetLink getPredecessorInValueSet() { + return predecessorInValueSet; + } + + @Override + public ValueSetLink getSuccessorInValueSet() { + return successorInValueSet; + } + + @Override + public void setPredecessorInValueSet(ValueSetLink entry) { + predecessorInValueSet = entry; + } + + @Override + public void setSuccessorInValueSet(ValueSetLink entry) { + successorInValueSet = entry; + } + + public ValueEntry getPredecessorInMultimap() { + return predecessorInMultimap; + } + + public ValueEntry getSuccessorInMultimap() { + return successorInMultimap; + } + + public void setSuccessorInMultimap(ValueEntry multimapSuccessor) { + this.successorInMultimap = multimapSuccessor; + } + + public void setPredecessorInMultimap(ValueEntry multimapPredecessor) { + this.predecessorInMultimap = multimapPredecessor; + } + } + + private static final int DEFAULT_KEY_CAPACITY = 16; + private static final int DEFAULT_VALUE_SET_CAPACITY = 2; + @VisibleForTesting + static final double VALUE_SET_LOAD_FACTOR = 1.0; + + @VisibleForTesting + transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY; + private transient ValueEntry multimapHeaderEntry; + + private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) { + super(new LinkedHashMap>(keyCapacity)); + checkNonnegative(valueSetCapacity, "expectedValuesPerKey"); + + this.valueSetCapacity = valueSetCapacity; + this.multimapHeaderEntry = new ValueEntry(null, null, 0, null); + succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); + } + + /** + * {@inheritDoc} + * + *

+ * Creates an empty {@code LinkedHashSet} for a collection of values for one + * key. + * + * @return a new {@code LinkedHashSet} containing a collection of values for one + * key + */ + @Override + Set createCollection() { + return new LinkedHashSet(valueSetCapacity); + } + + /** + * {@inheritDoc} + * + *

+ * Creates a decorated insertion-ordered set that also keeps track of the order + * in which key-value pairs are added to the multimap. + * + * @param key key to associate with values in the collection + * @return a new decorated set containing a collection of values for one key + */ + @Override + Collection createCollection(K key) { + return new ValueSet(key, valueSetCapacity); + } + + /** + * {@inheritDoc} + * + *

+ * If {@code values} is not empty and the multimap already contains a mapping + * for {@code key}, the {@code keySet()} ordering is unchanged. However, the + * provided values always come last in the {@link #entries()} and + * {@link #values()} iteration orderings. + */ + @Override + public Set replaceValues(@Nullable K key, Iterable values) { + return super.replaceValues(key, values); + } + + /** + * Returns a set of all key-value pairs. Changes to the returned set will update + * the underlying multimap, and vice versa. The entries set does not support the + * {@code add} or {@code addAll} operations. + * + *

+ * The iterator generated by the returned set traverses the entries in the order + * they were added to the multimap. + * + *

+ * Each entry is an immutable snapshot of a key-value mapping in the multimap, + * taken at the time the entry is returned by a method call to the collection or + * its iterator. + */ + @Override + public Set> entries() { + return super.entries(); + } + + /** + * Returns a collection of all values in the multimap. Changes to the returned + * collection will update the underlying multimap, and vice versa. + * + *

+ * The iterator generated by the returned collection traverses the values in the + * order they were added to the multimap. + */ + @Override + public Collection values() { + return super.values(); + } + + @VisibleForTesting + final class ValueSet extends Sets.ImprovedAbstractSet implements ValueSetLink { + /* + * We currently use a fixed load factor of 1.0, a bit higher than normal to + * reduce memory consumption. + */ + + private final K key; + @VisibleForTesting + ValueEntry[] hashTable; + private int size = 0; + private int modCount = 0; + + // We use the set object itself as the end of the linked list, avoiding an + // unnecessary + // entry object per key. + private ValueSetLink firstEntry; + private ValueSetLink lastEntry; + + ValueSet(K key, int expectedValues) { + this.key = key; + this.firstEntry = this; + this.lastEntry = this; + // Round expected values up to a power of 2 to get the table size. + int tableSize = Hashing.closedTableSize(expectedValues, VALUE_SET_LOAD_FACTOR); + + @SuppressWarnings("unchecked") + ValueEntry[] hashTable = new ValueEntry[tableSize]; + this.hashTable = hashTable; + } + + private int mask() { + return hashTable.length - 1; + } + + @Override + public ValueSetLink getPredecessorInValueSet() { + return lastEntry; + } + + @Override + public ValueSetLink getSuccessorInValueSet() { + return firstEntry; + } + + @Override + public void setPredecessorInValueSet(ValueSetLink entry) { + lastEntry = entry; + } + + @Override + public void setSuccessorInValueSet(ValueSetLink entry) { + firstEntry = entry; + } + + @Override + public Iterator iterator() { + return new Iterator() { + ValueSetLink nextEntry = firstEntry; + ValueEntry toRemove; + int expectedModCount = modCount; + + private void checkForComodification() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + checkForComodification(); + return nextEntry != ValueSet.this; + } + + @Override + public V next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + ValueEntry entry = (ValueEntry) nextEntry; + V result = entry.getValue(); + toRemove = entry; + nextEntry = entry.getSuccessorInValueSet(); + return result; + } + + @Override + public void remove() { + checkForComodification(); + checkRemove(toRemove != null); + ValueSet.this.remove(toRemove.getValue()); + expectedModCount = modCount; + toRemove = null; + } + }; + } + + @Override + public int size() { + return size; + } + + @Override + public boolean contains(@Nullable Object o) { + int smearedHash = Hashing.smearedHash(o); + for (ValueEntry entry = hashTable[smearedHash + & mask()]; entry != null; entry = entry.nextInValueBucket) { + if (entry.matchesValue(o, smearedHash)) { + return true; + } + } + return false; + } + + @Override + public boolean add(@Nullable V value) { + int smearedHash = Hashing.smearedHash(value); + int bucket = smearedHash & mask(); + ValueEntry rowHead = hashTable[bucket]; + for (ValueEntry entry = rowHead; entry != null; entry = entry.nextInValueBucket) { + if (entry.matchesValue(value, smearedHash)) { + return false; + } + } + + ValueEntry newEntry = new ValueEntry(key, value, smearedHash, rowHead); + succeedsInValueSet(lastEntry, newEntry); + succeedsInValueSet(newEntry, this); + succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry); + succeedsInMultimap(newEntry, multimapHeaderEntry); + hashTable[bucket] = newEntry; + size++; + modCount++; + rehashIfNecessary(); + return true; + } + + private void rehashIfNecessary() { + if (Hashing.needsResizing(size, hashTable.length, VALUE_SET_LOAD_FACTOR)) { + @SuppressWarnings("unchecked") + ValueEntry[] hashTable = new ValueEntry[this.hashTable.length * 2]; + this.hashTable = hashTable; + int mask = hashTable.length - 1; + for (ValueSetLink entry = firstEntry; entry != this; entry = entry.getSuccessorInValueSet()) { + ValueEntry valueEntry = (ValueEntry) entry; + int bucket = valueEntry.smearedValueHash & mask; + valueEntry.nextInValueBucket = hashTable[bucket]; + hashTable[bucket] = valueEntry; + } + } + } + + @Override + public boolean remove(@Nullable Object o) { + int smearedHash = Hashing.smearedHash(o); + int bucket = smearedHash & mask(); + ValueEntry prev = null; + for (ValueEntry entry = hashTable[bucket]; entry != null; prev = entry, entry = entry.nextInValueBucket) { + if (entry.matchesValue(o, smearedHash)) { + if (prev == null) { + // first entry in the bucket + hashTable[bucket] = entry.nextInValueBucket; + } else { + prev.nextInValueBucket = entry.nextInValueBucket; + } + deleteFromValueSet(entry); + deleteFromMultimap(entry); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public void clear() { + Arrays.fill(hashTable, null); + size = 0; + for (ValueSetLink entry = firstEntry; entry != this; entry = entry.getSuccessorInValueSet()) { + ValueEntry valueEntry = (ValueEntry) entry; + deleteFromMultimap(valueEntry); + } + succeedsInValueSet(this, this); + modCount++; + } + } + + @Override + Iterator> entryIterator() { + return new Iterator>() { + ValueEntry nextEntry = multimapHeaderEntry.successorInMultimap; + ValueEntry toRemove; + + @Override + public boolean hasNext() { + return nextEntry != multimapHeaderEntry; + } + + @Override + public Map.Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + ValueEntry result = nextEntry; + toRemove = result; + nextEntry = nextEntry.successorInMultimap; + return result; + } + + @Override + public void remove() { + checkRemove(toRemove != null); + LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue()); + toRemove = null; + } + }; + } + + @Override + Iterator valueIterator() { + return Maps.valueIterator(entryIterator()); + } + + @Override + public void clear() { + super.clear(); + succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); + } + + /** + * @serialData the expected values per key, the number of distinct keys, the + * number of entries, and the entries in order + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(valueSetCapacity); + stream.writeInt(keySet().size()); + for (K key : keySet()) { + stream.writeObject(key); + } + stream.writeInt(size()); + for (Map.Entry entry : entries()) { + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + multimapHeaderEntry = new ValueEntry(null, null, 0, null); + succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); + valueSetCapacity = stream.readInt(); + int distinctKeys = stream.readInt(); + Map> map = new LinkedHashMap>(Maps.capacity(distinctKeys)); + for (int i = 0; i < distinctKeys; i++) { + @SuppressWarnings("unchecked") + K key = (K) stream.readObject(); + map.put(key, createCollection(key)); + } + int entries = stream.readInt(); + for (int i = 0; i < entries; i++) { + @SuppressWarnings("unchecked") + K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") + V value = (V) stream.readObject(); + map.get(key).add(value); + } + setMap(map); + } + + @GwtIncompatible("java serialization not supported") + private static final long serialVersionUID = 1; +} diff --git a/sources/main/java/com/google/common/collect/LinkedHashMultiset.java b/sources/main/java/com/google/common/collect/LinkedHashMultiset.java new file mode 100644 index 0000000..ffeb990 --- /dev/null +++ b/sources/main/java/com/google/common/collect/LinkedHashMultiset.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.LinkedHashMap; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * A {@code Multiset} implementation with predictable iteration order. Its + * iterator orders elements according to when the first occurrence of the + * element was added. When the multiset contains multiple instances of an + * element, those instances are consecutive in the iteration order. If all + * occurrences of an element are removed, after which that element is added to + * the multiset, the element will appear at the end of the iteration. + * + *

+ * See the Guava User Guide article on + * {@code Multiset}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +public final class LinkedHashMultiset extends AbstractMapBasedMultiset { + + /** + * Creates a new, empty {@code LinkedHashMultiset} using the default initial + * capacity. + */ + public static LinkedHashMultiset create() { + return new LinkedHashMultiset(); + } + + /** + * Creates a new, empty {@code LinkedHashMultiset} with the specified expected + * number of distinct elements. + * + * @param distinctElements the expected number of distinct elements + * @throws IllegalArgumentException if {@code distinctElements} is negative + */ + public static LinkedHashMultiset create(int distinctElements) { + return new LinkedHashMultiset(distinctElements); + } + + /** + * Creates a new {@code LinkedHashMultiset} containing the specified elements. + * + *

+ * This implementation is highly efficient when {@code elements} is itself a + * {@link Multiset}. + * + * @param elements the elements that the multiset should contain + */ + public static LinkedHashMultiset create(Iterable elements) { + LinkedHashMultiset multiset = create(Multisets.inferDistinctElements(elements)); + Iterables.addAll(multiset, elements); + return multiset; + } + + private LinkedHashMultiset() { + super(new LinkedHashMap()); + } + + private LinkedHashMultiset(int distinctElements) { + // Could use newLinkedHashMapWithExpectedSize() if it existed + super(new LinkedHashMap(Maps.capacity(distinctElements))); + } + + /** + * @serialData the number of distinct elements, the first element, its count, + * the second element, its count, and so on + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int distinctElements = Serialization.readCount(stream); + setBackingMap(new LinkedHashMap(Maps.capacity(distinctElements))); + Serialization.populateMultiset(this, stream, distinctElements); + } + + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/LinkedListMultimap.java b/sources/main/java/com/google/common/collect/LinkedListMultimap.java new file mode 100644 index 0000000..a7ceeba --- /dev/null +++ b/sources/main/java/com/google/common/collect/LinkedListMultimap.java @@ -0,0 +1,863 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkPositionIndex; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.CollectPreconditions.checkRemove; +import static java.util.Collections.unmodifiableList; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractSequentialList; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An implementation of {@code ListMultimap} that supports deterministic + * iteration order for both keys and values. The iteration order is preserved + * across non-distinct key values. For example, for the following multimap + * definition: + * + *

+ * {
+ * 	@code
+ *
+ * 	Multimap multimap = LinkedListMultimap.create();
+ * 	multimap.put(key1, foo);
+ * 	multimap.put(key2, bar);
+ * 	multimap.put(key1, baz);
+ * }
+ * 
+ * + * ... the iteration order for {@link #keys()} is {@code [key1, key2, key1]}, + * and similarly for {@link #entries()}. Unlike {@link LinkedHashMultimap}, the + * iteration order is kept consistent between keys, entries and values. For + * example, calling: + * + *
+ *    {@code
+ *
+ *   map.remove(key1, foo);}
+ * 
+ * + *

+ * changes the entries iteration order to {@code [key2=bar, key1=baz]} and the + * key iteration order to {@code [key2, key1]}. The {@link #entries()} iterator + * returns mutable map entries, and {@link #replaceValues} attempts to preserve + * iteration order as much as possible. + * + *

+ * The collections returned by {@link #keySet()} and {@link #asMap} iterate + * through the keys in the order they were first added to the multimap. + * Similarly, {@link #get}, {@link #removeAll}, and {@link #replaceValues} + * return collections that iterate through the values in the order they were + * added. The collections generated by {@link #entries()}, {@link #keys()}, and + * {@link #values} iterate across the key-value mappings in the order they were + * added to the multimap. + * + *

+ * The {@link #values()} and {@link #entries()} methods both return a + * {@code List}, instead of the {@code Collection} specified by the + * {@link ListMultimap} interface. + * + *

+ * The methods {@link #get}, {@link #keySet()}, {@link #keys()}, + * {@link #values}, {@link #entries()}, and {@link #asMap} return collections + * that are views of the multimap. If the multimap is modified while an + * iteration over any of those collections is in progress, except through the + * iterator's methods, the results of the iteration are undefined. + * + *

+ * Keys and values may be null. All optional multimap methods are supported, and + * all returned views are modifiable. + * + *

+ * This class is not threadsafe when any concurrent operations update the + * multimap. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap your multimap with a call to + * {@link Multimaps#synchronizedListMultimap}. + * + *

+ * See the Guava User Guide article on + * {@code Multimap}. + * + * @author Mike Bostock + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public class LinkedListMultimap extends AbstractMultimap implements ListMultimap, Serializable { + /* + * Order is maintained using a linked list containing all key-value pairs. In + * addition, a series of disjoint linked lists of "siblings", each containing + * the values for a specific key, is used to implement {@link + * ValueForKeyIterator} in constant time. + */ + + private static final class Node extends AbstractMapEntry { + final K key; + V value; + Node next; // the next node (with any key) + Node previous; // the previous node (with any key) + Node nextSibling; // the next node with the same key + Node previousSibling; // the previous node with the same key + + Node(@Nullable K key, @Nullable V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public V setValue(@Nullable V newValue) { + V result = value; + this.value = newValue; + return result; + } + } + + private static class KeyList { + Node head; + Node tail; + int count; + + KeyList(Node firstNode) { + this.head = firstNode; + this.tail = firstNode; + firstNode.previousSibling = null; + firstNode.nextSibling = null; + this.count = 1; + } + } + + private transient Node head; // the head for all keys + private transient Node tail; // the tail for all keys + private transient Map> keyToKeyList; + private transient int size; + + /* + * Tracks modifications to keyToKeyList so that addition or removal of keys + * invalidates preexisting iterators. This does *not* track simple additions and + * removals of values that are not the first to be added or last to be removed + * for their key. + */ + private transient int modCount; + + /** + * Creates a new, empty {@code LinkedListMultimap} with the default initial + * capacity. + */ + public static LinkedListMultimap create() { + return new LinkedListMultimap(); + } + + /** + * Constructs an empty {@code LinkedListMultimap} with enough capacity to hold + * the specified number of keys without rehashing. + * + * @param expectedKeys the expected number of distinct keys + * @throws IllegalArgumentException if {@code expectedKeys} is negative + */ + public static LinkedListMultimap create(int expectedKeys) { + return new LinkedListMultimap(expectedKeys); + } + + /** + * Constructs a {@code LinkedListMultimap} with the same mappings as the + * specified {@code Multimap}. The new multimap has the same + * {@link Multimap#entries()} iteration order as the input multimap. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static LinkedListMultimap create(Multimap multimap) { + return new LinkedListMultimap(multimap); + } + + LinkedListMultimap() { + keyToKeyList = Maps.newHashMap(); + } + + private LinkedListMultimap(int expectedKeys) { + keyToKeyList = new HashMap>(expectedKeys); + } + + private LinkedListMultimap(Multimap multimap) { + this(multimap.keySet().size()); + putAll(multimap); + } + + /** + * Adds a new node for the specified key-value pair before the specified + * {@code nextSibling} element, or at the end of the list if {@code + * nextSibling} is null. Note: if {@code nextSibling} is specified, it MUST be + * for an node for the same {@code key}! + */ + private Node addNode(@Nullable K key, @Nullable V value, @Nullable Node nextSibling) { + Node node = new Node(key, value); + if (head == null) { // empty list + head = tail = node; + keyToKeyList.put(key, new KeyList(node)); + modCount++; + } else if (nextSibling == null) { // non-empty list, add to tail + tail.next = node; + node.previous = tail; + tail = node; + KeyList keyList = keyToKeyList.get(key); + if (keyList == null) { + keyToKeyList.put(key, keyList = new KeyList(node)); + modCount++; + } else { + keyList.count++; + Node keyTail = keyList.tail; + keyTail.nextSibling = node; + node.previousSibling = keyTail; + keyList.tail = node; + } + } else { // non-empty list, insert before nextSibling + KeyList keyList = keyToKeyList.get(key); + keyList.count++; + node.previous = nextSibling.previous; + node.previousSibling = nextSibling.previousSibling; + node.next = nextSibling; + node.nextSibling = nextSibling; + if (nextSibling.previousSibling == null) { // nextSibling was key head + keyToKeyList.get(key).head = node; + } else { + nextSibling.previousSibling.nextSibling = node; + } + if (nextSibling.previous == null) { // nextSibling was head + head = node; + } else { + nextSibling.previous.next = node; + } + nextSibling.previous = node; + nextSibling.previousSibling = node; + } + size++; + return node; + } + + /** + * Removes the specified node from the linked list. This method is only intended + * to be used from the {@code Iterator} classes. See also + * {@link LinkedListMultimap#removeAllNodes(Object)}. + */ + private void removeNode(Node node) { + if (node.previous != null) { + node.previous.next = node.next; + } else { // node was head + head = node.next; + } + if (node.next != null) { + node.next.previous = node.previous; + } else { // node was tail + tail = node.previous; + } + if (node.previousSibling == null && node.nextSibling == null) { + KeyList keyList = keyToKeyList.remove(node.key); + keyList.count = 0; + modCount++; + } else { + KeyList keyList = keyToKeyList.get(node.key); + keyList.count--; + + if (node.previousSibling == null) { + keyList.head = node.nextSibling; + } else { + node.previousSibling.nextSibling = node.nextSibling; + } + + if (node.nextSibling == null) { + keyList.tail = node.previousSibling; + } else { + node.nextSibling.previousSibling = node.previousSibling; + } + } + size--; + } + + /** Removes all nodes for the specified key. */ + private void removeAllNodes(@Nullable Object key) { + Iterators.clear(new ValueForKeyIterator(key)); + } + + /** Helper method for verifying that an iterator element is present. */ + private static void checkElement(@Nullable Object node) { + if (node == null) { + throw new NoSuchElementException(); + } + } + + /** An {@code Iterator} over all nodes. */ + private class NodeIterator implements ListIterator> { + int nextIndex; + Node next; + Node current; + Node previous; + int expectedModCount = modCount; + + NodeIterator(int index) { + int size = size(); + checkPositionIndex(index, size); + if (index >= (size / 2)) { + previous = tail; + nextIndex = size; + while (index++ < size) { + previous(); + } + } else { + next = head; + while (index-- > 0) { + next(); + } + } + current = null; + } + + private void checkForConcurrentModification() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + checkForConcurrentModification(); + return next != null; + } + + @Override + public Node next() { + checkForConcurrentModification(); + checkElement(next); + previous = current = next; + next = next.next; + nextIndex++; + return current; + } + + @Override + public void remove() { + checkForConcurrentModification(); + checkRemove(current != null); + if (current != next) { // after call to next() + previous = current.previous; + nextIndex--; + } else { // after call to previous() + next = current.next; + } + removeNode(current); + current = null; + expectedModCount = modCount; + } + + @Override + public boolean hasPrevious() { + checkForConcurrentModification(); + return previous != null; + } + + @Override + public Node previous() { + checkForConcurrentModification(); + checkElement(previous); + next = current = previous; + previous = previous.previous; + nextIndex--; + return current; + } + + @Override + public int nextIndex() { + return nextIndex; + } + + @Override + public int previousIndex() { + return nextIndex - 1; + } + + @Override + public void set(Entry e) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(Entry e) { + throw new UnsupportedOperationException(); + } + + void setValue(V value) { + checkState(current != null); + current.value = value; + } + } + + /** An {@code Iterator} over distinct keys in key head order. */ + private class DistinctKeyIterator implements Iterator { + final Set seenKeys = Sets.newHashSetWithExpectedSize(keySet().size()); + Node next = head; + Node current; + int expectedModCount = modCount; + + private void checkForConcurrentModification() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + checkForConcurrentModification(); + return next != null; + } + + @Override + public K next() { + checkForConcurrentModification(); + checkElement(next); + current = next; + seenKeys.add(current.key); + do { // skip ahead to next unseen key + next = next.next; + } while ((next != null) && !seenKeys.add(next.key)); + return current.key; + } + + @Override + public void remove() { + checkForConcurrentModification(); + checkRemove(current != null); + removeAllNodes(current.key); + current = null; + expectedModCount = modCount; + } + } + + /** A {@code ListIterator} over values for a specified key. */ + private class ValueForKeyIterator implements ListIterator { + final Object key; + int nextIndex; + Node next; + Node current; + Node previous; + + /** Constructs a new iterator over all values for the specified key. */ + ValueForKeyIterator(@Nullable Object key) { + this.key = key; + KeyList keyList = keyToKeyList.get(key); + next = (keyList == null) ? null : keyList.head; + } + + /** + * Constructs a new iterator over all values for the specified key starting at + * the specified index. This constructor is optimized so that it starts at + * either the head or the tail, depending on which is closer to the specified + * index. This allows adds to the tail to be done in constant time. + * + * @throws IndexOutOfBoundsException if index is invalid + */ + public ValueForKeyIterator(@Nullable Object key, int index) { + KeyList keyList = keyToKeyList.get(key); + int size = (keyList == null) ? 0 : keyList.count; + checkPositionIndex(index, size); + if (index >= (size / 2)) { + previous = (keyList == null) ? null : keyList.tail; + nextIndex = size; + while (index++ < size) { + previous(); + } + } else { + next = (keyList == null) ? null : keyList.head; + while (index-- > 0) { + next(); + } + } + this.key = key; + current = null; + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public V next() { + checkElement(next); + previous = current = next; + next = next.nextSibling; + nextIndex++; + return current.value; + } + + @Override + public boolean hasPrevious() { + return previous != null; + } + + @Override + public V previous() { + checkElement(previous); + next = current = previous; + previous = previous.previousSibling; + nextIndex--; + return current.value; + } + + @Override + public int nextIndex() { + return nextIndex; + } + + @Override + public int previousIndex() { + return nextIndex - 1; + } + + @Override + public void remove() { + checkRemove(current != null); + if (current != next) { // after call to next() + previous = current.previousSibling; + nextIndex--; + } else { // after call to previous() + next = current.nextSibling; + } + removeNode(current); + current = null; + } + + @Override + public void set(V value) { + checkState(current != null); + current.value = value; + } + + @Override + @SuppressWarnings("unchecked") + public void add(V value) { + previous = addNode((K) key, value, next); + nextIndex++; + current = null; + } + } + + // Query Operations + + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return head == null; + } + + @Override + public boolean containsKey(@Nullable Object key) { + return keyToKeyList.containsKey(key); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return values().contains(value); + } + + // Modification Operations + + /** + * Stores a key-value pair in the multimap. + * + * @param key key to store in the multimap + * @param value value to store in the multimap + * @return {@code true} always + */ + @Override + public boolean put(@Nullable K key, @Nullable V value) { + addNode(key, value, null); + return true; + } + + // Bulk Operations + + /** + * {@inheritDoc} + * + *

+ * If any entries for the specified {@code key} already exist in the multimap, + * their values are changed in-place without affecting the iteration order. + * + *

+ * The returned list is immutable and implements {@link net.lax1dude.eaglercraft.v1_8.RandomAccess}. + */ + @Override + public List replaceValues(@Nullable K key, Iterable values) { + List oldValues = getCopy(key); + ListIterator keyValues = new ValueForKeyIterator(key); + Iterator newValues = values.iterator(); + + // Replace existing values, if any. + while (keyValues.hasNext() && newValues.hasNext()) { + keyValues.next(); + keyValues.set(newValues.next()); + } + + // Remove remaining old values, if any. + while (keyValues.hasNext()) { + keyValues.next(); + keyValues.remove(); + } + + // Add remaining new values, if any. + while (newValues.hasNext()) { + keyValues.add(newValues.next()); + } + + return oldValues; + } + + private List getCopy(@Nullable Object key) { + return unmodifiableList(Lists.newArrayList(new ValueForKeyIterator(key))); + } + + /** + * {@inheritDoc} + * + *

+ * The returned list is immutable and implements {@link net.lax1dude.eaglercraft.v1_8.RandomAccess}. + */ + @Override + public List removeAll(@Nullable Object key) { + List oldValues = getCopy(key); + removeAllNodes(key); + return oldValues; + } + + @Override + public void clear() { + head = null; + tail = null; + keyToKeyList.clear(); + size = 0; + modCount++; + } + + // Views + + /** + * {@inheritDoc} + * + *

+ * If the multimap is modified while an iteration over the list is in progress + * (except through the iterator's own {@code add}, {@code set} or {@code remove} + * operations) the results of the iteration are undefined. + * + *

+ * The returned list is not serializable and does not have random access. + */ + @Override + public List get(final @Nullable K key) { + return new AbstractSequentialList() { + @Override + public int size() { + KeyList keyList = keyToKeyList.get(key); + return (keyList == null) ? 0 : keyList.count; + } + + @Override + public ListIterator listIterator(int index) { + return new ValueForKeyIterator(key, index); + } + }; + } + + @Override + Set createKeySet() { + return new Sets.ImprovedAbstractSet() { + @Override + public int size() { + return keyToKeyList.size(); + } + + @Override + public Iterator iterator() { + return new DistinctKeyIterator(); + } + + @Override + public boolean contains(Object key) { // for performance + return containsKey(key); + } + + @Override + public boolean remove(Object o) { // for performance + return !LinkedListMultimap.this.removeAll(o).isEmpty(); + } + }; + } + + /** + * {@inheritDoc} + * + *

+ * The iterator generated by the returned collection traverses the values in the + * order they were added to the multimap. Because the values may have duplicates + * and follow the insertion ordering, this method returns a {@link List}, + * instead of the {@link Collection} specified in the {@link ListMultimap} + * interface. + */ + @Override + public List values() { + return (List) super.values(); + } + + @Override + List createValues() { + return new AbstractSequentialList() { + @Override + public int size() { + return size; + } + + @Override + public ListIterator listIterator(int index) { + final NodeIterator nodeItr = new NodeIterator(index); + return new TransformedListIterator, V>(nodeItr) { + @Override + V transform(Entry entry) { + return entry.getValue(); + } + + @Override + public void set(V value) { + nodeItr.setValue(value); + } + }; + } + }; + } + + /** + * {@inheritDoc} + * + *

+ * The iterator generated by the returned collection traverses the entries in + * the order they were added to the multimap. Because the entries may have + * duplicates and follow the insertion ordering, this method returns a + * {@link List}, instead of the {@link Collection} specified in the + * {@link ListMultimap} interface. + * + *

+ * An entry's {@link Entry#getKey} method always returns the same key, + * regardless of what happens subsequently. As long as the corresponding + * key-value mapping is not removed from the multimap, {@link Entry#getValue} + * returns the value from the multimap, which may change over time, and + * {@link Entry#setValue} modifies that value. Removing the mapping from the + * multimap does not alter the value returned by {@code getValue()}, though a + * subsequent {@code setValue()} call won't update the multimap but will lead to + * a revised value being returned by {@code getValue()}. + */ + @Override + public List> entries() { + return (List>) super.entries(); + } + + @Override + List> createEntries() { + return new AbstractSequentialList>() { + @Override + public int size() { + return size; + } + + @Override + public ListIterator> listIterator(int index) { + return new NodeIterator(index); + } + }; + } + + @Override + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Map> createAsMap() { + return new Multimaps.AsMap(this); + } + + /** + * @serialData the number of distinct keys, and then for each distinct key: the + * first key, the number of values for that key, and the key's + * values, followed by successive keys and values from the entries() + * ordering + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(size()); + for (Entry entry : entries()) { + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyToKeyList = Maps.newLinkedHashMap(); + int size = stream.readInt(); + for (int i = 0; i < size; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeObject + K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") // reading data stored by writeObject + V value = (V) stream.readObject(); + put(key, value); + } + } + + @GwtIncompatible("java serialization not supported") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ListMultimap.java b/sources/main/java/com/google/common/collect/ListMultimap.java new file mode 100644 index 0000000..b566571 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ListMultimap.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A {@code Multimap} that can hold duplicate key-value pairs and that maintains + * the insertion ordering of values for a given key. See the {@link Multimap} + * documentation for information common to all multimaps. + * + *

+ * The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods each + * return a {@link List} of values. Though the method signature doesn't say so + * explicitly, the map returned by {@link #asMap} has {@code List} values. + * + *

+ * See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface ListMultimap extends Multimap { + /** + * {@inheritDoc} + * + *

+ * Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link java.util.Collection} specified in the {@link Multimap} interface. + */ + @Override + List get(@Nullable K key); + + /** + * {@inheritDoc} + * + *

+ * Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link java.util.Collection} specified in the {@link Multimap} interface. + */ + @Override + List removeAll(@Nullable Object key); + + /** + * {@inheritDoc} + * + *

+ * Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link java.util.Collection} specified in the {@link Multimap} interface. + */ + @Override + List replaceValues(K key, Iterable values); + + /** + * {@inheritDoc} + * + *

+ * Note: The returned map's values are guaranteed to be of type + * {@link List}. To obtain this map with the more specific generic type + * {@code Map>}, call {@link Multimaps#asMap(ListMultimap)} instead. + */ + @Override + Map> asMap(); + + /** + * Compares the specified object to this multimap for equality. + * + *

+ * Two {@code ListMultimap} instances are equal if, for each key, they contain + * the same values in the same order. If the value orderings disagree, the + * multimaps will not be considered equal. + * + *

+ * An empty {@code ListMultimap} is equal to any other empty {@code + * Multimap}, including an empty {@code SetMultimap}. + */ + @Override + boolean equals(@Nullable Object obj); +} diff --git a/sources/main/java/com/google/common/collect/Lists.java b/sources/main/java/com/google/common/collect/Lists.java new file mode 100644 index 0000000..fb816bf --- /dev/null +++ b/sources/main/java/com/google/common/collect/Lists.java @@ -0,0 +1,1098 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkElementIndex; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkPositionIndex; +import static com.google.common.base.Preconditions.checkPositionIndexes; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.Serializable; +import java.math.RoundingMode; +import java.util.AbstractList; +import java.util.AbstractSequentialList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.RandomAccess; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.math.IntMath; +import com.google.common.primitives.Ints; + +/** + * Static utility methods pertaining to {@link List} instances. Also see this + * class's counterparts {@link Sets}, {@link Maps} and {@link Queues}. + * + *

+ * See the Guava User Guide article on + * {@code Lists}. + * + * @author Kevin Bourrillion + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public final class Lists { + private Lists() { + } + + // ArrayList + + /** + * Creates a mutable, empty {@code ArrayList} instance. + * + *

+ * Note: if mutability is not required, use {@link ImmutableList#of()} + * instead. + * + * @return a new, empty {@code ArrayList} + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayList() { + return new ArrayList(); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given + * elements. + * + *

+ * Note: if mutability is not required and the elements are non-null, use + * an overload of {@link ImmutableList#of()} (for varargs) or + * {@link ImmutableList#copyOf(Object[])} (for an array) instead. + * + * @param elements the elements that the list should contain, in order + * @return a new {@code ArrayList} containing those elements + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayList(E... elements) { + checkNotNull(elements); // for GWT + // Avoid integer overflow when a large array is passed in + int capacity = computeArrayListCapacity(elements.length); + ArrayList list = new ArrayList(capacity); + Collections.addAll(list, elements); + return list; + } + + @VisibleForTesting + static int computeArrayListCapacity(int arraySize) { + checkNonnegative(arraySize, "arraySize"); + + // TODO(kevinb): Figure out the right behavior, and document it + return Ints.saturatedCast(5L + arraySize + (arraySize / 10)); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given + * elements. + * + *

+ * Note: if mutability is not required and the elements are non-null, use + * {@link ImmutableList#copyOf(Iterator)} instead. + * + * @param elements the elements that the list should contain, in order + * @return a new {@code ArrayList} containing those elements + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayList(Iterable elements) { + checkNotNull(elements); // for GWT + // Let ArrayList's sizing logic work, if possible + return (elements instanceof Collection) ? new ArrayList(Collections2.cast(elements)) + : newArrayList(elements.iterator()); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given + * elements. + * + *

+ * Note: if mutability is not required and the elements are non-null, use + * {@link ImmutableList#copyOf(Iterator)} instead. + * + * @param elements the elements that the list should contain, in order + * @return a new {@code ArrayList} containing those elements + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayList(Iterator elements) { + ArrayList list = newArrayList(); + Iterators.addAll(list, elements); + return list; + } + + /** + * Creates an {@code ArrayList} instance backed by an array of the exact + * size specified; equivalent to {@link ArrayList#ArrayList(int)}. + * + *

+ * Note: if you know the exact size your list will be, consider using a + * fixed-size list ({@link Arrays#asList(Object[])}) or an {@link ImmutableList} + * instead of a growable {@link ArrayList}. + * + *

+ * Note: If you have only an estimate of the eventual size of the + * list, consider padding this estimate by a suitable amount, or simply use + * {@link #newArrayListWithExpectedSize(int)} instead. + * + * @param initialArraySize the exact size of the initial backing array for the + * returned array list ({@code ArrayList} documentation + * calls this value the "capacity") + * @return a new, empty {@code ArrayList} which is guaranteed not to resize + * itself unless its size reaches {@code initialArraySize + 1} + * @throws IllegalArgumentException if {@code initialArraySize} is negative + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayListWithCapacity(int initialArraySize) { + checkNonnegative(initialArraySize, "initialArraySize"); // for GWT. + return new ArrayList(initialArraySize); + } + + /** + * Creates an {@code ArrayList} instance sized appropriately to hold an + * estimated number of elements without resizing. A small amount of + * padding is added in case the estimate is low. + * + *

+ * Note: If you know the exact number of elements the list will + * hold, or prefer to calculate your own amount of padding, refer to + * {@link #newArrayListWithCapacity(int)}. + * + * @param estimatedSize an estimate of the eventual {@link List#size()} of the + * new list + * @return a new, empty {@code ArrayList}, sized appropriately to hold the + * estimated number of elements + * @throws IllegalArgumentException if {@code estimatedSize} is negative + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayListWithExpectedSize(int estimatedSize) { + return new ArrayList(computeArrayListCapacity(estimatedSize)); + } + + // LinkedList + + /** + * Creates an empty {@code LinkedList} instance. + * + *

+ * Note: if you need an immutable empty {@link List}, use + * {@link ImmutableList#of()} instead. + * + * @return a new, empty {@code LinkedList} + */ + @GwtCompatible(serializable = true) + public static LinkedList newLinkedList() { + return new LinkedList(); + } + + /** + * Creates a {@code LinkedList} instance containing the given elements. + * + * @param elements the elements that the list should contain, in order + * @return a new {@code LinkedList} containing those elements + */ + @GwtCompatible(serializable = true) + public static LinkedList newLinkedList(Iterable elements) { + LinkedList list = newLinkedList(); + Iterables.addAll(list, elements); + return list; + } + + /** + * Returns an unmodifiable list containing the specified first element and + * backed by the specified array of additional elements. Changes to the {@code + * rest} array will be reflected in the returned list. Unlike + * {@link Arrays#asList}, the returned list is unmodifiable. + * + *

+ * This is useful when a varargs method needs to use a signature such as + * {@code (Foo firstFoo, Foo... moreFoos)}, in order to avoid overload ambiguity + * or to enforce a minimum argument count. + * + *

+ * The returned list is serializable and implements {@link RandomAccess}. + * + * @param first the first element + * @param rest an array of additional elements, possibly empty + * @return an unmodifiable list containing the specified elements + */ + public static List asList(@Nullable E first, E[] rest) { + return new OnePlusArrayList(first, rest); + } + + /** @see Lists#asList(Object, Object[]) */ + private static class OnePlusArrayList extends AbstractList implements Serializable, RandomAccess { + final E first; + final E[] rest; + + OnePlusArrayList(@Nullable E first, E[] rest) { + this.first = first; + this.rest = checkNotNull(rest); + } + + @Override + public int size() { + return rest.length + 1; + } + + @Override + public E get(int index) { + // check explicitly so the IOOBE will have the right message + checkElementIndex(index, size()); + return (index == 0) ? first : rest[index - 1]; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an unmodifiable list containing the specified first and second + * element, and backed by the specified array of additional elements. Changes to + * the {@code rest} array will be reflected in the returned list. Unlike + * {@link Arrays#asList}, the returned list is unmodifiable. + * + *

+ * This is useful when a varargs method needs to use a signature such as + * {@code (Foo firstFoo, Foo secondFoo, Foo... moreFoos)}, in order to avoid + * overload ambiguity or to enforce a minimum argument count. + * + *

+ * The returned list is serializable and implements {@link RandomAccess}. + * + * @param first the first element + * @param second the second element + * @param rest an array of additional elements, possibly empty + * @return an unmodifiable list containing the specified elements + */ + public static List asList(@Nullable E first, @Nullable E second, E[] rest) { + return new TwoPlusArrayList(first, second, rest); + } + + /** @see Lists#asList(Object, Object, Object[]) */ + private static class TwoPlusArrayList extends AbstractList implements Serializable, RandomAccess { + final E first; + final E second; + final E[] rest; + + TwoPlusArrayList(@Nullable E first, @Nullable E second, E[] rest) { + this.first = first; + this.second = second; + this.rest = checkNotNull(rest); + } + + @Override + public int size() { + return rest.length + 2; + } + + @Override + public E get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + default: + // check explicitly so the IOOBE will have the right message + checkElementIndex(index, size()); + return rest[index - 2]; + } + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns every possible list that can be formed by choosing one element from + * each of the given lists in order; the "n-ary + * Cartesian + * product" of the lists. For example: + * + *

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

+ * returns a list containing six lists in the following order: + * + *

    + *
  • {@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 : lists.get(0)) {
+	 *     for (B b1 : lists.get(1)) {
+	 *       ...
+	 *       ImmutableList tuple = ImmutableList.of(b0, b1, ...);
+	 *       // operate on tuple
+	 *     }
+	 *   }}
+	 * 
+ * + *

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

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

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

+ * returns a list containing six lists in the following order: + * + *

    + *
  • {@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 : lists.get(0)) {
+	 *     for (B b1 : lists.get(1)) {
+	 *       ...
+	 *       ImmutableList tuple = ImmutableList.of(b0, b1, ...);
+	 *       // operate on tuple
+	 *     }
+	 *   }}
+	 * 
+ * + *

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

+ * Performance notes: while the cartesian product of lists of size + * {@code m, n, p} is a list of size {@code m x n x p}, its actual memory + * consumption is much smaller. When the cartesian product is constructed, the + * input lists are merely copied. Only as the resulting list is iterated are the + * individual lists created, and these are not retained after iteration. + * + * @param lists the lists to choose elements from, in the order that the + * elements chosen from those lists should appear in the resulting + * lists + * @param any common base class shared by all axes (often just + * {@link Object}) + * @return the Cartesian product, as an immutable list containing immutable + * lists + * @throws IllegalArgumentException if the size of the cartesian product would + * be greater than {@link Integer#MAX_VALUE} + * @throws NullPointerException if {@code lists}, any one of the + * {@code lists}, or any element of a provided + * list is null + */ + static List> cartesianProduct(List... lists) { + return cartesianProduct(Arrays.asList(lists)); + } + + /** + * Returns a list that applies {@code function} to each element of {@code + * fromList}. The returned list is a transformed view of {@code fromList}; + * changes to {@code fromList} will be reflected in the returned list and vice + * versa. + * + *

+ * Since functions are not reversible, the transform is one-way and new items + * cannot be stored in the returned list. The {@code add}, {@code addAll} and + * {@code set} methods are unsupported in the returned list. + * + *

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

+ * If {@code fromList} implements {@link RandomAccess}, so will the returned + * list. The returned list is threadsafe if the supplied list and function are. + * + *

+ * If only a {@code Collection} or {@code Iterable} input is available, use + * {@link Collections2#transform} or {@link Iterables#transform}. + * + *

+ * Note: serializing the returned list is implemented by serializing + * {@code fromList}, its contents, and {@code function} -- not by + * serializing the transformed values. This can lead to surprising behavior, so + * serializing the returned list is not recommended. Instead, copy the + * list using {@link ImmutableList#copyOf(Collection)} (for example), then + * serialize the copy. Other methods similar to this do not implement + * serialization at all for this reason. + */ + public static List transform(List fromList, Function function) { + return (fromList instanceof RandomAccess) ? new TransformingRandomAccessList(fromList, function) + : new TransformingSequentialList(fromList, function); + } + + /** + * Implementation of a sequential transforming list. + * + * @see Lists#transform + */ + private static class TransformingSequentialList extends AbstractSequentialList implements Serializable { + final List fromList; + final Function function; + + TransformingSequentialList(List fromList, Function function) { + this.fromList = checkNotNull(fromList); + this.function = checkNotNull(function); + } + + /** + * The default implementation inherited is based on iteration and removal of + * each element which can be overkill. That's why we forward this call directly + * to the backing list. + */ + @Override + public void clear() { + fromList.clear(); + } + + @Override + public int size() { + return fromList.size(); + } + + @Override + public ListIterator listIterator(final int index) { + return new TransformedListIterator(fromList.listIterator(index)) { + @Override + T transform(F from) { + return function.apply(from); + } + }; + } + + private static final long serialVersionUID = 0; + } + + /** + * Implementation of a transforming random access list. We try to make as many + * of these methods pass-through to the source list as possible so that the + * performance characteristics of the source list and transformed list are + * similar. + * + * @see Lists#transform + */ + private static class TransformingRandomAccessList extends AbstractList + implements RandomAccess, Serializable { + final List fromList; + final Function function; + + TransformingRandomAccessList(List fromList, Function function) { + this.fromList = checkNotNull(fromList); + this.function = checkNotNull(function); + } + + @Override + public void clear() { + fromList.clear(); + } + + @Override + public T get(int index) { + return function.apply(fromList.get(index)); + } + + @Override + public Iterator iterator() { + return listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + return new TransformedListIterator(fromList.listIterator(index)) { + @Override + T transform(F from) { + return function.apply(from); + } + }; + } + + @Override + public boolean isEmpty() { + return fromList.isEmpty(); + } + + @Override + public T remove(int index) { + return function.apply(fromList.remove(index)); + } + + @Override + public int size() { + return fromList.size(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns consecutive {@linkplain List#subList(int, int) sublists} of a list, + * each of the same size (the final list may be smaller). For example, + * partitioning a list containing {@code [a, b, c, d, e]} with a partition size + * of 3 yields {@code [[a, b, c], [d, e]]} -- an outer list containing two inner + * lists of three and two elements, all in the original order. + * + *

+ * The outer list is unmodifiable, but reflects the latest state of the source + * list. The inner lists are sublist views of the original list, produced on + * demand using {@link List#subList(int, int)}, and are subject to all the usual + * caveats about modification as explained in that API. + * + * @param list the list to return consecutive sublists of + * @param size the desired size of each sublist (the last may be smaller) + * @return a list of consecutive sublists + * @throws IllegalArgumentException if {@code partitionSize} is nonpositive + */ + public static List> partition(List list, int size) { + checkNotNull(list); + checkArgument(size > 0); + return (list instanceof RandomAccess) ? new RandomAccessPartition(list, size) : new Partition(list, size); + } + + private static class Partition extends AbstractList> { + final List list; + final int size; + + Partition(List list, int size) { + this.list = list; + this.size = size; + } + + @Override + public List get(int index) { + checkElementIndex(index, size()); + int start = index * size; + int end = Math.min(start + size, list.size()); + return list.subList(start, end); + } + + @Override + public int size() { + return IntMath.divide(list.size(), size, RoundingMode.CEILING); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + } + + private static class RandomAccessPartition extends Partition implements RandomAccess { + RandomAccessPartition(List list, int size) { + super(list, size); + } + } + + /** + * Returns a view of the specified string as an immutable list of {@code + * Character} values. + * + * @since 7.0 + */ + @Beta + public static ImmutableList charactersOf(String string) { + return new StringAsImmutableList(checkNotNull(string)); + } + + @SuppressWarnings("serial") // serialized using ImmutableList serialization + private static final class StringAsImmutableList extends ImmutableList { + + private final String string; + + StringAsImmutableList(String string) { + this.string = string; + } + + @Override + public int indexOf(@Nullable Object object) { + return (object instanceof Character) ? string.indexOf((Character) object) : -1; + } + + @Override + public int lastIndexOf(@Nullable Object object) { + return (object instanceof Character) ? string.lastIndexOf((Character) object) : -1; + } + + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); // for GWT + return charactersOf(string.substring(fromIndex, toIndex)); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public Character get(int index) { + checkElementIndex(index, size()); // for GWT + return string.charAt(index); + } + + @Override + public int size() { + return string.length(); + } + } + + /** + * Returns a view of the specified {@code CharSequence} as a {@code + * List}, viewing {@code sequence} as a sequence of Unicode code + * units. The view does not support any modification operations, but reflects + * any changes to the underlying character sequence. + * + * @param sequence the character sequence to view as a {@code List} of + * characters + * @return an {@code List} view of the character sequence + * @since 7.0 + */ + @Beta + public static List charactersOf(CharSequence sequence) { + return new CharSequenceAsList(checkNotNull(sequence)); + } + + private static final class CharSequenceAsList extends AbstractList { + private final CharSequence sequence; + + CharSequenceAsList(CharSequence sequence) { + this.sequence = sequence; + } + + @Override + public Character get(int index) { + checkElementIndex(index, size()); // for GWT + return sequence.charAt(index); + } + + @Override + public int size() { + return sequence.length(); + } + } + + /** + * Returns a reversed view of the specified list. For example, {@code + * Lists.reverse(Arrays.asList(1, 2, 3))} returns a list containing {@code 3, + * 2, 1}. The returned list is backed by this list, so changes in the returned + * list are reflected in this list, and vice-versa. The returned list supports + * all of the optional list operations supported by this list. + * + *

+ * The returned list is random-access if the specified list is random access. + * + * @since 7.0 + */ + public static List reverse(List list) { + if (list instanceof ImmutableList) { + return ((ImmutableList) list).reverse(); + } else if (list instanceof ReverseList) { + return ((ReverseList) list).getForwardList(); + } else if (list instanceof RandomAccess) { + return new RandomAccessReverseList(list); + } else { + return new ReverseList(list); + } + } + + private static class ReverseList extends AbstractList { + private final List forwardList; + + ReverseList(List forwardList) { + this.forwardList = checkNotNull(forwardList); + } + + List getForwardList() { + return forwardList; + } + + private int reverseIndex(int index) { + int size = size(); + checkElementIndex(index, size); + return (size - 1) - index; + } + + private int reversePosition(int index) { + int size = size(); + checkPositionIndex(index, size); + return size - index; + } + + @Override + public void add(int index, @Nullable T element) { + forwardList.add(reversePosition(index), element); + } + + @Override + public void clear() { + forwardList.clear(); + } + + @Override + public T remove(int index) { + return forwardList.remove(reverseIndex(index)); + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + subList(fromIndex, toIndex).clear(); + } + + @Override + public T set(int index, @Nullable T element) { + return forwardList.set(reverseIndex(index), element); + } + + @Override + public T get(int index) { + return forwardList.get(reverseIndex(index)); + } + + @Override + public int size() { + return forwardList.size(); + } + + @Override + public List subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); + return reverse(forwardList.subList(reversePosition(toIndex), reversePosition(fromIndex))); + } + + @Override + public Iterator iterator() { + return listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + int start = reversePosition(index); + final ListIterator forwardIterator = forwardList.listIterator(start); + return new ListIterator() { + + boolean canRemoveOrSet; + + @Override + public void add(T e) { + forwardIterator.add(e); + forwardIterator.previous(); + canRemoveOrSet = false; + } + + @Override + public boolean hasNext() { + return forwardIterator.hasPrevious(); + } + + @Override + public boolean hasPrevious() { + return forwardIterator.hasNext(); + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + canRemoveOrSet = true; + return forwardIterator.previous(); + } + + @Override + public int nextIndex() { + return reversePosition(forwardIterator.nextIndex()); + } + + @Override + public T previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + canRemoveOrSet = true; + return forwardIterator.next(); + } + + @Override + public int previousIndex() { + return nextIndex() - 1; + } + + @Override + public void remove() { + checkRemove(canRemoveOrSet); + forwardIterator.remove(); + canRemoveOrSet = false; + } + + @Override + public void set(T e) { + checkState(canRemoveOrSet); + forwardIterator.set(e); + } + }; + } + } + + private static class RandomAccessReverseList extends ReverseList implements RandomAccess { + RandomAccessReverseList(List forwardList) { + super(forwardList); + } + } + + /** + * An implementation of {@link List#hashCode()}. + */ + static int hashCodeImpl(List list) { + // TODO(user): worth optimizing for RandomAccess? + int hashCode = 1; + for (Object o : list) { + hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode()); + + hashCode = ~~hashCode; + // needed to deal with GWT integer overflow + } + return hashCode; + } + + /** + * An implementation of {@link List#equals(Object)}. + */ + static boolean equalsImpl(List list, @Nullable Object object) { + if (object == checkNotNull(list)) { + return true; + } + if (!(object instanceof List)) { + return false; + } + + List o = (List) object; + + return list.size() == o.size() && Iterators.elementsEqual(list.iterator(), o.iterator()); + } + + /** + * An implementation of {@link List#addAll(int, Collection)}. + */ + static boolean addAllImpl(List list, int index, Iterable elements) { + boolean changed = false; + ListIterator listIterator = list.listIterator(index); + for (E e : elements) { + listIterator.add(e); + changed = true; + } + return changed; + } + + /** + * An implementation of {@link List#indexOf(Object)}. + */ + static int indexOfImpl(List list, @Nullable Object element) { + ListIterator listIterator = list.listIterator(); + while (listIterator.hasNext()) { + if (Objects.equal(element, listIterator.next())) { + return listIterator.previousIndex(); + } + } + return -1; + } + + /** + * An implementation of {@link List#lastIndexOf(Object)}. + */ + static int lastIndexOfImpl(List list, @Nullable Object element) { + ListIterator listIterator = list.listIterator(list.size()); + while (listIterator.hasPrevious()) { + if (Objects.equal(element, listIterator.previous())) { + return listIterator.nextIndex(); + } + } + return -1; + } + + /** + * Returns an implementation of {@link List#listIterator(int)}. + */ + static ListIterator listIteratorImpl(List list, int index) { + return new AbstractListWrapper(list).listIterator(index); + } + + /** + * An implementation of {@link List#subList(int, int)}. + */ + static List subListImpl(final List list, int fromIndex, int toIndex) { + List wrapper; + if (list instanceof RandomAccess) { + wrapper = new RandomAccessListWrapper(list) { + @Override + public ListIterator listIterator(int index) { + return backingList.listIterator(index); + } + + private static final long serialVersionUID = 0; + }; + } else { + wrapper = new AbstractListWrapper(list) { + @Override + public ListIterator listIterator(int index) { + return backingList.listIterator(index); + } + + private static final long serialVersionUID = 0; + }; + } + return wrapper.subList(fromIndex, toIndex); + } + + private static class AbstractListWrapper extends AbstractList { + final List backingList; + + AbstractListWrapper(List backingList) { + this.backingList = checkNotNull(backingList); + } + + @Override + public void add(int index, E element) { + backingList.add(index, element); + } + + @Override + public boolean addAll(int index, Collection c) { + return backingList.addAll(index, c); + } + + @Override + public E get(int index) { + return backingList.get(index); + } + + @Override + public E remove(int index) { + return backingList.remove(index); + } + + @Override + public E set(int index, E element) { + return backingList.set(index, element); + } + + @Override + public boolean contains(Object o) { + return backingList.contains(o); + } + + @Override + public int size() { + return backingList.size(); + } + } + + private static class RandomAccessListWrapper extends AbstractListWrapper implements RandomAccess { + RandomAccessListWrapper(List backingList) { + super(backingList); + } + } + + /** + * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 + */ + static List cast(Iterable iterable) { + return (List) iterable; + } +} diff --git a/sources/main/java/com/google/common/collect/MapConstraint.java b/sources/main/java/com/google/common/collect/MapConstraint.java new file mode 100644 index 0000000..f22a207 --- /dev/null +++ b/sources/main/java/com/google/common/collect/MapConstraint.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A constraint on the keys and values that may be added to a {@code Map} or + * {@code Multimap}. For example, {@link MapConstraints#notNull()}, which + * prevents a map from including any null keys or values, could be implemented + * like this: + * + *

+ *    {@code
+ *
+ *   public void checkKeyValue(Object key, Object value) {
+ *     if (key == null || value == null) {
+ *       throw new NullPointerException();
+ *     }
+ *   }}
+ * 
+ * + *

+ * In order to be effective, constraints should be deterministic; that is, they + * should not depend on state that can change (such as external state, random + * variables, and time) and should only depend on the value of the passed-in key + * and value. A non-deterministic constraint cannot reliably enforce that all + * the collection's elements meet the constraint, since the constraint is only + * enforced when elements are added. + * + * @author Mike Bostock + * @see MapConstraints + * @see Constraint + * @since 3.0 + */ +@GwtCompatible +@Beta +public interface MapConstraint { + /** + * Throws a suitable {@code RuntimeException} if the specified key or value is + * illegal. Typically this is either a {@link NullPointerException}, an + * {@link IllegalArgumentException}, or a {@link ClassCastException}, though an + * application-specific exception class may be used if appropriate. + */ + void checkKeyValue(@Nullable K key, @Nullable V value); + + /** + * Returns a brief human readable description of this constraint, such as "Not + * null". + */ + @Override + String toString(); +} diff --git a/sources/main/java/com/google/common/collect/MapConstraints.java b/sources/main/java/com/google/common/collect/MapConstraints.java new file mode 100644 index 0000000..7a3a2a6 --- /dev/null +++ b/sources/main/java/com/google/common/collect/MapConstraints.java @@ -0,0 +1,864 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Factory and utilities pertaining to the {@code MapConstraint} interface. + * + * @see Constraints + * @author Mike Bostock + * @since 3.0 + */ +@Beta +@GwtCompatible +public final class MapConstraints { + private MapConstraints() { + } + + /** + * Returns a constraint that verifies that neither the key nor the value is + * null. If either is null, a {@link NullPointerException} is thrown. + */ + public static MapConstraint notNull() { + return NotNullMapConstraint.INSTANCE; + } + + // enum singleton pattern + private enum NotNullMapConstraint implements MapConstraint { + INSTANCE; + + @Override + public void checkKeyValue(Object key, Object value) { + checkNotNull(key); + checkNotNull(value); + } + + @Override + public String toString() { + return "Not null"; + } + } + + /** + * Returns a constrained view of the specified map, using the specified + * constraint. Any operations that add new mappings will call the provided + * constraint. However, this method does not verify that existing mappings + * satisfy the constraint. + * + *

+ * The returned map is not serializable. + * + * @param map the map to constrain + * @param constraint the constraint that validates added entries + * @return a constrained view of the specified map + */ + public static Map constrainedMap(Map map, MapConstraint constraint) { + return new ConstrainedMap(map, constraint); + } + + /** + * Returns a constrained view of the specified multimap, using the specified + * constraint. Any operations that add new mappings will call the provided + * constraint. However, this method does not verify that existing mappings + * satisfy the constraint. + * + *

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that are not + * constrained. + * + *

+ * The returned multimap is not serializable. + * + * @param multimap the multimap to constrain + * @param constraint the constraint that validates added entries + * @return a constrained view of the multimap + */ + public static Multimap constrainedMultimap(Multimap multimap, + MapConstraint constraint) { + return new ConstrainedMultimap(multimap, constraint); + } + + /** + * Returns a constrained view of the specified list multimap, using the + * specified constraint. Any operations that add new mappings will call the + * provided constraint. However, this method does not verify that existing + * mappings satisfy the constraint. + * + *

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that are not + * constrained. + * + *

+ * The returned multimap is not serializable. + * + * @param multimap the multimap to constrain + * @param constraint the constraint that validates added entries + * @return a constrained view of the specified multimap + */ + public static ListMultimap constrainedListMultimap(ListMultimap multimap, + MapConstraint constraint) { + return new ConstrainedListMultimap(multimap, constraint); + } + + /** + * Returns a constrained view of the specified set multimap, using the specified + * constraint. Any operations that add new mappings will call the provided + * constraint. However, this method does not verify that existing mappings + * satisfy the constraint. + * + *

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that are not + * constrained. + *

+ * The returned multimap is not serializable. + * + * @param multimap the multimap to constrain + * @param constraint the constraint that validates added entries + * @return a constrained view of the specified multimap + */ + public static SetMultimap constrainedSetMultimap(SetMultimap multimap, + MapConstraint constraint) { + return new ConstrainedSetMultimap(multimap, constraint); + } + + /** + * Returns a constrained view of the specified sorted-set multimap, using the + * specified constraint. Any operations that add new mappings will call the + * provided constraint. However, this method does not verify that existing + * mappings satisfy the constraint. + * + *

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that are not + * constrained. + *

+ * The returned multimap is not serializable. + * + * @param multimap the multimap to constrain + * @param constraint the constraint that validates added entries + * @return a constrained view of the specified multimap + */ + public static SortedSetMultimap constrainedSortedSetMultimap(SortedSetMultimap multimap, + MapConstraint constraint) { + return new ConstrainedSortedSetMultimap(multimap, constraint); + } + + /** + * Returns a constrained view of the specified entry, using the specified + * constraint. The {@link Entry#setValue} operation will be verified with the + * constraint. + * + * @param entry the entry to constrain + * @param constraint the constraint for the entry + * @return a constrained view of the specified entry + */ + private static Entry constrainedEntry(final Entry entry, + final MapConstraint constraint) { + checkNotNull(entry); + checkNotNull(constraint); + return new ForwardingMapEntry() { + @Override + protected Entry delegate() { + return entry; + } + + @Override + public V setValue(V value) { + constraint.checkKeyValue(getKey(), value); + return entry.setValue(value); + } + }; + } + + /** + * Returns a constrained view of the specified {@code asMap} entry, using the + * specified constraint. The {@link Entry#setValue} operation will be verified + * with the constraint, and the collection returned by {@link Entry#getValue} + * will be similarly constrained. + * + * @param entry the {@code asMap} entry to constrain + * @param constraint the constraint for the entry + * @return a constrained view of the specified entry + */ + private static Entry> constrainedAsMapEntry(final Entry> entry, + final MapConstraint constraint) { + checkNotNull(entry); + checkNotNull(constraint); + return new ForwardingMapEntry>() { + @Override + protected Entry> delegate() { + return entry; + } + + @Override + public Collection getValue() { + return Constraints.constrainedTypePreservingCollection(entry.getValue(), new Constraint() { + @Override + public V checkElement(V value) { + constraint.checkKeyValue(getKey(), value); + return value; + } + }); + } + }; + } + + /** + * Returns a constrained view of the specified set of {@code asMap} entries, + * using the specified constraint. The {@link Entry#setValue} operation will be + * verified with the constraint, and the collection returned by + * {@link Entry#getValue} will be similarly constrained. The {@code add} and + * {@code + * addAll} operations simply forward to the underlying set, which throws an + * {@link UnsupportedOperationException} per the multimap specification. + * + * @param entries the entries to constrain + * @param constraint the constraint for the entries + * @return a constrained view of the entries + */ + private static Set>> constrainedAsMapEntries(Set>> entries, + MapConstraint constraint) { + return new ConstrainedAsMapEntries(entries, constraint); + } + + /** + * Returns a constrained view of the specified collection (or set) of entries, + * using the specified constraint. The {@link Entry#setValue} operation will be + * verified with the constraint, along with add operations on the returned + * collection. The {@code add} and {@code addAll} operations simply forward to + * the underlying collection, which throws an + * {@link UnsupportedOperationException} per the map and multimap specification. + * + * @param entries the entries to constrain + * @param constraint the constraint for the entries + * @return a constrained view of the specified entries + */ + private static Collection> constrainedEntries(Collection> entries, + MapConstraint constraint) { + if (entries instanceof Set) { + return constrainedEntrySet((Set>) entries, constraint); + } + return new ConstrainedEntries(entries, constraint); + } + + /** + * Returns a constrained view of the specified set of entries, using the + * specified constraint. The {@link Entry#setValue} operation will be verified + * with the constraint, along with add operations on the returned set. The + * {@code add} and {@code addAll} operations simply forward to the underlying + * set, which throws an {@link UnsupportedOperationException} per the map and + * multimap specification. + * + *

+ * The returned multimap is not serializable. + * + * @param entries the entries to constrain + * @param constraint the constraint for the entries + * @return a constrained view of the specified entries + */ + private static Set> constrainedEntrySet(Set> entries, + MapConstraint constraint) { + return new ConstrainedEntrySet(entries, constraint); + } + + /** @see MapConstraints#constrainedMap */ + static class ConstrainedMap extends ForwardingMap { + private final Map delegate; + final MapConstraint constraint; + private transient Set> entrySet; + + ConstrainedMap(Map delegate, MapConstraint constraint) { + this.delegate = checkNotNull(delegate); + this.constraint = checkNotNull(constraint); + } + + @Override + protected Map delegate() { + return delegate; + } + + @Override + public Set> entrySet() { + Set> result = entrySet; + if (result == null) { + entrySet = result = constrainedEntrySet(delegate.entrySet(), constraint); + } + return result; + } + + @Override + public V put(K key, V value) { + constraint.checkKeyValue(key, value); + return delegate.put(key, value); + } + + @Override + public void putAll(Map map) { + delegate.putAll(checkMap(map, constraint)); + } + } + + /** + * Returns a constrained view of the specified bimap, using the specified + * constraint. Any operations that modify the bimap will have the associated + * keys and values verified with the constraint. + * + *

+ * The returned bimap is not serializable. + * + * @param map the bimap to constrain + * @param constraint the constraint that validates added entries + * @return a constrained view of the specified bimap + */ + public static BiMap constrainedBiMap(BiMap map, MapConstraint constraint) { + return new ConstrainedBiMap(map, null, constraint); + } + + /** @see MapConstraints#constrainedBiMap */ + private static class ConstrainedBiMap extends ConstrainedMap implements BiMap { + /* + * We could switch to racy single-check lazy init and remove volatile, but + * there's a downside. That's because this field is also written in the + * constructor. Without volatile, the constructor's write of the existing + * inverse BiMap could occur after inverse()'s read of the field's initial null + * value, leading inverse() to overwrite the existing inverse with a doubly + * indirect version. This wouldn't be catastrophic, but it's something to keep + * in mind if we make the change. + * + * Note that UnmodifiableBiMap *does* use racy single-check lazy init. + * TODO(cpovirk): pick one and standardize + */ + volatile BiMap inverse; + + ConstrainedBiMap(BiMap delegate, @Nullable BiMap inverse, + MapConstraint constraint) { + super(delegate, constraint); + this.inverse = inverse; + } + + @Override + protected BiMap delegate() { + return (BiMap) super.delegate(); + } + + @Override + public V forcePut(K key, V value) { + constraint.checkKeyValue(key, value); + return delegate().forcePut(key, value); + } + + @Override + public BiMap inverse() { + if (inverse == null) { + inverse = new ConstrainedBiMap(delegate().inverse(), this, + new InverseConstraint(constraint)); + } + return inverse; + } + + @Override + public Set values() { + return delegate().values(); + } + } + + /** @see MapConstraints#constrainedBiMap */ + private static class InverseConstraint implements MapConstraint { + final MapConstraint constraint; + + public InverseConstraint(MapConstraint constraint) { + this.constraint = checkNotNull(constraint); + } + + @Override + public void checkKeyValue(K key, V value) { + constraint.checkKeyValue(value, key); + } + } + + /** @see MapConstraints#constrainedMultimap */ + private static class ConstrainedMultimap extends ForwardingMultimap implements Serializable { + final MapConstraint constraint; + final Multimap delegate; + transient Collection> entries; + transient Map> asMap; + + public ConstrainedMultimap(Multimap delegate, MapConstraint constraint) { + this.delegate = checkNotNull(delegate); + this.constraint = checkNotNull(constraint); + } + + @Override + protected Multimap delegate() { + return delegate; + } + + @Override + public Map> asMap() { + Map> result = asMap; + if (result == null) { + final Map> asMapDelegate = delegate.asMap(); + + asMap = result = new ForwardingMap>() { + Set>> entrySet; + Collection> values; + + @Override + protected Map> delegate() { + return asMapDelegate; + } + + @Override + public Set>> entrySet() { + Set>> result = entrySet; + if (result == null) { + entrySet = result = constrainedAsMapEntries(asMapDelegate.entrySet(), constraint); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public Collection get(Object key) { + try { + Collection collection = ConstrainedMultimap.this.get((K) key); + return collection.isEmpty() ? null : collection; + } catch (ClassCastException e) { + return null; // key wasn't a K + } + } + + @Override + public Collection> values() { + Collection> result = values; + if (result == null) { + values = result = new ConstrainedAsMapValues(delegate().values(), entrySet()); + } + return result; + } + + @Override + public boolean containsValue(Object o) { + return values().contains(o); + } + }; + } + return result; + } + + @Override + public Collection> entries() { + Collection> result = entries; + if (result == null) { + entries = result = constrainedEntries(delegate.entries(), constraint); + } + return result; + } + + @Override + public Collection get(final K key) { + return Constraints.constrainedTypePreservingCollection(delegate.get(key), new Constraint() { + @Override + public V checkElement(V value) { + constraint.checkKeyValue(key, value); + return value; + } + }); + } + + @Override + public boolean put(K key, V value) { + constraint.checkKeyValue(key, value); + return delegate.put(key, value); + } + + @Override + public boolean putAll(K key, Iterable values) { + return delegate.putAll(key, checkValues(key, values, constraint)); + } + + @Override + public boolean putAll(Multimap multimap) { + boolean changed = false; + for (Entry entry : multimap.entries()) { + changed |= put(entry.getKey(), entry.getValue()); + } + return changed; + } + + @Override + public Collection replaceValues(K key, Iterable values) { + return delegate.replaceValues(key, checkValues(key, values, constraint)); + } + } + + /** @see ConstrainedMultimap#asMap */ + private static class ConstrainedAsMapValues extends ForwardingCollection> { + final Collection> delegate; + final Set>> entrySet; + + /** + * @param entrySet map entries, linking each key with its corresponding values, + * that already enforce the constraint + */ + ConstrainedAsMapValues(Collection> delegate, Set>> entrySet) { + this.delegate = delegate; + this.entrySet = entrySet; + } + + @Override + protected Collection> delegate() { + return delegate; + } + + @Override + public Iterator> iterator() { + final Iterator>> iterator = entrySet.iterator(); + return new Iterator>() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Collection next() { + return iterator.next().getValue(); + } + + @Override + public void remove() { + iterator.remove(); + } + }; + } + + @Override + public Object[] toArray() { + return standardToArray(); + } + + @Override + public T[] toArray(T[] array) { + return standardToArray(array); + } + + @Override + public boolean contains(Object o) { + return standardContains(o); + } + + @Override + public boolean containsAll(Collection c) { + return standardContainsAll(c); + } + + @Override + public boolean remove(Object o) { + return standardRemove(o); + } + + @Override + public boolean removeAll(Collection c) { + return standardRemoveAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return standardRetainAll(c); + } + } + + /** @see MapConstraints#constrainedEntries */ + private static class ConstrainedEntries extends ForwardingCollection> { + final MapConstraint constraint; + final Collection> entries; + + ConstrainedEntries(Collection> entries, MapConstraint constraint) { + this.entries = entries; + this.constraint = constraint; + } + + @Override + protected Collection> delegate() { + return entries; + } + + @Override + public Iterator> iterator() { + final Iterator> iterator = entries.iterator(); + return new ForwardingIterator>() { + @Override + public Entry next() { + return constrainedEntry(iterator.next(), constraint); + } + + @Override + protected Iterator> delegate() { + return iterator; + } + }; + } + + // See Collections.CheckedMap.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 remove(Object o) { + return Maps.removeEntryImpl(delegate(), o); + } + + @Override + public boolean removeAll(Collection c) { + return standardRemoveAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return standardRetainAll(c); + } + } + + /** @see MapConstraints#constrainedEntrySet */ + static class ConstrainedEntrySet extends ConstrainedEntries implements Set> { + ConstrainedEntrySet(Set> entries, MapConstraint constraint) { + super(entries, constraint); + } + + // See Collections.CheckedMap.CheckedEntrySet for details on attacks. + + @Override + public boolean equals(@Nullable Object object) { + return Sets.equalsImpl(this, object); + } + + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + } + + /** @see MapConstraints#constrainedAsMapEntries */ + static class ConstrainedAsMapEntries extends ForwardingSet>> { + private final MapConstraint constraint; + private final Set>> entries; + + ConstrainedAsMapEntries(Set>> entries, MapConstraint constraint) { + this.entries = entries; + this.constraint = constraint; + } + + @Override + protected Set>> delegate() { + return entries; + } + + @Override + public Iterator>> iterator() { + final Iterator>> iterator = entries.iterator(); + return new ForwardingIterator>>() { + @Override + public Entry> next() { + return constrainedAsMapEntry(iterator.next(), constraint); + } + + @Override + protected Iterator>> delegate() { + return iterator; + } + }; + } + + // See Collections.CheckedMap.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 equals(@Nullable Object object) { + return standardEquals(object); + } + + @Override + public int hashCode() { + return standardHashCode(); + } + + @Override + public boolean remove(Object o) { + return Maps.removeEntryImpl(delegate(), o); + } + + @Override + public boolean removeAll(Collection c) { + return standardRemoveAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return standardRetainAll(c); + } + } + + private static class ConstrainedListMultimap extends ConstrainedMultimap implements ListMultimap { + ConstrainedListMultimap(ListMultimap delegate, MapConstraint constraint) { + super(delegate, constraint); + } + + @Override + public List get(K key) { + return (List) super.get(key); + } + + @Override + public List removeAll(Object key) { + return (List) super.removeAll(key); + } + + @Override + public List replaceValues(K key, Iterable values) { + return (List) super.replaceValues(key, values); + } + } + + private static class ConstrainedSetMultimap extends ConstrainedMultimap implements SetMultimap { + ConstrainedSetMultimap(SetMultimap delegate, MapConstraint constraint) { + super(delegate, constraint); + } + + @Override + public Set get(K key) { + return (Set) super.get(key); + } + + @Override + public Set> entries() { + return (Set>) super.entries(); + } + + @Override + public Set removeAll(Object key) { + return (Set) super.removeAll(key); + } + + @Override + public Set replaceValues(K key, Iterable values) { + return (Set) super.replaceValues(key, values); + } + } + + private static class ConstrainedSortedSetMultimap extends ConstrainedSetMultimap + implements SortedSetMultimap { + ConstrainedSortedSetMultimap(SortedSetMultimap delegate, MapConstraint constraint) { + super(delegate, constraint); + } + + @Override + public SortedSet get(K key) { + return (SortedSet) super.get(key); + } + + @Override + public SortedSet removeAll(Object key) { + return (SortedSet) super.removeAll(key); + } + + @Override + public SortedSet replaceValues(K key, Iterable values) { + return (SortedSet) super.replaceValues(key, values); + } + + @Override + public Comparator valueComparator() { + return ((SortedSetMultimap) delegate()).valueComparator(); + } + } + + private static Collection checkValues(K key, Iterable values, + MapConstraint constraint) { + Collection copy = Lists.newArrayList(values); + for (V value : copy) { + constraint.checkKeyValue(key, value); + } + return copy; + } + + private static Map checkMap(Map map, + MapConstraint constraint) { + Map copy = new LinkedHashMap(map); + for (Entry entry : copy.entrySet()) { + constraint.checkKeyValue(entry.getKey(), entry.getValue()); + } + return copy; + } +} diff --git a/sources/main/java/com/google/common/collect/MapDifference.java b/sources/main/java/com/google/common/collect/MapDifference.java new file mode 100644 index 0000000..6d09421 --- /dev/null +++ b/sources/main/java/com/google/common/collect/MapDifference.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An object representing the differences between two maps. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface MapDifference { + /** + * Returns {@code true} if there are no differences between the two maps; that + * is, if the maps are equal. + */ + boolean areEqual(); + + /** + * Returns an unmodifiable map containing the entries from the left map whose + * keys are not present in the right map. + */ + Map entriesOnlyOnLeft(); + + /** + * Returns an unmodifiable map containing the entries from the right map whose + * keys are not present in the left map. + */ + Map entriesOnlyOnRight(); + + /** + * Returns an unmodifiable map containing the entries that appear in both maps; + * that is, the intersection of the two maps. + */ + Map entriesInCommon(); + + /** + * Returns an unmodifiable map describing keys that appear in both maps, but + * with different values. + */ + Map> entriesDiffering(); + + /** + * Compares the specified object with this instance for equality. Returns + * {@code true} if the given object is also a {@code MapDifference} and the + * values returned by the {@link #entriesOnlyOnLeft()}, + * {@link #entriesOnlyOnRight()}, {@link #entriesInCommon()} and + * {@link #entriesDiffering()} of the two instances are equal. + */ + @Override + boolean equals(@Nullable Object object); + + /** + * Returns the hash code for this instance. This is defined as the hash code of + * + *

+	 *    {@code
+	 *
+	 *   Arrays.asList(entriesOnlyOnLeft(), entriesOnlyOnRight(),
+	 *       entriesInCommon(), entriesDiffering())}
+	 * 
+ */ + @Override + int hashCode(); + + /** + * A difference between the mappings from two maps with the same key. The + * {@link #leftValue} and {@link #rightValue} are not equal, and one but not + * both of them may be null. + * + * @since 2.0 (imported from Google Collections Library) + */ + interface ValueDifference { + /** + * Returns the value from the left map (possibly null). + */ + V leftValue(); + + /** + * Returns the value from the right map (possibly null). + */ + V rightValue(); + + /** + * Two instances are considered equal if their {@link #leftValue()} values are + * equal and their {@link #rightValue()} values are also equal. + */ + @Override + boolean equals(@Nullable Object other); + + /** + * The hash code equals the value + * {@code Arrays.asList(leftValue(), rightValue()).hashCode()}. + */ + @Override + int hashCode(); + } + +} diff --git a/sources/main/java/com/google/common/collect/Maps.java b/sources/main/java/com/google/common/collect/Maps.java new file mode 100644 index 0000000..2647ad3 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Maps.java @@ -0,0 +1,4133 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.compose; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.in; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; + +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Properties; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Converter; +import com.google.common.base.Equivalence; +import com.google.common.base.Function; +import com.google.common.base.Joiner.MapJoiner; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.MapDifference.ValueDifference; +import com.google.common.primitives.Ints; + +/** + * 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) + */ +@GwtCompatible(emulated = true) +public final class Maps { + private Maps() { + } + + private enum EntryFunction implements Function, Object> { + KEY { + @Override + @Nullable + public Object apply(Entry entry) { + return entry.getKey(); + } + }, + VALUE { + @Override + @Nullable + public Object apply(Entry entry) { + return entry.getValue(); + } + }; + } + + @SuppressWarnings("unchecked") + static Function, K> keyFunction() { + return (Function) EntryFunction.KEY; + } + + @SuppressWarnings("unchecked") + 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 + */ + @GwtCompatible(serializable = true) + @Beta + public static , V> ImmutableMap immutableEnumMap(Map map) { + if (map instanceof ImmutableEnumMap) { + @SuppressWarnings("unchecked") // 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 < Ints.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); + } + + /** + * 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(@Nullable 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 + */ + @SuppressWarnings("unchecked") + 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(@Nullable V left, @Nullable V right) { + return new ValueDifferenceImpl(left, right); + } + + private ValueDifferenceImpl(@Nullable V left, @Nullable V right) { + this.left = left; + this.right = right; + } + + @Override + public V leftValue() { + return left; + } + + @Override + public V rightValue() { + return right; + } + + @Override + public boolean equals(@Nullable 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. + */ + @SuppressWarnings("unchecked") + static Comparator orNaturalOrder(@Nullable 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 + @GwtIncompatible("NavigableMap") + 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 Collections2.transform(set, function); + } + + @Override + public int size() { + return backingSet().size(); + } + + @Override + public boolean containsKey(@Nullable Object key) { + return backingSet().contains(key); + } + + @Override + public V get(@Nullable Object key) { + if (Collections2.safeContains(backingSet(), key)) { + @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it + K k = (K) key; + return function.apply(k); + } else { + return null; + } + } + + @Override + public V remove(@Nullable Object key) { + if (backingSet().remove(key)) { + @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it + K k = (K) key; + return function.apply(k); + } else { + return null; + } + } + + @Override + public void clear() { + backingSet().clear(); + } + + @Override + protected Set> 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(); + } + } + + @GwtIncompatible("NavigableMap") + 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 + @Nullable + public V get(@Nullable Object key) { + if (Collections2.safeContains(set, key)) { + @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it + K k = (K) key; + return function.apply(k); + } else { + return null; + } + } + + @Override + public void clear() { + set.clear(); + } + + @Override + Iterator> 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)); + } + }; + } + + @GwtIncompatible("NavigableSet") + 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 + */ + @GwtIncompatible("java.util.Properties") + 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 + */ + @GwtCompatible(serializable = true) + public static Entry immutableEntry(@Nullable K key, @Nullable 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(@Nullable 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 com.google.common.base.Functions#forMap(Map)} or + * {@link com.google.common.base.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(@Nullable 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, @Nullable 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 + */ + @GwtIncompatible("NavigableMap") + 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 + */ + @GwtIncompatible("NavigableMap") + 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(@Nullable K key, @Nullable 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(@Nullable 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 + @SuppressWarnings("unchecked") + @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 + @SuppressWarnings("unchecked") + @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); + } + } + + @GwtIncompatible("NavigableMap") + 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); + } + + @Nullable + private Entry transformEntry(@Nullable 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 + */ + @GwtIncompatible("NavigableMap") + 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 + */ + @GwtIncompatible("NavigableMap") + 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 + */ + @GwtIncompatible("NavigableMap") + 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(@Nullable Object key, @Nullable V value) { + // This method is called only when the key is in the map, implying that + // key is a K. + @SuppressWarnings("unchecked") + 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 + @SuppressWarnings("unchecked") + 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. + */ + @GwtIncompatible("NavigableMap") + private static NavigableMap filterFiltered(FilteredEntryNavigableMap map, + Predicate> entryPredicate) { + Predicate> predicate = Predicates.and(map.entryPredicate, entryPredicate); + return new FilteredEntryNavigableMap(map.unfiltered, predicate); + } + + @GwtIncompatible("NavigableMap") + 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 + @Nullable + public V get(@Nullable Object key) { + return filteredDelegate.get(key); + } + + @Override + public boolean containsKey(@Nullable Object key) { + return filteredDelegate.containsKey(key); + } + + @Override + public V put(K key, V value) { + return filteredDelegate.put(key, value); + } + + @Override + public V remove(@Nullable 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 apply(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(@Nullable K key, @Nullable 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 + */ + @GwtIncompatible("NavigableMap") + public static NavigableMap unmodifiableNavigableMap(NavigableMap map) { + checkNotNull(map); + if (map instanceof UnmodifiableNavigableMap) { + return map; + } else { + return new UnmodifiableNavigableMap(map); + } + } + + @Nullable + private static Entry unmodifiableOrNull(@Nullable Entry entry) { + return (entry == null) ? null : Maps.unmodifiableEntry(entry); + } + + @GwtIncompatible("NavigableMap") + 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 + */ + @GwtIncompatible("NavigableMap") + 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. + */ + @GwtCompatible + 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, @Nullable 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, @Nullable Object key) { + return Iterators.contains(keyIterator(map.entrySet().iterator()), key); + } + + /** + * An implementation of {@link Map#containsValue}. + */ + static boolean containsValueImpl(Map map, @Nullable 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 = Collections2.STANDARD_JOINER.withKeyValueSeparator("="); + + /** + * An implementation of {@link Map#toString}. + */ + static String toStringImpl(Map map) { + StringBuilder sb = Collections2.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(); + } + } + + @Nullable + static K keyOrNull(@Nullable Entry entry) { + return (entry == null) ? null : entry.getKey(); + } + + @Nullable + static V valueOrNull(@Nullable Entry entry) { + return (entry == null) ? null : entry.getValue(); + } + + static 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(); + } + } + + @GwtIncompatible("NavigableMap") + 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 (Objects.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(@Nullable 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 Objects.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); + } + } + } + + @GwtIncompatible("NavigableMap") + abstract static class DescendingMap extends ForwardingMap implements NavigableMap { + + abstract NavigableMap forward(); + + @Override + protected final Map delegate() { + return forward(); + } + + private transient Comparator comparator; + + @SuppressWarnings("unchecked") + @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/sources/main/java/com/google/common/collect/MinMaxPriorityQueue.java b/sources/main/java/com/google/common/collect/MinMaxPriorityQueue.java new file mode 100644 index 0000000..13497a1 --- /dev/null +++ b/sources/main/java/com/google/common/collect/MinMaxPriorityQueue.java @@ -0,0 +1,944 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkPositionIndex; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.util.AbstractQueue; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; +import java.util.Queue; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.math.IntMath; + +/** + * A double-ended priority queue, which provides constant-time access to both + * its least element and its greatest element, as determined by the queue's + * specified comparator. If no comparator is given at construction time, the + * natural order of elements is used. + * + *

+ * As a {@link Queue} it functions exactly as a {@link PriorityQueue}: its head + * element -- the implicit target of the methods {@link #peek()}, + * {@link #poll()} and {@link #remove()} -- is defined as the least + * element in the queue according to the queue's comparator. But unlike a + * regular priority queue, the methods {@link #peekLast}, {@link #pollLast} and + * {@link #removeLast} are also provided, to act on the greatest element + * in the queue instead. + * + *

+ * A min-max priority queue can be configured with a maximum size. If so, each + * time the size of the queue exceeds that value, the queue automatically + * removes its greatest element according to its comparator (which might be the + * element that was just added). This is different from conventional bounded + * queues, which either block or reject new elements when full. + * + *

+ * This implementation is based on the + * min-max heap + * developed by Atkinson, et al. Unlike many other double-ended priority queues, + * it stores elements in a single array, as compact as the traditional heap data + * structure used in {@link PriorityQueue}. + * + *

+ * This class is not thread-safe, and does not accept null elements. + * + *

+ * Performance notes: + * + *

    + *
  • The retrieval operations {@link #peek}, {@link #peekFirst}, + * {@link #peekLast}, {@link #element}, and {@link #size} are constant-time + *
  • The enqueing and dequeing operations ({@link #offer}, {@link #add}, and + * all the forms of {@link #poll} and {@link #remove()}) run in {@code + * O(log n) time} + *
  • The {@link #remove(Object)} and {@link #contains} operations require + * linear ({@code O(n)}) time + *
  • If you only access one end of the queue, and don't use a maximum size, + * this class is functionally equivalent to {@link PriorityQueue}, but + * significantly slower. + *
+ * + * @author Sverre Sundsdal + * @author Torbjorn Gannholm + * @since 8.0 + */ +// TODO(kevinb): GWT compatibility +@Beta +public final class MinMaxPriorityQueue extends AbstractQueue { + + /** + * Creates a new min-max priority queue with default settings: natural order, no + * maximum size, no initial contents, and an initial expected size of 11. + */ + public static > MinMaxPriorityQueue create() { + return new Builder(Ordering.natural()).create(); + } + + /** + * Creates a new min-max priority queue using natural order, no maximum size, + * and initially containing the given elements. + */ + public static > MinMaxPriorityQueue create(Iterable initialContents) { + return new Builder(Ordering.natural()).create(initialContents); + } + + /** + * Creates and returns a new builder, configured to build {@code + * MinMaxPriorityQueue} instances that use {@code comparator} to determine the + * least and greatest elements. + */ + public static Builder orderedBy(Comparator comparator) { + return new Builder(comparator); + } + + /** + * Creates and returns a new builder, configured to build {@code + * MinMaxPriorityQueue} instances sized appropriately to hold {@code + * expectedSize} elements. + */ + public static Builder expectedSize(int expectedSize) { + return new Builder(Ordering.natural()).expectedSize(expectedSize); + } + + /** + * Creates and returns a new builder, configured to build {@code + * MinMaxPriorityQueue} instances that are limited to {@code maximumSize} + * elements. Each time a queue grows beyond this bound, it immediately removes + * its greatest element (according to its comparator), which might be the + * element that was just added. + */ + public static Builder maximumSize(int maximumSize) { + return new Builder(Ordering.natural()).maximumSize(maximumSize); + } + + /** + * The builder class used in creation of min-max priority queues. Instead of + * constructing one directly, use + * {@link MinMaxPriorityQueue#orderedBy(Comparator)}, + * {@link MinMaxPriorityQueue#expectedSize(int)} or + * {@link MinMaxPriorityQueue#maximumSize(int)}. + * + * @param the upper bound on the eventual type that can be produced by this + * builder (for example, a {@code Builder} can produce a + * {@code Queue} or {@code Queue} but not a {@code + * Queue}). + * @since 8.0 + */ + @Beta + public static final class Builder { + /* + * TODO(kevinb): when the dust settles, see if we still need this or can just + * default to DEFAULT_CAPACITY. + */ + private static final int UNSET_EXPECTED_SIZE = -1; + + private final Comparator comparator; + private int expectedSize = UNSET_EXPECTED_SIZE; + private int maximumSize = Integer.MAX_VALUE; + + private Builder(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + /** + * Configures this builder to build min-max priority queues with an initial + * expected size of {@code expectedSize}. + */ + public Builder expectedSize(int expectedSize) { + checkArgument(expectedSize >= 0); + this.expectedSize = expectedSize; + return this; + } + + /** + * Configures this builder to build {@code MinMaxPriorityQueue} instances that + * are limited to {@code maximumSize} elements. Each time a queue grows beyond + * this bound, it immediately removes its greatest element (according to its + * comparator), which might be the element that was just added. + */ + public Builder maximumSize(int maximumSize) { + checkArgument(maximumSize > 0); + this.maximumSize = maximumSize; + return this; + } + + /** + * Builds a new min-max priority queue using the previously specified options, + * and having no initial contents. + */ + public MinMaxPriorityQueue create() { + return create(Collections.emptySet()); + } + + /** + * Builds a new min-max priority queue using the previously specified options, + * and having the given initial elements. + */ + public MinMaxPriorityQueue create(Iterable initialContents) { + MinMaxPriorityQueue queue = new MinMaxPriorityQueue(this, + initialQueueSize(expectedSize, maximumSize, initialContents)); + for (T element : initialContents) { + queue.offer(element); + } + return queue; + } + + @SuppressWarnings("unchecked") // safe "contravariant cast" + private Ordering ordering() { + return Ordering.from((Comparator) comparator); + } + } + + private final Heap minHeap; + private final Heap maxHeap; + @VisibleForTesting + final int maximumSize; + private Object[] queue; + private int size; + private int modCount; + + private MinMaxPriorityQueue(Builder builder, int queueSize) { + Ordering ordering = builder.ordering(); + this.minHeap = new Heap(ordering); + this.maxHeap = new Heap(ordering.reverse()); + minHeap.otherHeap = maxHeap; + maxHeap.otherHeap = minHeap; + + this.maximumSize = builder.maximumSize; + // TODO(kevinb): pad? + this.queue = new Object[queueSize]; + } + + @Override + public int size() { + return size; + } + + /** + * Adds the given element to this queue. If this queue has a maximum size, after + * adding {@code element} the queue will automatically evict its greatest + * element (according to its comparator), which may be {@code + * element} itself. + * + * @return {@code true} always + */ + @Override + public boolean add(E element) { + offer(element); + return true; + } + + @Override + public boolean addAll(Collection newElements) { + boolean modified = false; + for (E element : newElements) { + offer(element); + modified = true; + } + return modified; + } + + /** + * Adds the given element to this queue. If this queue has a maximum size, after + * adding {@code element} the queue will automatically evict its greatest + * element (according to its comparator), which may be {@code + * element} itself. + */ + @Override + public boolean offer(E element) { + checkNotNull(element); + modCount++; + int insertIndex = size++; + + growIfNeeded(); + + // Adds the element to the end of the heap and bubbles it up to the correct + // position. + heapForIndex(insertIndex).bubbleUp(insertIndex, element); + return size <= maximumSize || pollLast() != element; + } + + @Override + public E poll() { + return isEmpty() ? null : removeAndGet(0); + } + + @SuppressWarnings("unchecked") // we must carefully only allow Es to get in + E elementData(int index) { + return (E) queue[index]; + } + + @Override + public E peek() { + return isEmpty() ? null : elementData(0); + } + + /** + * Returns the index of the max element. + */ + private int getMaxElementIndex() { + switch (size) { + case 1: + return 0; // The lone element in the queue is the maximum. + case 2: + return 1; // The lone element in the maxHeap is the maximum. + default: + // The max element must sit on the first level of the maxHeap. It is + // actually the *lesser* of the two from the maxHeap's perspective. + return (maxHeap.compareElements(1, 2) <= 0) ? 1 : 2; + } + } + + /** + * Removes and returns the least element of this queue, or returns {@code + * null} if the queue is empty. + */ + public E pollFirst() { + return poll(); + } + + /** + * Removes and returns the least element of this queue. + * + * @throws NoSuchElementException if the queue is empty + */ + public E removeFirst() { + return remove(); + } + + /** + * Retrieves, but does not remove, the least element of this queue, or returns + * {@code null} if the queue is empty. + */ + public E peekFirst() { + return peek(); + } + + /** + * Removes and returns the greatest element of this queue, or returns {@code + * null} if the queue is empty. + */ + public E pollLast() { + return isEmpty() ? null : removeAndGet(getMaxElementIndex()); + } + + /** + * Removes and returns the greatest element of this queue. + * + * @throws NoSuchElementException if the queue is empty + */ + public E removeLast() { + if (isEmpty()) { + throw new NoSuchElementException(); + } + return removeAndGet(getMaxElementIndex()); + } + + /** + * Retrieves, but does not remove, the greatest element of this queue, or + * returns {@code null} if the queue is empty. + */ + public E peekLast() { + return isEmpty() ? null : elementData(getMaxElementIndex()); + } + + /** + * Removes the element at position {@code index}. + * + *

+ * Normally this method leaves the elements at up to {@code index - 1}, + * inclusive, untouched. Under these circumstances, it returns {@code null}. + * + *

+ * Occasionally, in order to maintain the heap invariant, it must swap a later + * element of the list with one before {@code index}. Under these circumstances + * it returns a pair of elements as a {@link MoveDesc}. The first one is the + * element that was previously at the end of the heap and is now at some + * position before {@code index}. The second element is the one that was swapped + * down to replace the element at {@code index}. This fact is used by + * iterator.remove so as to visit elements during a traversal once and only + * once. + */ + @VisibleForTesting + MoveDesc removeAt(int index) { + checkPositionIndex(index, size); + modCount++; + size--; + if (size == index) { + queue[size] = null; + return null; + } + E actualLastElement = elementData(size); + int lastElementAt = heapForIndex(size).getCorrectLastElement(actualLastElement); + E toTrickle = elementData(size); + queue[size] = null; + MoveDesc changes = fillHole(index, toTrickle); + if (lastElementAt < index) { + // Last element is moved to before index, swapped with trickled element. + if (changes == null) { + // The trickled element is still after index. + return new MoveDesc(actualLastElement, toTrickle); + } else { + // The trickled element is back before index, but the replaced element + // has now been moved after index. + return new MoveDesc(actualLastElement, changes.replaced); + } + } + // Trickled element was after index to begin with, no adjustment needed. + return changes; + } + + private MoveDesc fillHole(int index, E toTrickle) { + Heap heap = heapForIndex(index); + // We consider elementData(index) a "hole", and we want to fill it + // with the last element of the heap, toTrickle. + // Since the last element of the heap is from the bottom level, we + // optimistically fill index position with elements from lower levels, + // moving the hole down. In most cases this reduces the number of + // comparisons with toTrickle, but in some cases we will need to bubble it + // all the way up again. + int vacated = heap.fillHoleAt(index); + // Try to see if toTrickle can be bubbled up min levels. + int bubbledTo = heap.bubbleUpAlternatingLevels(vacated, toTrickle); + if (bubbledTo == vacated) { + // Could not bubble toTrickle up min levels, try moving + // it from min level to max level (or max to min level) and bubble up + // there. + return heap.tryCrossOverAndBubbleUp(index, vacated, toTrickle); + } else { + return (bubbledTo < index) ? new MoveDesc(toTrickle, elementData(index)) : null; + } + } + + // Returned from removeAt() to iterator.remove() + static class MoveDesc { + final E toTrickle; + final E replaced; + + MoveDesc(E toTrickle, E replaced) { + this.toTrickle = toTrickle; + this.replaced = replaced; + } + } + + /** + * Removes and returns the value at {@code index}. + */ + private E removeAndGet(int index) { + E value = elementData(index); + removeAt(index); + return value; + } + + private Heap heapForIndex(int i) { + return isEvenLevel(i) ? minHeap : maxHeap; + } + + private static final int EVEN_POWERS_OF_TWO = 0x55555555; + private static final int ODD_POWERS_OF_TWO = 0xaaaaaaaa; + + @VisibleForTesting + static boolean isEvenLevel(int index) { + int oneBased = index + 1; + checkState(oneBased > 0, "negative index"); + return (oneBased & EVEN_POWERS_OF_TWO) > (oneBased & ODD_POWERS_OF_TWO); + } + + /** + * Returns {@code true} if the MinMax heap structure holds. This is only used in + * testing. + * + * TODO(kevinb): move to the test class? + */ + @VisibleForTesting + boolean isIntact() { + for (int i = 1; i < size; i++) { + if (!heapForIndex(i).verifyIndex(i)) { + return false; + } + } + return true; + } + + /** + * Each instance of MinMaxPriortyQueue encapsulates two instances of Heap: a + * min-heap and a max-heap. Conceptually, these might each have their own array + * for storage, but for efficiency's sake they are stored interleaved on + * alternate heap levels in the same array (MMPQ.queue). + */ + private class Heap { + final Ordering ordering; + Heap otherHeap; + + Heap(Ordering ordering) { + this.ordering = ordering; + } + + int compareElements(int a, int b) { + return ordering.compare(elementData(a), elementData(b)); + } + + /** + * Tries to move {@code toTrickle} from a min to a max level and bubble up + * there. If it moved before {@code removeIndex} this method returns a pair as + * described in {@link #removeAt}. + */ + MoveDesc tryCrossOverAndBubbleUp(int removeIndex, int vacated, E toTrickle) { + int crossOver = crossOver(vacated, toTrickle); + if (crossOver == vacated) { + return null; + } + // Successfully crossed over from min to max. + // Bubble up max levels. + E parent; + // If toTrickle is moved up to a parent of removeIndex, the parent is + // placed in removeIndex position. We must return that to the iterator so + // that it knows to skip it. + if (crossOver < removeIndex) { + // We crossed over to the parent level in crossOver, so the parent + // has already been moved. + parent = elementData(removeIndex); + } else { + parent = elementData(getParentIndex(removeIndex)); + } + // bubble it up the opposite heap + if (otherHeap.bubbleUpAlternatingLevels(crossOver, toTrickle) < removeIndex) { + return new MoveDesc(toTrickle, parent); + } else { + return null; + } + } + + /** + * Bubbles a value from {@code index} up the appropriate heap if required. + */ + void bubbleUp(int index, E x) { + int crossOver = crossOverUp(index, x); + + Heap heap; + if (crossOver == index) { + heap = this; + } else { + index = crossOver; + heap = otherHeap; + } + heap.bubbleUpAlternatingLevels(index, x); + } + + /** + * Bubbles a value from {@code index} up the levels of this heap, and returns + * the index the element ended up at. + */ + int bubbleUpAlternatingLevels(int index, E x) { + while (index > 2) { + int grandParentIndex = getGrandparentIndex(index); + E e = elementData(grandParentIndex); + if (ordering.compare(e, x) <= 0) { + break; + } + queue[index] = e; + index = grandParentIndex; + } + queue[index] = x; + return index; + } + + /** + * Returns the index of minimum value between {@code index} and + * {@code index + len}, or {@code -1} if {@code index} is greater than + * {@code size}. + */ + int findMin(int index, int len) { + if (index >= size) { + return -1; + } + checkState(index > 0); + int limit = Math.min(index, size - len) + len; + int minIndex = index; + for (int i = index + 1; i < limit; i++) { + if (compareElements(i, minIndex) < 0) { + minIndex = i; + } + } + return minIndex; + } + + /** + * Returns the minimum child or {@code -1} if no child exists. + */ + int findMinChild(int index) { + return findMin(getLeftChildIndex(index), 2); + } + + /** + * Returns the minimum grand child or -1 if no grand child exists. + */ + int findMinGrandChild(int index) { + int leftChildIndex = getLeftChildIndex(index); + if (leftChildIndex < 0) { + return -1; + } + return findMin(getLeftChildIndex(leftChildIndex), 4); + } + + /** + * Moves an element one level up from a min level to a max level (or vice + * versa). Returns the new position of the element. + */ + int crossOverUp(int index, E x) { + if (index == 0) { + queue[0] = x; + return 0; + } + int parentIndex = getParentIndex(index); + E parentElement = elementData(parentIndex); + if (parentIndex != 0) { + // This is a guard for the case of the childless uncle. + // Since the end of the array is actually the middle of the heap, + // a smaller childless uncle can become a child of x when we + // bubble up alternate levels, violating the invariant. + int grandparentIndex = getParentIndex(parentIndex); + int uncleIndex = getRightChildIndex(grandparentIndex); + if (uncleIndex != parentIndex && getLeftChildIndex(uncleIndex) >= size) { + E uncleElement = elementData(uncleIndex); + if (ordering.compare(uncleElement, parentElement) < 0) { + parentIndex = uncleIndex; + parentElement = uncleElement; + } + } + } + if (ordering.compare(parentElement, x) < 0) { + queue[index] = parentElement; + queue[parentIndex] = x; + return parentIndex; + } + queue[index] = x; + return index; + } + + /** + * Returns the conceptually correct last element of the heap. + * + *

+ * Since the last element of the array is actually in the middle of the sorted + * structure, a childless uncle node could be smaller, which would corrupt the + * invariant if this element becomes the new parent of the uncle. In that case, + * we first switch the last element with its uncle, before returning. + */ + int getCorrectLastElement(E actualLastElement) { + int parentIndex = getParentIndex(size); + if (parentIndex != 0) { + int grandparentIndex = getParentIndex(parentIndex); + int uncleIndex = getRightChildIndex(grandparentIndex); + if (uncleIndex != parentIndex && getLeftChildIndex(uncleIndex) >= size) { + E uncleElement = elementData(uncleIndex); + if (ordering.compare(uncleElement, actualLastElement) < 0) { + queue[uncleIndex] = actualLastElement; + queue[size] = uncleElement; + return uncleIndex; + } + } + } + return size; + } + + /** + * Crosses an element over to the opposite heap by moving it one level down (or + * up if there are no elements below it). + * + * Returns the new position of the element. + */ + int crossOver(int index, E x) { + int minChildIndex = findMinChild(index); + // TODO(kevinb): split the && into two if's and move crossOverUp so it's + // only called when there's no child. + if ((minChildIndex > 0) && (ordering.compare(elementData(minChildIndex), x) < 0)) { + queue[index] = elementData(minChildIndex); + queue[minChildIndex] = x; + return minChildIndex; + } + return crossOverUp(index, x); + } + + /** + * Fills the hole at {@code index} by moving in the least of its grandchildren + * to this position, then recursively filling the new hole created. + * + * @return the position of the new hole (where the lowest grandchild moved from, + * that had no grandchild to replace it) + */ + int fillHoleAt(int index) { + int minGrandchildIndex; + while ((minGrandchildIndex = findMinGrandChild(index)) > 0) { + queue[index] = elementData(minGrandchildIndex); + index = minGrandchildIndex; + } + return index; + } + + private boolean verifyIndex(int i) { + if ((getLeftChildIndex(i) < size) && (compareElements(i, getLeftChildIndex(i)) > 0)) { + return false; + } + if ((getRightChildIndex(i) < size) && (compareElements(i, getRightChildIndex(i)) > 0)) { + return false; + } + if ((i > 0) && (compareElements(i, getParentIndex(i)) > 0)) { + return false; + } + if ((i > 2) && (compareElements(getGrandparentIndex(i), i) > 0)) { + return false; + } + return true; + } + + // These would be static if inner classes could have static members. + + private int getLeftChildIndex(int i) { + return i * 2 + 1; + } + + private int getRightChildIndex(int i) { + return i * 2 + 2; + } + + private int getParentIndex(int i) { + return (i - 1) / 2; + } + + private int getGrandparentIndex(int i) { + return getParentIndex(getParentIndex(i)); // (i - 3) / 4 + } + } + + /** + * Iterates the elements of the queue in no particular order. + * + * If the underlying queue is modified during iteration an exception will be + * thrown. + */ + private class QueueIterator implements Iterator { + private int cursor = -1; + private int expectedModCount = modCount; + private Queue forgetMeNot; + private List skipMe; + private E lastFromForgetMeNot; + private boolean canRemove; + + @Override + public boolean hasNext() { + checkModCount(); + return (nextNotInSkipMe(cursor + 1) < size()) || ((forgetMeNot != null) && !forgetMeNot.isEmpty()); + } + + @Override + public E next() { + checkModCount(); + int tempCursor = nextNotInSkipMe(cursor + 1); + if (tempCursor < size()) { + cursor = tempCursor; + canRemove = true; + return elementData(cursor); + } else if (forgetMeNot != null) { + cursor = size(); + lastFromForgetMeNot = forgetMeNot.poll(); + if (lastFromForgetMeNot != null) { + canRemove = true; + return lastFromForgetMeNot; + } + } + throw new NoSuchElementException("iterator moved past last element in queue."); + } + + @Override + public void remove() { + checkRemove(canRemove); + checkModCount(); + canRemove = false; + expectedModCount++; + if (cursor < size()) { + MoveDesc moved = removeAt(cursor); + if (moved != null) { + if (forgetMeNot == null) { + forgetMeNot = new ArrayDeque(); + skipMe = new ArrayList(3); + } + forgetMeNot.add(moved.toTrickle); + skipMe.add(moved.replaced); + } + cursor--; + } else { // we must have set lastFromForgetMeNot in next() + checkState(removeExact(lastFromForgetMeNot)); + lastFromForgetMeNot = null; + } + } + + // Finds only this exact instance, not others that are equals() + private boolean containsExact(Iterable elements, E target) { + for (E element : elements) { + if (element == target) { + return true; + } + } + return false; + } + + // Removes only this exact instance, not others that are equals() + boolean removeExact(Object target) { + for (int i = 0; i < size; i++) { + if (queue[i] == target) { + removeAt(i); + return true; + } + } + return false; + } + + void checkModCount() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + /** + * Returns the index of the first element after {@code c} that is not in + * {@code skipMe} and returns {@code size()} if there is no such element. + */ + private int nextNotInSkipMe(int c) { + if (skipMe != null) { + while (c < size() && containsExact(skipMe, elementData(c))) { + c++; + } + } + return c; + } + } + + /** + * Returns an iterator over the elements contained in this collection, in no + * particular order. + * + *

+ * The iterator is fail-fast: If the MinMaxPriorityQueue is modified at + * any time after the iterator is created, in any way except through the + * iterator's own remove method, the iterator will generally throw a + * {@link ConcurrentModificationException}. Thus, in the face of concurrent + * modification, the iterator fails quickly and cleanly, rather than risking + * arbitrary, non-deterministic behavior at an undetermined time in the future. + * + *

+ * Note that the fail-fast behavior of an iterator cannot be guaranteed as it + * is, generally speaking, impossible to make any hard guarantees in the + * presence of unsynchronized concurrent modification. Fail-fast iterators throw + * {@code ConcurrentModificationException} on a best-effort basis. Therefore, it + * would be wrong to write a program that depended on this exception for its + * correctness: the fail-fast behavior of iterators should be used only to + * detect bugs. + * + * @return an iterator over the elements contained in this collection + */ + @Override + public Iterator iterator() { + return new QueueIterator(); + } + + @Override + public void clear() { + for (int i = 0; i < size; i++) { + queue[i] = null; + } + size = 0; + } + + @Override + public Object[] toArray() { + Object[] copyTo = new Object[size]; + System.arraycopy(queue, 0, copyTo, 0, size); + return copyTo; + } + + /** + * Returns the comparator used to order the elements in this queue. Obeys the + * general contract of {@link PriorityQueue#comparator}, but returns + * {@link Ordering#natural} instead of {@code null} to indicate natural + * ordering. + */ + public Comparator comparator() { + return minHeap.ordering; + } + + @VisibleForTesting + int capacity() { + return queue.length; + } + + // Size/capacity-related methods + + private static final int DEFAULT_CAPACITY = 11; + + @VisibleForTesting + static int initialQueueSize(int configuredExpectedSize, int maximumSize, Iterable initialContents) { + // Start with what they said, if they said it, otherwise DEFAULT_CAPACITY + int result = (configuredExpectedSize == Builder.UNSET_EXPECTED_SIZE) ? DEFAULT_CAPACITY + : configuredExpectedSize; + + // Enlarge to contain initial contents + if (initialContents instanceof Collection) { + int initialSize = ((Collection) initialContents).size(); + result = Math.max(result, initialSize); + } + + // Now cap it at maxSize + 1 + return capAtMaximumSize(result, maximumSize); + } + + private void growIfNeeded() { + if (size > queue.length) { + int newCapacity = calculateNewCapacity(); + Object[] newQueue = new Object[newCapacity]; + System.arraycopy(queue, 0, newQueue, 0, queue.length); + queue = newQueue; + } + } + + /** Returns ~2x the old capacity if small; ~1.5x otherwise. */ + private int calculateNewCapacity() { + int oldCapacity = queue.length; + int newCapacity = (oldCapacity < 64) ? (oldCapacity + 1) * 2 : IntMath.checkedMultiply(oldCapacity / 2, 3); + return capAtMaximumSize(newCapacity, maximumSize); + } + + /** There's no reason for the queueSize to ever be more than maxSize + 1 */ + private static int capAtMaximumSize(int queueSize, int maximumSize) { + return Math.min(queueSize - 1, maximumSize) + 1; // don't overflow + } +} diff --git a/sources/main/java/com/google/common/collect/Multimap.java b/sources/main/java/com/google/common/collect/Multimap.java new file mode 100644 index 0000000..0c3f3c6 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Multimap.java @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A collection that maps keys to values, similar to {@link Map}, but in which + * each key may be associated with multiple values. You can visualize the + * contents of a multimap either as a map from keys to nonempty + * collections of values: + * + *

    + *
  • a → 1, 2 + *
  • b → 3 + *
+ * + * ... or as a single "flattened" collection of key-value pairs: + * + *
    + *
  • a → 1 + *
  • a → 2 + *
  • b → 3 + *
+ * + *

+ * Important: although the first interpretation resembles how most + * multimaps are implemented, the design of the {@code Multimap} API is + * based on the second form. So, using the multimap shown above as an + * example, the {@link #size} is {@code 3}, not {@code 2}, and the + * {@link #values} collection is {@code [1, 2, 3]}, not {@code [[1, 2], [3]]}. + * For those times when the first style is more useful, use the multimap's + * {@link #asMap} view (or create a {@code Map>} in the first + * place). + * + *

Example

+ * + *

+ * The following code: + * + *

+ * {
+ * 	@code
+ *
+ * 	ListMultimap multimap = ArrayListMultimap.create();
+ * 	for (President pres : US_PRESIDENTS_IN_ORDER) {
+ * 		multimap.put(pres.firstName(), pres.lastName());
+ * 	}
+ * 	for (String firstName : multimap.keySet()) {
+ * 		List lastNames = multimap.get(firstName);
+ * 		out.println(firstName + ": " + lastNames);
+ * 	}
+ * }
+ * 
+ * + * ... produces output such as: + * + *
+ *    {@code
+ *
+ *   Zachary: [Taylor]
+ *   John: [Adams, Adams, Tyler, Kennedy]  // Remember, Quincy!
+ *   George: [Washington, Bush, Bush]
+ *   Grover: [Cleveland, Cleveland]        // Two, non-consecutive terms, rep'ing NJ!
+ *   ...}
+ * 
+ * + *

Views

+ * + *

+ * Much of the power of the multimap API comes from the view collections + * it provides. These always reflect the latest state of the multimap itself. + * When they support modification, the changes are write-through (they + * automatically update the backing multimap). These view collections are: + * + *

    + *
  • {@link #asMap}, mentioned above
  • + *
  • {@link #keys}, {@link #keySet}, {@link #values}, {@link #entries}, which + * are similar to the corresponding view collections of {@link Map} + *
  • and, notably, even the collection returned by {@link #get get(key)} is an + * active view of the values corresponding to {@code key} + *
+ * + *

+ * The collections returned by the {@link #replaceValues replaceValues} and + * {@link #removeAll removeAll} methods, which contain values that have just + * been removed from the multimap, are naturally not views. + * + *

Subinterfaces

+ * + *

+ * Instead of using the {@code Multimap} interface directly, prefer the + * subinterfaces {@link ListMultimap} and {@link SetMultimap}. These take their + * names from the fact that the collections they return from {@code get} behave + * like (and, of course, implement) {@link List} and {@link Set}, respectively. + * + *

+ * For example, the "presidents" code snippet above used a {@code + * ListMultimap}; if it had used a {@code SetMultimap} instead, two presidents + * would have vanished, and last names might or might not appear in + * chronological order. + * + *

+ * Warning: instances of type {@code Multimap} may not implement + * {@link Object#equals} in the way you expect (multimaps containing the same + * key-value pairs, even in the same order, may or may not be equal). The + * recommended subinterfaces provide a much stronger guarantee. + * + *

Comparison to a map of collections

+ * + *

+ * Multimaps are commonly used in places where a {@code Map>} would otherwise have appeared. The differences include: + * + *

    + *
  • There is no need to populate an empty collection before adding an entry + * with {@link #put put}. + *
  • {@code get} never returns {@code null}, only an empty collection. + *
  • A key is contained in the multimap if and only if it maps to at least one + * value. Any operation that causes a key to have zero associated values has the + * effect of removing that key from the multimap. + *
  • The total entry count is available as {@link #size}. + *
  • Many complex operations become easier; for example, {@code + * Collections.min(multimap.values())} finds the smallest value across all + * keys. + *
+ * + *

Implementations

+ * + *

+ * As always, prefer the immutable implementations, + * {@link ImmutableListMultimap} and {@link ImmutableSetMultimap}. + * General-purpose mutable implementations are listed above under "All Known + * Implementing Classes". You can also create a custom multimap, backed + * by any {@code + * Map} and {@link Collection} types, using the {@link Multimaps#newMultimap + * Multimaps.newMultimap} family of methods. Finally, another popular way to + * obtain a multimap is using {@link Multimaps#index Multimaps.index}. See the + * {@link Multimaps} class for these and other static utilities related to + * multimaps. + * + *

Other Notes

+ * + *

+ * As with {@code Map}, the behavior of a {@code Multimap} is not specified if + * key objects already present in the multimap change in a manner that affects + * {@code equals} comparisons. Use caution if mutable objects are used as keys + * in a {@code Multimap}. + * + *

+ * All methods that modify the multimap are optional. The view collections + * returned by the multimap may or may not be modifiable. Any modification + * method that is not supported will throw + * {@link UnsupportedOperationException}. + * + *

+ * See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface Multimap { + // Query Operations + + /** + * Returns the number of key-value pairs in this multimap. + * + *

+ * Note: this method does not return the number of distinct keys + * in the multimap, which is given by {@code keySet().size()} or + * {@code asMap().size()}. See the opening section of the {@link Multimap} class + * documentation for clarification. + */ + int size(); + + /** + * Returns {@code true} if this multimap contains no key-value pairs. Equivalent + * to {@code size() == 0}, but can in some cases be more efficient. + */ + boolean isEmpty(); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair + * with the key {@code key}. + */ + boolean containsKey(@Nullable Object key); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair + * with the value {@code value}. + */ + boolean containsValue(@Nullable Object value); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair + * with the key {@code key} and the value {@code value}. + */ + boolean containsEntry(@Nullable Object key, @Nullable Object value); + + // Modification Operations + + /** + * Stores a key-value pair in this multimap. + * + *

+ * Some multimap implementations allow duplicate key-value pairs, in which case + * {@code put} always adds a new key-value pair and increases the multimap size + * by 1. Other implementations prohibit duplicates, and storing a key-value pair + * that's already in the multimap has no effect. + * + * @return {@code true} if the method increased the size of the multimap, or + * {@code false} if the multimap already contained the key-value pair + * and doesn't allow duplicates + */ + boolean put(@Nullable K key, @Nullable V value); + + /** + * Removes a single key-value pair with the key {@code key} and the value + * {@code value} from this multimap, if such exists. If multiple key-value pairs + * in the multimap fit this description, which one is removed is unspecified. + * + * @return {@code true} if the multimap changed + */ + boolean remove(@Nullable Object key, @Nullable Object value); + + // Bulk Operations + + /** + * Stores a key-value pair in this multimap for each of {@code values}, all + * using the same key, {@code key}. Equivalent to (but expected to be more + * efficient than): + * + *

+	 *    {@code
+	 * 
+	 *   for (V value : values) {
+	 *     put(key, value);
+	 *   }}
+	 * 
+ * + *

+ * In particular, this is a no-op if {@code values} is empty. + * + * @return {@code true} if the multimap changed + */ + boolean putAll(@Nullable K key, Iterable values); + + /** + * Stores all key-value pairs of {@code multimap} in this multimap, in the order + * returned by {@code multimap.entries()}. + * + * @return {@code true} if the multimap changed + */ + boolean putAll(Multimap multimap); + + /** + * Stores a collection of values with the same key, replacing any existing + * values for that key. + * + *

+ * If {@code values} is empty, this is equivalent to {@link #removeAll(Object) + * removeAll(key)}. + * + * @return the collection of replaced values, or an empty collection if no + * values were previously associated with the key. The collection + * may be modifiable, but updating it will have no effect on the + * multimap. + */ + Collection replaceValues(@Nullable K key, Iterable values); + + /** + * Removes all values associated with the key {@code key}. + * + *

+ * Once this method returns, {@code key} will not be mapped to any values, so it + * will not appear in {@link #keySet()}, {@link #asMap()}, or any other views. + * + * @return the values that were removed (possibly empty). The returned + * collection may be modifiable, but updating it will have no + * effect on the multimap. + */ + Collection removeAll(@Nullable Object key); + + /** + * Removes all key-value pairs from the multimap, leaving it + * {@linkplain #isEmpty empty}. + */ + void clear(); + + // Views + + /** + * Returns a view collection of the values associated with {@code key} in this + * multimap, if any. Note that when {@code containsKey(key)} is false, this + * returns an empty collection, not {@code null}. + * + *

+ * Changes to the returned collection will update the underlying multimap, and + * vice versa. + */ + Collection get(@Nullable K key); + + /** + * Returns a view collection of all distinct keys contained in this + * multimap. Note that the key set contains a key if and only if this multimap + * maps that key to at least one value. + * + *

+ * Changes to the returned set will update the underlying multimap, and vice + * versa. However, adding to the returned set is not possible. + */ + Set keySet(); + + /** + * Returns a view collection containing the key from each key-value pair in this + * multimap, without collapsing duplicates. This collection has the same + * size as this multimap, and {@code keys().count(k) == + * get(k).size()} for all {@code k}. + * + *

+ * Changes to the returned multiset will update the underlying multimap, and + * vice versa. However, adding to the returned collection is not + * possible. + */ + Multiset keys(); + + /** + * Returns a view collection containing the value from each key-value + * pair contained in this multimap, without collapsing duplicates (so {@code + * values().size() == size()}). + * + *

+ * Changes to the returned collection will update the underlying multimap, and + * vice versa. However, adding to the returned collection is not + * possible. + */ + Collection values(); + + /** + * Returns a view collection of all key-value pairs contained in this multimap, + * as {@link Map.Entry} instances. + * + *

+ * Changes to the returned collection or the entries it contains will update the + * underlying multimap, and vice versa. However, adding to the returned + * collection is not possible. + */ + Collection> entries(); + + /** + * Returns a view of this multimap as a {@code Map} from each distinct key to + * the nonempty collection of that key's associated values. Note that + * {@code this.asMap().get(k)} is equivalent to {@code this.get(k)} only when + * {@code k} is a key contained in the multimap; otherwise it returns {@code + * null} as opposed to an empty collection. + * + *

+ * Changes to the returned map or the collections that serve as its values will + * update the underlying multimap, and vice versa. The map does not support + * {@code put} or {@code putAll}, nor do its entries support + * {@link Map.Entry#setValue setValue}. + */ + Map> asMap(); + + // Comparison and hashing + + /** + * Compares the specified object with this multimap for equality. Two multimaps + * are equal when their map views, as returned by {@link #asMap}, are also + * equal. + * + *

+ * In general, two multimaps with identical key-value mappings may or may not be + * equal, depending on the implementation. For example, two {@link SetMultimap} + * instances with the same key-value mappings are equal, but equality of two + * {@link ListMultimap} instances depends on the ordering of the values for each + * key. + * + *

+ * A non-empty {@link SetMultimap} cannot be equal to a non-empty + * {@link ListMultimap}, since their {@link #asMap} views contain unequal + * collections as values. However, any two empty multimaps are equal, because + * they both have empty {@link #asMap} views. + */ + @Override + boolean equals(@Nullable Object obj); + + /** + * Returns the hash code for this multimap. + * + *

+ * The hash code of a multimap is defined as the hash code of the map view, as + * returned by {@link Multimap#asMap}. + */ + @Override + int hashCode(); +} diff --git a/sources/main/java/com/google/common/collect/MultimapBuilder.java b/sources/main/java/com/google/common/collect/MultimapBuilder.java new file mode 100644 index 0000000..1ca9caa --- /dev/null +++ b/sources/main/java/com/google/common/collect/MultimapBuilder.java @@ -0,0 +1,504 @@ +/* + * 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 com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Supplier; + +/** + * A builder for a multimap implementation that allows customization of the + * backing map and value collection implementations used in a particular + * multimap. + * + *

+ * This can be used to easily configure multimap data structure implementations + * not provided explicitly in {@code com.google.common.collect}, for example: + * + *

+ * {
+ * 	@code
+ * 	ListMultimap treeListMultimap = MultimapBuilder.treeKeys().arrayListValues().build();
+ * 	SetMultimap hashEnumMultimap = MultimapBuilder.hashKeys().enumSetValues(MyEnum.class).build();
+ * }
+ * 
+ * + *

+ * {@code MultimapBuilder} instances are immutable. Invoking a configuration + * method has no effect on the receiving instance; you must store and use the + * new builder instance it returns instead. + * + *

+ * The generated multimaps are serializable if the key and value types are + * serializable, unless stated otherwise in one of the configuration methods. + * + * @author Louis Wasserman + * @param An upper bound on the key type of the generated multimap. + * @param An upper bound on the value type of the generated multimap. + * @since 16.0 + */ +@Beta +@GwtCompatible +public abstract class MultimapBuilder { + /* + * Leaving K and V as upper bounds rather than the actual key and value types + * allows type parameters to be left implicit more often. CacheBuilder uses the + * same technique. + */ + + private MultimapBuilder() { + } + + private static final int DEFAULT_EXPECTED_KEYS = 8; + + /** + * Uses a {@link HashMap} to map keys to value collections. + */ + public static MultimapBuilderWithKeys hashKeys() { + return hashKeys(DEFAULT_EXPECTED_KEYS); + } + + /** + * Uses a {@link HashMap} to map keys to value collections, initialized to + * expect the specified number of keys. + * + * @throws IllegalArgumentException if {@code expectedKeys < 0} + */ + public static MultimapBuilderWithKeys hashKeys(final int expectedKeys) { + checkNonnegative(expectedKeys, "expectedKeys"); + return new MultimapBuilderWithKeys() { + @Override + Map> createMap() { + return new HashMap>(expectedKeys); + } + }; + } + + /** + * Uses a {@link LinkedHashMap} to map keys to value collections. + * + *

+ * The collections returned by {@link Multimap#keySet()}, + * {@link Multimap#keys()}, and {@link Multimap#asMap()} will iterate through + * the keys in the order that they were first added to the multimap, save that + * if all values associated with a key are removed and then the key is added + * back into the multimap, that key will come last in the key iteration order. + */ + public static MultimapBuilderWithKeys linkedHashKeys() { + return linkedHashKeys(DEFAULT_EXPECTED_KEYS); + } + + /** + * Uses a {@link LinkedHashMap} to map keys to value collections, initialized to + * expect the specified number of keys. + * + *

+ * The collections returned by {@link Multimap#keySet()}, + * {@link Multimap#keys()}, and {@link Multimap#asMap()} will iterate through + * the keys in the order that they were first added to the multimap, save that + * if all values associated with a key are removed and then the key is added + * back into the multimap, that key will come last in the key iteration order. + */ + public static MultimapBuilderWithKeys linkedHashKeys(final int expectedKeys) { + checkNonnegative(expectedKeys, "expectedKeys"); + return new MultimapBuilderWithKeys() { + @Override + Map> createMap() { + return new LinkedHashMap>(expectedKeys); + } + }; + } + + /** + * Uses a naturally-ordered {@link TreeMap} to map keys to value collections. + * + *

+ * The collections returned by {@link Multimap#keySet()}, + * {@link Multimap#keys()}, and {@link Multimap#asMap()} will iterate through + * the keys in sorted order. + * + *

+ * For all multimaps generated by the resulting builder, the + * {@link Multimap#keySet()} can be safely cast to a + * {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be + * cast to a {@link java.util.SortedMap}. + */ + @SuppressWarnings("rawtypes") + public static MultimapBuilderWithKeys treeKeys() { + return treeKeys(Ordering.natural()); + } + + /** + * Uses a {@link TreeMap} sorted by the specified comparator to map keys to + * value collections. + * + *

+ * The collections returned by {@link Multimap#keySet()}, + * {@link Multimap#keys()}, and {@link Multimap#asMap()} will iterate through + * the keys in sorted order. + * + *

+ * For all multimaps generated by the resulting builder, the + * {@link Multimap#keySet()} can be safely cast to a + * {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be + * cast to a {@link java.util.SortedMap}. + * + *

+ * Multimaps generated by the resulting builder will not be serializable if + * {@code comparator} is not serializable. + */ + public static MultimapBuilderWithKeys treeKeys(final Comparator comparator) { + checkNotNull(comparator); + return new MultimapBuilderWithKeys() { + @Override + Map> createMap() { + return new TreeMap>(comparator); + } + }; + } + + /** + * Uses an {@link EnumMap} to map keys to value collections. + */ + public static > MultimapBuilderWithKeys enumKeys(final Class keyClass) { + checkNotNull(keyClass); + return new MultimapBuilderWithKeys() { + @SuppressWarnings("unchecked") + @Override + Map> createMap() { + // K must actually be K0, since enums are effectively final + // (their subclasses are inaccessible) + return (Map>) new EnumMap>(keyClass); + } + }; + } + + private static final class ArrayListSupplier implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + ArrayListSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + @Override + public List get() { + return new ArrayList(expectedValuesPerKey); + } + } + + private enum LinkedListSupplier implements Supplier> { + INSTANCE; + + public static Supplier> instance() { + // Each call generates a fresh LinkedList, which can serve as a List for any + // V. + @SuppressWarnings({ "rawtypes", "unchecked" }) + Supplier> result = (Supplier) INSTANCE; + return result; + } + + @Override + public List get() { + return new LinkedList(); + } + } + + private static final class HashSetSupplier implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + HashSetSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + @Override + public Set get() { + return new HashSet(expectedValuesPerKey); + } + } + + private static final class LinkedHashSetSupplier implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + LinkedHashSetSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + @Override + public Set get() { + return new LinkedHashSet(expectedValuesPerKey); + } + } + + private static final class TreeSetSupplier implements Supplier>, Serializable { + private final Comparator comparator; + + TreeSetSupplier(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + @Override + public SortedSet get() { + return new TreeSet(comparator); + } + } + + private static final class EnumSetSupplier> implements Supplier>, Serializable { + private final Class clazz; + + EnumSetSupplier(Class clazz) { + this.clazz = checkNotNull(clazz); + } + + @Override + public Set get() { + return EnumSet.noneOf(clazz); + } + } + + /** + * An intermediate stage in a {@link MultimapBuilder} in which the key-value + * collection map implementation has been specified, but the value collection + * implementation has not. + * + * @param The upper bound on the key type of the generated multimap. + */ + public abstract static class MultimapBuilderWithKeys { + + private static final int DEFAULT_EXPECTED_VALUES_PER_KEY = 2; + + MultimapBuilderWithKeys() { + } + + abstract Map> createMap(); + + /** + * Uses an {@link ArrayList} to store value collections. + */ + public ListMultimapBuilder arrayListValues() { + return arrayListValues(DEFAULT_EXPECTED_VALUES_PER_KEY); + } + + /** + * Uses an {@link ArrayList} to store value collections, initialized to expect + * the specified number of values per key. + * + * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} + */ + public ListMultimapBuilder arrayListValues(final int expectedValuesPerKey) { + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new ListMultimapBuilder() { + @Override + public ListMultimap build() { + return Multimaps.newListMultimap(MultimapBuilderWithKeys.this.createMap(), + new ArrayListSupplier(expectedValuesPerKey)); + } + }; + } + + /** + * Uses a {@link LinkedList} to store value collections. + */ + public ListMultimapBuilder linkedListValues() { + return new ListMultimapBuilder() { + @Override + public ListMultimap build() { + return Multimaps.newListMultimap(MultimapBuilderWithKeys.this.createMap(), + LinkedListSupplier.instance()); + } + }; + } + + /** + * Uses a {@link HashSet} to store value collections. + */ + public SetMultimapBuilder hashSetValues() { + return hashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); + } + + /** + * Uses a {@link HashSet} to store value collections, initialized to expect the + * specified number of values per key. + * + * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} + */ + public SetMultimapBuilder hashSetValues(final int expectedValuesPerKey) { + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new SetMultimapBuilder() { + @Override + public SetMultimap build() { + return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), + new HashSetSupplier(expectedValuesPerKey)); + } + }; + } + + /** + * Uses a {@link LinkedHashSet} to store value collections. + */ + public SetMultimapBuilder linkedHashSetValues() { + return linkedHashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); + } + + /** + * Uses a {@link LinkedHashSet} to store value collections, initialized to + * expect the specified number of values per key. + * + * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} + */ + public SetMultimapBuilder linkedHashSetValues(final int expectedValuesPerKey) { + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new SetMultimapBuilder() { + @Override + public SetMultimap build() { + return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), + new LinkedHashSetSupplier(expectedValuesPerKey)); + } + }; + } + + /** + * Uses a naturally-ordered {@link TreeSet} to store value collections. + */ + @SuppressWarnings("rawtypes") + public SortedSetMultimapBuilder treeSetValues() { + return treeSetValues(Ordering.natural()); + } + + /** + * Uses a {@link TreeSet} ordered by the specified comparator to store value + * collections. + * + *

+ * Multimaps generated by the resulting builder will not be serializable if + * {@code comparator} is not serializable. + */ + public SortedSetMultimapBuilder treeSetValues(final Comparator comparator) { + checkNotNull(comparator, "comparator"); + return new SortedSetMultimapBuilder() { + @Override + public SortedSetMultimap build() { + return Multimaps.newSortedSetMultimap(MultimapBuilderWithKeys.this.createMap(), + new TreeSetSupplier(comparator)); + } + }; + } + + /** + * Uses an {@link EnumSet} to store value collections. + */ + public > SetMultimapBuilder enumSetValues(final Class valueClass) { + checkNotNull(valueClass, "valueClass"); + return new SetMultimapBuilder() { + @Override + public SetMultimap build() { + // V must actually be V0, since enums are effectively final + // (their subclasses are inaccessible) + @SuppressWarnings({ "unchecked", "rawtypes" }) + Supplier> factory = (Supplier) new EnumSetSupplier(valueClass); + return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), factory); + } + }; + } + } + + /** + * Returns a new, empty {@code Multimap} with the specified implementation. + */ + public abstract Multimap build(); + + /** + * Returns a {@code Multimap} with the specified implementation, initialized + * with the entries of {@code multimap}. + */ + public Multimap build(Multimap multimap) { + Multimap result = build(); + result.putAll(multimap); + return result; + } + + /** + * A specialization of {@link MultimapBuilder} that generates + * {@link ListMultimap} instances. + */ + public abstract static class ListMultimapBuilder extends MultimapBuilder { + ListMultimapBuilder() { + } + + @Override + public abstract ListMultimap build(); + + @Override + public ListMultimap build(Multimap multimap) { + return (ListMultimap) super.build(multimap); + } + } + + /** + * A specialization of {@link MultimapBuilder} that generates + * {@link SetMultimap} instances. + */ + public abstract static class SetMultimapBuilder extends MultimapBuilder { + SetMultimapBuilder() { + } + + @Override + public abstract SetMultimap build(); + + @Override + public SetMultimap build(Multimap multimap) { + return (SetMultimap) super.build(multimap); + } + } + + /** + * A specialization of {@link MultimapBuilder} that generates + * {@link SortedSetMultimap} instances. + */ + public abstract static class SortedSetMultimapBuilder extends SetMultimapBuilder { + SortedSetMultimapBuilder() { + } + + @Override + public abstract SortedSetMultimap build(); + + @Override + public SortedSetMultimap build(Multimap multimap) { + return (SortedSetMultimap) super.build(multimap); + } + } +} diff --git a/sources/main/java/com/google/common/collect/Multimaps.java b/sources/main/java/com/google/common/collect/Multimaps.java new file mode 100644 index 0000000..13efdda --- /dev/null +++ b/sources/main/java/com/google/common/collect/Multimaps.java @@ -0,0 +1,2223 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import com.google.common.collect.Maps.EntryTransformer; + +/** + * Provides static methods acting on or generating a {@code Multimap}. + * + *

+ * See the Guava User Guide article on + * {@code Multimaps}. + * + * @author Jared Levy + * @author Robert Konigsberg + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public final class Multimaps { + private Multimaps() { + } + + /** + * Creates a new {@code Multimap} backed by {@code map}, whose internal value + * collections are generated by {@code factory}. + * + * Warning: do not use this method when the collections returned by + * {@code factory} implement either {@link List} or {@code Set}! Use the more + * specific method {@link #newListMultimap}, {@link #newSetMultimap} or + * {@link #newSortedSetMultimap} instead, to avoid very surprising behavior from + * {@link Multimap#equals}. + * + *

+ * The {@code factory}-generated and {@code map} classes determine the multimap + * iteration order. They also specify the behavior of the {@code equals}, + * {@code hashCode}, and {@code toString} methods for the multimap and its + * returned views. However, the multimap's {@code get} method returns instances + * of a different class than {@code factory.get()} does. + * + *

+ * The multimap is serializable if {@code map}, {@code factory}, the collections + * generated by {@code factory}, and the multimap contents are all serializable. + * + *

+ * The multimap is not threadsafe when any concurrent operations update the + * multimap, even if {@code map} and the instances generated by {@code factory} + * are. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap the multimap with a call to + * {@link #synchronizedMultimap}. + * + *

+ * Call this method only when the simpler methods + * {@link ArrayListMultimap#create()}, {@link HashMultimap#create()}, + * {@link LinkedHashMultimap#create()}, {@link LinkedListMultimap#create()}, + * {@link TreeMultimap#create()}, and + * {@link TreeMultimap#create(Comparator, Comparator)} won't suffice. + * + *

+ * Note: the multimap assumes complete ownership over of {@code map} and the + * collections returned by {@code factory}. Those objects should not be manually + * updated and they should not use soft, weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding + * values + * @param factory supplier of new, empty collections that will each hold all + * values for a given key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static Multimap newMultimap(Map> map, + final Supplier> factory) { + return new CustomMultimap(map, factory); + } + + private static class CustomMultimap extends AbstractMapBasedMultimap { + transient Supplier> factory; + + CustomMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + } + + @Override + protected Collection createCollection() { + return factory.get(); + } + + // can't use Serialization writeMultimap and populateMultimap methods since + // there's no way to generate the empty backing map. + + /** @serialData the factory and the backing map */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + + @GwtIncompatible("java serialization not supported") + private static final long serialVersionUID = 0; + } + + /** + * Creates a new {@code ListMultimap} that uses the provided map and factory. It + * can generate a multimap based on arbitrary {@link Map} and {@link List} + * classes. + * + *

+ * The {@code factory}-generated and {@code map} classes determine the multimap + * iteration order. They also specify the behavior of the {@code equals}, + * {@code hashCode}, and {@code toString} methods for the multimap and its + * returned views. The multimap's {@code get}, {@code + * removeAll}, and {@code replaceValues} methods return {@code RandomAccess} + * lists if the factory does. However, the multimap's {@code get} method returns + * instances of a different class than does {@code factory.get()}. + * + *

+ * The multimap is serializable if {@code map}, {@code factory}, the lists + * generated by {@code factory}, and the multimap contents are all serializable. + * + *

+ * The multimap is not threadsafe when any concurrent operations update the + * multimap, even if {@code map} and the instances generated by {@code factory} + * are. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap the multimap with a call to + * {@link #synchronizedListMultimap}. + * + *

+ * Call this method only when the simpler methods + * {@link ArrayListMultimap#create()} and {@link LinkedListMultimap#create()} + * won't suffice. + * + *

+ * Note: the multimap assumes complete ownership over of {@code map} and the + * lists returned by {@code factory}. Those objects should not be manually + * updated, they should be empty when provided, and they should not use soft, + * weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding + * values + * @param factory supplier of new, empty lists that will each hold all values + * for a given key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static ListMultimap newListMultimap(Map> map, + final Supplier> factory) { + return new CustomListMultimap(map, factory); + } + + private static class CustomListMultimap extends AbstractListMultimap { + transient Supplier> factory; + + CustomListMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + } + + @Override + protected List createCollection() { + return factory.get(); + } + + /** @serialData the factory and the backing map */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + + @GwtIncompatible("java serialization not supported") + private static final long serialVersionUID = 0; + } + + /** + * Creates a new {@code SetMultimap} that uses the provided map and factory. It + * can generate a multimap based on arbitrary {@link Map} and {@link Set} + * classes. + * + *

+ * The {@code factory}-generated and {@code map} classes determine the multimap + * iteration order. They also specify the behavior of the {@code equals}, + * {@code hashCode}, and {@code toString} methods for the multimap and its + * returned views. However, the multimap's {@code get} method returns instances + * of a different class than {@code factory.get()} does. + * + *

+ * The multimap is serializable if {@code map}, {@code factory}, the sets + * generated by {@code factory}, and the multimap contents are all serializable. + * + *

+ * The multimap is not threadsafe when any concurrent operations update the + * multimap, even if {@code map} and the instances generated by {@code factory} + * are. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap the multimap with a call to + * {@link #synchronizedSetMultimap}. + * + *

+ * Call this method only when the simpler methods {@link HashMultimap#create()}, + * {@link LinkedHashMultimap#create()}, {@link TreeMultimap#create()}, and + * {@link TreeMultimap#create(Comparator, Comparator)} won't suffice. + * + *

+ * Note: the multimap assumes complete ownership over of {@code map} and the + * sets returned by {@code factory}. Those objects should not be manually + * updated and they should not use soft, weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding + * values + * @param factory supplier of new, empty sets that will each hold all values for + * a given key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static SetMultimap newSetMultimap(Map> map, + final Supplier> factory) { + return new CustomSetMultimap(map, factory); + } + + private static class CustomSetMultimap extends AbstractSetMultimap { + transient Supplier> factory; + + CustomSetMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + } + + @Override + protected Set createCollection() { + return factory.get(); + } + + /** @serialData the factory and the backing map */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0; + } + + /** + * Creates a new {@code SortedSetMultimap} that uses the provided map and + * factory. It can generate a multimap based on arbitrary {@link Map} and + * {@link SortedSet} classes. + * + *

+ * The {@code factory}-generated and {@code map} classes determine the multimap + * iteration order. They also specify the behavior of the {@code equals}, + * {@code hashCode}, and {@code toString} methods for the multimap and its + * returned views. However, the multimap's {@code get} method returns instances + * of a different class than {@code factory.get()} does. + * + *

+ * The multimap is serializable if {@code map}, {@code factory}, the sets + * generated by {@code factory}, and the multimap contents are all serializable. + * + *

+ * The multimap is not threadsafe when any concurrent operations update the + * multimap, even if {@code map} and the instances generated by {@code factory} + * are. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap the multimap with a call to + * {@link #synchronizedSortedSetMultimap}. + * + *

+ * Call this method only when the simpler methods {@link TreeMultimap#create()} + * and {@link TreeMultimap#create(Comparator, Comparator)} won't suffice. + * + *

+ * Note: the multimap assumes complete ownership over of {@code map} and the + * sets returned by {@code factory}. Those objects should not be manually + * updated and they should not use soft, weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding + * values + * @param factory supplier of new, empty sorted sets that will each hold all + * values for a given key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static SortedSetMultimap newSortedSetMultimap(Map> map, + final Supplier> factory) { + return new CustomSortedSetMultimap(map, factory); + } + + private static class CustomSortedSetMultimap extends AbstractSortedSetMultimap { + transient Supplier> factory; + transient Comparator valueComparator; + + CustomSortedSetMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + valueComparator = factory.get().comparator(); + } + + @Override + protected SortedSet createCollection() { + return factory.get(); + } + + @Override + public Comparator valueComparator() { + return valueComparator; + } + + /** @serialData the factory and the backing map */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + valueComparator = factory.get().comparator(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0; + } + + /** + * Copies each key-value mapping in {@code source} into {@code dest}, with its + * key and value reversed. + * + *

+ * If {@code source} is an {@link ImmutableMultimap}, consider using + * {@link ImmutableMultimap#inverse} instead. + * + * @param source any multimap + * @param dest the multimap to copy into; usually empty + * @return {@code dest} + */ + public static > M invertFrom(Multimap source, M dest) { + checkNotNull(dest); + for (Map.Entry entry : source.entries()) { + dest.put(entry.getValue(), entry.getKey()); + } + return dest; + } + + /** + * Returns a synchronized (thread-safe) multimap backed by the specified + * multimap. In order to guarantee serial access, it is critical that all + * access to the backing multimap is accomplished through the returned multimap. + * + *

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

+	 *    {@code
+	 *
+	 *   Multimap multimap = Multimaps.synchronizedMultimap(
+	 *       HashMultimap.create());
+	 *   ...
+	 *   Collection values = multimap.get(key);  // Needn't be in synchronized block
+	 *   ...
+	 *   synchronized (multimap) {  // Synchronizing on multimap, not values!
+	 *     Iterator i = values.iterator(); // Must be in synchronized block
+	 *     while (i.hasNext()) {
+	 *       foo(i.next());
+	 *     }
+	 *   }}
+	 * 
+ * + *

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

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that aren't + * synchronized. + * + *

+ * The returned multimap will be serializable if the specified multimap is + * serializable. + * + * @param multimap the multimap to be wrapped in a synchronized view + * @return a synchronized view of the specified multimap + */ + public static Multimap synchronizedMultimap(Multimap multimap) { + return Synchronized.multimap(multimap, null); + } + + /** + * Returns an unmodifiable view of the specified multimap. Query operations on + * the returned multimap "read through" to the specified multimap, and attempts + * to modify the returned multimap, either directly or through the multimap's + * views, result in an {@code UnsupportedOperationException}. + * + *

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that are + * modifiable. + * + *

+ * The returned multimap will be serializable if the specified multimap is + * serializable. + * + * @param delegate the multimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified multimap + */ + public static Multimap unmodifiableMultimap(Multimap delegate) { + if (delegate instanceof UnmodifiableMultimap || delegate instanceof ImmutableMultimap) { + return delegate; + } + return new UnmodifiableMultimap(delegate); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static Multimap unmodifiableMultimap(ImmutableMultimap delegate) { + return checkNotNull(delegate); + } + + private static class UnmodifiableMultimap extends ForwardingMultimap implements Serializable { + final Multimap delegate; + transient Collection> entries; + transient Multiset keys; + transient Set keySet; + transient Collection values; + transient Map> map; + + UnmodifiableMultimap(final Multimap delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + protected Multimap delegate() { + return delegate; + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Map> asMap() { + Map> result = map; + if (result == null) { + result = map = Collections.unmodifiableMap( + Maps.transformValues(delegate.asMap(), new Function, Collection>() { + @Override + public Collection apply(Collection collection) { + return unmodifiableValueCollection(collection); + } + })); + } + return result; + } + + @Override + public Collection> entries() { + Collection> result = entries; + if (result == null) { + entries = result = unmodifiableEntries(delegate.entries()); + } + return result; + } + + @Override + public Collection get(K key) { + return unmodifiableValueCollection(delegate.get(key)); + } + + @Override + public Multiset keys() { + Multiset result = keys; + if (result == null) { + keys = result = Multisets.unmodifiableMultiset(delegate.keys()); + } + return result; + } + + @Override + public Set keySet() { + Set result = keySet; + if (result == null) { + keySet = result = Collections.unmodifiableSet(delegate.keySet()); + } + return result; + } + + @Override + public boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection values() { + Collection result = values; + if (result == null) { + values = result = Collections.unmodifiableCollection(delegate.values()); + } + return result; + } + + private static final long serialVersionUID = 0; + } + + private static class UnmodifiableListMultimap extends UnmodifiableMultimap + implements ListMultimap { + UnmodifiableListMultimap(ListMultimap delegate) { + super(delegate); + } + + @Override + public ListMultimap delegate() { + return (ListMultimap) super.delegate(); + } + + @Override + public List get(K key) { + return Collections.unmodifiableList(delegate().get(key)); + } + + @Override + public List removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public List replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + private static final long serialVersionUID = 0; + } + + private static class UnmodifiableSetMultimap extends UnmodifiableMultimap implements SetMultimap { + UnmodifiableSetMultimap(SetMultimap delegate) { + super(delegate); + } + + @Override + public SetMultimap delegate() { + return (SetMultimap) super.delegate(); + } + + @Override + public Set get(K key) { + /* + * Note that this doesn't return a SortedSet when delegate is a + * SortedSetMultiset, unlike (SortedSet) super.get(). + */ + return Collections.unmodifiableSet(delegate().get(key)); + } + + @Override + public Set> entries() { + return Maps.unmodifiableEntrySet(delegate().entries()); + } + + @Override + public Set removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public Set replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + private static final long serialVersionUID = 0; + } + + private static class UnmodifiableSortedSetMultimap extends UnmodifiableSetMultimap + implements SortedSetMultimap { + UnmodifiableSortedSetMultimap(SortedSetMultimap delegate) { + super(delegate); + } + + @Override + public SortedSetMultimap delegate() { + return (SortedSetMultimap) super.delegate(); + } + + @Override + public SortedSet get(K key) { + return Collections.unmodifiableSortedSet(delegate().get(key)); + } + + @Override + public SortedSet removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public SortedSet replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public Comparator valueComparator() { + return delegate().valueComparator(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a synchronized (thread-safe) {@code SetMultimap} backed by the + * specified multimap. + * + *

+ * You must follow the warnings described in {@link #synchronizedMultimap}. + * + *

+ * The returned multimap will be serializable if the specified multimap is + * serializable. + * + * @param multimap the multimap to be wrapped + * @return a synchronized view of the specified multimap + */ + public static SetMultimap synchronizedSetMultimap(SetMultimap multimap) { + return Synchronized.setMultimap(multimap, null); + } + + /** + * Returns an unmodifiable view of the specified {@code SetMultimap}. Query + * operations on the returned multimap "read through" to the specified multimap, + * and attempts to modify the returned multimap, either directly or through the + * multimap's views, result in an {@code UnsupportedOperationException}. + * + *

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that are + * modifiable. + * + *

+ * The returned multimap will be serializable if the specified multimap is + * serializable. + * + * @param delegate the multimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified multimap + */ + public static SetMultimap unmodifiableSetMultimap(SetMultimap delegate) { + if (delegate instanceof UnmodifiableSetMultimap || delegate instanceof ImmutableSetMultimap) { + return delegate; + } + return new UnmodifiableSetMultimap(delegate); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static SetMultimap unmodifiableSetMultimap(ImmutableSetMultimap delegate) { + return checkNotNull(delegate); + } + + /** + * Returns a synchronized (thread-safe) {@code SortedSetMultimap} backed by the + * specified multimap. + * + *

+ * You must follow the warnings described in {@link #synchronizedMultimap}. + * + *

+ * The returned multimap will be serializable if the specified multimap is + * serializable. + * + * @param multimap the multimap to be wrapped + * @return a synchronized view of the specified multimap + */ + public static SortedSetMultimap synchronizedSortedSetMultimap(SortedSetMultimap multimap) { + return Synchronized.sortedSetMultimap(multimap, null); + } + + /** + * Returns an unmodifiable view of the specified {@code SortedSetMultimap}. + * Query operations on the returned multimap "read through" to the specified + * multimap, and attempts to modify the returned multimap, either directly or + * through the multimap's views, result in an + * {@code UnsupportedOperationException}. + * + *

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that are + * modifiable. + * + *

+ * The returned multimap will be serializable if the specified multimap is + * serializable. + * + * @param delegate the multimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified multimap + */ + public static SortedSetMultimap unmodifiableSortedSetMultimap(SortedSetMultimap delegate) { + if (delegate instanceof UnmodifiableSortedSetMultimap) { + return delegate; + } + return new UnmodifiableSortedSetMultimap(delegate); + } + + /** + * Returns a synchronized (thread-safe) {@code ListMultimap} backed by the + * specified multimap. + * + *

+ * You must follow the warnings described in {@link #synchronizedMultimap}. + * + * @param multimap the multimap to be wrapped + * @return a synchronized view of the specified multimap + */ + public static ListMultimap synchronizedListMultimap(ListMultimap multimap) { + return Synchronized.listMultimap(multimap, null); + } + + /** + * Returns an unmodifiable view of the specified {@code ListMultimap}. Query + * operations on the returned multimap "read through" to the specified multimap, + * and attempts to modify the returned multimap, either directly or through the + * multimap's views, result in an {@code UnsupportedOperationException}. + * + *

+ * Note that the generated multimap's {@link Multimap#removeAll} and + * {@link Multimap#replaceValues} methods return collections that are + * modifiable. + * + *

+ * The returned multimap will be serializable if the specified multimap is + * serializable. + * + * @param delegate the multimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified multimap + */ + public static ListMultimap unmodifiableListMultimap(ListMultimap delegate) { + if (delegate instanceof UnmodifiableListMultimap || delegate instanceof ImmutableListMultimap) { + return delegate; + } + return new UnmodifiableListMultimap(delegate); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static ListMultimap unmodifiableListMultimap(ImmutableListMultimap delegate) { + return checkNotNull(delegate); + } + + /** + * Returns an unmodifiable view of the specified collection, preserving the + * interface for instances of {@code SortedSet}, {@code Set}, {@code List} and + * {@code Collection}, in that order of preference. + * + * @param collection the collection for which to return an unmodifiable view + * @return an unmodifiable view of the collection + */ + private static Collection unmodifiableValueCollection(Collection collection) { + if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet) collection); + } else if (collection instanceof Set) { + return Collections.unmodifiableSet((Set) collection); + } else if (collection instanceof List) { + return Collections.unmodifiableList((List) collection); + } + return Collections.unmodifiableCollection(collection); + } + + /** + * Returns an unmodifiable view of the specified collection of entries. The + * {@link Entry#setValue} operation throws an + * {@link UnsupportedOperationException}. If the specified collection is a + * {@code + * Set}, the returned collection is also a {@code Set}. + * + * @param entries the entries for which to return an unmodifiable view + * @return an unmodifiable view of the entries + */ + private static Collection> unmodifiableEntries(Collection> entries) { + if (entries instanceof Set) { + return Maps.unmodifiableEntrySet((Set>) entries); + } + return new Maps.UnmodifiableEntries(Collections.unmodifiableCollection(entries)); + } + + /** + * Returns {@link ListMultimap#asMap multimap.asMap()}, with its type corrected + * from {@code Map>} to {@code Map>}. + * + * @since 15.0 + */ + @Beta + @SuppressWarnings("unchecked") + // safe by specification of ListMultimap.asMap() + public static Map> asMap(ListMultimap multimap) { + return (Map>) (Map) multimap.asMap(); + } + + /** + * Returns {@link SetMultimap#asMap multimap.asMap()}, with its type corrected + * from {@code Map>} to {@code Map>}. + * + * @since 15.0 + */ + @Beta + @SuppressWarnings("unchecked") + // safe by specification of SetMultimap.asMap() + public static Map> asMap(SetMultimap multimap) { + return (Map>) (Map) multimap.asMap(); + } + + /** + * Returns {@link SortedSetMultimap#asMap multimap.asMap()}, with its type + * corrected from {@code Map>} to {@code Map>}. + * + * @since 15.0 + */ + @Beta + @SuppressWarnings("unchecked") + // safe by specification of SortedSetMultimap.asMap() + public static Map> asMap(SortedSetMultimap multimap) { + return (Map>) (Map) multimap.asMap(); + } + + /** + * Returns {@link Multimap#asMap multimap.asMap()}. This is provided for parity + * with the other more strongly-typed {@code asMap()} implementations. + * + * @since 15.0 + */ + @Beta + public static Map> asMap(Multimap multimap) { + return multimap.asMap(); + } + + /** + * Returns a multimap view of the specified map. The multimap is backed by the + * map, so changes to the map are reflected in the multimap, and vice versa. If + * the map is modified while an iteration over one of the multimap's collection + * views is in progress (except through the iterator's own {@code + * remove} operation, or through the {@code setValue} operation on a map entry + * returned by the iterator), the results of the iteration are undefined. + * + *

+ * The multimap supports mapping removal, which removes the corresponding + * mapping from the map. It does not support any operations which might add + * mappings, such as {@code put}, {@code putAll} or {@code replaceValues}. + * + *

+ * The returned multimap will be serializable if the specified map is + * serializable. + * + * @param map the backing map for the returned multimap view + */ + public static SetMultimap forMap(Map map) { + return new MapMultimap(map); + } + + /** @see Multimaps#forMap */ + private static class MapMultimap extends AbstractMultimap implements SetMultimap, Serializable { + final Map map; + + MapMultimap(Map map) { + this.map = checkNotNull(map); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public boolean containsEntry(Object key, Object value) { + return map.entrySet().contains(Maps.immutableEntry(key, value)); + } + + @Override + public Set get(final K key) { + return new Sets.ImprovedAbstractSet() { + @Override + public Iterator iterator() { + return new Iterator() { + int i; + + @Override + public boolean hasNext() { + return (i == 0) && map.containsKey(key); + } + + @Override + public V next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + i++; + return map.get(key); + } + + @Override + public void remove() { + checkRemove(i == 1); + i = -1; + map.remove(key); + } + }; + } + + @Override + public int size() { + return map.containsKey(key) ? 1 : 0; + } + }; + } + + @Override + public boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + @Override + public Set replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object key, Object value) { + return map.entrySet().remove(Maps.immutableEntry(key, value)); + } + + @Override + public Set removeAll(Object key) { + Set values = new HashSet(2); + if (!map.containsKey(key)) { + return values; + } + values.add(map.remove(key)); + return values; + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Set keySet() { + return map.keySet(); + } + + @Override + public Collection values() { + return map.values(); + } + + @Override + public Set> entries() { + return map.entrySet(); + } + + @Override + Iterator> entryIterator() { + return map.entrySet().iterator(); + } + + @Override + Map> createAsMap() { + return new AsMap(this); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + private static final long serialVersionUID = 7845222491160860175L; + } + + /** + * Returns a view of a multimap where each value is transformed by a function. + * All other properties of the multimap, such as iteration order, are left + * intact. For example, the code: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	Multimap multimap = ImmutableSetMultimap.of("a", 2, "b", -3, "b", -3, "a", 4, "c", 6);
+	 * 	Function square = new Function() {
+	 * 		public String apply(Integer in) {
+	 * 			return Integer.toString(in * in);
+	 * 		}
+	 * 	};
+	 * 	Multimap transformed = Multimaps.transformValues(multimap, square);
+	 * 	System.out.println(transformed);
+	 * }
+	 * 
+ * + * ... prints {@code {a=[4, 16], b=[9, 9], c=[36]}}. + * + *

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

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

+ * The returned multimap is not thread-safe or serializable, even if the + * underlying multimap is. The {@code equals} and {@code hashCode} methods of + * the returned multimap are meaningless, since there is not a definition of + * {@code equals} or {@code hashCode} for general collections, and {@code get()} + * will return a general {@code Collection} as opposed to a {@code List} or a + * {@code Set}. + * + *

+ * The function is applied lazily, invoked when needed. This is necessary for + * the returned multimap to be a view, but it means that the function will be + * applied many times for bulk operations like {@link Multimap#containsValue} + * and {@code Multimap.toString()}. For this to perform well, {@code function} + * should be fast. To avoid lazy evaluation when the returned multimap doesn't + * need to be a view, copy the returned multimap into a new multimap of your + * choosing. + * + * @since 7.0 + */ + public static Multimap transformValues(Multimap fromMultimap, + final Function function) { + checkNotNull(function); + EntryTransformer transformer = Maps.asEntryTransformer(function); + return transformEntries(fromMultimap, transformer); + } + + /** + * Returns a view of a multimap whose values are derived from the original + * multimap'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 multimap, such as iteration order, + * are left intact. For example, the code: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	SetMultimap multimap = ImmutableSetMultimap.of("a", 1, "a", 4, "b", -6);
+	 * 	EntryTransformer transformer = new EntryTransformer() {
+	 * 		public String transformEntry(String key, Integer value) {
+	 * 			return (value >= 0) ? key : "no" + key;
+	 * 		}
+	 * 	};
+	 * 	Multimap transformed = Multimaps.transformEntries(multimap, transformer);
+	 * 	System.out.println(transformed);
+	 * }
+	 * 
+ * + * ... prints {@code {a=[a, a], b=[nob]}}. + * + *

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

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

+ * The returned multimap is not thread-safe or serializable, even if the + * underlying multimap is. The {@code equals} and {@code hashCode} methods of + * the returned multimap are meaningless, since there is not a definition of + * {@code equals} or {@code hashCode} for general collections, and {@code get()} + * will return a general {@code Collection} as opposed to a {@code List} or a + * {@code Set}. + * + *

+ * The transformer is applied lazily, invoked when needed. This is necessary for + * the returned multimap to be a view, but it means that the transformer will be + * applied many times for bulk operations like {@link Multimap#containsValue} + * and {@link Object#toString}. For this to perform well, {@code transformer} + * should be fast. To avoid lazy evaluation when the returned multimap doesn't + * need to be a view, copy the returned multimap into a new multimap 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 multimap. + * + * @since 7.0 + */ + public static Multimap transformEntries(Multimap fromMap, + EntryTransformer transformer) { + return new TransformedEntriesMultimap(fromMap, transformer); + } + + private static class TransformedEntriesMultimap extends AbstractMultimap { + final Multimap fromMultimap; + final EntryTransformer transformer; + + TransformedEntriesMultimap(Multimap fromMultimap, + final EntryTransformer transformer) { + this.fromMultimap = checkNotNull(fromMultimap); + this.transformer = checkNotNull(transformer); + } + + Collection transform(K key, Collection values) { + Function function = Maps.asValueToValueFunction(transformer, key); + if (values instanceof List) { + return Lists.transform((List) values, function); + } else { + return Collections2.transform(values, function); + } + } + + @Override + Map> createAsMap() { + return Maps.transformEntries(fromMultimap.asMap(), + new EntryTransformer, Collection>() { + @Override + public Collection transformEntry(K key, Collection value) { + return transform(key, value); + } + }); + } + + @Override + public void clear() { + fromMultimap.clear(); + } + + @Override + public boolean containsKey(Object key) { + return fromMultimap.containsKey(key); + } + + @Override + Iterator> entryIterator() { + return Iterators.transform(fromMultimap.entries().iterator(), + Maps.asEntryToEntryFunction(transformer)); + } + + @Override + public Collection get(final K key) { + return transform(key, fromMultimap.get(key)); + } + + @Override + public boolean isEmpty() { + return fromMultimap.isEmpty(); + } + + @Override + public Set keySet() { + return fromMultimap.keySet(); + } + + @Override + public Multiset keys() { + return fromMultimap.keys(); + } + + @Override + public boolean put(K key, V2 value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("unchecked") + @Override + public boolean remove(Object key, Object value) { + return get((K) key).remove(value); + } + + @SuppressWarnings("unchecked") + @Override + public Collection removeAll(Object key) { + return transform((K) key, fromMultimap.removeAll(key)); + } + + @Override + public Collection replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return fromMultimap.size(); + } + + @Override + Collection createValues() { + return Collections2.transform(fromMultimap.entries(), Maps.asEntryToValueFunction(transformer)); + } + } + + /** + * Returns a view of a {@code ListMultimap} where each value is transformed by a + * function. All other properties of the multimap, such as iteration order, are + * left intact. For example, the code: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	ListMultimap multimap = ImmutableListMultimap.of("a", 4, "a", 16, "b", 9);
+	 * 	Function sqrt = new Function() {
+	 * 		public Double apply(Integer in) {
+	 * 			return Math.sqrt((int) in);
+	 * 		}
+	 * 	};
+	 * 	ListMultimap transformed = Multimaps.transformValues(map, sqrt);
+	 * 	System.out.println(transformed);
+	 * }
+	 * 
+ * + * ... prints {@code {a=[2.0, 4.0], b=[3.0]}}. + * + *

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

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

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

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

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

+	 * {
+	 * 	@code
+	 *
+	 * 	Multimap multimap = ImmutableMultimap.of("a", 1, "a", 4, "b", 6);
+	 * 	EntryTransformer transformer = new EntryTransformer() {
+	 * 		public String transformEntry(String key, Integer value) {
+	 * 			return key + value;
+	 * 		}
+	 * 	};
+	 * 	Multimap transformed = Multimaps.transformEntries(multimap, transformer);
+	 * 	System.out.println(transformed);
+	 * }
+	 * 
+ * + * ... prints {@code {"a"=["a1", "a4"], "b"=["b6"]}}. + * + *

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

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

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

+ * The transformer is applied lazily, invoked when needed. This is necessary for + * the returned multimap to be a view, but it means that the transformer will be + * applied many times for bulk operations like {@link Multimap#containsValue} + * and {@link Object#toString}. For this to perform well, {@code transformer} + * should be fast. To avoid lazy evaluation when the returned multimap doesn't + * need to be a view, copy the returned multimap into a new multimap 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 multimap. + * + * @since 7.0 + */ + public static ListMultimap transformEntries(ListMultimap fromMap, + EntryTransformer transformer) { + return new TransformedEntriesListMultimap(fromMap, transformer); + } + + private static final class TransformedEntriesListMultimap extends TransformedEntriesMultimap + implements ListMultimap { + + TransformedEntriesListMultimap(ListMultimap fromMultimap, + EntryTransformer transformer) { + super(fromMultimap, transformer); + } + + @Override + List transform(K key, Collection values) { + return Lists.transform((List) values, Maps.asValueToValueFunction(transformer, key)); + } + + @Override + public List get(K key) { + return transform(key, fromMultimap.get(key)); + } + + @SuppressWarnings("unchecked") + @Override + public List removeAll(Object key) { + return transform((K) key, fromMultimap.removeAll(key)); + } + + @Override + public List replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + } + + /** + * Creates an index {@code ImmutableListMultimap} that contains the results of + * applying a specified function to each item in an {@code Iterable} of values. + * Each value will be stored as a value in the resulting multimap, yielding a + * multimap with the same size as the input iterable. The key used to store that + * value in the multimap will be the result of calling the function on that + * value. The resulting multimap is created as an immutable snapshot. In the + * returned multimap, keys appear in the order they are first encountered, and + * the values corresponding to each key appear in the same order as they are + * encountered. + * + *

+ * For example, + * + *

+	 *    {@code
+	 *
+	 *   List badGuys =
+	 *       Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
+	 *   Function stringLengthFunction = ...;
+	 *   Multimap index =
+	 *       Multimaps.index(badGuys, stringLengthFunction);
+	 *   System.out.println(index);}
+	 * 
+ * + *

+ * prints + * + *

+	 *    {@code
+	 *
+	 *   {4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}}
+	 * 
+ * + *

+ * The returned multimap is serializable if its keys and values are all + * serializable. + * + * @param values the values to use when constructing the {@code + * ImmutableListMultimap} + * @param keyFunction the function used to produce the key for each value + * @return {@code ImmutableListMultimap} mapping the result of evaluating the + * function {@code keyFunction} on each value in the input collection to + * that value + * @throws NullPointerException if any of the following cases is true: + *

    + *
  • {@code values} is null + *
  • {@code keyFunction} is null + *
  • An element in {@code values} is null + *
  • {@code keyFunction} returns {@code null} for + * any element of {@code + * values} + *
+ */ + public static ImmutableListMultimap index(Iterable values, Function keyFunction) { + return index(values.iterator(), keyFunction); + } + + /** + * Creates an index {@code ImmutableListMultimap} that contains the results of + * applying a specified function to each item in an {@code Iterator} of values. + * Each value will be stored as a value in the resulting multimap, yielding a + * multimap with the same size as the input iterator. The key used to store that + * value in the multimap will be the result of calling the function on that + * value. The resulting multimap is created as an immutable snapshot. In the + * returned multimap, keys appear in the order they are first encountered, and + * the values corresponding to each key appear in the same order as they are + * encountered. + * + *

+ * For example, + * + *

+	 *    {@code
+	 *
+	 *   List badGuys =
+	 *       Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
+	 *   Function stringLengthFunction = ...;
+	 *   Multimap index =
+	 *       Multimaps.index(badGuys.iterator(), stringLengthFunction);
+	 *   System.out.println(index);}
+	 * 
+ * + *

+ * prints + * + *

+	 *    {@code
+	 *
+	 *   {4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}}
+	 * 
+ * + *

+ * The returned multimap is serializable if its keys and values are all + * serializable. + * + * @param values the values to use when constructing the {@code + * ImmutableListMultimap} + * @param keyFunction the function used to produce the key for each value + * @return {@code ImmutableListMultimap} mapping the result of evaluating the + * function {@code keyFunction} on each value in the input collection to + * that value + * @throws NullPointerException if any of the following cases is true: + *

    + *
  • {@code values} is null + *
  • {@code keyFunction} is null + *
  • An element in {@code values} is null + *
  • {@code keyFunction} returns {@code null} for + * any element of {@code + * values} + *
+ * @since 10.0 + */ + public static ImmutableListMultimap index(Iterator values, Function keyFunction) { + checkNotNull(keyFunction); + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + while (values.hasNext()) { + V value = values.next(); + checkNotNull(value, values); + builder.put(keyFunction.apply(value), value); + } + return builder.build(); + } + + static class Keys extends AbstractMultiset { + final Multimap multimap; + + Keys(Multimap multimap) { + this.multimap = multimap; + } + + @Override + Iterator> entryIterator() { + return new TransformedIterator>, Multiset.Entry>( + multimap.asMap().entrySet().iterator()) { + @Override + Multiset.Entry transform(final Map.Entry> backingEntry) { + return new Multisets.AbstractEntry() { + @Override + public K getElement() { + return backingEntry.getKey(); + } + + @Override + public int getCount() { + return backingEntry.getValue().size(); + } + }; + } + }; + } + + @Override + int distinctElements() { + return multimap.asMap().size(); + } + + @Override + Set> createEntrySet() { + return new KeysEntrySet(); + } + + class KeysEntrySet extends Multisets.EntrySet { + @Override + Multiset multiset() { + return Keys.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + + @Override + public int size() { + return distinctElements(); + } + + @Override + public boolean isEmpty() { + return multimap.isEmpty(); + } + + @Override + public boolean contains(@Nullable Object o) { + if (o instanceof Multiset.Entry) { + Multiset.Entry entry = (Multiset.Entry) o; + Collection collection = multimap.asMap().get(entry.getElement()); + return collection != null && collection.size() == entry.getCount(); + } + return false; + } + + @Override + public boolean remove(@Nullable Object o) { + if (o instanceof Multiset.Entry) { + Multiset.Entry entry = (Multiset.Entry) o; + Collection collection = multimap.asMap().get(entry.getElement()); + if (collection != null && collection.size() == entry.getCount()) { + collection.clear(); + return true; + } + } + return false; + } + } + + @Override + public boolean contains(@Nullable Object element) { + return multimap.containsKey(element); + } + + @Override + public Iterator iterator() { + return Maps.keyIterator(multimap.entries().iterator()); + } + + @Override + public int count(@Nullable Object element) { + Collection values = Maps.safeGet(multimap.asMap(), element); + return (values == null) ? 0 : values.size(); + } + + @Override + public int remove(@Nullable Object element, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } + + Collection values = Maps.safeGet(multimap.asMap(), element); + + if (values == null) { + return 0; + } + + int oldCount = values.size(); + if (occurrences >= oldCount) { + values.clear(); + } else { + Iterator iterator = values.iterator(); + for (int i = 0; i < occurrences; i++) { + iterator.next(); + iterator.remove(); + } + } + return oldCount; + } + + @Override + public void clear() { + multimap.clear(); + } + + @Override + public Set elementSet() { + return multimap.keySet(); + } + } + + /** + * A skeleton implementation of {@link Multimap#entries()}. + */ + abstract static class Entries extends AbstractCollection> { + abstract Multimap multimap(); + + @Override + public int size() { + return multimap().size(); + } + + @Override + public boolean contains(@Nullable Object o) { + if (o instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) o; + return multimap().containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public boolean remove(@Nullable Object o) { + if (o instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) o; + return multimap().remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public void clear() { + multimap().clear(); + } + } + + /** + * A skeleton implementation of {@link Multimap#asMap()}. + */ + static final class AsMap extends Maps.ImprovedAbstractMap> { + private final Multimap multimap; + + AsMap(Multimap multimap) { + this.multimap = checkNotNull(multimap); + } + + @Override + public int size() { + return multimap.keySet().size(); + } + + @Override + protected Set>> createEntrySet() { + return new EntrySet(); + } + + void removeValuesForKey(Object key) { + multimap.keySet().remove(key); + } + + class EntrySet extends Maps.EntrySet> { + @Override + Map> map() { + return AsMap.this; + } + + @Override + public Iterator>> iterator() { + return Maps.asMapEntryIterator(multimap.keySet(), new Function>() { + @Override + public Collection apply(K key) { + return multimap.get(key); + } + }); + } + + @Override + public boolean remove(Object o) { + if (!contains(o)) { + return false; + } + Map.Entry entry = (Map.Entry) o; + removeValuesForKey(entry.getKey()); + return true; + } + } + + @SuppressWarnings("unchecked") + @Override + public Collection get(Object key) { + return containsKey(key) ? multimap.get((K) key) : null; + } + + @Override + public Collection remove(Object key) { + return containsKey(key) ? multimap.removeAll(key) : null; + } + + @Override + public Set keySet() { + return multimap.keySet(); + } + + @Override + public boolean isEmpty() { + return multimap.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return multimap.containsKey(key); + } + + @Override + public void clear() { + multimap.clear(); + } + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose keys + * satisfy a predicate. The returned multimap is a live view of + * {@code unfiltered}; changes to one affect the other. + * + *

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

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

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

+ * Many of the filtered multimap's methods, such as {@code size()}, iterate + * across every key/value mapping in the underlying multimap and determine which + * satisfy the filter. When a live view is not needed, it may be faster + * to copy the filtered multimap 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 Multimap filterKeys(Multimap unfiltered, final Predicate keyPredicate) { + if (unfiltered instanceof SetMultimap) { + return filterKeys((SetMultimap) unfiltered, keyPredicate); + } else if (unfiltered instanceof ListMultimap) { + return filterKeys((ListMultimap) unfiltered, keyPredicate); + } else if (unfiltered instanceof FilteredKeyMultimap) { + FilteredKeyMultimap prev = (FilteredKeyMultimap) unfiltered; + return new FilteredKeyMultimap(prev.unfiltered, Predicates.and(prev.keyPredicate, keyPredicate)); + } else if (unfiltered instanceof FilteredMultimap) { + FilteredMultimap prev = (FilteredMultimap) unfiltered; + return filterFiltered(prev, Maps.keyPredicateOnEntries(keyPredicate)); + } else { + return new FilteredKeyMultimap(unfiltered, keyPredicate); + } + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose keys + * satisfy a predicate. The returned multimap is a live view of + * {@code unfiltered}; changes to one affect the other. + * + *

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

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

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

+ * Many of the filtered multimap's methods, such as {@code size()}, iterate + * across every key/value mapping in the underlying multimap and determine which + * satisfy the filter. When a live view is not needed, it may be faster + * to copy the filtered multimap 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 SetMultimap filterKeys(SetMultimap unfiltered, + final Predicate keyPredicate) { + if (unfiltered instanceof FilteredKeySetMultimap) { + FilteredKeySetMultimap prev = (FilteredKeySetMultimap) unfiltered; + return new FilteredKeySetMultimap(prev.unfiltered(), Predicates.and(prev.keyPredicate, keyPredicate)); + } else if (unfiltered instanceof FilteredSetMultimap) { + FilteredSetMultimap prev = (FilteredSetMultimap) unfiltered; + return filterFiltered(prev, Maps.keyPredicateOnEntries(keyPredicate)); + } else { + return new FilteredKeySetMultimap(unfiltered, keyPredicate); + } + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose keys + * satisfy a predicate. The returned multimap is a live view of + * {@code unfiltered}; changes to one affect the other. + * + *

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

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

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

+ * Many of the filtered multimap's methods, such as {@code size()}, iterate + * across every key/value mapping in the underlying multimap and determine which + * satisfy the filter. When a live view is not needed, it may be faster + * to copy the filtered multimap 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 ListMultimap filterKeys(ListMultimap unfiltered, + final Predicate keyPredicate) { + if (unfiltered instanceof FilteredKeyListMultimap) { + FilteredKeyListMultimap prev = (FilteredKeyListMultimap) unfiltered; + return new FilteredKeyListMultimap(prev.unfiltered(), + Predicates.and(prev.keyPredicate, keyPredicate)); + } else { + return new FilteredKeyListMultimap(unfiltered, keyPredicate); + } + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose values + * satisfy a predicate. The returned multimap is a live view of + * {@code unfiltered}; changes to one affect the other. + * + *

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

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

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

+ * Many of the filtered multimap's methods, such as {@code size()}, iterate + * across every key/value mapping in the underlying multimap and determine which + * satisfy the filter. When a live view is not needed, it may be faster + * to copy the filtered multimap 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 Multimap filterValues(Multimap unfiltered, + final Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose values + * satisfy a predicate. The returned multimap is a live view of + * {@code unfiltered}; changes to one affect the other. + * + *

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

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

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

+ * Many of the filtered multimap's methods, such as {@code size()}, iterate + * across every key/value mapping in the underlying multimap and determine which + * satisfy the filter. When a live view is not needed, it may be faster + * to copy the filtered multimap 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 SetMultimap filterValues(SetMultimap unfiltered, + final Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} that satisfy + * a predicate. The returned multimap is a live view of {@code unfiltered}; + * changes to one affect the other. + * + *

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

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

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

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

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

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

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

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

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

+ * Warning: {@code entryPredicate} must be consistent with equals, + * as documented at {@link Predicate#apply}. + * + * @since 14.0 + */ + public static SetMultimap filterEntries(SetMultimap unfiltered, + Predicate> entryPredicate) { + checkNotNull(entryPredicate); + return (unfiltered instanceof FilteredSetMultimap) + ? filterFiltered((FilteredSetMultimap) unfiltered, entryPredicate) + : new FilteredEntrySetMultimap(checkNotNull(unfiltered), entryPredicate); + } + + /** + * Support removal operations when filtering a filtered multimap. Since a + * filtered multimap has iterators that don't support remove, passing one to the + * FilteredEntryMultimap constructor would lead to a multimap whose removal + * operations would fail. This method combines the predicates to avoid that + * problem. + */ + private static Multimap filterFiltered(FilteredMultimap multimap, + Predicate> entryPredicate) { + Predicate> predicate = Predicates.and(multimap.entryPredicate(), entryPredicate); + return new FilteredEntryMultimap(multimap.unfiltered(), predicate); + } + + /** + * Support removal operations when filtering a filtered multimap. Since a + * filtered multimap has iterators that don't support remove, passing one to the + * FilteredEntryMultimap constructor would lead to a multimap whose removal + * operations would fail. This method combines the predicates to avoid that + * problem. + */ + private static SetMultimap filterFiltered(FilteredSetMultimap multimap, + Predicate> entryPredicate) { + Predicate> predicate = Predicates.and(multimap.entryPredicate(), entryPredicate); + return new FilteredEntrySetMultimap(multimap.unfiltered(), predicate); + } + + static boolean equalsImpl(Multimap multimap, @Nullable Object object) { + if (object == multimap) { + return true; + } + if (object instanceof Multimap) { + Multimap that = (Multimap) object; + return multimap.asMap().equals(that.asMap()); + } + return false; + } + + // TODO(jlevy): Create methods that filter a SortedSetMultimap. +} diff --git a/sources/main/java/com/google/common/collect/Multiset.java b/sources/main/java/com/google/common/collect/Multiset.java new file mode 100644 index 0000000..58b0338 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Multiset.java @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A collection that supports order-independent equality, like {@link Set}, but + * may have duplicate elements. A multiset is also sometimes called a + * bag. + * + *

+ * Elements of a multiset that are equal to one another are referred to as + * occurrences of the same single element. The total number of + * occurrences of an element in a multiset is called the count of that + * element (the terms "frequency" and "multiplicity" are equivalent, but not + * used in this API). Since the count of an element is represented as an {@code + * int}, a multiset may never contain more than {@link Integer#MAX_VALUE} + * occurrences of any one element. + * + *

+ * {@code Multiset} refines the specifications of several methods from + * {@code Collection}. It also defines an additional query operation, + * {@link #count}, which returns the count of an element. There are five new + * bulk-modification operations, for example {@link #add(Object, int)}, to add + * or remove multiple occurrences of an element at once, or to set the count of + * an element to a specific value. These modification operations are optional, + * but implementations which support the standard collection operations + * {@link #add(Object)} or {@link #remove(Object)} are encouraged to implement + * the related methods as well. Finally, two collection views are provided: + * {@link #elementSet} contains the distinct elements of the multiset "with + * duplicates collapsed", and {@link #entrySet} is similar but contains + * {@link Entry Multiset.Entry} instances, each providing both a distinct + * element and the count of that element. + * + *

+ * In addition to these required methods, implementations of {@code + * Multiset} are expected to provide two {@code static} creation methods: + * {@code create()}, returning an empty multiset, and {@code + * create(Iterable)}, returning a multiset containing the given + * initial elements. This is simply a refinement of {@code Collection}'s + * constructor recommendations, reflecting the new developments of Java 5. + * + *

+ * As with other collection types, the modification operations are optional, and + * should throw {@link UnsupportedOperationException} when they are not + * implemented. Most implementations should support either all add operations or + * none of them, all removal operations or none of them, and if and only if all + * of these are supported, the {@code setCount} methods as well. + * + *

+ * A multiset uses {@link Object#equals} to determine whether two instances + * should be considered "the same," unless specified otherwise by the + * implementation. + * + *

+ * Common implementations include {@link ImmutableMultiset}, + * {@link HashMultiset}, and {@link ConcurrentHashMultiset}. + * + *

+ * If your values may be zero, negative, or outside the range of an int, you may + * wish to use {@link com.google.common.util.concurrent.AtomicLongMap} instead. + * Note, however, that unlike {@code Multiset}, {@code AtomicLongMap} does not + * automatically remove zeros. + * + *

+ * See the Guava User Guide article on + * {@code Multiset}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface Multiset extends Collection { + // Query Operations + + /** + * Returns the number of occurrences of an element in this multiset (the + * count of the element). Note that for an {@link Object#equals}-based + * multiset, this gives the same result as {@link Collections#frequency} (which + * would presumably perform more poorly). + * + *

+ * Note: the utility method {@link Iterables#frequency} generalizes this + * operation; it correctly delegates to this method when dealing with a + * multiset, but it can also accept any other iterable type. + * + * @param element the element to count occurrences of + * @return the number of occurrences of the element in this multiset; possibly + * zero but never negative + */ + int count(@Nullable Object element); + + // Bulk Operations + + /** + * Adds a number of occurrences of an element to this multiset. Note that if + * {@code occurrences == 1}, this method has the identical effect to + * {@link #add(Object)}. This method is functionally equivalent (except in the + * case of overflow) to the call {@code addAll(Collections.nCopies(element, + * occurrences))}, which would presumably perform much more poorly. + * + * @param element the element to add occurrences of; may be null only if + * explicitly allowed by the implementation + * @param occurrences the number of occurrences of the element to add. May be + * zero, in which case no change will be made. + * @return the count of the element before the operation; possibly zero + * @throws IllegalArgumentException if {@code occurrences} is negative, or if + * this operation would result in more than + * {@link Integer#MAX_VALUE} occurrences of the + * element + * @throws NullPointerException if {@code element} is null and this + * implementation does not permit null + * elements. Note that if {@code + * occurrences} is zero, the implementation may opt to + * return normally. + */ + int add(@Nullable E element, int occurrences); + + /** + * Removes a number of occurrences of the specified element from this multiset. + * If the multiset contains fewer than this number of occurrences to begin with, + * all occurrences will be removed. Note that if {@code occurrences == 1}, this + * is functionally equivalent to the call {@code remove(element)}. + * + * @param element the element to conditionally remove occurrences of + * @param occurrences the number of occurrences of the element to remove. May be + * zero, in which case no change will be made. + * @return the count of the element before the operation; possibly zero + * @throws IllegalArgumentException if {@code occurrences} is negative + */ + int remove(@Nullable Object element, int occurrences); + + /** + * Adds or removes the necessary occurrences of an element such that the element + * attains the desired count. + * + * @param element the element to add or remove occurrences of; may be null only + * if explicitly allowed by the implementation + * @param count the desired count of the element in this multiset + * @return the count of the element before the operation; possibly zero + * @throws IllegalArgumentException if {@code count} is negative + * @throws NullPointerException if {@code element} is null and this + * implementation does not permit null + * elements. Note that if {@code + * count} is zero, the implementor may optionally + * return zero instead. + */ + int setCount(E element, int count); + + /** + * Conditionally sets the count of an element to a new value, as described in + * {@link #setCount(Object, int)}, provided that the element has the expected + * current count. If the current count is not {@code oldCount}, no change is + * made. + * + * @param element the element to conditionally set the count of; may be null + * only if explicitly allowed by the implementation + * @param oldCount the expected present count of the element in this multiset + * @param newCount the desired count of the element in this multiset + * @return {@code true} if the condition for modification was met. This implies + * that the multiset was indeed modified, unless + * {@code oldCount == newCount}. + * @throws IllegalArgumentException if {@code oldCount} or {@code newCount} is + * negative + * @throws NullPointerException if {@code element} is null and the + * implementation does not permit null + * elements. Note that if {@code + * oldCount} and {@code newCount} are both zero, the + * implementor may optionally return + * {@code true} instead. + */ + boolean setCount(E element, int oldCount, int newCount); + + // Views + + /** + * Returns the set of distinct elements contained in this multiset. The element + * set is backed by the same data as the multiset, so any change to either is + * immediately reflected in the other. The order of the elements in the element + * set is unspecified. + * + *

+ * If the element set supports any removal operations, these necessarily cause + * all occurrences of the removed element(s) to be removed from the + * multiset. Implementations are not expected to support the add operations, + * although this is possible. + * + *

+ * A common use for the element set is to find the number of distinct elements + * in the multiset: {@code elementSet().size()}. + * + * @return a view of the set of distinct elements in this multiset + */ + Set elementSet(); + + /** + * Returns a view of the contents of this multiset, grouped into {@code + * Multiset.Entry} instances, each providing an element of the multiset and the + * count of that element. This set contains exactly one entry for each distinct + * element in the multiset (thus it always has the same size as the + * {@link #elementSet}). The order of the elements in the element set is + * unspecified. + * + *

+ * The entry set is backed by the same data as the multiset, so any change to + * either is immediately reflected in the other. However, multiset changes may + * or may not be reflected in any {@code Entry} instances already retrieved from + * the entry set (this is implementation-dependent). Furthermore, + * implementations are not required to support modifications to the entry set at + * all, and the {@code Entry} instances themselves don't even have methods for + * modification. See the specific implementation class for more details on how + * its entry set handles modifications. + * + * @return a set of entries representing the data of this multiset + */ + Set> entrySet(); + + /** + * An unmodifiable element-count pair for a multiset. The + * {@link Multiset#entrySet} method returns a view of the multiset whose + * elements are of this class. A multiset implementation may return Entry + * instances that are either live "read-through" views to the Multiset, or + * immutable snapshots. Note that this type is unrelated to the similarly-named + * type {@code Map.Entry}. + * + * @since 2.0 (imported from Google Collections Library) + */ + interface Entry { + + /** + * Returns the multiset element corresponding to this entry. Multiple calls to + * this method always return the same instance. + * + * @return the element corresponding to this entry + */ + E getElement(); + + /** + * Returns the count of the associated element in the underlying multiset. This + * count may either be an unchanging snapshot of the count at the time the entry + * was retrieved, or a live view of the current count of the element in the + * multiset, depending on the implementation. Note that in the former case, this + * method can never return zero, while in the latter, it will return zero if all + * occurrences of the element were since removed from the multiset. + * + * @return the count of the element; never negative + */ + int getCount(); + + /** + * {@inheritDoc} + * + *

+ * Returns {@code true} if the given object is also a multiset entry and the two + * entries represent the same element and count. That is, two entries {@code a} + * and {@code b} are equal if: + * + *

+		 *    {@code
+		 *
+		 *   Objects.equal(a.getElement(), b.getElement())
+		 *       && a.getCount() == b.getCount()}
+		 * 
+ */ + @Override + // TODO(kevinb): check this wrt TreeMultiset? + boolean equals(Object o); + + /** + * {@inheritDoc} + * + *

+ * The hash code of a multiset entry for element {@code element} and count + * {@code count} is defined as: + * + *

+		 *    {@code
+		 *
+		 *   ((element == null) ? 0 : element.hashCode()) ^ count}
+		 * 
+ */ + @Override + int hashCode(); + + /** + * Returns the canonical string representation of this entry, defined as + * follows. If the count for this entry is one, this is simply the string + * representation of the corresponding element. Otherwise, it is the string + * representation of the element, followed by the three characters {@code + * " x "} (space, letter x, space), followed by the count. + */ + @Override + String toString(); + } + + // Comparison and hashing + + /** + * Compares the specified object with this multiset for equality. Returns + * {@code true} if the given object is also a multiset and contains equal + * elements with equal counts, regardless of order. + */ + @Override + // TODO(kevinb): caveats about equivalence-relation? + boolean equals(@Nullable Object object); + + /** + * Returns the hash code for this multiset. This is defined as the sum of + * + *
+	 *    {@code
+	 *
+	 *   ((element == null) ? 0 : element.hashCode()) ^ count(element)}
+	 * 
+ * + *

+ * over all distinct elements in the multiset. It follows that a multiset and + * its entry set always have the same hash code. + */ + @Override + int hashCode(); + + /** + * {@inheritDoc} + * + *

+ * It is recommended, though not mandatory, that this method return the result + * of invoking {@link #toString} on the {@link #entrySet}, yielding a result + * such as {@code [a x 3, c, d x 2, e]}. + */ + @Override + String toString(); + + // Refined Collection Methods + + /** + * {@inheritDoc} + * + *

+ * Elements that occur multiple times in the multiset will appear multiple times + * in this iterator, though not necessarily sequentially. + */ + @Override + Iterator iterator(); + + /** + * Determines whether this multiset contains the specified element. + * + *

+ * This method refines {@link Collection#contains} to further specify that it + * may not throw an exception in response to {@code element} being null + * or of the wrong type. + * + * @param element the element to check for + * @return {@code true} if this multiset contains at least one occurrence of the + * element + */ + @Override + boolean contains(@Nullable Object element); + + /** + * Returns {@code true} if this multiset contains at least one occurrence of + * each element in the specified collection. + * + *

+ * This method refines {@link Collection#containsAll} to further specify that it + * may not throw an exception in response to any of {@code + * elements} being null or of the wrong type. + * + *

+ * Note: this method does not take into account the occurrence count of + * an element in the two collections; it may still return {@code + * true} even if {@code elements} contains several occurrences of an element and + * this multiset contains only one. This is no different than any other + * collection type like {@link List}, but it may be unexpected to the user of a + * multiset. + * + * @param elements the collection of elements to be checked for containment in + * this multiset + * @return {@code true} if this multiset contains at least one occurrence of + * each element contained in {@code elements} + * @throws NullPointerException if {@code elements} is null + */ + @Override + boolean containsAll(Collection elements); + + /** + * Adds a single occurrence of the specified element to this multiset. + * + *

+ * This method refines {@link Collection#add}, which only ensures the + * presence of the element, to further specify that a successful call must + * always increment the count of the element, and the overall size of the + * collection, by one. + * + * @param element the element to add one occurrence of; may be null only if + * explicitly allowed by the implementation + * @return {@code true} always, since this call is required to modify the + * multiset, unlike other {@link Collection} types + * @throws NullPointerException if {@code element} is null and this + * implementation does not permit null elements + * @throws IllegalArgumentException if {@link Integer#MAX_VALUE} occurrences of + * {@code element} are already contained in + * this multiset + */ + @Override + boolean add(E element); + + /** + * Removes a single occurrence of the specified element from this + * multiset, if present. + * + *

+ * This method refines {@link Collection#remove} to further specify that it + * may not throw an exception in response to {@code element} being null + * or of the wrong type. + * + * @param element the element to remove one occurrence of + * @return {@code true} if an occurrence was found and removed + */ + @Override + boolean remove(@Nullable Object element); + + /** + * {@inheritDoc} + * + *

+ * Note: This method ignores how often any element might appear in + * {@code c}, and only cares whether or not an element appears at all. If you + * wish to remove one occurrence in this multiset for every occurrence in + * {@code c}, see {@link Multisets#removeOccurrences(Multiset, Multiset)}. + * + *

+ * This method refines {@link Collection#removeAll} to further specify that it + * may not throw an exception in response to any of {@code elements} + * being null or of the wrong type. + */ + @Override + boolean removeAll(Collection c); + + /** + * {@inheritDoc} + * + *

+ * Note: This method ignores how often any element might appear in + * {@code c}, and only cares whether or not an element appears at all. If you + * wish to remove one occurrence in this multiset for every occurrence in + * {@code c}, see {@link Multisets#retainOccurrences(Multiset, Multiset)}. + * + *

+ * This method refines {@link Collection#retainAll} to further specify that it + * may not throw an exception in response to any of {@code elements} + * being null or of the wrong type. + * + * @see Multisets#retainOccurrences(Multiset, Multiset) + */ + @Override + boolean retainAll(Collection c); +} diff --git a/sources/main/java/com/google/common/collect/Multisets.java b/sources/main/java/com/google/common/collect/Multisets.java new file mode 100644 index 0000000..c33ab0f --- /dev/null +++ b/sources/main/java/com/google/common/collect/Multisets.java @@ -0,0 +1,1104 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Multiset.Entry; +import com.google.common.primitives.Ints; + +/** + * Provides static utility methods for creating and working with + * {@link Multiset} instances. + * + *

+ * See the Guava User Guide article on + * {@code Multisets}. + * + * @author Kevin Bourrillion + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public final class Multisets { + private Multisets() { + } + + /** + * Returns an unmodifiable view of the specified multiset. Query operations on + * the returned multiset "read through" to the specified multiset, and attempts + * to modify the returned multiset result in an + * {@link UnsupportedOperationException}. + * + *

+ * The returned multiset will be serializable if the specified multiset is + * serializable. + * + * @param multiset the multiset for which an unmodifiable view is to be + * generated + * @return an unmodifiable view of the multiset + */ + public static Multiset unmodifiableMultiset(Multiset multiset) { + if (multiset instanceof UnmodifiableMultiset || multiset instanceof ImmutableMultiset) { + // Since it's unmodifiable, the covariant cast is safe + @SuppressWarnings("unchecked") + Multiset result = (Multiset) multiset; + return result; + } + return new UnmodifiableMultiset(checkNotNull(multiset)); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static Multiset unmodifiableMultiset(ImmutableMultiset multiset) { + return checkNotNull(multiset); + } + + static class UnmodifiableMultiset extends ForwardingMultiset implements Serializable { + final Multiset delegate; + + UnmodifiableMultiset(Multiset delegate) { + this.delegate = delegate; + } + + @SuppressWarnings("unchecked") + @Override + protected Multiset delegate() { + // This is safe because all non-covariant methods are overriden + return (Multiset) delegate; + } + + transient Set elementSet; + + Set createElementSet() { + return Collections.unmodifiableSet(delegate.elementSet()); + } + + @Override + public Set elementSet() { + Set es = elementSet; + return (es == null) ? elementSet = createElementSet() : es; + } + + transient Set> entrySet; + + @SuppressWarnings("unchecked") + @Override + public Set> entrySet() { + Set> es = entrySet; + return (es == null) + // Safe because the returned set is made unmodifiable and Entry + // itself is readonly + ? entrySet = (Set) Collections.unmodifiableSet(delegate.entrySet()) + : es; + } + + @SuppressWarnings("unchecked") + @Override + public Iterator iterator() { + // Safe because the returned Iterator is made unmodifiable + return (Iterator) Iterators.unmodifiableIterator(delegate.iterator()); + } + + @Override + public boolean add(E element) { + throw new UnsupportedOperationException(); + } + + @Override + public int add(E element, int occurences) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection elementsToAdd) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object element) { + throw new UnsupportedOperationException(); + } + + @Override + public int remove(Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection elementsToRemove) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection elementsToRetain) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public int setCount(E element, int count) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean setCount(E element, int oldCount, int newCount) { + throw new UnsupportedOperationException(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an unmodifiable view of the specified sorted multiset. Query + * operations on the returned multiset "read through" to the specified multiset, + * and attempts to modify the returned multiset result in an + * {@link UnsupportedOperationException}. + * + *

+ * The returned multiset will be serializable if the specified multiset is + * serializable. + * + * @param sortedMultiset the sorted multiset for which an unmodifiable view is + * to be generated + * @return an unmodifiable view of the multiset + * @since 11.0 + */ + @Beta + public static SortedMultiset unmodifiableSortedMultiset(SortedMultiset sortedMultiset) { + // it's in its own file so it can be emulated for GWT + return new UnmodifiableSortedMultiset(checkNotNull(sortedMultiset)); + } + + /** + * Returns an immutable multiset entry with the specified element and count. The + * entry will be serializable if {@code e} is. + * + * @param e the element to be associated with the returned entry + * @param n the count to be associated with the returned entry + * @throws IllegalArgumentException if {@code n} is negative + */ + public static Multiset.Entry immutableEntry(@Nullable E e, int n) { + return new ImmutableEntry(e, n); + } + + static final class ImmutableEntry extends AbstractEntry implements Serializable { + @Nullable + final E element; + final int count; + + ImmutableEntry(@Nullable E element, int count) { + this.element = element; + this.count = count; + checkNonnegative(count, "count"); + } + + @Override + @Nullable + public E getElement() { + return element; + } + + @Override + public int getCount() { + return count; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a view of the elements of {@code unfiltered} that satisfy a + * predicate. The returned multiset is a live view of {@code unfiltered}; + * changes to one affect the other. + * + *

+ * The resulting multiset's iterators, and those of its {@code entrySet()} and + * {@code elementSet()}, do not support {@code remove()}. However, all other + * multiset methods supported by {@code unfiltered} are supported by the + * returned multiset. When given an element that doesn't satisfy the predicate, + * the multiset's {@code add()} and {@code addAll()} methods throw an + * {@link IllegalArgumentException}. When methods such as {@code removeAll()} + * and {@code clear()} are called on the filtered multiset, only elements that + * satisfy the filter will be removed from the underlying multiset. + * + *

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

+ * Many of the filtered multiset's methods, such as {@code size()}, iterate + * across every element in the underlying multiset and determine which elements + * satisfy the filter. When a live view is not needed, it may be faster + * to copy the returned multiset 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 + */ + @Beta + public static Multiset filter(Multiset unfiltered, Predicate predicate) { + if (unfiltered instanceof FilteredMultiset) { + // Support clear(), removeAll(), and retainAll() when filtering a filtered + // collection. + FilteredMultiset filtered = (FilteredMultiset) unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredMultiset(filtered.unfiltered, combinedPredicate); + } + return new FilteredMultiset(unfiltered, predicate); + } + + private static final class FilteredMultiset extends AbstractMultiset { + final Multiset unfiltered; + final Predicate predicate; + + FilteredMultiset(Multiset unfiltered, Predicate predicate) { + this.unfiltered = checkNotNull(unfiltered); + this.predicate = checkNotNull(predicate); + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.filter(unfiltered.iterator(), predicate); + } + + @Override + Set createElementSet() { + return Sets.filter(unfiltered.elementSet(), predicate); + } + + @Override + Set> createEntrySet() { + return Sets.filter(unfiltered.entrySet(), new Predicate>() { + @Override + public boolean apply(Entry entry) { + return predicate.apply(entry.getElement()); + } + }); + } + + @Override + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + @Override + int distinctElements() { + return elementSet().size(); + } + + @Override + public int count(@Nullable Object element) { + int count = unfiltered.count(element); + if (count > 0) { + @SuppressWarnings("unchecked") // element is equal to an E + E e = (E) element; + return predicate.apply(e) ? count : 0; + } + return 0; + } + + @Override + public int add(@Nullable E element, int occurrences) { + checkArgument(predicate.apply(element), "Element %s does not match predicate %s", element, predicate); + return unfiltered.add(element, occurrences); + } + + @Override + public int remove(@Nullable Object element, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } else { + return contains(element) ? unfiltered.remove(element, occurrences) : 0; + } + } + + @Override + public void clear() { + elementSet().clear(); + } + } + + /** + * Returns the expected number of distinct elements given the specified + * elements. The number of distinct elements is only computed if {@code + * elements} is an instance of {@code Multiset}; otherwise the default value of + * 11 is returned. + */ + static int inferDistinctElements(Iterable elements) { + if (elements instanceof Multiset) { + return ((Multiset) elements).elementSet().size(); + } + return 11; // initial capacity will be rounded up to 16 + } + + /** + * Returns an unmodifiable view of the union of two multisets. In the returned + * multiset, the count of each element is the maximum of its counts in + * the two backing multisets. The iteration order of the returned multiset + * matches that of the element set of {@code multiset1} followed by the members + * of the element set of {@code multiset2} that are not contained in + * {@code multiset1}, with repeated occurrences of the same element appearing + * consecutively. + * + *

+ * Results are undefined if {@code multiset1} and {@code multiset2} are based on + * different equivalence relations (as {@code HashMultiset} and + * {@code TreeMultiset} are). + * + * @since 14.0 + */ + @Beta + public static Multiset union(final Multiset multiset1, final Multiset multiset2) { + checkNotNull(multiset1); + checkNotNull(multiset2); + + return new AbstractMultiset() { + @Override + public boolean contains(@Nullable Object element) { + return multiset1.contains(element) || multiset2.contains(element); + } + + @Override + public boolean isEmpty() { + return multiset1.isEmpty() && multiset2.isEmpty(); + } + + @Override + public int count(Object element) { + return Math.max(multiset1.count(element), multiset2.count(element)); + } + + @Override + Set createElementSet() { + return Sets.union(multiset1.elementSet(), multiset2.elementSet()); + } + + @Override + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + final Iterator> iterator2 = multiset2.entrySet().iterator(); + // TODO(user): consider making the entries live views + return new AbstractIterator>() { + @Override + protected Entry computeNext() { + if (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + int count = Math.max(entry1.getCount(), multiset2.count(element)); + return immutableEntry(element, count); + } + while (iterator2.hasNext()) { + Entry entry2 = iterator2.next(); + E element = entry2.getElement(); + if (!multiset1.contains(element)) { + return immutableEntry(element, entry2.getCount()); + } + } + return endOfData(); + } + }; + } + + @Override + int distinctElements() { + return elementSet().size(); + } + }; + } + + /** + * Returns an unmodifiable view of the intersection of two multisets. In the + * returned multiset, the count of each element is the minimum of its + * counts in the two backing multisets, with elements that would have a count of + * 0 not included. The iteration order of the returned multiset matches that of + * the element set of {@code multiset1}, with repeated occurrences of the same + * element appearing consecutively. + * + *

+ * Results are undefined if {@code multiset1} and {@code multiset2} are based on + * different equivalence relations (as {@code HashMultiset} and + * {@code TreeMultiset} are). + * + * @since 2.0 + */ + public static Multiset intersection(final Multiset multiset1, final Multiset multiset2) { + checkNotNull(multiset1); + checkNotNull(multiset2); + + return new AbstractMultiset() { + @Override + public int count(Object element) { + int count1 = multiset1.count(element); + return (count1 == 0) ? 0 : Math.min(count1, multiset2.count(element)); + } + + @Override + Set createElementSet() { + return Sets.intersection(multiset1.elementSet(), multiset2.elementSet()); + } + + @Override + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + // TODO(user): consider making the entries live views + return new AbstractIterator>() { + @Override + protected Entry computeNext() { + while (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + int count = Math.min(entry1.getCount(), multiset2.count(element)); + if (count > 0) { + return immutableEntry(element, count); + } + } + return endOfData(); + } + }; + } + + @Override + int distinctElements() { + return elementSet().size(); + } + }; + } + + /** + * Returns an unmodifiable view of the sum of two multisets. In the returned + * multiset, the count of each element is the sum of its counts in the + * two backing multisets. The iteration order of the returned multiset matches + * that of the element set of {@code multiset1} followed by the members of the + * element set of {@code multiset2} that are not contained in {@code multiset1}, + * with repeated occurrences of the same element appearing consecutively. + * + *

+ * Results are undefined if {@code multiset1} and {@code multiset2} are based on + * different equivalence relations (as {@code HashMultiset} and + * {@code TreeMultiset} are). + * + * @since 14.0 + */ + @Beta + public static Multiset sum(final Multiset multiset1, final Multiset multiset2) { + checkNotNull(multiset1); + checkNotNull(multiset2); + + // TODO(user): consider making the entries live views + return new AbstractMultiset() { + @Override + public boolean contains(@Nullable Object element) { + return multiset1.contains(element) || multiset2.contains(element); + } + + @Override + public boolean isEmpty() { + return multiset1.isEmpty() && multiset2.isEmpty(); + } + + @Override + public int size() { + return multiset1.size() + multiset2.size(); + } + + @Override + public int count(Object element) { + return multiset1.count(element) + multiset2.count(element); + } + + @Override + Set createElementSet() { + return Sets.union(multiset1.elementSet(), multiset2.elementSet()); + } + + @Override + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + final Iterator> iterator2 = multiset2.entrySet().iterator(); + return new AbstractIterator>() { + @Override + protected Entry computeNext() { + if (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + int count = entry1.getCount() + multiset2.count(element); + return immutableEntry(element, count); + } + while (iterator2.hasNext()) { + Entry entry2 = iterator2.next(); + E element = entry2.getElement(); + if (!multiset1.contains(element)) { + return immutableEntry(element, entry2.getCount()); + } + } + return endOfData(); + } + }; + } + + @Override + int distinctElements() { + return elementSet().size(); + } + }; + } + + /** + * Returns an unmodifiable view of the difference of two multisets. In the + * returned multiset, the count of each element is the result of the + * zero-truncated subtraction of its count in the second multiset from + * its count in the first multiset, with elements that would have a count of 0 + * not included. The iteration order of the returned multiset matches that of + * the element set of {@code multiset1}, with repeated occurrences of the same + * element appearing consecutively. + * + *

+ * Results are undefined if {@code multiset1} and {@code multiset2} are based on + * different equivalence relations (as {@code HashMultiset} and + * {@code TreeMultiset} are). + * + * @since 14.0 + */ + @Beta + public static Multiset difference(final Multiset multiset1, final Multiset multiset2) { + checkNotNull(multiset1); + checkNotNull(multiset2); + + // TODO(user): consider making the entries live views + return new AbstractMultiset() { + @Override + public int count(@Nullable Object element) { + int count1 = multiset1.count(element); + return (count1 == 0) ? 0 : Math.max(0, count1 - multiset2.count(element)); + } + + @Override + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + return new AbstractIterator>() { + @Override + protected Entry computeNext() { + while (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + int count = entry1.getCount() - multiset2.count(element); + if (count > 0) { + return immutableEntry(element, count); + } + } + return endOfData(); + } + }; + } + + @Override + int distinctElements() { + return Iterators.size(entryIterator()); + } + }; + } + + /** + * Returns {@code true} if {@code subMultiset.count(o) <= + * superMultiset.count(o)} for all {@code o}. + * + * @since 10.0 + */ + public static boolean containsOccurrences(Multiset superMultiset, Multiset subMultiset) { + checkNotNull(superMultiset); + checkNotNull(subMultiset); + for (Entry entry : subMultiset.entrySet()) { + int superCount = superMultiset.count(entry.getElement()); + if (superCount < entry.getCount()) { + return false; + } + } + return true; + } + + /** + * Modifies {@code multisetToModify} so that its count for an element {@code e} + * is at most {@code multisetToRetain.count(e)}. + * + *

+ * To be precise, {@code multisetToModify.count(e)} is set to + * {@code Math.min(multisetToModify.count(e), + * multisetToRetain.count(e))}. This is similar to + * {@link #intersection(Multiset, Multiset) intersection} + * {@code (multisetToModify, multisetToRetain)}, but mutates + * {@code multisetToModify} instead of returning a view. + * + *

+ * In contrast, {@code multisetToModify.retainAll(multisetToRetain)} keeps all + * occurrences of elements that appear at all in {@code + * multisetToRetain}, and deletes all occurrences of all other elements. + * + * @return {@code true} if {@code multisetToModify} was changed as a result of + * this operation + * @since 10.0 + */ + public static boolean retainOccurrences(Multiset multisetToModify, Multiset multisetToRetain) { + return retainOccurrencesImpl(multisetToModify, multisetToRetain); + } + + /** + * Delegate implementation which cares about the element type. + */ + private static boolean retainOccurrencesImpl(Multiset multisetToModify, Multiset occurrencesToRetain) { + checkNotNull(multisetToModify); + checkNotNull(occurrencesToRetain); + // Avoiding ConcurrentModificationExceptions is tricky. + Iterator> entryIterator = multisetToModify.entrySet().iterator(); + boolean changed = false; + while (entryIterator.hasNext()) { + Entry entry = entryIterator.next(); + int retainCount = occurrencesToRetain.count(entry.getElement()); + if (retainCount == 0) { + entryIterator.remove(); + changed = true; + } else if (retainCount < entry.getCount()) { + multisetToModify.setCount(entry.getElement(), retainCount); + changed = true; + } + } + return changed; + } + + /** + * For each occurrence of an element {@code e} in {@code occurrencesToRemove}, + * removes one occurrence of {@code e} in {@code multisetToModify}. + * + *

+ * Equivalently, this method modifies {@code multisetToModify} so that + * {@code multisetToModify.count(e)} is set to + * {@code Math.max(0, multisetToModify.count(e) - + * occurrencesToRemove.count(e))}. + * + *

+ * This is not the same as {@code multisetToModify.} + * {@link Multiset#removeAll removeAll}{@code (occurrencesToRemove)}, which + * removes all occurrences of elements that appear in + * {@code occurrencesToRemove}. However, this operation is equivalent to, + * albeit more efficient than, the following: + * + *

+	 *    {@code
+	 *
+	 *   for (E e : occurrencesToRemove) {
+	 *     multisetToModify.remove(e);
+	 *   }}
+	 * 
+ * + * @return {@code true} if {@code multisetToModify} was changed as a result of + * this operation + * @since 10.0 + */ + public static boolean removeOccurrences(Multiset multisetToModify, Multiset occurrencesToRemove) { + return removeOccurrencesImpl(multisetToModify, occurrencesToRemove); + } + + /** + * Delegate that cares about the element types in occurrencesToRemove. + */ + private static boolean removeOccurrencesImpl(Multiset multisetToModify, Multiset occurrencesToRemove) { + // TODO(user): generalize to removing an Iterable, perhaps + checkNotNull(multisetToModify); + checkNotNull(occurrencesToRemove); + + boolean changed = false; + Iterator> entryIterator = multisetToModify.entrySet().iterator(); + while (entryIterator.hasNext()) { + Entry entry = entryIterator.next(); + int removeCount = occurrencesToRemove.count(entry.getElement()); + if (removeCount >= entry.getCount()) { + entryIterator.remove(); + changed = true; + } else if (removeCount > 0) { + multisetToModify.remove(entry.getElement(), removeCount); + changed = true; + } + } + return changed; + } + + /** + * Implementation of the {@code equals}, {@code hashCode}, and {@code toString} + * methods of {@link Multiset.Entry}. + */ + abstract static class AbstractEntry implements Multiset.Entry { + /** + * Indicates whether an object equals this entry, following the behavior + * specified in {@link Multiset.Entry#equals}. + */ + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof Multiset.Entry) { + Multiset.Entry that = (Multiset.Entry) object; + return this.getCount() == that.getCount() && Objects.equal(this.getElement(), that.getElement()); + } + return false; + } + + /** + * Return this entry's hash code, following the behavior specified in + * {@link Multiset.Entry#hashCode}. + */ + @Override + public int hashCode() { + E e = getElement(); + return ((e == null) ? 0 : e.hashCode()) ^ getCount(); + } + + /** + * Returns a string representation of this multiset entry. The string + * representation consists of the associated element if the associated count is + * one, and otherwise the associated element followed by the characters " x " + * (space, x and space) followed by the count. Elements and counts are converted + * to strings as by {@code String.valueOf}. + */ + @Override + public String toString() { + String text = String.valueOf(getElement()); + int n = getCount(); + return (n == 1) ? text : (text + " x " + n); + } + } + + /** + * An implementation of {@link Multiset#equals}. + */ + static boolean equalsImpl(Multiset multiset, @Nullable Object object) { + if (object == multiset) { + return true; + } + if (object instanceof Multiset) { + Multiset that = (Multiset) object; + /* + * We can't simply check whether the entry sets are equal, since that approach + * fails when a TreeMultiset has a comparator that returns 0 when passed unequal + * elements. + */ + + if (multiset.size() != that.size() || multiset.entrySet().size() != that.entrySet().size()) { + return false; + } + for (Entry entry : that.entrySet()) { + if (multiset.count(entry.getElement()) != entry.getCount()) { + return false; + } + } + return true; + } + return false; + } + + /** + * An implementation of {@link Multiset#addAll}. + */ + static boolean addAllImpl(Multiset self, Collection elements) { + if (elements.isEmpty()) { + return false; + } + if (elements instanceof Multiset) { + Multiset that = cast(elements); + for (Entry entry : that.entrySet()) { + self.add(entry.getElement(), entry.getCount()); + } + } else { + Iterators.addAll(self, elements.iterator()); + } + return true; + } + + /** + * An implementation of {@link Multiset#removeAll}. + */ + static boolean removeAllImpl(Multiset self, Collection elementsToRemove) { + Collection collection = (elementsToRemove instanceof Multiset) + ? ((Multiset) elementsToRemove).elementSet() + : elementsToRemove; + + return self.elementSet().removeAll(collection); + } + + /** + * An implementation of {@link Multiset#retainAll}. + */ + static boolean retainAllImpl(Multiset self, Collection elementsToRetain) { + checkNotNull(elementsToRetain); + Collection collection = (elementsToRetain instanceof Multiset) + ? ((Multiset) elementsToRetain).elementSet() + : elementsToRetain; + + return self.elementSet().retainAll(collection); + } + + /** + * An implementation of {@link Multiset#setCount(Object, int)}. + */ + static int setCountImpl(Multiset self, E element, int count) { + checkNonnegative(count, "count"); + + int oldCount = self.count(element); + + int delta = count - oldCount; + if (delta > 0) { + self.add(element, delta); + } else if (delta < 0) { + self.remove(element, -delta); + } + + return oldCount; + } + + /** + * An implementation of {@link Multiset#setCount(Object, int, int)}. + */ + static boolean setCountImpl(Multiset self, E element, int oldCount, int newCount) { + checkNonnegative(oldCount, "oldCount"); + checkNonnegative(newCount, "newCount"); + + if (self.count(element) == oldCount) { + self.setCount(element, newCount); + return true; + } else { + return false; + } + } + + abstract static class ElementSet extends Sets.ImprovedAbstractSet { + abstract Multiset multiset(); + + @Override + public void clear() { + multiset().clear(); + } + + @Override + public boolean contains(Object o) { + return multiset().contains(o); + } + + @Override + public boolean containsAll(Collection c) { + return multiset().containsAll(c); + } + + @Override + public boolean isEmpty() { + return multiset().isEmpty(); + } + + @Override + public Iterator iterator() { + return new TransformedIterator, E>(multiset().entrySet().iterator()) { + @Override + E transform(Entry entry) { + return entry.getElement(); + } + }; + } + + @Override + public boolean remove(Object o) { + int count = multiset().count(o); + if (count > 0) { + multiset().remove(o, count); + return true; + } + return false; + } + + @Override + public int size() { + return multiset().entrySet().size(); + } + } + + abstract static class EntrySet extends Sets.ImprovedAbstractSet> { + abstract Multiset multiset(); + + @Override + public boolean contains(@Nullable Object o) { + if (o instanceof Entry) { + /* + * The GWT compiler wrongly issues a warning here. + */ + @SuppressWarnings("cast") + Entry entry = (Entry) o; + if (entry.getCount() <= 0) { + return false; + } + int count = multiset().count(entry.getElement()); + return count == entry.getCount(); + + } + return false; + } + + // GWT compiler warning; see contains(). + @SuppressWarnings("cast") + @Override + public boolean remove(Object object) { + if (object instanceof Multiset.Entry) { + Entry entry = (Entry) object; + Object element = entry.getElement(); + int entryCount = entry.getCount(); + if (entryCount != 0) { + // Safe as long as we never add a new entry, which we won't. + @SuppressWarnings("unchecked") + Multiset multiset = (Multiset) multiset(); + return multiset.setCount(element, entryCount, 0); + } + } + return false; + } + + @Override + public void clear() { + multiset().clear(); + } + } + + /** + * An implementation of {@link Multiset#iterator}. + */ + static Iterator iteratorImpl(Multiset multiset) { + return new MultisetIteratorImpl(multiset, multiset.entrySet().iterator()); + } + + static final class MultisetIteratorImpl implements Iterator { + private final Multiset multiset; + private final Iterator> entryIterator; + private Entry currentEntry; + /** Count of subsequent elements equal to current element */ + private int laterCount; + /** Count of all elements equal to current element */ + private int totalCount; + private boolean canRemove; + + MultisetIteratorImpl(Multiset multiset, Iterator> entryIterator) { + this.multiset = multiset; + this.entryIterator = entryIterator; + } + + @Override + public boolean hasNext() { + return laterCount > 0 || entryIterator.hasNext(); + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + if (laterCount == 0) { + currentEntry = entryIterator.next(); + totalCount = laterCount = currentEntry.getCount(); + } + laterCount--; + canRemove = true; + return currentEntry.getElement(); + } + + @Override + public void remove() { + checkRemove(canRemove); + if (totalCount == 1) { + entryIterator.remove(); + } else { + multiset.remove(currentEntry.getElement()); + } + totalCount--; + canRemove = false; + } + } + + /** + * An implementation of {@link Multiset#size}. + */ + static int sizeImpl(Multiset multiset) { + long size = 0; + for (Entry entry : multiset.entrySet()) { + size += entry.getCount(); + } + return Ints.saturatedCast(size); + } + + /** + * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 + */ + static Multiset cast(Iterable iterable) { + return (Multiset) iterable; + } + + private static final Ordering> DECREASING_COUNT_ORDERING = new Ordering>() { + @Override + public int compare(Entry entry1, Entry entry2) { + return Ints.compare(entry2.getCount(), entry1.getCount()); + } + }; + + /** + * Returns a copy of {@code multiset} as an {@link ImmutableMultiset} whose + * iteration order is highest count first, with ties broken by the iteration + * order of the original multiset. + * + * @since 11.0 + */ + @Beta + public static ImmutableMultiset copyHighestCountFirst(Multiset multiset) { + List> sortedEntries = Multisets.DECREASING_COUNT_ORDERING.immutableSortedCopy(multiset.entrySet()); + return ImmutableMultiset.copyFromEntries(sortedEntries); + } +} diff --git a/sources/main/java/com/google/common/collect/MutableClassToInstanceMap.java b/sources/main/java/com/google/common/collect/MutableClassToInstanceMap.java new file mode 100644 index 0000000..25fab7d --- /dev/null +++ b/sources/main/java/com/google/common/collect/MutableClassToInstanceMap.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.MapConstraints.ConstrainedMap; +import com.google.common.primitives.Primitives; + +/** + * A mutable class-to-instance map backed by an arbitrary user-provided map. See + * also {@link ImmutableClassToInstanceMap}. + * + *

+ * See the Guava User Guide article on + * {@code ClassToInstanceMap}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +public final class MutableClassToInstanceMap extends ConstrainedMap, B> + implements ClassToInstanceMap { + + /** + * Returns a new {@code MutableClassToInstanceMap} instance backed by a + * {@link HashMap} using the default initial capacity and load factor. + */ + public static MutableClassToInstanceMap create() { + return new MutableClassToInstanceMap(new HashMap, B>()); + } + + /** + * Returns a new {@code MutableClassToInstanceMap} instance backed by a given + * empty {@code backingMap}. The caller surrenders control of the backing map, + * and thus should not allow any direct references to it to remain accessible. + */ + public static MutableClassToInstanceMap create(Map, B> backingMap) { + return new MutableClassToInstanceMap(backingMap); + } + + private MutableClassToInstanceMap(Map, B> delegate) { + super(delegate, VALUE_CAN_BE_CAST_TO_KEY); + } + + private static final MapConstraint, Object> VALUE_CAN_BE_CAST_TO_KEY = new MapConstraint, Object>() { + @Override + public void checkKeyValue(Class key, Object value) { + cast(key, value); + } + }; + + @Override + public T putInstance(Class type, T value) { + return cast(type, put(type, value)); + } + + @Override + public T getInstance(Class type) { + return cast(type, get(type)); + } + + private static T cast(Class type, B value) { + return Primitives.wrap(type).cast(value); + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/NaturalOrdering.java b/sources/main/java/com/google/common/collect/NaturalOrdering.java new file mode 100644 index 0000000..5e85c03 --- /dev/null +++ b/sources/main/java/com/google/common/collect/NaturalOrdering.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; + +import com.google.common.annotations.GwtCompatible; + +/** An ordering that uses the natural order of the values. */ +@GwtCompatible(serializable = true) +@SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? +final class NaturalOrdering extends Ordering implements Serializable { + static final NaturalOrdering INSTANCE = new NaturalOrdering(); + + @Override + public int compare(Comparable left, Comparable right) { + checkNotNull(left); // for GWT + checkNotNull(right); + return left.compareTo(right); + } + + @Override + public Ordering reverse() { + return (Ordering) ReverseNaturalOrdering.INSTANCE; + } + + // preserving singleton-ness gives equals()/hashCode() for free + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.natural()"; + } + + private NaturalOrdering() { + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/NullsFirstOrdering.java b/sources/main/java/com/google/common/collect/NullsFirstOrdering.java new file mode 100644 index 0000000..6176fcc --- /dev/null +++ b/sources/main/java/com/google/common/collect/NullsFirstOrdering.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** An ordering that treats {@code null} as less than all other values. */ +@GwtCompatible(serializable = true) +final class NullsFirstOrdering extends Ordering implements Serializable { + final Ordering ordering; + + NullsFirstOrdering(Ordering ordering) { + this.ordering = ordering; + } + + @Override + public int compare(@Nullable T left, @Nullable T right) { + if (left == right) { + return 0; + } + if (left == null) { + return RIGHT_IS_GREATER; + } + if (right == null) { + return LEFT_IS_GREATER; + } + return ordering.compare(left, right); + } + + @Override + public Ordering reverse() { + // ordering.reverse() might be optimized, so let it do its thing + return ordering.reverse().nullsLast(); + } + + @SuppressWarnings("unchecked") // still need the right way to explain this + @Override + public Ordering nullsFirst() { + return (Ordering) this; + } + + @Override + public Ordering nullsLast() { + return ordering.nullsLast(); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof NullsFirstOrdering) { + NullsFirstOrdering that = (NullsFirstOrdering) object; + return this.ordering.equals(that.ordering); + } + return false; + } + + @Override + public int hashCode() { + return ordering.hashCode() ^ 957692532; // meaningless + } + + @Override + public String toString() { + return ordering + ".nullsFirst()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/NullsLastOrdering.java b/sources/main/java/com/google/common/collect/NullsLastOrdering.java new file mode 100644 index 0000000..d8cf607 --- /dev/null +++ b/sources/main/java/com/google/common/collect/NullsLastOrdering.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** An ordering that treats {@code null} as greater than all other values. */ +@GwtCompatible(serializable = true) +final class NullsLastOrdering extends Ordering implements Serializable { + final Ordering ordering; + + NullsLastOrdering(Ordering ordering) { + this.ordering = ordering; + } + + @Override + public int compare(@Nullable T left, @Nullable T right) { + if (left == right) { + return 0; + } + if (left == null) { + return LEFT_IS_GREATER; + } + if (right == null) { + return RIGHT_IS_GREATER; + } + return ordering.compare(left, right); + } + + @Override + public Ordering reverse() { + // ordering.reverse() might be optimized, so let it do its thing + return ordering.reverse().nullsFirst(); + } + + @Override + public Ordering nullsFirst() { + return ordering.nullsFirst(); + } + + @SuppressWarnings("unchecked") // still need the right way to explain this + @Override + public Ordering nullsLast() { + return (Ordering) this; + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof NullsLastOrdering) { + NullsLastOrdering that = (NullsLastOrdering) object; + return this.ordering.equals(that.ordering); + } + return false; + } + + @Override + public int hashCode() { + return ordering.hashCode() ^ -921210296; // meaningless + } + + @Override + public String toString() { + return ordering + ".nullsLast()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ObjectArrays.java b/sources/main/java/com/google/common/collect/ObjectArrays.java new file mode 100644 index 0000000..b0ee28f --- /dev/null +++ b/sources/main/java/com/google/common/collect/ObjectArrays.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkPositionIndexes; + +import java.lang.reflect.Array; +import java.util.Collection; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * Static utility methods pertaining to object arrays. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public 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 + */ + @GwtIncompatible("Array.newInstance(Class, int)") + @SuppressWarnings("unchecked") + 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); + } + + /** + * 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 + */ + @GwtIncompatible("Array.newInstance(Class, int)") + public static T[] concat(T[] first, T[] second, Class type) { + T[] result = newArray(type, first.length + second.length); + System.arraycopy(first, 0, result, 0, first.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } + + /** + * 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(@Nullable T element, T[] array) { + T[] result = newArray(array, array.length + 1); + result[0] = element; + System.arraycopy(array, 0, result, 1, array.length); + return result; + } + + /** + * 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, @Nullable 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/sources/main/java/com/google/common/collect/Ordering.java b/sources/main/java/com/google/common/collect/Ordering.java new file mode 100644 index 0000000..dc070b3 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Ordering.java @@ -0,0 +1,871 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.TreeSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; + +/** + * A comparator, with additional methods to support common operations. This is + * an "enriched" version of {@code Comparator}, in the same sense that + * {@link FluentIterable} is an enriched {@link Iterable}. + * + *

+ * The common ways to get an instance of {@code Ordering} are: + * + *

    + *
  • Subclass it and implement {@link #compare} instead of implementing + * {@link Comparator} directly + *
  • Pass a pre-existing {@link Comparator} instance to + * {@link #from(Comparator)} + *
  • Use the natural ordering, {@link Ordering#natural} + *
+ * + *

+ * Then you can use the chaining methods to get an altered version of + * that {@code Ordering}, including: + * + *

    + *
  • {@link #reverse} + *
  • {@link #compound(Comparator)} + *
  • {@link #onResultOf(Function)} + *
  • {@link #nullsFirst} / {@link #nullsLast} + *
+ * + *

+ * Finally, use the resulting {@code Ordering} anywhere a {@link Comparator} is + * required, or use any of its special operations, such as: + *

+ * + *
    + *
  • {@link #immutableSortedCopy} + *
  • {@link #isOrdered} / {@link #isStrictlyOrdered} + *
  • {@link #min} / {@link #max} + *
+ * + *

+ * Except as noted, the orderings returned by the factory methods of this class + * are serializable if and only if the provided instances that back them are. + * For example, if {@code ordering} and {@code function} can themselves be + * serialized, then {@code ordering.onResultOf(function)} can as well. + * + *

+ * See the Guava User Guide article on + * + * {@code Ordering}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public abstract class Ordering implements Comparator { + // Natural order + + /** + * Returns a serializable ordering that uses the natural order of the values. + * The ordering throws a {@link NullPointerException} when passed a null + * parameter. + * + *

+ * The type specification is {@code }, instead of the + * technically correct {@code >}, to support + * legacy types from before Java 5. + */ + @GwtCompatible(serializable = true) + @SuppressWarnings("unchecked") // TODO(kevinb): right way to explain this?? + public static Ordering natural() { + return (Ordering) NaturalOrdering.INSTANCE; + } + + // Static factories + + /** + * Returns an ordering based on an existing comparator instance. Note + * that it is unnecessary to create a new anonymous inner class + * implementing {@code Comparator} just to pass it in here. Instead, simply + * subclass {@code Ordering} and implement its {@code compare} method directly. + * + * @param comparator the comparator that defines the order + * @return comparator itself if it is already an {@code Ordering}; otherwise an + * ordering that wraps that comparator + */ + @GwtCompatible(serializable = true) + public static Ordering from(Comparator comparator) { + return (comparator instanceof Ordering) ? (Ordering) comparator : new ComparatorOrdering(comparator); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + */ + @GwtCompatible(serializable = true) + @Deprecated + public static Ordering from(Ordering ordering) { + return checkNotNull(ordering); + } + + /** + * Returns an ordering that compares objects according to the order in which + * they appear in the given list. Only objects present in the list (according to + * {@link Object#equals}) may be compared. This comparator imposes a "partial + * ordering" over the type {@code T}. Subsequent changes to the + * {@code valuesInOrder} list will have no effect on the returned comparator. + * Null values in the list are not supported. + * + *

+ * The returned comparator throws an {@link ClassCastException} when it receives + * an input parameter that isn't among the provided values. + * + *

+ * The generated comparator is serializable if all the provided values are + * serializable. + * + * @param valuesInOrder the values that the returned comparator will be able to + * compare, in the order the comparator should induce + * @return the comparator described above + * @throws NullPointerException if any of the provided values is null + * @throws IllegalArgumentException if {@code valuesInOrder} contains any + * duplicate values (according to + * {@link Object#equals}) + */ + @GwtCompatible(serializable = true) + public static Ordering explicit(List valuesInOrder) { + return new ExplicitOrdering(valuesInOrder); + } + + /** + * Returns an ordering that compares objects according to the order in which + * they are given to this method. Only objects present in the argument list + * (according to {@link Object#equals}) may be compared. This comparator imposes + * a "partial ordering" over the type {@code T}. Null values in the argument + * list are not supported. + * + *

+ * The returned comparator throws a {@link ClassCastException} when it receives + * an input parameter that isn't among the provided values. + * + *

+ * The generated comparator is serializable if all the provided values are + * serializable. + * + * @param leastValue the value which the returned comparator should + * consider the "least" of all values + * @param remainingValuesInOrder the rest of the values that the returned + * comparator will be able to compare, in the + * order the comparator should follow + * @return the comparator described above + * @throws NullPointerException if any of the provided values is null + * @throws IllegalArgumentException if any duplicate values (according to + * {@link Object#equals(Object)}) are present + * among the method arguments + */ + @GwtCompatible(serializable = true) + public static Ordering explicit(T leastValue, T... remainingValuesInOrder) { + return explicit(Lists.asList(leastValue, remainingValuesInOrder)); + } + + // Ordering singletons + + /** + * Returns an ordering which treats all values as equal, indicating "no + * ordering." Passing this ordering to any stable sort algorithm results + * in no change to the order of elements. Note especially that + * {@link #sortedCopy} and {@link #immutableSortedCopy} are stable, and in the + * returned instance these are implemented by simply copying the source list. + * + *

+ * Example: + * + *

+	 *    {@code
+	 *
+	 *   Ordering.allEqual().nullsLast().sortedCopy(
+	 *       asList(t, null, e, s, null, t, null))}
+	 * 
+ * + *

+ * Assuming {@code t}, {@code e} and {@code s} are non-null, this returns + * {@code [t, e, s, t, null, null, null]} regardlesss of the true comparison + * order of those three values (which might not even implement + * {@link Comparable} at all). + * + *

+ * Warning: by definition, this comparator is not consistent with + * equals (as defined {@linkplain Comparator here}). Avoid its use in APIs, + * such as {@link TreeSet#TreeSet(Comparator)}, where such consistency is + * expected. + * + *

+ * The returned comparator is serializable. + */ + @GwtCompatible(serializable = true) + @SuppressWarnings("unchecked") + public static Ordering allEqual() { + return AllEqualOrdering.INSTANCE; + } + + /** + * Returns an ordering that compares objects by the natural ordering of their + * string representations as returned by {@code toString()}. It does not support + * null values. + * + *

+ * The comparator is serializable. + */ + @GwtCompatible(serializable = true) + public static Ordering usingToString() { + return UsingToStringOrdering.INSTANCE; + } + + // Constructor + + /** + * Constructs a new instance of this class (only invokable by the subclass + * constructor, typically implicit). + */ + protected Ordering() { + } + + // Instance-based factories (and any static equivalents) + + /** + * Returns the reverse of this ordering; the {@code Ordering} equivalent to + * {@link Collections#reverseOrder(Comparator)}. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().reverse(); + @GwtCompatible(serializable = true) + public Ordering reverse() { + return new ReverseOrdering(this); + } + + /** + * Returns an ordering that treats {@code null} as less than all other values + * and uses {@code this} to compare non-null values. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().nullsFirst(); + @GwtCompatible(serializable = true) + public Ordering nullsFirst() { + return new NullsFirstOrdering(this); + } + + /** + * Returns an ordering that treats {@code null} as greater than all other values + * and uses this ordering to compare non-null values. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().nullsLast(); + @GwtCompatible(serializable = true) + public Ordering nullsLast() { + return new NullsLastOrdering(this); + } + + /** + * Returns a new ordering on {@code F} which orders elements by first applying a + * function to them, then comparing those results using {@code this}. For + * example, to compare objects by their string forms, in a case-insensitive + * manner, use: + * + *
+	 *    {@code
+	 *
+	 *   Ordering.from(String.CASE_INSENSITIVE_ORDER)
+	 *       .onResultOf(Functions.toStringFunction())}
+	 * 
+ */ + @GwtCompatible(serializable = true) + public Ordering onResultOf(Function function) { + return new ByFunctionOrdering(function, this); + } + + Ordering> onKeys() { + return onResultOf(Maps.keyFunction()); + } + + /** + * Returns an ordering which first uses the ordering {@code this}, but which in + * the event of a "tie", then delegates to {@code secondaryComparator}. For + * example, to sort a bug list first by status and second by priority, you might + * use {@code byStatus.compound(byPriority)}. For a compound ordering with three + * or more components, simply chain multiple calls to this method. + * + *

+ * An ordering produced by this method, or a chain of calls to this method, is + * equivalent to one created using {@link Ordering#compound(Iterable)} on the + * same component comparators. + */ + @GwtCompatible(serializable = true) + public Ordering compound(Comparator secondaryComparator) { + return new CompoundOrdering(this, checkNotNull(secondaryComparator)); + } + + /** + * Returns an ordering which tries each given comparator in order until a + * non-zero result is found, returning that result, and returning zero only if + * all comparators return zero. The returned ordering is based on the state of + * the {@code comparators} iterable at the time it was provided to this method. + * + *

+ * The returned ordering is equivalent to that produced using {@code + * Ordering.from(comp1).compound(comp2).compound(comp3) . . .}. + * + *

+ * Warning: Supplying an argument with undefined iteration order, such as + * a {@link HashSet}, will produce non-deterministic results. + * + * @param comparators the comparators to try in order + */ + @GwtCompatible(serializable = true) + public static Ordering compound(Iterable> comparators) { + return new CompoundOrdering(comparators); + } + + /** + * Returns a new ordering which sorts iterables by comparing corresponding + * elements pairwise until a nonzero result is found; imposes "dictionary + * order". If the end of one iterable is reached, but not the other, the shorter + * iterable is considered to be less than the longer one. For example, a + * lexicographical natural ordering over integers considers {@code + * [] < [1] < [1, 1] < [1, 2] < [2]}. + * + *

+ * Note that {@code ordering.lexicographical().reverse()} is not equivalent to + * {@code ordering.reverse().lexicographical()} (consider how each would order + * {@code [1]} and {@code [1, 1]}). + * + * @since 2.0 + */ + @GwtCompatible(serializable = true) + // type parameter lets us avoid the extra in statements like: + // Ordering> o = + // Ordering.natural().lexicographical(); + public Ordering> lexicographical() { + /* + * Note that technically the returned ordering should be capable of handling not + * just {@code Iterable} instances, but also any {@code Iterable}. However, the need for this comes up so rarely that it doesn't justify + * making everyone else deal with the very ugly wildcard. + */ + return new LexicographicalOrdering(this); + } + + // Regular instance methods + + // Override to add @Nullable + @Override + public abstract int compare(@Nullable T left, @Nullable T right); + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. The iterator + * will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @param iterator the iterator whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + * + * @since 11.0 + */ + public E min(Iterator iterator) { + // let this throw NoSuchElementException as necessary + E minSoFar = iterator.next(); + + while (iterator.hasNext()) { + minSoFar = min(minSoFar, iterator.next()); + } + + return minSoFar; + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. + * + * @param iterable the iterable whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + public E min(Iterable iterable) { + return min(iterable.iterator()); + } + + /** + * Returns the lesser of the two values according to this ordering. If the + * values compare as 0, the first is returned. + * + *

+ * Implementation note: this method is invoked by the default + * implementations of the other {@code min} overloads, so overriding it will + * affect their behavior. + * + * @param a value to compare, returned if less than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + public E min(@Nullable E a, @Nullable E b) { + return (compare(a, b) <= 0) ? a : b; + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. + * + * @param a value to compare, returned if less than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + public E min(@Nullable E a, @Nullable E b, @Nullable E c, E... rest) { + E minSoFar = min(min(a, b), c); + + for (E r : rest) { + minSoFar = min(minSoFar, r); + } + + return minSoFar; + } + + /** + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. The + * iterator will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @param iterator the iterator whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + * + * @since 11.0 + */ + public E max(Iterator iterator) { + // let this throw NoSuchElementException as necessary + E maxSoFar = iterator.next(); + + while (iterator.hasNext()) { + maxSoFar = max(maxSoFar, iterator.next()); + } + + return maxSoFar; + } + + /** + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. + * + * @param iterable the iterable whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + public E max(Iterable iterable) { + return max(iterable.iterator()); + } + + /** + * Returns the greater of the two values according to this ordering. If the + * values compare as 0, the first is returned. + * + *

+ * Implementation note: this method is invoked by the default + * implementations of the other {@code max} overloads, so overriding it will + * affect their behavior. + * + * @param a value to compare, returned if greater than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + public E max(@Nullable E a, @Nullable E b) { + return (compare(a, b) >= 0) ? a : b; + } + + /** + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. + * + * @param a value to compare, returned if greater than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + public E max(@Nullable E a, @Nullable E b, @Nullable E c, E... rest) { + E maxSoFar = max(max(a, b), c); + + for (E r : rest) { + maxSoFar = max(maxSoFar, r); + } + + return maxSoFar; + } + + /** + * Returns the {@code k} least elements of the given iterable according to this + * ordering, in order from least to greatest. If there are fewer than {@code k} + * elements present, all will be included. + * + *

+ * The implementation does not necessarily use a stable sorting + * algorithm; when multiple elements are equivalent, it is undefined which will + * come first. + * + * @return an immutable {@code RandomAccess} list of the {@code k} least + * elements in ascending order + * @throws IllegalArgumentException if {@code k} is negative + * @since 8.0 + */ + public List leastOf(Iterable iterable, int k) { + if (iterable instanceof Collection) { + Collection collection = (Collection) iterable; + if (collection.size() <= 2L * k) { + // In this case, just dumping the collection to an array and sorting is + // faster than using the implementation for Iterator, which is + // specialized for k much smaller than n. + + @SuppressWarnings("unchecked") // c only contains E's and doesn't escape + E[] array = (E[]) collection.toArray(); + Arrays.sort(array, this); + if (array.length > k) { + array = ObjectArrays.arraysCopyOf(array, k); + } + return Collections.unmodifiableList(Arrays.asList(array)); + } + } + return leastOf(iterable.iterator(), k); + } + + /** + * Returns the {@code k} least elements from the given iterator according to + * this ordering, in order from least to greatest. If there are fewer than + * {@code k} elements present, all will be included. + * + *

+ * The implementation does not necessarily use a stable sorting + * algorithm; when multiple elements are equivalent, it is undefined which will + * come first. + * + * @return an immutable {@code RandomAccess} list of the {@code k} least + * elements in ascending order + * @throws IllegalArgumentException if {@code k} is negative + * @since 14.0 + */ + public List leastOf(Iterator elements, int k) { + checkNotNull(elements); + checkNonnegative(k, "k"); + + if (k == 0 || !elements.hasNext()) { + return ImmutableList.of(); + } else if (k >= Integer.MAX_VALUE / 2) { + // k is really large; just do a straightforward sorted-copy-and-sublist + ArrayList list = Lists.newArrayList(elements); + Collections.sort(list, this); + if (list.size() > k) { + list.subList(k, list.size()).clear(); + } + list.trimToSize(); + return Collections.unmodifiableList(list); + } + + /* + * Our goal is an O(n) algorithm using only one pass and O(k) additional memory. + * + * We use the following algorithm: maintain a buffer of size 2*k. Every time the + * buffer gets full, find the median and partition around it, keeping only the + * lowest k elements. This requires n/k find-median-and-partition steps, each of + * which take O(k) time with a traditional quickselect. + * + * After sorting the output, the whole algorithm is O(n + k log k). It degrades + * gracefully for worst-case input (descending order), performs competitively or + * wins outright for randomly ordered input, and doesn't require the whole + * collection to fit into memory. + */ + int bufferCap = k * 2; + @SuppressWarnings("unchecked") // we'll only put E's in + E[] buffer = (E[]) new Object[bufferCap]; + E threshold = elements.next(); + buffer[0] = threshold; + int bufferSize = 1; + // threshold is the kth smallest element seen so far. Once bufferSize >= k, + // anything larger than threshold can be ignored immediately. + + while (bufferSize < k && elements.hasNext()) { + E e = elements.next(); + buffer[bufferSize++] = e; + threshold = max(threshold, e); + } + + while (elements.hasNext()) { + E e = elements.next(); + if (compare(e, threshold) >= 0) { + continue; + } + + buffer[bufferSize++] = e; + if (bufferSize == bufferCap) { + // We apply the quickselect algorithm to partition about the median, + // and then ignore the last k elements. + int left = 0; + int right = bufferCap - 1; + + int minThresholdPosition = 0; + // The leftmost position at which the greatest of the k lower elements + // -- the new value of threshold -- might be found. + + while (left < right) { + int pivotIndex = (left + right + 1) >>> 1; + int pivotNewIndex = partition(buffer, left, right, pivotIndex); + if (pivotNewIndex > k) { + right = pivotNewIndex - 1; + } else if (pivotNewIndex < k) { + left = Math.max(pivotNewIndex, left + 1); + minThresholdPosition = pivotNewIndex; + } else { + break; + } + } + bufferSize = k; + + threshold = buffer[minThresholdPosition]; + for (int i = minThresholdPosition + 1; i < bufferSize; i++) { + threshold = max(threshold, buffer[i]); + } + } + } + + Arrays.sort(buffer, 0, bufferSize, this); + + bufferSize = Math.min(bufferSize, k); + return Collections.unmodifiableList(Arrays.asList(ObjectArrays.arraysCopyOf(buffer, bufferSize))); + // We can't use ImmutableList; we have to be null-friendly! + } + + private int partition(E[] values, int left, int right, int pivotIndex) { + E pivotValue = values[pivotIndex]; + + values[pivotIndex] = values[right]; + values[right] = pivotValue; + + int storeIndex = left; + for (int i = left; i < right; i++) { + if (compare(values[i], pivotValue) < 0) { + ObjectArrays.swap(values, storeIndex, i); + storeIndex++; + } + } + ObjectArrays.swap(values, right, storeIndex); + return storeIndex; + } + + /** + * Returns the {@code k} greatest elements of the given iterable according to + * this ordering, in order from greatest to least. If there are fewer than + * {@code k} elements present, all will be included. + * + *

+ * The implementation does not necessarily use a stable sorting + * algorithm; when multiple elements are equivalent, it is undefined which will + * come first. + * + * @return an immutable {@code RandomAccess} list of the {@code k} greatest + * elements in descending order + * @throws IllegalArgumentException if {@code k} is negative + * @since 8.0 + */ + public List greatestOf(Iterable iterable, int k) { + // TODO(kevinb): see if delegation is hurting performance noticeably + // TODO(kevinb): if we change this implementation, add full unit tests. + return reverse().leastOf(iterable, k); + } + + /** + * Returns the {@code k} greatest elements from the given iterator according to + * this ordering, in order from greatest to least. If there are fewer than + * {@code k} elements present, all will be included. + * + *

+ * The implementation does not necessarily use a stable sorting + * algorithm; when multiple elements are equivalent, it is undefined which will + * come first. + * + * @return an immutable {@code RandomAccess} list of the {@code k} greatest + * elements in descending order + * @throws IllegalArgumentException if {@code k} is negative + * @since 14.0 + */ + public List greatestOf(Iterator iterator, int k) { + return reverse().leastOf(iterator, k); + } + + /** + * Returns a mutable list containing {@code elements} sorted by this + * ordering; use this only when the resulting list may need further + * modification, or may contain {@code null}. The input is not modified. The + * returned list is serializable and has random access. + * + *

+ * Unlike {@link Sets#newTreeSet(Iterable)}, this method does not discard + * elements that are duplicates according to the comparator. The sort performed + * is stable, meaning that such elements will appear in the returned list + * in the same order they appeared in {@code elements}. + * + *

+ * Performance note: According to our benchmarking on Open JDK 7, + * {@link #immutableSortedCopy} generally performs better (in both time and + * space) than this method, and this method in turn generally performs better + * than copying the list and calling {@link Collections#sort(List)}. + */ + public List sortedCopy(Iterable elements) { + @SuppressWarnings("unchecked") // does not escape, and contains only E's + E[] array = (E[]) Iterables.toArray(elements); + Arrays.sort(array, this); + return Lists.newArrayList(Arrays.asList(array)); + } + + /** + * Returns an immutable list containing {@code elements} sorted by this + * ordering. The input is not modified. + * + *

+ * Unlike {@link Sets#newTreeSet(Iterable)}, this method does not discard + * elements that are duplicates according to the comparator. The sort performed + * is stable, meaning that such elements will appear in the returned list + * in the same order they appeared in {@code elements}. + * + *

+ * Performance note: According to our benchmarking on Open JDK 7, this + * method is the most efficient way to make a sorted copy of a collection. + * + * @throws NullPointerException if any of {@code elements} (or {@code + * elements} itself) is null + * @since 3.0 + */ + public ImmutableList immutableSortedCopy(Iterable elements) { + @SuppressWarnings("unchecked") // we'll only ever have E's in here + E[] array = (E[]) Iterables.toArray(elements); + for (E e : array) { + checkNotNull(e); + } + Arrays.sort(array, this); + return ImmutableList.asImmutableList(array); + } + + /** + * Returns {@code true} if each element in {@code iterable} after the first is + * greater than or equal to the element that preceded it, according to this + * ordering. Note that this is always true when the iterable has fewer than two + * elements. + */ + public boolean isOrdered(Iterable iterable) { + Iterator it = iterable.iterator(); + if (it.hasNext()) { + T prev = it.next(); + while (it.hasNext()) { + T next = it.next(); + if (compare(prev, next) > 0) { + return false; + } + prev = next; + } + } + return true; + } + + /** + * Returns {@code true} if each element in {@code iterable} after the first is + * strictly greater than the element that preceded it, according to this + * ordering. Note that this is always true when the iterable has fewer than two + * elements. + */ + public boolean isStrictlyOrdered(Iterable iterable) { + Iterator it = iterable.iterator(); + if (it.hasNext()) { + T prev = it.next(); + while (it.hasNext()) { + T next = it.next(); + if (compare(prev, next) >= 0) { + return false; + } + prev = next; + } + } + return true; + } + + /** + * {@link Collections#binarySearch(List, Object, Comparator) Searches} + * {@code sortedList} for {@code key} using the binary search algorithm. The + * list must be sorted using this ordering. + * + * @param sortedList the list to be searched + * @param key the key to be searched for + */ + public int binarySearch(List sortedList, @Nullable T key) { + return Collections.binarySearch(sortedList, key, this); + } + + /** + * Exception thrown by a {@link Ordering#explicit(List)} or + * {@link Ordering#explicit(Object, Object[])} comparator when comparing a value + * outside the set of values it can compare. Extending + * {@link ClassCastException} may seem odd, but it is required. + */ + // TODO(kevinb): make this public, document it right + @VisibleForTesting + static class IncomparableValueException extends ClassCastException { + final Object value; + + IncomparableValueException(Object value) { + super("Cannot compare value: " + value); + this.value = value; + } + + private static final long serialVersionUID = 0; + } + + // Never make these public + static final int LEFT_IS_GREATER = 1; + static final int RIGHT_IS_GREATER = -1; +} diff --git a/sources/main/java/com/google/common/collect/PeekingIterator.java b/sources/main/java/com/google/common/collect/PeekingIterator.java new file mode 100644 index 0000000..6a16c69 --- /dev/null +++ b/sources/main/java/com/google/common/collect/PeekingIterator.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import com.google.common.annotations.GwtCompatible; + +/** + * An iterator that supports a one-element lookahead while iterating. + * + *

+ * See the Guava User Guide article on + * {@code PeekingIterator}. + * + * @author Mick Killianey + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface PeekingIterator extends Iterator { + /** + * Returns the next element in the iteration, without advancing the iteration. + * + *

+ * Calls to {@code peek()} should not change the state of the iteration, except + * that it may prevent removal of the most recent element via + * {@link #remove()}. + * + * @throws NoSuchElementException if the iteration has no more elements + * according to {@link #hasNext()} + */ + E peek(); + + /** + * {@inheritDoc} + * + *

+ * The objects returned by consecutive calls to {@link #peek()} then + * {@link #next()} are guaranteed to be equal to each other. + */ + @Override + E next(); + + /** + * {@inheritDoc} + * + *

+ * Implementations may or may not support removal when a call to {@link #peek()} + * has occurred since the most recent call to {@link #next()}. + * + * @throws IllegalStateException if there has been a call to {@link #peek()} + * since the most recent call to {@link #next()} + * and this implementation does not support this + * sequence of calls (optional) + */ + @Override + void remove(); +} diff --git a/sources/main/java/com/google/common/collect/Platform.java b/sources/main/java/com/google/common/collect/Platform.java new file mode 100644 index 0000000..0853408 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Platform.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.lang.reflect.Array; +import java.util.Collections; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Maps.EntryTransformer; + +/** + * Methods factored out so that they can be emulated differently in GWT. + * + * @author Hayward Chan + */ +@GwtCompatible(emulated = true) +final class Platform { + /** + * 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 + */ + static T[] newArray(T[] reference, int length) { + Class type = reference.getClass().getComponentType(); + + // the cast is safe because + // result.getClass() == reference.getClass().getComponentType() + @SuppressWarnings("unchecked") + T[] result = (T[]) Array.newInstance(type, length); + return result; + } + + static Set newSetFromMap(Map map) { + return Collections.newSetFromMap(map); + } + + static SortedMap mapsTransformEntriesSortedMap(SortedMap fromMap, + EntryTransformer transformer) { + return (fromMap instanceof NavigableMap) ? Maps.transformEntries((NavigableMap) fromMap, transformer) + : Maps.transformEntriesIgnoreNavigable(fromMap, transformer); + } + + static SortedMap mapsAsMapSortedSet(SortedSet set, Function function) { + return (set instanceof NavigableSet) ? Maps.asMap((NavigableSet) set, function) + : Maps.asMapSortedIgnoreNavigable(set, function); + } + + static SortedSet setsFilterSortedSet(SortedSet set, Predicate predicate) { + return (set instanceof NavigableSet) ? Sets.filter((NavigableSet) set, predicate) + : Sets.filterSortedIgnoreNavigable(set, predicate); + } + + static SortedMap mapsFilterSortedMap(SortedMap map, + Predicate> predicate) { + return (map instanceof NavigableMap) ? Maps.filterEntries((NavigableMap) map, predicate) + : Maps.filterSortedIgnoreNavigable(map, predicate); + } + + private Platform() { + } +} diff --git a/sources/main/java/com/google/common/collect/Range.java b/sources/main/java/com/google/common/collect/Range.java new file mode 100644 index 0000000..9521fef --- /dev/null +++ b/sources/main/java/com/google/common/collect/Range.java @@ -0,0 +1,746 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Equivalence; +import com.google.common.base.Function; +import com.google.common.base.Predicate; + +/** + * A range (or "interval") defines the boundaries around a contiguous + * span of values of some {@code Comparable} type; for example, "integers from 1 + * to 100 inclusive." Note that it is not possible to iterate over these + * contained values. To do so, pass this range instance and an appropriate + * {@link DiscreteDomain} to {@link ContiguousSet#create}. + * + *

Types of ranges

+ * + *

+ * Each end of the range may be bounded or unbounded. If bounded, there is an + * associated endpoint value, and the range is considered to be either + * open (does not include the endpoint) or closed (includes the + * endpoint) on that side. With three possibilities on each side, this yields + * nine basic types of ranges, enumerated below. (Notation: a square bracket + * ({@code [ ]}) indicates that the range is closed on that side; a parenthesis + * ({@code ( )}) means it is either open or unbounded. The construct {@code {x | + * statement}} is read "the set of all x such that statement.") + * + *

+ * + * + * + * + * + * + * + * + * + * + * + *
Notation + * Definition + * Factory method + *
{@code (a..b)} + * {@code {x | a < x < b}} + * {@link Range#open open} + *
{@code [a..b]} + * {@code {x | a <= x <= b}} + * {@link Range#closed closed} + *
{@code (a..b]} + * {@code {x | a < x <= b}} + * {@link Range#openClosed openClosed} + *
{@code [a..b)} + * {@code {x | a <= x < b}} + * {@link Range#closedOpen closedOpen} + *
{@code (a..+∞)} + * {@code {x | x > a}} + * {@link Range#greaterThan greaterThan} + *
{@code [a..+∞)} + * {@code {x | x >= a}} + * {@link Range#atLeast atLeast} + *
{@code (-∞..b)} + * {@code {x | x < b}} + * {@link Range#lessThan lessThan} + *
{@code (-∞..b]} + * {@code {x | x <= b}} + * {@link Range#atMost atMost} + *
{@code (-∞..+∞)} + * {@code {x}} + * {@link Range#all all} + *
+ *
+ * + *

+ * When both endpoints exist, the upper endpoint may not be less than the lower. + * The endpoints may be equal only if at least one of the bounds is closed: + * + *

    + *
  • {@code [a..a]} : a singleton range + *
  • {@code [a..a); (a..a]} : {@linkplain #isEmpty empty} ranges; also valid + *
  • {@code (a..a)} : invalid; an exception will be thrown + *
+ * + *

Warnings

+ * + *
    + *
  • Use immutable value types only, if at all possible. If you must use a + * mutable type, do not allow the endpoint instances to mutate after the + * range is created! + *
  • Your value type's comparison method should be {@linkplain Comparable + * consistent with equals} if at all possible. Otherwise, be aware that concepts + * used throughout this documentation such as "equal", "same", "unique" and so + * on actually refer to whether {@link Comparable#compareTo compareTo} returns + * zero, not whether {@link Object#equals equals} returns {@code true}. + *
  • A class which implements {@code Comparable} is very + * broken, and will cause undefined horrible things to happen in {@code Range}. + * For now, the Range API does not prevent its use, because this would also rule + * out all ungenerified (pre-JDK1.5) data types. This may change in the + * future. + *
+ * + *

Other notes

+ * + *
    + *
  • Instances of this type are obtained using the static factory methods in + * this class. + *
  • Ranges are convex: whenever two values are contained, all values + * in between them must also be contained. More formally, for any + * {@code c1 <= c2 <= c3} of type {@code C}, {@code + * r.contains(c1) && r.contains(c3)} implies {@code r.contains(c2)}). This + * means that a {@code + * Range} can never be used to represent, say, "all prime + * numbers from 1 to 100." + *
  • When evaluated as a {@link Predicate}, a range yields the same result as + * invoking {@link #contains}. + *
  • Terminology note: a range {@code a} is said to be the maximal + * range having property P if, for all ranges {@code b} also having + * property P, {@code a.encloses(b)}. Likewise, {@code a} is + * minimal when {@code b.encloses(a)} for all {@code b} having property + * P. See, for example, the definition of {@link #intersection + * intersection}. + *
+ * + *

Further reading

+ * + *

+ * See the Guava User Guide article on {@code Range}. + * + * @author Kevin Bourrillion + * @author Gregory Kick + * @since 10.0 + */ +@GwtCompatible +@SuppressWarnings("rawtypes") +public final class Range implements Predicate, Serializable { + + private static final Function LOWER_BOUND_FN = new Function() { + @Override + public Cut apply(Range range) { + return range.lowerBound; + } + }; + + @SuppressWarnings("unchecked") + static > Function, Cut> lowerBoundFn() { + return (Function) LOWER_BOUND_FN; + } + + private static final Function UPPER_BOUND_FN = new Function() { + @Override + public Cut apply(Range range) { + return range.upperBound; + } + }; + + @SuppressWarnings("unchecked") + static > Function, Cut> upperBoundFn() { + return (Function) UPPER_BOUND_FN; + } + + static final Ordering> RANGE_LEX_ORDERING = new Ordering>() { + @Override + public int compare(Range left, Range right) { + return ComparisonChain.start().compare(left.lowerBound, right.lowerBound) + .compare(left.upperBound, right.upperBound).result(); + } + }; + + static > Range create(Cut lowerBound, Cut upperBound) { + return new Range(lowerBound, upperBound); + } + + /** + * Returns a range that contains all values strictly greater than {@code + * lower} and strictly less than {@code upper}. + * + * @throws IllegalArgumentException if {@code lower} is greater than or equal + * to {@code upper} + * @since 14.0 + */ + public static > Range open(C lower, C upper) { + return create(Cut.aboveValue(lower), Cut.belowValue(upper)); + } + + /** + * Returns a range that contains all values greater than or equal to + * {@code lower} and less than or equal to {@code upper}. + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code + * upper} + * @since 14.0 + */ + public static > Range closed(C lower, C upper) { + return create(Cut.belowValue(lower), Cut.aboveValue(upper)); + } + + /** + * Returns a range that contains all values greater than or equal to + * {@code lower} and strictly less than {@code upper}. + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code + * upper} + * @since 14.0 + */ + public static > Range closedOpen(C lower, C upper) { + return create(Cut.belowValue(lower), Cut.belowValue(upper)); + } + + /** + * Returns a range that contains all values strictly greater than {@code + * lower} and less than or equal to {@code upper}. + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code + * upper} + * @since 14.0 + */ + public static > Range openClosed(C lower, C upper) { + return create(Cut.aboveValue(lower), Cut.aboveValue(upper)); + } + + /** + * Returns a range that contains any value from {@code lower} to {@code + * upper}, where each endpoint may be either inclusive (closed) or exclusive + * (open). + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code + * upper} + * @since 14.0 + */ + public static > Range range(C lower, BoundType lowerType, C upper, BoundType upperType) { + checkNotNull(lowerType); + checkNotNull(upperType); + + Cut lowerBound = (lowerType == BoundType.OPEN) ? Cut.aboveValue(lower) : Cut.belowValue(lower); + Cut upperBound = (upperType == BoundType.OPEN) ? Cut.belowValue(upper) : Cut.aboveValue(upper); + return create(lowerBound, upperBound); + } + + /** + * Returns a range that contains all values strictly less than {@code + * endpoint}. + * + * @since 14.0 + */ + public static > Range lessThan(C endpoint) { + return create(Cut.belowAll(), Cut.belowValue(endpoint)); + } + + /** + * Returns a range that contains all values less than or equal to + * {@code endpoint}. + * + * @since 14.0 + */ + public static > Range atMost(C endpoint) { + return create(Cut.belowAll(), Cut.aboveValue(endpoint)); + } + + /** + * Returns a range with no lower bound up to the given endpoint, which may be + * either inclusive (closed) or exclusive (open). + * + * @since 14.0 + */ + public static > Range upTo(C endpoint, BoundType boundType) { + switch (boundType) { + case OPEN: + return lessThan(endpoint); + case CLOSED: + return atMost(endpoint); + default: + throw new AssertionError(); + } + } + + /** + * Returns a range that contains all values strictly greater than {@code + * endpoint}. + * + * @since 14.0 + */ + public static > Range greaterThan(C endpoint) { + return create(Cut.aboveValue(endpoint), Cut.aboveAll()); + } + + /** + * Returns a range that contains all values greater than or equal to + * {@code endpoint}. + * + * @since 14.0 + */ + public static > Range atLeast(C endpoint) { + return create(Cut.belowValue(endpoint), Cut.aboveAll()); + } + + /** + * Returns a range from the given endpoint, which may be either inclusive + * (closed) or exclusive (open), with no upper bound. + * + * @since 14.0 + */ + public static > Range downTo(C endpoint, BoundType boundType) { + switch (boundType) { + case OPEN: + return greaterThan(endpoint); + case CLOSED: + return atLeast(endpoint); + default: + throw new AssertionError(); + } + } + + private static final Range ALL = new Range(Cut.belowAll(), Cut.aboveAll()); + + /** + * Returns a range that contains every value of type {@code C}. + * + * @since 14.0 + */ + @SuppressWarnings("unchecked") + public static > Range all() { + return (Range) ALL; + } + + /** + * Returns a range that {@linkplain Range#contains(Comparable) contains} only + * the given value. The returned range is {@linkplain BoundType#CLOSED closed} + * on both ends. + * + * @since 14.0 + */ + public static > Range singleton(C value) { + return closed(value, value); + } + + /** + * Returns the minimal range that {@linkplain Range#contains(Comparable) + * contains} all of the given values. The returned range is + * {@linkplain BoundType#CLOSED closed} on both ends. + * + * @throws ClassCastException if the parameters are not mutually + * comparable + * @throws NoSuchElementException if {@code values} is empty + * @throws NullPointerException if any of {@code values} is null + * @since 14.0 + */ + public static > Range encloseAll(Iterable values) { + checkNotNull(values); + if (values instanceof ContiguousSet) { + return ((ContiguousSet) values).range(); + } + Iterator valueIterator = values.iterator(); + C min = checkNotNull(valueIterator.next()); + C max = min; + while (valueIterator.hasNext()) { + C value = checkNotNull(valueIterator.next()); + min = Ordering.natural().min(min, value); + max = Ordering.natural().max(max, value); + } + return closed(min, max); + } + + final Cut lowerBound; + final Cut upperBound; + + private Range(Cut lowerBound, Cut upperBound) { + if (lowerBound.compareTo(upperBound) > 0 || lowerBound == Cut.aboveAll() + || upperBound == Cut.belowAll()) { + throw new IllegalArgumentException("Invalid range: " + toString(lowerBound, upperBound)); + } + this.lowerBound = checkNotNull(lowerBound); + this.upperBound = checkNotNull(upperBound); + } + + /** + * Returns {@code true} if this range has a lower endpoint. + */ + public boolean hasLowerBound() { + return lowerBound != Cut.belowAll(); + } + + /** + * Returns the lower endpoint of this range. + * + * @throws IllegalStateException if this range is unbounded below (that is, + * {@link #hasLowerBound()} returns {@code false}) + */ + public C lowerEndpoint() { + return lowerBound.endpoint(); + } + + /** + * Returns the type of this range's lower bound: {@link BoundType#CLOSED} if the + * range includes its lower endpoint, {@link BoundType#OPEN} if it does not. + * + * @throws IllegalStateException if this range is unbounded below (that is, + * {@link #hasLowerBound()} returns {@code false}) + */ + public BoundType lowerBoundType() { + return lowerBound.typeAsLowerBound(); + } + + /** + * Returns {@code true} if this range has an upper endpoint. + */ + public boolean hasUpperBound() { + return upperBound != Cut.aboveAll(); + } + + /** + * Returns the upper endpoint of this range. + * + * @throws IllegalStateException if this range is unbounded above (that is, + * {@link #hasUpperBound()} returns {@code false}) + */ + public C upperEndpoint() { + return upperBound.endpoint(); + } + + /** + * Returns the type of this range's upper bound: {@link BoundType#CLOSED} if the + * range includes its upper endpoint, {@link BoundType#OPEN} if it does not. + * + * @throws IllegalStateException if this range is unbounded above (that is, + * {@link #hasUpperBound()} returns {@code false}) + */ + public BoundType upperBoundType() { + return upperBound.typeAsUpperBound(); + } + + /** + * Returns {@code true} if this range is of the form {@code [v..v)} or + * {@code (v..v]}. (This does not encompass ranges of the form {@code (v..v)}, + * because such ranges are invalid and can't be constructed at all.) + * + *

+ * Note that certain discrete ranges such as the integer range {@code (3..4)} + * are not considered empty, even though they contain no actual values. + * In these cases, it may be helpful to preprocess ranges with + * {@link #canonical(DiscreteDomain)}. + */ + public boolean isEmpty() { + return lowerBound.equals(upperBound); + } + + /** + * Returns {@code true} if {@code value} is within the bounds of this range. For + * example, on the range {@code [0..2)}, {@code contains(1)} returns + * {@code true}, while {@code contains(2)} returns {@code false}. + */ + public boolean contains(C value) { + checkNotNull(value); + // let this throw CCE if there is some trickery going on + return lowerBound.isLessThan(value) && !upperBound.isLessThan(value); + } + + /** + * @deprecated Provided only to satisfy the {@link Predicate} interface; use + * {@link #contains} instead. + */ + @Deprecated + @Override + public boolean apply(C input) { + return contains(input); + } + + /** + * Returns {@code true} if every element in {@code values} is + * {@linkplain #contains contained} in this range. + */ + public boolean containsAll(Iterable values) { + if (Iterables.isEmpty(values)) { + return true; + } + + // this optimizes testing equality of two range-backed sets + if (values instanceof SortedSet) { + SortedSet set = cast(values); + Comparator comparator = set.comparator(); + if (Ordering.natural().equals(comparator) || comparator == null) { + return contains(set.first()) && contains(set.last()); + } + } + + for (C value : values) { + if (!contains(value)) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} if the bounds of {@code other} do not extend outside the + * bounds of this range. Examples: + * + *

    + *
  • {@code [3..6]} encloses {@code [4..5]} + *
  • {@code (3..6)} encloses {@code (3..6)} + *
  • {@code [3..6]} encloses {@code [4..4)} (even though the latter is empty) + *
  • {@code (3..6]} does not enclose {@code [3..6]} + *
  • {@code [4..5]} does not enclose {@code (3..6)} (even though it contains + * every value contained by the latter range) + *
  • {@code [3..6]} does not enclose {@code (1..1]} (even though it contains + * every value contained by the latter range) + *
+ * + *

+ * Note that if {@code a.encloses(b)}, then {@code b.contains(v)} implies + * {@code a.contains(v)}, but as the last two examples illustrate, the converse + * is not always true. + * + *

+ * Being reflexive, antisymmetric and transitive, the {@code encloses} relation + * defines a partial order over ranges. There exists a unique + * {@linkplain Range#all maximal} range according to this relation, and also + * numerous {@linkplain #isEmpty minimal} ranges. Enclosure also implies + * {@linkplain #isConnected connectedness}. + */ + public boolean encloses(Range other) { + return lowerBound.compareTo(other.lowerBound) <= 0 && upperBound.compareTo(other.upperBound) >= 0; + } + + /** + * Returns {@code true} if there exists a (possibly empty) range which is + * {@linkplain #encloses enclosed} by both this range and {@code other}. + * + *

+ * For example, + *

    + *
  • {@code [2, 4)} and {@code [5, 7)} are not connected + *
  • {@code [2, 4)} and {@code [3, 5)} are connected, because both enclose + * {@code [3, 4)} + *
  • {@code [2, 4)} and {@code [4, 6)} are connected, because both enclose the + * empty range {@code [4, 4)} + *
+ * + *

+ * Note that this range and {@code other} have a well-defined {@linkplain #span + * union} and {@linkplain #intersection intersection} (as a single, + * possibly-empty range) if and only if this method returns {@code true}. + * + *

+ * The connectedness relation is both reflexive and symmetric, but does not form + * an {@linkplain Equivalence equivalence relation} as it is not transitive. + * + *

+ * Note that certain discrete ranges are not considered connected, even though + * there are no elements "between them." For example, {@code [3, 5]} is not + * considered connected to {@code + * [6, 10]}. In these cases, it may be desirable for both input ranges to be + * preprocessed with {@link #canonical(DiscreteDomain)} before testing for + * connectedness. + */ + public boolean isConnected(Range other) { + return lowerBound.compareTo(other.upperBound) <= 0 && other.lowerBound.compareTo(upperBound) <= 0; + } + + /** + * Returns the maximal range {@linkplain #encloses enclosed} by both this range + * and {@code + * connectedRange}, if such a range exists. + * + *

+ * For example, the intersection of {@code [1..5]} and {@code (3..7)} is + * {@code (3..5]}. The resulting range may be empty; for example, {@code [1..5)} + * intersected with {@code [5..7)} yields the empty range {@code [5..5)}. + * + *

+ * The intersection exists if and only if the two ranges are + * {@linkplain #isConnected connected}. + * + *

+ * The intersection operation is commutative, associative and idempotent, and + * its identity element is {@link Range#all}). + * + * @throws IllegalArgumentException if {@code isConnected(connectedRange)} is + * {@code false} + */ + public Range intersection(Range connectedRange) { + int lowerCmp = lowerBound.compareTo(connectedRange.lowerBound); + int upperCmp = upperBound.compareTo(connectedRange.upperBound); + if (lowerCmp >= 0 && upperCmp <= 0) { + return this; + } else if (lowerCmp <= 0 && upperCmp >= 0) { + return connectedRange; + } else { + Cut newLower = (lowerCmp >= 0) ? lowerBound : connectedRange.lowerBound; + Cut newUpper = (upperCmp <= 0) ? upperBound : connectedRange.upperBound; + return create(newLower, newUpper); + } + } + + /** + * Returns the minimal range that {@linkplain #encloses encloses} both this + * range and {@code + * other}. For example, the span of {@code [1..3]} and {@code (5..7)} is + * {@code [1..7)}. + * + *

+ * If the input ranges are {@linkplain #isConnected connected}, the + * returned range can also be called their union. If they are not, note + * that the span might contain values that are not contained in either input + * range. + * + *

+ * Like {@link #intersection(Range) intersection}, this operation is + * commutative, associative and idempotent. Unlike it, it is always well-defined + * for any two input ranges. + */ + public Range span(Range other) { + int lowerCmp = lowerBound.compareTo(other.lowerBound); + int upperCmp = upperBound.compareTo(other.upperBound); + if (lowerCmp <= 0 && upperCmp >= 0) { + return this; + } else if (lowerCmp >= 0 && upperCmp <= 0) { + return other; + } else { + Cut newLower = (lowerCmp <= 0) ? lowerBound : other.lowerBound; + Cut newUpper = (upperCmp >= 0) ? upperBound : other.upperBound; + return create(newLower, newUpper); + } + } + + /** + * Returns the canonical form of this range in the given domain. The canonical + * form has the following properties: + * + *

    + *
  • equivalence: {@code a.canonical().contains(v) == a.contains(v)} for all + * {@code v} (in other words, + * {@code ContiguousSet.create(a.canonical(domain), domain).equals( + * ContiguousSet.create(a, domain))} + *
  • uniqueness: unless {@code a.isEmpty()}, + * {@code ContiguousSet.create(a, domain).equals(ContiguousSet.create(b, domain))} + * implies {@code a.canonical(domain).equals(b.canonical(domain))} + *
  • idempotence: + * {@code a.canonical(domain).canonical(domain).equals(a.canonical(domain))} + *
+ * + *

+ * Furthermore, this method guarantees that the range returned will be one of + * the following canonical forms: + * + *

    + *
  • [start..end) + *
  • [start..+∞) + *
  • (-∞..end) (only if type {@code C} is unbounded below) + *
  • (-∞..+∞) (only if type {@code C} is unbounded below) + *
+ */ + public Range canonical(DiscreteDomain domain) { + checkNotNull(domain); + Cut lower = lowerBound.canonical(domain); + Cut upper = upperBound.canonical(domain); + return (lower == lowerBound && upper == upperBound) ? this : create(lower, upper); + } + + /** + * Returns {@code true} if {@code object} is a range having the same endpoints + * and bound types as this range. Note that discrete ranges such as + * {@code (1..4)} and {@code [2..3]} are not equal to one another, + * despite the fact that they each contain precisely the same set of values. + * Similarly, empty ranges are not equal unless they have exactly the same + * representation, so {@code [3..3)}, {@code (3..3]}, {@code (4..4]} are all + * unequal. + */ + @Override + public boolean equals(@Nullable Object object) { + if (object instanceof Range) { + Range other = (Range) object; + return lowerBound.equals(other.lowerBound) && upperBound.equals(other.upperBound); + } + return false; + } + + /** Returns a hash code for this range. */ + @Override + public int hashCode() { + return lowerBound.hashCode() * 31 + upperBound.hashCode(); + } + + /** + * Returns a string representation of this range, such as {@code "[3..5)"} + * (other examples are listed in the class documentation). + */ + @Override + public String toString() { + return toString(lowerBound, upperBound); + } + + private static String toString(Cut lowerBound, Cut upperBound) { + StringBuilder sb = new StringBuilder(16); + lowerBound.describeAsLowerBound(sb); + sb.append('\u2025'); + upperBound.describeAsUpperBound(sb); + return sb.toString(); + } + + /** + * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 + */ + private static SortedSet cast(Iterable iterable) { + return (SortedSet) iterable; + } + + Object readResolve() { + if (this.equals(ALL)) { + return all(); + } else { + return this; + } + } + + @SuppressWarnings("unchecked") // this method may throw CCE + static int compareOrThrow(Comparable left, Comparable right) { + return left.compareTo(right); + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/RangeMap.java b/sources/main/java/com/google/common/collect/RangeMap.java new file mode 100644 index 0000000..37c947e --- /dev/null +++ b/sources/main/java/com/google/common/collect/RangeMap.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map; +import java.util.NoSuchElementException; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; + +/** + * A mapping from disjoint nonempty ranges to non-null values. Queries look up + * the value associated with the range (if any) that contains a specified key. + * + *

+ * In contrast to {@link RangeSet}, no "coalescing" is done of + * {@linkplain Range#isConnected(Range) connected} ranges, even if they are + * mapped to the same value. + * + * @author Louis Wasserman + * @since 14.0 + */ +@Beta +public interface RangeMap { + /** + * Returns the value associated with the specified key, or {@code null} if there + * is no such value. + * + *

+ * Specifically, if any range in this range map contains the specified key, the + * value associated with that range is returned. + */ + @Nullable + V get(K key); + + /** + * Returns the range containing this key and its associated value, if such a + * range is present in the range map, or {@code null} otherwise. + */ + @Nullable + Map.Entry, V> getEntry(K key); + + /** + * Returns the minimal range {@linkplain Range#encloses(Range) enclosing} the + * ranges in this {@code RangeMap}. + * + * @throws NoSuchElementException if this range map is empty + */ + Range span(); + + /** + * Maps a range to a specified value (optional operation). + * + *

+ * Specifically, after a call to {@code put(range, value)}, if + * {@link Range#contains(Comparable) range.contains(k)}, then + * {@link #get(Comparable) get(k)} will return {@code value}. + * + *

+ * If {@code range} {@linkplain Range#isEmpty() is empty}, then this is a no-op. + */ + void put(Range range, V value); + + /** + * Puts all the associations from {@code rangeMap} into this range map (optional + * operation). + */ + void putAll(RangeMap rangeMap); + + /** + * Removes all associations from this range map (optional operation). + */ + void clear(); + + /** + * Removes all associations from this range map in the specified range (optional + * operation). + * + *

+ * If {@code !range.contains(k)}, {@link #get(Comparable) get(k)} will return + * the same result before and after a call to {@code remove(range)}. If + * {@code range.contains(k)}, then after a call to {@code remove(range)}, + * {@code get(k)} will return {@code null}. + */ + void remove(Range range); + + /** + * Returns a view of this range map as an unmodifiable {@code Map, V>}. + * Modifications to this range map are guaranteed to read through to the + * returned {@code Map}. + * + *

+ * It is guaranteed that no empty ranges will be in the returned {@code Map}. + */ + Map, V> asMapOfRanges(); + + /** + * Returns a view of the part of this range map that intersects with + * {@code range}. + * + *

+ * For example, if {@code rangeMap} had the entries + * {@code [1, 5] => "foo", (6, 8) => "bar", (10, \u2025) => "baz"} then + * {@code rangeMap.subRangeMap(Range.open(3, 12))} would return a range map with + * the entries {@code (3, 5) => "foo", (6, 8) => "bar", (10, 12) => "baz"}. + * + *

+ * The returned range map supports all optional operations that this range map + * supports, except for {@code asMapOfRanges().iterator().remove()}. + * + *

+ * The returned range map will throw an {@link IllegalArgumentException} on an + * attempt to insert a range not {@linkplain Range#encloses(Range) enclosed} by + * {@code range}. + */ + RangeMap subRangeMap(Range range); + + /** + * Returns {@code true} if {@code obj} is another {@code RangeMap} that has an + * equivalent {@link #asMapOfRanges()}. + */ + @Override + boolean equals(@Nullable Object o); + + /** + * Returns {@code asMapOfRanges().hashCode()}. + */ + @Override + int hashCode(); + + /** + * Returns a readable string representation of this range map. + */ + @Override + String toString(); +} diff --git a/sources/main/java/com/google/common/collect/RangeSet.java b/sources/main/java/com/google/common/collect/RangeSet.java new file mode 100644 index 0000000..48da68f --- /dev/null +++ b/sources/main/java/com/google/common/collect/RangeSet.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; + +/** + * A set comprising zero or more {@linkplain Range#isEmpty nonempty}, + * {@linkplain Range#isConnected(Range) disconnected} ranges of type {@code C}. + * + *

+ * Implementations that choose to support the {@link #add(Range)} operation are + * required to ignore empty ranges and coalesce connected ranges. For example: + * + *

+ *    {@code
+ *
+ *   RangeSet rangeSet = TreeRangeSet.create();
+ *   rangeSet.add(Range.closed(1, 10)); // {[1, 10]}
+ *   rangeSet.add(Range.closedOpen(11, 15)); // disconnected range; {[1, 10], [11, 15)}
+ *   rangeSet.add(Range.closedOpen(15, 20)); // connected range; {[1, 10], [11, 20)}
+ *   rangeSet.add(Range.openClosed(0, 0)); // empty range; {[1, 10], [11, 20)}
+ *   rangeSet.remove(Range.open(5, 10)); // splits [1, 10]; {[1, 5], [10, 10], [11, 20)}}
+ * 
+ * + *

+ * Note that the behavior of {@link Range#isEmpty()} and + * {@link Range#isConnected(Range)} may not be as expected on discrete ranges. + * See the Javadoc of those methods for details. + * + *

+ * For a {@link Set} whose contents are specified by a {@link Range}, see + * {@link ContiguousSet}. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 14.0 + */ +@Beta +public interface RangeSet { + + // Query methods + + /** + * Determines whether any of this range set's member ranges contains + * {@code value}. + */ + boolean contains(C value); + + /** + * Returns the unique range from this range set that {@linkplain Range#contains + * contains} {@code value}, or {@code null} if this range set does not contain + * {@code value}. + */ + Range rangeContaining(C value); + + /** + * Returns {@code true} if there exists a member range in this range set which + * {@linkplain Range#encloses encloses} the specified range. + */ + boolean encloses(Range otherRange); + + /** + * Returns {@code true} if for each member range in {@code other} there exists a + * member range in this range set which {@linkplain Range#encloses encloses} it. + * It follows that {@code this.contains(value)} whenever + * {@code other.contains(value)}. Returns {@code true} if {@code other} is + * empty. + * + *

+ * This is equivalent to checking if this range set {@link #encloses} each of + * the ranges in {@code other}. + */ + boolean enclosesAll(RangeSet other); + + /** + * Returns {@code true} if this range set contains no ranges. + */ + boolean isEmpty(); + + /** + * Returns the minimal range which {@linkplain Range#encloses(Range) encloses} + * all ranges in this range set. + * + * @throws NoSuchElementException if this range set is {@linkplain #isEmpty() + * empty} + */ + Range span(); + + // Views + + /** + * Returns a view of the {@linkplain Range#isConnected disconnected} ranges that + * make up this range set. The returned set may be empty. The iterators returned + * by its {@link Iterable#iterator} method return the ranges in increasing order + * of lower bound (equivalently, of upper bound). + */ + Set> asRanges(); + + /** + * Returns a view of the complement of this {@code RangeSet}. + * + *

+ * The returned view supports the {@link #add} operation if this + * {@code RangeSet} supports {@link #remove}, and vice versa. + */ + RangeSet complement(); + + /** + * Returns a view of the intersection of this {@code RangeSet} with the + * specified range. + * + *

+ * The returned view supports all optional operations supported by this + * {@code RangeSet}, with the caveat that an {@link IllegalArgumentException} is + * thrown on an attempt to {@linkplain #add(Range) add} any range not + * {@linkplain Range#encloses(Range) enclosed} by {@code view}. + */ + RangeSet subRangeSet(Range view); + + // Modification + + /** + * Adds the specified range to this {@code RangeSet} (optional operation). That + * is, for equal range sets a and b, the result of {@code a.add(range)} is that + * {@code a} will be the minimal range set for which both + * {@code a.enclosesAll(b)} and {@code a.encloses(range)}. + * + *

+ * Note that {@code range} will be {@linkplain Range#span(Range) coalesced} with + * any ranges in the range set that are {@linkplain Range#isConnected(Range) + * connected} with it. Moreover, if {@code range} is empty, this is a no-op. + * + * @throws UnsupportedOperationException if this range set does not support the + * {@code add} operation + */ + void add(Range range); + + /** + * Removes the specified range from this {@code RangeSet} (optional operation). + * After this operation, if {@code range.contains(c)}, {@code this.contains(c)} + * will return {@code false}. + * + *

+ * If {@code range} is empty, this is a no-op. + * + * @throws UnsupportedOperationException if this range set does not support the + * {@code remove} operation + */ + void remove(Range range); + + /** + * Removes all ranges from this {@code RangeSet} (optional operation). After + * this operation, {@code this.contains(c)} will return false for all {@code c}. + * + *

+ * This is equivalent to {@code remove(Range.all())}. + * + * @throws UnsupportedOperationException if this range set does not support the + * {@code clear} operation + */ + void clear(); + + /** + * Adds all of the ranges from the specified range set to this range set + * (optional operation). After this operation, this range set is the minimal + * range set that {@linkplain #enclosesAll(RangeSet) encloses} both the original + * range set and {@code other}. + * + *

+ * This is equivalent to calling {@link #add} on each of the ranges in + * {@code other} in turn. + * + * @throws UnsupportedOperationException if this range set does not support the + * {@code addAll} operation + */ + void addAll(RangeSet other); + + /** + * Removes all of the ranges from the specified range set from this range set + * (optional operation). After this operation, if {@code other.contains(c)}, + * {@code this.contains(c)} will return {@code false}. + * + *

+ * This is equivalent to calling {@link #remove} on each of the ranges in + * {@code other} in turn. + * + * @throws UnsupportedOperationException if this range set does not support the + * {@code removeAll} operation + */ + void removeAll(RangeSet other); + + // Object methods + + /** + * Returns {@code true} if {@code obj} is another {@code RangeSet} that contains + * the same ranges according to {@link Range#equals(Object)}. + */ + @Override + boolean equals(@Nullable Object obj); + + /** + * Returns {@code asRanges().hashCode()}. + */ + @Override + int hashCode(); + + /** + * Returns a readable string representation of this range set. For example, if + * this {@code RangeSet} consisted of {@code Range.closed(1, 3)} and + * {@code Range.greaterThan(4)}, this might return + * {@code " [1‥3](4‥+∞)}"}. + */ + @Override + String toString(); +} diff --git a/sources/main/java/com/google/common/collect/RegularContiguousSet.java b/sources/main/java/com/google/common/collect/RegularContiguousSet.java new file mode 100644 index 0000000..51ede32 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularContiguousSet.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.BoundType.CLOSED; + +import java.io.Serializable; +import java.util.Collection; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An implementation of {@link ContiguousSet} that contains one or more + * elements. + * + * @author Gregory Kick + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("unchecked") // allow ungenerified Comparable types +final class RegularContiguousSet extends ContiguousSet { + private final Range range; + + RegularContiguousSet(Range range, DiscreteDomain domain) { + super(domain); + this.range = range; + } + + private ContiguousSet intersectionInCurrentDomain(Range other) { + return (range.isConnected(other)) ? ContiguousSet.create(range.intersection(other), domain) + : new EmptyContiguousSet(domain); + } + + @Override + ContiguousSet headSetImpl(C toElement, boolean inclusive) { + return intersectionInCurrentDomain(Range.upTo(toElement, BoundType.forBoolean(inclusive))); + } + + @Override + ContiguousSet subSetImpl(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + if (fromElement.compareTo(toElement) == 0 && !fromInclusive && !toInclusive) { + // Range would reject our attempt to create (x, x). + return new EmptyContiguousSet(domain); + } + return intersectionInCurrentDomain(Range.range(fromElement, BoundType.forBoolean(fromInclusive), toElement, + BoundType.forBoolean(toInclusive))); + } + + @Override + ContiguousSet tailSetImpl(C fromElement, boolean inclusive) { + return intersectionInCurrentDomain(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); + } + + @GwtIncompatible("not used by GWT emulation") + @Override + int indexOf(Object target) { + return contains(target) ? (int) domain.distance(first(), (C) target) : -1; + } + + @Override + public UnmodifiableIterator iterator() { + return new AbstractSequentialIterator(first()) { + final C last = last(); + + @Override + protected C computeNext(C previous) { + return equalsOrThrow(previous, last) ? null : domain.next(previous); + } + }; + } + + @GwtIncompatible("NavigableSet") + @Override + public UnmodifiableIterator descendingIterator() { + return new AbstractSequentialIterator(last()) { + final C first = first(); + + @Override + protected C computeNext(C previous) { + return equalsOrThrow(previous, first) ? null : domain.previous(previous); + } + }; + } + + private static boolean equalsOrThrow(Comparable left, @Nullable Comparable right) { + return right != null && Range.compareOrThrow(left, right) == 0; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public C first() { + return range.lowerBound.leastValueAbove(domain); + } + + @Override + public C last() { + return range.upperBound.greatestValueBelow(domain); + } + + @Override + public int size() { + long distance = domain.distance(first(), last()); + return (distance >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) distance + 1; + } + + @Override + public boolean contains(@Nullable Object object) { + if (object == null) { + return false; + } + try { + return range.contains((C) object); + } catch (ClassCastException e) { + return false; + } + } + + @Override + public boolean containsAll(Collection targets) { + return Collections2.containsAllImpl(this, targets); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public ContiguousSet intersection(ContiguousSet other) { + checkNotNull(other); + checkArgument(this.domain.equals(other.domain)); + if (other.isEmpty()) { + return other; + } else { + C lowerEndpoint = Ordering.natural().max(this.first(), other.first()); + C upperEndpoint = Ordering.natural().min(this.last(), other.last()); + return (lowerEndpoint.compareTo(upperEndpoint) < 0) + ? ContiguousSet.create(Range.closed(lowerEndpoint, upperEndpoint), domain) + : new EmptyContiguousSet(domain); + } + } + + @Override + public Range range() { + return range(CLOSED, CLOSED); + } + + @Override + public Range range(BoundType lowerBoundType, BoundType upperBoundType) { + return Range.create(range.lowerBound.withLowerBoundType(lowerBoundType, domain), + range.upperBound.withUpperBoundType(upperBoundType, domain)); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (object instanceof RegularContiguousSet) { + RegularContiguousSet that = (RegularContiguousSet) object; + if (this.domain.equals(that.domain)) { + return this.first().equals(that.first()) && this.last().equals(that.last()); + } + } + return super.equals(object); + } + + // copied to make sure not to use the GWT-emulated version + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @GwtIncompatible("serialization") + private static final class SerializedForm implements Serializable { + final Range range; + final DiscreteDomain domain; + + private SerializedForm(Range range, DiscreteDomain domain) { + this.range = range; + this.domain = domain; + } + + private Object readResolve() { + return new RegularContiguousSet(range, domain); + } + } + + @GwtIncompatible("serialization") + @Override + Object writeReplace() { + return new SerializedForm(range, domain); + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/RegularImmutableAsList.java b/sources/main/java/com/google/common/collect/RegularImmutableAsList.java new file mode 100644 index 0000000..52d5f2a --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableAsList.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An {@link ImmutableAsList} implementation specialized for when the delegate + * collection is already backed by an {@code ImmutableList} or array. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") // 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; + } + + @SuppressWarnings("unchecked") // safe covariant cast! + @Override + public UnmodifiableListIterator listIterator(int index) { + return (UnmodifiableListIterator) delegateList.listIterator(index); + } + + @GwtIncompatible("not present in emulated superclass") + @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/sources/main/java/com/google/common/collect/RegularImmutableBiMap.java b/sources/main/java/com/google/common/collect/RegularImmutableBiMap.java new file mode 100644 index 0000000..96e44a1 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableBiMap.java @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.ImmutableMapEntry.TerminalEntry; + +/** + * Bimap with two or more mappings. + * + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +class RegularImmutableBiMap extends ImmutableBiMap { + + static final double MAX_LOAD_FACTOR = 1.2; + + private final transient ImmutableMapEntry[] keyTable; + private final transient ImmutableMapEntry[] valueTable; + private final transient ImmutableMapEntry[] entries; + private final transient int mask; + private final transient int hashCode; + + RegularImmutableBiMap(TerminalEntry... entriesToAdd) { + this(entriesToAdd.length, entriesToAdd); + } + + /** + * Constructor for RegularImmutableBiMap 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. + */ + RegularImmutableBiMap(int n, TerminalEntry[] entriesToAdd) { + int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); + this.mask = tableSize - 1; + ImmutableMapEntry[] keyTable = createEntryArray(tableSize); + ImmutableMapEntry[] valueTable = createEntryArray(tableSize); + ImmutableMapEntry[] entries = createEntryArray(n); + int hashCode = 0; + + for (int i = 0; i < n; i++) { + @SuppressWarnings("unchecked") + TerminalEntry entry = (TerminalEntry) entriesToAdd[i]; + K key = entry.getKey(); + V value = entry.getValue(); + + int keyHash = key.hashCode(); + int valueHash = value.hashCode(); + int keyBucket = Hashing.smear(keyHash) & mask; + int valueBucket = Hashing.smear(valueHash) & mask; + + ImmutableMapEntry nextInKeyBucket = keyTable[keyBucket]; + for (ImmutableMapEntry keyEntry = nextInKeyBucket; keyEntry != null; keyEntry = keyEntry + .getNextInKeyBucket()) { + checkNoConflict(!key.equals(keyEntry.getKey()), "key", entry, keyEntry); + } + ImmutableMapEntry nextInValueBucket = valueTable[valueBucket]; + for (ImmutableMapEntry valueEntry = nextInValueBucket; valueEntry != null; valueEntry = valueEntry + .getNextInValueBucket()) { + checkNoConflict(!value.equals(valueEntry.getValue()), "value", entry, valueEntry); + } + ImmutableMapEntry newEntry = (nextInKeyBucket == null && nextInValueBucket == null) ? entry + : new NonTerminalBiMapEntry(entry, nextInKeyBucket, nextInValueBucket); + keyTable[keyBucket] = newEntry; + valueTable[valueBucket] = newEntry; + entries[i] = newEntry; + hashCode += keyHash ^ valueHash; + } + + this.keyTable = keyTable; + this.valueTable = valueTable; + this.entries = entries; + this.hashCode = hashCode; + } + + /** + * Constructor for RegularImmutableBiMap that makes no assumptions about the + * input entries. + */ + RegularImmutableBiMap(Entry[] entriesToAdd) { + int n = entriesToAdd.length; + int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); + this.mask = tableSize - 1; + ImmutableMapEntry[] keyTable = createEntryArray(tableSize); + ImmutableMapEntry[] valueTable = createEntryArray(tableSize); + ImmutableMapEntry[] entries = createEntryArray(n); + int hashCode = 0; + + for (int i = 0; i < n; i++) { + @SuppressWarnings("unchecked") + Entry entry = (Entry) entriesToAdd[i]; + K key = entry.getKey(); + V value = entry.getValue(); + checkEntryNotNull(key, value); + int keyHash = key.hashCode(); + int valueHash = value.hashCode(); + int keyBucket = Hashing.smear(keyHash) & mask; + int valueBucket = Hashing.smear(valueHash) & mask; + + ImmutableMapEntry nextInKeyBucket = keyTable[keyBucket]; + for (ImmutableMapEntry keyEntry = nextInKeyBucket; keyEntry != null; keyEntry = keyEntry + .getNextInKeyBucket()) { + checkNoConflict(!key.equals(keyEntry.getKey()), "key", entry, keyEntry); + } + ImmutableMapEntry nextInValueBucket = valueTable[valueBucket]; + for (ImmutableMapEntry valueEntry = nextInValueBucket; valueEntry != null; valueEntry = valueEntry + .getNextInValueBucket()) { + checkNoConflict(!value.equals(valueEntry.getValue()), "value", entry, valueEntry); + } + ImmutableMapEntry newEntry = (nextInKeyBucket == null && nextInValueBucket == null) + ? new TerminalEntry(key, value) + : new NonTerminalBiMapEntry(key, value, nextInKeyBucket, nextInValueBucket); + keyTable[keyBucket] = newEntry; + valueTable[valueBucket] = newEntry; + entries[i] = newEntry; + hashCode += keyHash ^ valueHash; + } + + this.keyTable = keyTable; + this.valueTable = valueTable; + this.entries = entries; + this.hashCode = hashCode; + } + + private static final class NonTerminalBiMapEntry extends ImmutableMapEntry { + @Nullable + private final ImmutableMapEntry nextInKeyBucket; + @Nullable + private final ImmutableMapEntry nextInValueBucket; + + NonTerminalBiMapEntry(K key, V value, @Nullable ImmutableMapEntry nextInKeyBucket, + @Nullable ImmutableMapEntry nextInValueBucket) { + super(key, value); + this.nextInKeyBucket = nextInKeyBucket; + this.nextInValueBucket = nextInValueBucket; + } + + NonTerminalBiMapEntry(ImmutableMapEntry contents, @Nullable ImmutableMapEntry nextInKeyBucket, + @Nullable ImmutableMapEntry nextInValueBucket) { + super(contents); + this.nextInKeyBucket = nextInKeyBucket; + this.nextInValueBucket = nextInValueBucket; + } + + @Override + @Nullable + ImmutableMapEntry getNextInKeyBucket() { + return nextInKeyBucket; + } + + @Override + @Nullable + ImmutableMapEntry getNextInValueBucket() { + return nextInValueBucket; + } + } + + @SuppressWarnings("unchecked") + private static ImmutableMapEntry[] createEntryArray(int length) { + return new ImmutableMapEntry[length]; + } + + @Override + @Nullable + public V get(@Nullable Object key) { + if (key == null) { + return null; + } + int bucket = Hashing.smear(key.hashCode()) & mask; + for (ImmutableMapEntry entry = keyTable[bucket]; entry != null; entry = entry.getNextInKeyBucket()) { + if (key.equals(entry.getKey())) { + return entry.getValue(); + } + } + return null; + } + + @Override + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet() { + @Override + ImmutableMap map() { + return RegularImmutableBiMap.this; + } + + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + ImmutableList> createAsList() { + return new RegularImmutableAsList>(this, entries); + } + + @Override + boolean isHashCodeFast() { + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + }; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public int size() { + return entries.length; + } + + private transient ImmutableBiMap inverse; + + @Override + public ImmutableBiMap inverse() { + ImmutableBiMap result = inverse; + return (result == null) ? inverse = new Inverse() : result; + } + + private final class Inverse extends ImmutableBiMap { + + @Override + public int size() { + return inverse().size(); + } + + @Override + public ImmutableBiMap inverse() { + return RegularImmutableBiMap.this; + } + + @Override + public K get(@Nullable Object value) { + if (value == null) { + return null; + } + int bucket = Hashing.smear(value.hashCode()) & mask; + for (ImmutableMapEntry entry = valueTable[bucket]; entry != null; entry = entry + .getNextInValueBucket()) { + if (value.equals(entry.getValue())) { + return entry.getKey(); + } + } + return null; + } + + @Override + ImmutableSet> createEntrySet() { + return new InverseEntrySet(); + } + + final class InverseEntrySet extends ImmutableMapEntrySet { + @Override + ImmutableMap map() { + return Inverse.this; + } + + @Override + boolean isHashCodeFast() { + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + ImmutableList> createAsList() { + return new ImmutableAsList>() { + @Override + public Entry get(int index) { + Entry entry = entries[index]; + return Maps.immutableEntry(entry.getValue(), entry.getKey()); + } + + @Override + ImmutableCollection> delegateCollection() { + return InverseEntrySet.this; + } + }; + } + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + Object writeReplace() { + return new InverseSerializedForm(RegularImmutableBiMap.this); + } + } + + private static class InverseSerializedForm implements Serializable { + private final ImmutableBiMap forward; + + InverseSerializedForm(ImmutableBiMap forward) { + this.forward = forward; + } + + Object readResolve() { + return forward.inverse(); + } + + private static final long serialVersionUID = 1; + } +} diff --git a/sources/main/java/com/google/common/collect/RegularImmutableList.java b/sources/main/java/com/google/common/collect/RegularImmutableList.java new file mode 100644 index 0000000..dba3378 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableList.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; + +/** + * Implementation of {@link ImmutableList} with one or more elements. + * + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // 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 + @SuppressWarnings("unchecked") + public E get(int index) { + Preconditions.checkElementIndex(index, size); + return (E) array[index + offset]; + } + + @Override + public int indexOf(@Nullable 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(@Nullable 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); + } + + @SuppressWarnings("unchecked") + @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/sources/main/java/com/google/common/collect/RegularImmutableMap.java b/sources/main/java/com/google/common/collect/RegularImmutableMap.java new file mode 100644 index 0000000..5fae227 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableMap.java @@ -0,0 +1,212 @@ +/* +* Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.ImmutableMapEntry.TerminalEntry; + +/** + * Implementation of {@link ImmutableMap} with two or more entries. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + * @author Gregory Kick + */ +@GwtCompatible(serializable = true, emulated = true) +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++) { + @SuppressWarnings("unchecked") + TerminalEntry entry = (TerminalEntry) theEntries[entryIndex]; + K key = entry.getKey(); + int tableIndex = Hashing.smear(key.hashCode()) & mask; + @Nullable + 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++) { + @SuppressWarnings("unchecked") // 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; + @Nullable + 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 + @Nullable + 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. + */ + @SuppressWarnings("unchecked") // Safe as long as the javadocs are followed + private ImmutableMapEntry[] createEntryArray(int size) { + return new ImmutableMapEntry[size]; + } + + @Override + public V get(@Nullable 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(); + } + + @SuppressWarnings("serial") // 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/sources/main/java/com/google/common/collect/RegularImmutableMultiset.java b/sources/main/java/com/google/common/collect/RegularImmutableMultiset.java new file mode 100644 index 0000000..833c16c --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableMultiset.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Implementation of {@link ImmutableMultiset} with zero or more elements. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true) +@SuppressWarnings("serial") +// uses writeReplace(), not default serialization +class RegularImmutableMultiset extends ImmutableMultiset { + private final transient ImmutableMap map; + private final transient int size; + + RegularImmutableMultiset(ImmutableMap map, int size) { + this.map = map; + this.size = size; + } + + @Override + boolean isPartialView() { + return map.isPartialView(); + } + + @Override + public int count(@Nullable Object element) { + Integer value = map.get(element); + return (value == null) ? 0 : value; + } + + @Override + public int size() { + return size; + } + + @Override + public boolean contains(@Nullable Object element) { + return map.containsKey(element); + } + + @Override + public ImmutableSet elementSet() { + return map.keySet(); + } + + @Override + Entry getEntry(int index) { + Map.Entry mapEntry = map.entrySet().asList().get(index); + return Multisets.immutableEntry(mapEntry.getKey(), mapEntry.getValue()); + } + + @Override + public int hashCode() { + return map.hashCode(); + } +} diff --git a/sources/main/java/com/google/common/collect/RegularImmutableSet.java b/sources/main/java/com/google/common/collect/RegularImmutableSet.java new file mode 100644 index 0000000..7064295 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableSet.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; + +/** + * Implementation of {@link ImmutableSet} with two or more elements. + * + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // 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; + } + + @SuppressWarnings("unchecked") // 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/sources/main/java/com/google/common/collect/RegularImmutableSortedMap.java b/sources/main/java/com/google/common/collect/RegularImmutableSortedMap.java new file mode 100644 index 0000000..44c0046 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableSortedMap.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An implementation of an immutable sorted map with one or more entries. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") // uses writeReplace, not default serialization +final class RegularImmutableSortedMap extends ImmutableSortedMap { + private final transient RegularImmutableSortedSet keySet; + private final transient ImmutableList valueList; + + RegularImmutableSortedMap(RegularImmutableSortedSet keySet, ImmutableList valueList) { + this.keySet = keySet; + this.valueList = valueList; + } + + RegularImmutableSortedMap(RegularImmutableSortedSet keySet, ImmutableList valueList, + ImmutableSortedMap descendingMap) { + super(descendingMap); + this.keySet = keySet; + this.valueList = valueList; + } + + @Override + ImmutableSet> createEntrySet() { + return new EntrySet(); + } + + private class EntrySet extends ImmutableMapEntrySet { + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + ImmutableList> createAsList() { + return new ImmutableAsList>() { + // avoid additional indirection + private final ImmutableList keyList = keySet().asList(); + + @Override + public Entry get(int index) { + return Maps.immutableEntry(keyList.get(index), valueList.get(index)); + } + + @Override + ImmutableCollection> delegateCollection() { + return EntrySet.this; + } + }; + } + + @Override + ImmutableMap map() { + return RegularImmutableSortedMap.this; + } + } + + @Override + public ImmutableSortedSet keySet() { + return keySet; + } + + @Override + public ImmutableCollection values() { + return valueList; + } + + @Override + public V get(@Nullable Object key) { + int index = keySet.indexOf(key); + return (index == -1) ? null : valueList.get(index); + } + + private ImmutableSortedMap getSubMap(int fromIndex, int toIndex) { + if (fromIndex == 0 && toIndex == size()) { + return this; + } else if (fromIndex == toIndex) { + return emptyMap(comparator()); + } else { + return from(keySet.getSubSet(fromIndex, toIndex), valueList.subList(fromIndex, toIndex)); + } + } + + @Override + public ImmutableSortedMap headMap(K toKey, boolean inclusive) { + return getSubMap(0, keySet.headIndex(checkNotNull(toKey), inclusive)); + } + + @Override + public ImmutableSortedMap tailMap(K fromKey, boolean inclusive) { + return getSubMap(keySet.tailIndex(checkNotNull(fromKey), inclusive), size()); + } + + @Override + ImmutableSortedMap createDescendingMap() { + return new RegularImmutableSortedMap((RegularImmutableSortedSet) keySet.descendingSet(), + valueList.reverse(), this); + } + +} diff --git a/sources/main/java/com/google/common/collect/RegularImmutableSortedMultiset.java b/sources/main/java/com/google/common/collect/RegularImmutableSortedMultiset.java new file mode 100644 index 0000000..b91ea87 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableSortedMultiset.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkPositionIndexes; +import static com.google.common.collect.BoundType.CLOSED; + +import javax.annotation.Nullable; + +import com.google.common.primitives.Ints; + +/** + * An immutable sorted multiset with one or more distinct elements. + * + * @author Louis Wasserman + */ +@SuppressWarnings("serial") // uses writeReplace, not default serialization +final class RegularImmutableSortedMultiset extends ImmutableSortedMultiset { + private final transient RegularImmutableSortedSet elementSet; + private final transient int[] counts; + private final transient long[] cumulativeCounts; + private final transient int offset; + private final transient int length; + + RegularImmutableSortedMultiset(RegularImmutableSortedSet elementSet, int[] counts, long[] cumulativeCounts, + int offset, int length) { + this.elementSet = elementSet; + this.counts = counts; + this.cumulativeCounts = cumulativeCounts; + this.offset = offset; + this.length = length; + } + + @Override + Entry getEntry(int index) { + return Multisets.immutableEntry(elementSet.asList().get(index), counts[offset + index]); + } + + @Override + public Entry firstEntry() { + return getEntry(0); + } + + @Override + public Entry lastEntry() { + return getEntry(length - 1); + } + + @Override + public int count(@Nullable Object element) { + int index = elementSet.indexOf(element); + return (index == -1) ? 0 : counts[index + offset]; + } + + @Override + public int size() { + long size = cumulativeCounts[offset + length] - cumulativeCounts[offset]; + return Ints.saturatedCast(size); + } + + @Override + public ImmutableSortedSet elementSet() { + return elementSet; + } + + @Override + public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { + return getSubMultiset(0, elementSet.headIndex(upperBound, checkNotNull(boundType) == CLOSED)); + } + + @Override + public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return getSubMultiset(elementSet.tailIndex(lowerBound, checkNotNull(boundType) == CLOSED), length); + } + + ImmutableSortedMultiset getSubMultiset(int from, int to) { + checkPositionIndexes(from, to, length); + if (from == to) { + return emptyMultiset(comparator()); + } else if (from == 0 && to == length) { + return this; + } else { + RegularImmutableSortedSet subElementSet = (RegularImmutableSortedSet) elementSet.getSubSet(from, to); + return new RegularImmutableSortedMultiset(subElementSet, counts, cumulativeCounts, offset + from, + to - from); + } + } + + @Override + boolean isPartialView() { + return offset > 0 || length < counts.length; + } +} diff --git a/sources/main/java/com/google/common/collect/RegularImmutableSortedSet.java b/sources/main/java/com/google/common/collect/RegularImmutableSortedSet.java new file mode 100644 index 0000000..2c1ce79 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableSortedSet.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.INVERTED_INSERTION_INDEX; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_HIGHER; +import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; +import static com.google.common.collect.SortedLists.KeyPresentBehavior.FIRST_AFTER; +import static com.google.common.collect.SortedLists.KeyPresentBehavior.FIRST_PRESENT; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * An immutable sorted set with one or more elements. TODO(jlevy): Consider + * separate class for a single-element sorted set. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") +final class RegularImmutableSortedSet extends ImmutableSortedSet { + + private transient final ImmutableList elements; + + RegularImmutableSortedSet(ImmutableList elements, Comparator comparator) { + super(comparator); + this.elements = elements; + checkArgument(!elements.isEmpty()); + } + + @Override + public UnmodifiableIterator iterator() { + return elements.iterator(); + } + + @GwtIncompatible("NavigableSet") + @Override + public UnmodifiableIterator descendingIterator() { + return elements.reverse().iterator(); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public int size() { + return elements.size(); + } + + @Override + public boolean contains(Object o) { + try { + return o != null && unsafeBinarySearch(o) >= 0; + } catch (ClassCastException e) { + return false; + } + } + + @Override + public boolean containsAll(Collection targets) { + // TODO(jlevy): For optimal performance, use a binary search when + // targets.size() < size() / log(size()) + // TODO(kevinb): see if we can share code with OrderedIterator after it + // graduates from labs. + if (targets instanceof Multiset) { + targets = ((Multiset) targets).elementSet(); + } + if (!SortedIterables.hasSameComparator(comparator(), targets) || (targets.size() <= 1)) { + return super.containsAll(targets); + } + + /* + * If targets is a sorted set with the same comparator, containsAll can run in + * O(n) time stepping through the two collections. + */ + PeekingIterator thisIterator = Iterators.peekingIterator(iterator()); + Iterator thatIterator = targets.iterator(); + Object target = thatIterator.next(); + + try { + + while (thisIterator.hasNext()) { + + int cmp = unsafeCompare(thisIterator.peek(), target); + + if (cmp < 0) { + thisIterator.next(); + } else if (cmp == 0) { + + if (!thatIterator.hasNext()) { + + return true; + } + + target = thatIterator.next(); + + } else if (cmp > 0) { + return false; + } + } + } catch (NullPointerException e) { + return false; + } catch (ClassCastException e) { + return false; + } + + return false; + } + + private int unsafeBinarySearch(Object key) throws ClassCastException { + return Collections.binarySearch(elements, key, unsafeComparator()); + } + + @Override + boolean isPartialView() { + return elements.isPartialView(); + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + return elements.copyIntoArray(dst, offset); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (!(object instanceof Set)) { + return false; + } + + Set that = (Set) object; + if (size() != that.size()) { + return false; + } + + if (SortedIterables.hasSameComparator(comparator, that)) { + Iterator otherIterator = that.iterator(); + try { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + Object element = iterator.next(); + Object otherElement = otherIterator.next(); + if (otherElement == null || unsafeCompare(element, otherElement) != 0) { + return false; + } + } + return true; + } catch (ClassCastException e) { + return false; + } catch (NoSuchElementException e) { + return false; // concurrent change to other set + } + } + return this.containsAll(that); + } + + @Override + public E first() { + return elements.get(0); + } + + @Override + public E last() { + return elements.get(size() - 1); + } + + @Override + public E lower(E element) { + int index = headIndex(element, false) - 1; + return (index == -1) ? null : elements.get(index); + } + + @Override + public E floor(E element) { + int index = headIndex(element, true) - 1; + return (index == -1) ? null : elements.get(index); + } + + @Override + public E ceiling(E element) { + int index = tailIndex(element, true); + return (index == size()) ? null : elements.get(index); + } + + @Override + public E higher(E element) { + int index = tailIndex(element, false); + return (index == size()) ? null : elements.get(index); + } + + @Override + ImmutableSortedSet headSetImpl(E toElement, boolean inclusive) { + return getSubSet(0, headIndex(toElement, inclusive)); + } + + int headIndex(E toElement, boolean inclusive) { + return SortedLists.binarySearch(elements, checkNotNull(toElement), comparator(), + inclusive ? FIRST_AFTER : FIRST_PRESENT, NEXT_HIGHER); + } + + @Override + ImmutableSortedSet subSetImpl(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return tailSetImpl(fromElement, fromInclusive).headSetImpl(toElement, toInclusive); + } + + @Override + ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive) { + return getSubSet(tailIndex(fromElement, inclusive), size()); + } + + int tailIndex(E fromElement, boolean inclusive) { + return SortedLists.binarySearch(elements, checkNotNull(fromElement), comparator(), + inclusive ? FIRST_PRESENT : FIRST_AFTER, NEXT_HIGHER); + } + + // Pretend the comparator can compare anything. If it turns out it can't + // compare two elements, it'll throw a CCE. Only methods that are specified to + // throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator unsafeComparator() { + return (Comparator) comparator; + } + + ImmutableSortedSet getSubSet(int newFromIndex, int newToIndex) { + if (newFromIndex == 0 && newToIndex == size()) { + return this; + } else if (newFromIndex < newToIndex) { + return new RegularImmutableSortedSet(elements.subList(newFromIndex, newToIndex), comparator); + } else { + return emptySet(comparator); + } + } + + @Override + int indexOf(@Nullable Object target) { + if (target == null) { + return -1; + } + int position; + try { + position = SortedLists.binarySearch(elements, target, unsafeComparator(), ANY_PRESENT, + INVERTED_INSERTION_INDEX); + } catch (ClassCastException e) { + return -1; + } + return (position >= 0) ? position : -1; + } + + @Override + ImmutableList createAsList() { + return new ImmutableSortedAsList(this, elements); + } + + @Override + ImmutableSortedSet createDescendingSet() { + return new RegularImmutableSortedSet(elements.reverse(), Ordering.from(comparator).reverse()); + } +} diff --git a/sources/main/java/com/google/common/collect/RegularImmutableTable.java b/sources/main/java/com/google/common/collect/RegularImmutableTable.java new file mode 100644 index 0000000..5684a65 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RegularImmutableTable.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An implementation of {@link ImmutableTable} holding an arbitrary number of + * cells. + * + * @author Gregory Kick + */ +@GwtCompatible +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(@Nullable Object object) { + if (object instanceof Cell) { + Cell cell = (Cell) object; + Object value = get(cell.getRowKey(), cell.getColumnKey()); + return value != null && value.equals(cell.getValue()); + } + return false; + } + + @Override + boolean isPartialView() { + return false; + } + } + + 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, + @Nullable final Comparator rowComparator, + @Nullable 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, + @Nullable Comparator rowComparator, @Nullable 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/sources/main/java/com/google/common/collect/ReverseNaturalOrdering.java b/sources/main/java/com/google/common/collect/ReverseNaturalOrdering.java new file mode 100644 index 0000000..8d39a11 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ReverseNaturalOrdering.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Iterator; + +import com.google.common.annotations.GwtCompatible; + +/** An ordering that uses the reverse of the natural order of the values. */ +@GwtCompatible(serializable = true) +@SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? +final class ReverseNaturalOrdering extends Ordering implements Serializable { + static final ReverseNaturalOrdering INSTANCE = new ReverseNaturalOrdering(); + + @Override + public int compare(Comparable left, Comparable right) { + checkNotNull(left); // right null is caught later + if (left == right) { + return 0; + } + + return right.compareTo(left); + } + + @Override + public Ordering reverse() { + return Ordering.natural(); + } + + // Override the min/max methods to "hoist" delegation outside loops + + @Override + public E min(E a, E b) { + return NaturalOrdering.INSTANCE.max(a, b); + } + + @Override + public E min(E a, E b, E c, E... rest) { + return NaturalOrdering.INSTANCE.max(a, b, c, rest); + } + + @Override + public E min(Iterator iterator) { + return NaturalOrdering.INSTANCE.max(iterator); + } + + @Override + public E min(Iterable iterable) { + return NaturalOrdering.INSTANCE.max(iterable); + } + + @Override + public E max(E a, E b) { + return NaturalOrdering.INSTANCE.min(a, b); + } + + @Override + public E max(E a, E b, E c, E... rest) { + return NaturalOrdering.INSTANCE.min(a, b, c, rest); + } + + @Override + public E max(Iterator iterator) { + return NaturalOrdering.INSTANCE.min(iterator); + } + + @Override + public E max(Iterable iterable) { + return NaturalOrdering.INSTANCE.min(iterable); + } + + // preserving singleton-ness gives equals()/hashCode() for free + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.natural().reverse()"; + } + + private ReverseNaturalOrdering() { + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/ReverseOrdering.java b/sources/main/java/com/google/common/collect/ReverseOrdering.java new file mode 100644 index 0000000..59373a7 --- /dev/null +++ b/sources/main/java/com/google/common/collect/ReverseOrdering.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Iterator; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** An ordering that uses the reverse of a given order. */ +@GwtCompatible(serializable = true) +final class ReverseOrdering extends Ordering implements Serializable { + final Ordering forwardOrder; + + ReverseOrdering(Ordering forwardOrder) { + this.forwardOrder = checkNotNull(forwardOrder); + } + + @Override + public int compare(T a, T b) { + return forwardOrder.compare(b, a); + } + + @SuppressWarnings("unchecked") // how to explain? + @Override + public Ordering reverse() { + return (Ordering) forwardOrder; + } + + // Override the min/max methods to "hoist" delegation outside loops + + @Override + public E min(E a, E b) { + return forwardOrder.max(a, b); + } + + @Override + public E min(E a, E b, E c, E... rest) { + return forwardOrder.max(a, b, c, rest); + } + + @Override + public E min(Iterator iterator) { + return forwardOrder.max(iterator); + } + + @Override + public E min(Iterable iterable) { + return forwardOrder.max(iterable); + } + + @Override + public E max(E a, E b) { + return forwardOrder.min(a, b); + } + + @Override + public E max(E a, E b, E c, E... rest) { + return forwardOrder.min(a, b, c, rest); + } + + @Override + public E max(Iterator iterator) { + return forwardOrder.min(iterator); + } + + @Override + public E max(Iterable iterable) { + return forwardOrder.min(iterable); + } + + @Override + public int hashCode() { + return -forwardOrder.hashCode(); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof ReverseOrdering) { + ReverseOrdering that = (ReverseOrdering) object; + return this.forwardOrder.equals(that.forwardOrder); + } + return false; + } + + @Override + public String toString() { + return forwardOrder + ".reverse()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/RowSortedTable.java b/sources/main/java/com/google/common/collect/RowSortedTable.java new file mode 100644 index 0000000..bb77423 --- /dev/null +++ b/sources/main/java/com/google/common/collect/RowSortedTable.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Interface that extends {@code Table} and whose rows are sorted. + * + *

+ * The {@link #rowKeySet} method returns a {@link SortedSet} and the + * {@link #rowMap} method returns a {@link SortedMap}, instead of the + * {@link Set} and {@link Map} specified by the {@link Table} interface. + * + * @author Warren Dukes + * @since 8.0 + */ +@GwtCompatible +@Beta +public interface RowSortedTable extends Table { + /** + * {@inheritDoc} + * + *

+ * This method returns a {@link SortedSet}, instead of the {@code Set} specified + * in the {@link Table} interface. + */ + @Override + SortedSet rowKeySet(); + + /** + * {@inheritDoc} + * + *

+ * This method returns a {@link SortedMap}, instead of the {@code Map} specified + * in the {@link Table} interface. + */ + @Override + SortedMap> rowMap(); +} diff --git a/sources/main/java/com/google/common/collect/Serialization.java b/sources/main/java/com/google/common/collect/Serialization.java new file mode 100644 index 0000000..e821f05 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Serialization.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Map; + +/** + * Provides static methods for serializing collection classes. + * + *

+ * This class assists the implementation of collection classes. Do not use this + * class to serialize collections that are defined elsewhere. + * + * @author Jared Levy + */ +final class Serialization { + private Serialization() { + } + + /** + * Reads a count corresponding to a serialized map, multiset, or multimap. It + * returns the size of a map serialized by + * {@link #writeMap(Map, ObjectOutputStream)}, the number of distinct elements + * in a multiset serialized by + * {@link #writeMultiset(Multiset, ObjectOutputStream)}, or the number of + * distinct keys in a multimap serialized by + * {@link #writeMultimap(Multimap, ObjectOutputStream)}. + * + *

+ * The returned count may be used to construct an empty collection of the + * appropriate capacity before calling any of the {@code populate} methods. + */ + static int readCount(ObjectInputStream stream) throws IOException { + return stream.readInt(); + } + + /** + * Stores the contents of a map in an output stream, as part of serialization. + * It does not support concurrent maps whose content may change while the method + * is running. + * + *

+ * The serialized output consists of the number of entries, first key, first + * value, second key, second value, and so on. + */ + static void writeMap(Map map, ObjectOutputStream stream) throws IOException { + stream.writeInt(map.size()); + for (Map.Entry entry : map.entrySet()) { + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + } + + /** + * Populates a map by reading an input stream, as part of deserialization. See + * {@link #writeMap} for the data format. + */ + static void populateMap(Map map, ObjectInputStream stream) throws IOException, ClassNotFoundException { + int size = stream.readInt(); + populateMap(map, stream, size); + } + + /** + * Populates a map by reading an input stream, as part of deserialization. See + * {@link #writeMap} for the data format. The size is determined by a prior call + * to {@link #readCount}. + */ + static void populateMap(Map map, ObjectInputStream stream, int size) + throws IOException, ClassNotFoundException { + for (int i = 0; i < size; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMap + K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") // reading data stored by writeMap + V value = (V) stream.readObject(); + map.put(key, value); + } + } + + /** + * Stores the contents of a multiset in an output stream, as part of + * serialization. It does not support concurrent multisets whose content may + * change while the method is running. + * + *

+ * The serialized output consists of the number of distinct elements, the first + * element, its count, the second element, its count, and so on. + */ + static void writeMultiset(Multiset multiset, ObjectOutputStream stream) throws IOException { + int entryCount = multiset.entrySet().size(); + stream.writeInt(entryCount); + for (Multiset.Entry entry : multiset.entrySet()) { + stream.writeObject(entry.getElement()); + stream.writeInt(entry.getCount()); + } + } + + /** + * Populates a multiset by reading an input stream, as part of deserialization. + * See {@link #writeMultiset} for the data format. + */ + static void populateMultiset(Multiset multiset, ObjectInputStream stream) + throws IOException, ClassNotFoundException { + int distinctElements = stream.readInt(); + populateMultiset(multiset, stream, distinctElements); + } + + /** + * Populates a multiset by reading an input stream, as part of deserialization. + * See {@link #writeMultiset} for the data format. The number of distinct + * elements is determined by a prior call to {@link #readCount}. + */ + static void populateMultiset(Multiset multiset, ObjectInputStream stream, int distinctElements) + throws IOException, ClassNotFoundException { + for (int i = 0; i < distinctElements; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultiset + E element = (E) stream.readObject(); + int count = stream.readInt(); + multiset.add(element, count); + } + } + + /** + * Stores the contents of a multimap in an output stream, as part of + * serialization. It does not support concurrent multimaps whose content may + * change while the method is running. The {@link Multimap#asMap} view + * determines the ordering in which data is written to the stream. + * + *

+ * The serialized output consists of the number of distinct keys, and then for + * each distinct key: the key, the number of values for that key, and the key's + * values. + */ + static void writeMultimap(Multimap multimap, ObjectOutputStream stream) throws IOException { + stream.writeInt(multimap.asMap().size()); + for (Map.Entry> entry : multimap.asMap().entrySet()) { + stream.writeObject(entry.getKey()); + stream.writeInt(entry.getValue().size()); + for (V value : entry.getValue()) { + stream.writeObject(value); + } + } + } + + /** + * Populates a multimap by reading an input stream, as part of deserialization. + * See {@link #writeMultimap} for the data format. + */ + static void populateMultimap(Multimap multimap, ObjectInputStream stream) + throws IOException, ClassNotFoundException { + int distinctKeys = stream.readInt(); + populateMultimap(multimap, stream, distinctKeys); + } + + /** + * Populates a multimap by reading an input stream, as part of deserialization. + * See {@link #writeMultimap} for the data format. The number of distinct keys + * is determined by a prior call to {@link #readCount}. + */ + static void populateMultimap(Multimap multimap, ObjectInputStream stream, int distinctKeys) + throws IOException, ClassNotFoundException { + for (int i = 0; i < distinctKeys; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultimap + K key = (K) stream.readObject(); + Collection values = multimap.get(key); + int valueCount = stream.readInt(); + for (int j = 0; j < valueCount; j++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultimap + V value = (V) stream.readObject(); + values.add(value); + } + } + } + + // Secret sauce for setting final fields; don't make it public. + static FieldSetter getFieldSetter(final Class clazz, String fieldName) { + try { + Field field = clazz.getDeclaredField(fieldName); + return new FieldSetter(field); + } catch (NoSuchFieldException e) { + throw new AssertionError(e); // programmer error + } + } + + // Secret sauce for setting final fields; don't make it public. + static final class FieldSetter { + private final Field field; + + private FieldSetter(Field field) { + this.field = field; + field.setAccessible(true); + } + + void set(T instance, Object value) { + try { + field.set(instance, value); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } + } + + void set(T instance, int value) { + try { + field.set(instance, value); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } + } + } +} diff --git a/sources/main/java/com/google/common/collect/SetMultimap.java b/sources/main/java/com/google/common/collect/SetMultimap.java new file mode 100644 index 0000000..e7e7c73 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SetMultimap.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A {@code Multimap} that cannot hold duplicate key-value pairs. Adding a + * key-value pair that's already in the multimap has no effect. See the + * {@link Multimap} documentation for information common to all multimaps. + * + *

+ * The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods each + * return a {@link Set} of values, while {@link #entries} returns a {@code + * Set} of map entries. Though the method signature doesn't say so explicitly, + * the map returned by {@link #asMap} has {@code Set} values. + * + *

+ * If the values corresponding to a single key should be ordered according to a + * {@link java.util.Comparator} (or the natural order), see the + * {@link SortedSetMultimap} subinterface. + * + *

+ * Since the value collections are sets, the behavior of a {@code SetMultimap} + * is not specified if key or value objects already present in the + * multimap change in a manner that affects {@code equals} comparisons. Use + * caution if mutable objects are used as keys or values in a + * {@code SetMultimap}. + * + *

+ * See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface SetMultimap extends Multimap { + /** + * {@inheritDoc} + * + *

+ * Because a {@code SetMultimap} has unique values for a given key, this method + * returns a {@link Set}, instead of the {@link java.util.Collection} specified + * in the {@link Multimap} interface. + */ + @Override + Set get(@Nullable K key); + + /** + * {@inheritDoc} + * + *

+ * Because a {@code SetMultimap} has unique values for a given key, this method + * returns a {@link Set}, instead of the {@link java.util.Collection} specified + * in the {@link Multimap} interface. + */ + @Override + Set removeAll(@Nullable Object key); + + /** + * {@inheritDoc} + * + *

+ * Because a {@code SetMultimap} has unique values for a given key, this method + * returns a {@link Set}, instead of the {@link java.util.Collection} specified + * in the {@link Multimap} interface. + * + *

+ * Any duplicates in {@code values} will be stored in the multimap once. + */ + @Override + Set replaceValues(K key, Iterable values); + + /** + * {@inheritDoc} + * + *

+ * Because a {@code SetMultimap} has unique values for a given key, this method + * returns a {@link Set}, instead of the {@link java.util.Collection} specified + * in the {@link Multimap} interface. + */ + @Override + Set> entries(); + + /** + * {@inheritDoc} + * + *

+ * Note: The returned map's values are guaranteed to be of type + * {@link Set}. To obtain this map with the more specific generic type + * {@code Map>}, call {@link Multimaps#asMap(SetMultimap)} instead. + */ + @Override + Map> asMap(); + + /** + * Compares the specified object to this multimap for equality. + * + *

+ * Two {@code SetMultimap} instances are equal if, for each key, they contain + * the same values. Equality does not depend on the ordering of keys or values. + * + *

+ * An empty {@code SetMultimap} is equal to any other empty {@code + * Multimap}, including an empty {@code ListMultimap}. + */ + @Override + boolean equals(@Nullable Object obj); +} diff --git a/sources/main/java/com/google/common/collect/Sets.java b/sources/main/java/com/google/common/collect/Sets.java new file mode 100644 index 0000000..094fbff --- /dev/null +++ b/sources/main/java/com/google/common/collect/Sets.java @@ -0,0 +1,1688 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2.FilteredCollection; + +/** + * 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) + */ +@GwtCompatible(emulated = true) +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 + @GwtCompatible(serializable = true) + 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 + @GwtCompatible(serializable = true) + public static > ImmutableSet immutableEnumSet(Iterable elements) { + if (elements instanceof ImmutableEnumSet) { + return (ImmutableEnumSet) elements; + } else if (elements instanceof Collection) { + Collection collection = (Collection) elements; + 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(Collections2.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; + } + + // LinkedHashSet + + /** + * Creates a mutable, empty {@code LinkedHashSet} instance. + * + *

+ * Note: if mutability is not required, use {@link ImmutableSet#of()} + * instead. + * + * @return a new, empty {@code LinkedHashSet} + */ + public static LinkedHashSet newLinkedHashSet() { + return new 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(Collections2.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 Set} that uses identity to determine equality. It + * compares object references, instead of calling {@code equals}, to determine + * whether a provided object matches an element in the set. For example, + * {@code contains} returns {@code false} when passed an object that equals a + * set member, but isn't the same instance. This behavior is similar to the way + * {@code IdentityHashMap} handles key lookups. + * + * @since 8.0 + */ + public static Set newIdentityHashSet() { + return Sets.newSetFromMap(Maps.newIdentityHashMap()); + } + + /** + * 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; + } + + /** + * Returns a set backed by the specified map. The resulting set displays the + * same ordering, concurrency, and performance characteristics as the backing + * map. In essence, this factory method provides a {@link Set} implementation + * corresponding to any {@link Map} implementation. There is no need to use this + * method on a {@link Map} implementation that already has a corresponding + * {@link Set} implementation (such as {@link java.util.HashMap} or + * {@link java.util.TreeMap}). + * + *

+ * Each method invocation on the set returned by this method results in exactly + * one method invocation on the backing map or its {@code keySet} view, with one + * exception. The {@code addAll} method is implemented as a sequence of + * {@code put} invocations on the backing map. + * + *

+ * The specified map must be empty at the time this method is invoked, and + * should not be accessed directly after this method returns. These conditions + * are ensured if the map is created empty, passed directly to this method, and + * no reference to the map is retained, as illustrated in the following code + * fragment: + * + *

+	 * {
+	 * 	@code
+	 *
+	 * 	Set identityHashSet = Sets.newSetFromMap(new IdentityHashMap());
+	 * }
+	 * 
+	 *
+	 * 

+ * This method has the same behavior as the JDK 6 method + * {@code Collections.newSetFromMap()}. The returned set is serializable if the + * backing map is. + * + * @param map the backing map + * @return the set backed by the map + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static Set newSetFromMap(Map map) { + return Platform.newSetFromMap(map); + } + + /** + * 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(@Nullable 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 + */ + @GwtIncompatible("NavigableSet") + @SuppressWarnings("unchecked") + 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)); + } + + @GwtIncompatible("NavigableSet") + private static class FilteredNavigableSet extends FilteredSortedSet implements NavigableSet { + FilteredNavigableSet(NavigableSet unfiltered, Predicate predicate) { + super(unfiltered, predicate); + } + + NavigableSet unfiltered() { + return (NavigableSet) unfiltered; + } + + @Override + @Nullable + public E lower(E e) { + return Iterators.getNext(headSet(e, false).descendingIterator(), null); + } + + @Override + @Nullable + public E floor(E e) { + return Iterators.getNext(headSet(e, true).descendingIterator(), null); + } + + @Override + public E ceiling(E e) { + return Iterables.getFirst(tailSet(e, true), null); + } + + @Override + public E higher(E e) { + return Iterables.getFirst(tailSet(e, false), null); + } + + @Override + public E pollFirst() { + 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(@Nullable 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 + */ + @GwtCompatible(serializable = false) + 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(@Nullable 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(@Nullable Object obj) { + if (obj instanceof Set) { + Set set = (Set) obj; + return inputSet.keySet().containsAll(set); + } + return false; + } + + @Override + public boolean equals(@Nullable 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, @Nullable 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 + */ + @GwtIncompatible("NavigableSet") + public static NavigableSet unmodifiableNavigableSet(NavigableSet set) { + if (set instanceof ImmutableSortedSet || set instanceof UnmodifiableNavigableSet) { + return set; + } + return new UnmodifiableNavigableSet(set); + } + + @GwtIncompatible("NavigableSet") + 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 + */ + @GwtIncompatible("NavigableSet") + 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()); + } + } + + @GwtIncompatible("NavigableSet") + 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(); + } + + @SuppressWarnings("unchecked") + @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/sources/main/java/com/google/common/collect/SingletonImmutableBiMap.java b/sources/main/java/com/google/common/collect/SingletonImmutableBiMap.java new file mode 100644 index 0000000..ac0a607 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SingletonImmutableBiMap.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.CollectPreconditions.checkEntryNotNull; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * Implementation of {@link ImmutableMap} with exactly one entry. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +final class SingletonImmutableBiMap extends ImmutableBiMap { + + final transient K singleKey; + final transient V singleValue; + + SingletonImmutableBiMap(K singleKey, V singleValue) { + checkEntryNotNull(singleKey, singleValue); + this.singleKey = singleKey; + this.singleValue = singleValue; + } + + private SingletonImmutableBiMap(K singleKey, V singleValue, ImmutableBiMap inverse) { + this.singleKey = singleKey; + this.singleValue = singleValue; + this.inverse = inverse; + } + + SingletonImmutableBiMap(Entry entry) { + this(entry.getKey(), entry.getValue()); + } + + @Override + public V get(@Nullable Object key) { + return singleKey.equals(key) ? singleValue : null; + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean containsKey(@Nullable Object key) { + return singleKey.equals(key); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return singleValue.equals(value); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + ImmutableSet> createEntrySet() { + return ImmutableSet.of(Maps.immutableEntry(singleKey, singleValue)); + } + + @Override + ImmutableSet createKeySet() { + return ImmutableSet.of(singleKey); + } + + transient ImmutableBiMap inverse; + + @Override + public ImmutableBiMap inverse() { + // racy single-check idiom + ImmutableBiMap result = inverse; + if (result == null) { + return inverse = new SingletonImmutableBiMap(singleValue, singleKey, this); + } else { + return result; + } + } +} diff --git a/sources/main/java/com/google/common/collect/SingletonImmutableList.java b/sources/main/java/com/google/common/collect/SingletonImmutableList.java new file mode 100644 index 0000000..42eb9bf --- /dev/null +++ b/sources/main/java/com/google/common/collect/SingletonImmutableList.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; + +/** + * Implementation of {@link ImmutableList} with exactly one element. + * + * @author Hayward Chan + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +final class SingletonImmutableList extends ImmutableList { + + final transient E element; + + SingletonImmutableList(E element) { + this.element = checkNotNull(element); + } + + @Override + public E get(int index) { + Preconditions.checkElementIndex(index, 1); + return element; + } + + @Override + public int indexOf(@Nullable Object object) { + return element.equals(object) ? 0 : -1; + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.singletonIterator(element); + } + + @Override + public int lastIndexOf(@Nullable Object object) { + return indexOf(object); + } + + @Override + public int size() { + return 1; + } + + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, 1); + return (fromIndex == toIndex) ? ImmutableList.of() : this; + } + + @Override + public ImmutableList reverse() { + return this; + } + + @Override + public boolean contains(@Nullable Object object) { + return element.equals(object); + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof List) { + List that = (List) object; + return that.size() == 1 && element.equals(that.get(0)); + } + return false; + } + + @Override + public int hashCode() { + // not caching hash code since it could change if the element is mutable + // in a way that modifies its hash code. + return 31 + element.hashCode(); + } + + @Override + public String toString() { + String elementToString = element.toString(); + return new StringBuilder(elementToString.length() + 2).append('[').append(elementToString).append(']') + .toString(); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + dst[offset] = element; + return offset + 1; + } +} diff --git a/sources/main/java/com/google/common/collect/SingletonImmutableSet.java b/sources/main/java/com/google/common/collect/SingletonImmutableSet.java new file mode 100644 index 0000000..c8847e0 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SingletonImmutableSet.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; + +/** + * Implementation of {@link ImmutableSet} with exactly one element. + * + * @author Kevin Bourrillion + * @author Nick Kralevich + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +final class SingletonImmutableSet extends ImmutableSet { + + final transient E element; + // This is transient because it will be recalculated on the first + // call to hashCode(). + // + // A race condition is avoided since threads will either see that the value + // is zero and recalculate it themselves, or two threads will see it at + // the same time, and both recalculate it. If the cachedHashCode is 0, + // it will always be recalculated, unfortunately. + private transient int cachedHashCode; + + SingletonImmutableSet(E element) { + this.element = Preconditions.checkNotNull(element); + } + + SingletonImmutableSet(E element, int hashCode) { + // Guaranteed to be non-null by the presence of the pre-computed hash code. + this.element = element; + cachedHashCode = hashCode; + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object target) { + return element.equals(target); + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.singletonIterator(element); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + dst[offset] = element; + return offset + 1; + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof Set) { + Set that = (Set) object; + return that.size() == 1 && element.equals(that.iterator().next()); + } + return false; + } + + @Override + public final int hashCode() { + // Racy single-check. + int code = cachedHashCode; + if (code == 0) { + cachedHashCode = code = element.hashCode(); + } + return code; + } + + @Override + boolean isHashCodeFast() { + return cachedHashCode != 0; + } + + @Override + public String toString() { + String elementToString = element.toString(); + return new StringBuilder(elementToString.length() + 2).append('[').append(elementToString).append(']') + .toString(); + } +} diff --git a/sources/main/java/com/google/common/collect/SingletonImmutableTable.java b/sources/main/java/com/google/common/collect/SingletonImmutableTable.java new file mode 100644 index 0000000..cc1aae9 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SingletonImmutableTable.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; + +import com.google.common.annotations.GwtCompatible; + +/** + * An implementation of {@link ImmutableTable} that holds a single cell. + * + * @author Gregory Kick + */ +@GwtCompatible +class SingletonImmutableTable extends ImmutableTable { + final R singleRowKey; + final C singleColumnKey; + final V singleValue; + + SingletonImmutableTable(R rowKey, C columnKey, V value) { + this.singleRowKey = checkNotNull(rowKey); + this.singleColumnKey = checkNotNull(columnKey); + this.singleValue = checkNotNull(value); + } + + SingletonImmutableTable(Cell cell) { + this(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + + @Override + public ImmutableMap column(C columnKey) { + checkNotNull(columnKey); + return containsColumn(columnKey) ? ImmutableMap.of(singleRowKey, singleValue) : ImmutableMap.of(); + } + + @Override + public ImmutableMap> columnMap() { + return ImmutableMap.of(singleColumnKey, (Map) ImmutableMap.of(singleRowKey, singleValue)); + } + + @Override + public ImmutableMap> rowMap() { + return ImmutableMap.of(singleRowKey, (Map) ImmutableMap.of(singleColumnKey, singleValue)); + } + + @Override + public int size() { + return 1; + } + + @Override + ImmutableSet> createCellSet() { + return ImmutableSet.of(cellOf(singleRowKey, singleColumnKey, singleValue)); + } + + @Override + ImmutableCollection createValues() { + return ImmutableSet.of(singleValue); + } +} diff --git a/sources/main/java/com/google/common/collect/SortedIterable.java b/sources/main/java/com/google/common/collect/SortedIterable.java new file mode 100644 index 0000000..4301269 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SortedIterable.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Comparator; +import java.util.Iterator; + +import com.google.common.annotations.GwtCompatible; + +/** + * An {@code Iterable} whose elements are sorted relative to a + * {@code Comparator}, typically provided at creation time. + * + * @author Louis Wasserman + */ +@GwtCompatible +interface SortedIterable extends Iterable { + /** + * Returns the {@code Comparator} by which the elements of this iterable are + * ordered, or {@code + * Ordering.natural()} if the elements are ordered by their natural ordering. + */ + Comparator comparator(); + + /** + * Returns an iterator over elements of type {@code T}. The elements are + * returned in nondecreasing order according to the associated + * {@link #comparator}. + */ + @Override + Iterator iterator(); +} diff --git a/sources/main/java/com/google/common/collect/SortedIterables.java b/sources/main/java/com/google/common/collect/SortedIterables.java new file mode 100644 index 0000000..5a19e3f --- /dev/null +++ b/sources/main/java/com/google/common/collect/SortedIterables.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Comparator; +import java.util.SortedSet; + +import com.google.common.annotations.GwtCompatible; + +/** + * Utilities for dealing with sorted collections of all types. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class SortedIterables { + private SortedIterables() { + } + + /** + * Returns {@code true} if {@code elements} is a sorted collection using an + * ordering equivalent to {@code comparator}. + */ + public static boolean hasSameComparator(Comparator comparator, Iterable elements) { + checkNotNull(comparator); + checkNotNull(elements); + Comparator comparator2; + if (elements instanceof SortedSet) { + comparator2 = comparator((SortedSet) elements); + } else if (elements instanceof SortedIterable) { + comparator2 = ((SortedIterable) elements).comparator(); + } else { + return false; + } + return comparator.equals(comparator2); + } + + @SuppressWarnings("unchecked") + // if sortedSet.comparator() is null, the set must be naturally ordered + public static Comparator comparator(SortedSet sortedSet) { + Comparator result = sortedSet.comparator(); + if (result == null) { + result = (Comparator) Ordering.natural(); + } + return result; + } +} diff --git a/sources/main/java/com/google/common/collect/SortedLists.java b/sources/main/java/com/google/common/collect/SortedLists.java new file mode 100644 index 0000000..1f67f86 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SortedLists.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; + +/** + * Static methods pertaining to sorted {@link List} instances. + * + * In this documentation, the terms greatest, greater, + * least, and lesser are considered to refer to the comparator on + * the elements, and the terms first and last are considered to + * refer to the elements' ordering in a list. + * + * @author Louis Wasserman + */ +@GwtCompatible +@Beta +final class SortedLists { + private SortedLists() { + } + + /** + * A specification for which index to return if the list contains at least one + * element that compares as equal to the key. + */ + public enum KeyPresentBehavior { + /** + * Return the index of any list element that compares as equal to the key. No + * guarantees are made as to which index is returned, if more than one element + * compares as equal to the key. + */ + ANY_PRESENT { + @Override + int resultIndex(Comparator comparator, E key, List list, int foundIndex) { + return foundIndex; + } + }, + /** + * Return the index of the last list element that compares as equal to the key. + */ + LAST_PRESENT { + @Override + int resultIndex(Comparator comparator, E key, List list, int foundIndex) { + // Of course, we have to use binary search to find the precise + // breakpoint... + int lower = foundIndex; + int upper = list.size() - 1; + // Everything between lower and upper inclusive compares at >= 0. + while (lower < upper) { + int middle = (lower + upper + 1) >>> 1; + int c = comparator.compare(list.get(middle), key); + if (c > 0) { + upper = middle - 1; + } else { // c == 0 + lower = middle; + } + } + return lower; + } + }, + /** + * Return the index of the first list element that compares as equal to the key. + */ + FIRST_PRESENT { + @Override + int resultIndex(Comparator comparator, E key, List list, int foundIndex) { + // Of course, we have to use binary search to find the precise + // breakpoint... + int lower = 0; + int upper = foundIndex; + // Of course, we have to use binary search to find the precise breakpoint... + // Everything between lower and upper inclusive compares at <= 0. + while (lower < upper) { + int middle = (lower + upper) >>> 1; + int c = comparator.compare(list.get(middle), key); + if (c < 0) { + lower = middle + 1; + } else { // c == 0 + upper = middle; + } + } + return lower; + } + }, + /** + * Return the index of the first list element that compares as greater than the + * key, or {@code + * list.size()} if there is no such element. + */ + FIRST_AFTER { + @Override + public int resultIndex(Comparator comparator, E key, List list, + int foundIndex) { + return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1; + } + }, + /** + * Return the index of the last list element that compares as less than the key, + * or {@code -1} if there is no such element. + */ + LAST_BEFORE { + @Override + public int resultIndex(Comparator comparator, E key, List list, + int foundIndex) { + return FIRST_PRESENT.resultIndex(comparator, key, list, foundIndex) - 1; + } + }; + + abstract int resultIndex(Comparator comparator, E key, List list, int foundIndex); + } + + /** + * A specification for which index to return if the list contains no elements + * that compare as equal to the key. + */ + public enum KeyAbsentBehavior { + /** + * Return the index of the next lower element in the list, or {@code -1} if + * there is no such element. + */ + NEXT_LOWER { + @Override + int resultIndex(int higherIndex) { + return higherIndex - 1; + } + }, + /** + * Return the index of the next higher element in the list, or + * {@code list.size()} if there is no such element. + */ + NEXT_HIGHER { + @Override + public int resultIndex(int higherIndex) { + return higherIndex; + } + }, + /** + * Return {@code ~insertionIndex}, where {@code insertionIndex} is defined as + * the point at which the key would be inserted into the list: the index of the + * next higher element in the list, or {@code list.size()} if there is no such + * element. + * + *

+ * Note that the return value will be {@code >= 0} if and only if there is an + * element of the list that compares as equal to the key. + * + *

+ * This is equivalent to the behavior of + * {@link java.util.Collections#binarySearch(List, Object)} when the key isn't + * present, since {@code ~insertionIndex} is equal to + * {@code -1 - insertionIndex}. + */ + INVERTED_INSERTION_INDEX { + @Override + public int resultIndex(int higherIndex) { + return ~higherIndex; + } + }; + + abstract int resultIndex(int higherIndex); + } + + /** + * Searches the specified naturally ordered list for the specified object using + * the binary search algorithm. + * + *

+ * Equivalent to + * {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior, KeyAbsentBehavior)} + * using {@link Ordering#natural}. + */ + public static int binarySearch(List list, E e, + KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { + checkNotNull(e); + return binarySearch(list, checkNotNull(e), Ordering.natural(), presentBehavior, absentBehavior); + } + + /** + * Binary searches the list for the specified key, using the specified key + * function. + * + *

+ * Equivalent to + * {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior, KeyAbsentBehavior)} + * using {@link Ordering#natural}. + */ + public static int binarySearch(List list, Function keyFunction, + @Nullable K key, KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { + return binarySearch(list, keyFunction, key, Ordering.natural(), presentBehavior, absentBehavior); + } + + /** + * Binary searches the list for the specified key, using the specified key + * function. + * + *

+ * Equivalent to + * {@link #binarySearch(List, Object, Comparator, KeyPresentBehavior, KeyAbsentBehavior)} + * using {@link Lists#transform(List, Function) Lists.transform(list, + * keyFunction)}. + */ + public static int binarySearch(List list, Function keyFunction, @Nullable K key, + Comparator keyComparator, KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { + return binarySearch(Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior); + } + + /** + * Searches the specified list for the specified object using the binary search + * algorithm. The list must be sorted into ascending order according to the + * specified comparator (as by the {@link Collections#sort(List, Comparator) + * Collections.sort(List, Comparator)} method), prior to making this call. If it + * is not sorted, the results are undefined. + * + *

+ * If there are elements in the list which compare as equal to the key, the + * choice of {@link KeyPresentBehavior} decides which index is returned. If no + * elements compare as equal to the key, the choice of {@link KeyAbsentBehavior} + * decides which index is returned. + * + *

+ * This method runs in log(n) time on random-access lists, which offer + * near-constant-time access to each list element. + * + * @param list the list to be searched. + * @param key the value to be searched for. + * @param comparator the comparator by which the list is ordered. + * @param presentBehavior the specification for what to do if at least one + * element of the list compares as equal to the key. + * @param absentBehavior the specification for what to do if no elements of the + * list compare as equal to the key. + * @return the index determined by the {@code KeyPresentBehavior}, if the key is + * in the list; otherwise the index determined by the + * {@code KeyAbsentBehavior}. + */ + public static int binarySearch(List list, @Nullable E key, Comparator comparator, + KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { + checkNotNull(comparator); + checkNotNull(list); + checkNotNull(presentBehavior); + checkNotNull(absentBehavior); + if (!(list instanceof RandomAccess)) { + list = Lists.newArrayList(list); + } + // TODO(user): benchmark when it's best to do a linear search + + int lower = 0; + int upper = list.size() - 1; + + while (lower <= upper) { + int middle = (lower + upper) >>> 1; + int c = comparator.compare(key, list.get(middle)); + if (c < 0) { + upper = middle - 1; + } else if (c > 0) { + lower = middle + 1; + } else { + return lower + + presentBehavior.resultIndex(comparator, key, list.subList(lower, upper + 1), middle - lower); + } + } + return absentBehavior.resultIndex(lower); + } +} diff --git a/sources/main/java/com/google/common/collect/SortedMapDifference.java b/sources/main/java/com/google/common/collect/SortedMapDifference.java new file mode 100644 index 0000000..ffb345c --- /dev/null +++ b/sources/main/java/com/google/common/collect/SortedMapDifference.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.SortedMap; + +import com.google.common.annotations.GwtCompatible; + +/** + * An object representing the differences between two sorted maps. + * + * @author Louis Wasserman + * @since 8.0 + */ +@GwtCompatible +public interface SortedMapDifference extends MapDifference { + + @Override + SortedMap entriesOnlyOnLeft(); + + @Override + SortedMap entriesOnlyOnRight(); + + @Override + SortedMap entriesInCommon(); + + @Override + SortedMap> entriesDiffering(); +} diff --git a/sources/main/java/com/google/common/collect/SortedMultiset.java b/sources/main/java/com/google/common/collect/SortedMultiset.java new file mode 100644 index 0000000..9c72f72 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SortedMultiset.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.Set; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A {@link Multiset} which maintains the ordering of its elements, according to + * either their natural order or an explicit {@link Comparator}. This order is + * reflected when iterating over the sorted multiset, either directly, or + * through its {@code elementSet} or {@code entrySet} views. In all cases, this + * implementation uses {@link Comparable#compareTo} or + * {@link Comparator#compare} instead of {@link Object#equals} to determine + * equivalence of instances. + * + *

+ * Warning: The comparison must be consistent with equals as + * explained by the {@link Comparable} class specification. Otherwise, the + * resulting multiset will violate the {@link Collection} contract, which it is + * specified in terms of {@link Object#equals}. + * + *

+ * See the Guava User Guide article on + * {@code Multiset}. + * + * @author Louis Wasserman + * @since 11.0 + */ +@Beta +@GwtCompatible(emulated = true) +public interface SortedMultiset extends SortedMultisetBridge, SortedIterable { + /** + * Returns the comparator that orders this multiset, or + * {@link Ordering#natural()} if the natural ordering of the elements is used. + */ + Comparator comparator(); + + /** + * Returns the entry of the first element in this multiset, or {@code null} if + * this multiset is empty. + */ + Entry firstEntry(); + + /** + * Returns the entry of the last element in this multiset, or {@code null} if + * this multiset is empty. + */ + Entry lastEntry(); + + /** + * Returns and removes the entry associated with the lowest element in this + * multiset, or returns {@code null} if this multiset is empty. + */ + Entry pollFirstEntry(); + + /** + * Returns and removes the entry associated with the greatest element in this + * multiset, or returns {@code null} if this multiset is empty. + */ + Entry pollLastEntry(); + + /** + * Returns a {@link NavigableSet} view of the distinct elements in this + * multiset. + * + * @since 14.0 (present with return type {@code SortedSet} since 11.0) + */ + @Override + NavigableSet elementSet(); + + /** + * {@inheritDoc} + * + *

+ * The {@code entrySet}'s iterator returns entries in ascending element order + * according to the this multiset's comparator. + */ + @Override + Set> entrySet(); + + /** + * {@inheritDoc} + * + *

+ * The iterator returns the elements in ascending order according to this + * multiset's comparator. + */ + @Override + Iterator iterator(); + + /** + * Returns a descending view of this multiset. Modifications made to either map + * will be reflected in the other. + */ + SortedMultiset descendingMultiset(); + + /** + * Returns a view of this multiset restricted to the elements less than + * {@code upperBound}, optionally including {@code upperBound} itself. The + * returned multiset is a view of this multiset, so changes to one will be + * reflected in the other. The returned multiset supports all operations that + * this multiset supports. + * + *

+ * The returned multiset will throw an {@link IllegalArgumentException} on + * attempts to add elements outside its range. + */ + SortedMultiset headMultiset(E upperBound, BoundType boundType); + + /** + * Returns a view of this multiset restricted to the range between + * {@code lowerBound} and {@code upperBound}. The returned multiset is a view of + * this multiset, so changes to one will be reflected in the other. The returned + * multiset supports all operations that this multiset supports. + * + *

+ * The returned multiset will throw an {@link IllegalArgumentException} on + * attempts to add elements outside its range. + * + *

+ * This method is equivalent to + * {@code tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, + * upperBoundType)}. + */ + SortedMultiset subMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType); + + /** + * Returns a view of this multiset restricted to the elements greater than + * {@code lowerBound}, optionally including {@code lowerBound} itself. The + * returned multiset is a view of this multiset, so changes to one will be + * reflected in the other. The returned multiset supports all operations that + * this multiset supports. + * + *

+ * The returned multiset will throw an {@link IllegalArgumentException} on + * attempts to add elements outside its range. + */ + SortedMultiset tailMultiset(E lowerBound, BoundType boundType); +} diff --git a/sources/main/java/com/google/common/collect/SortedMultisetBridge.java b/sources/main/java/com/google/common/collect/SortedMultisetBridge.java new file mode 100644 index 0000000..7efb55a --- /dev/null +++ b/sources/main/java/com/google/common/collect/SortedMultisetBridge.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.SortedSet; + +/** + * Superinterface of {@link SortedMultiset} to introduce a bridge method for + * {@code elementSet()}, to ensure binary compatibility with older Guava + * versions that specified {@code elementSet()} to return {@code SortedSet}. + * + * @author Louis Wasserman + */ +interface SortedMultisetBridge extends Multiset { + @Override + SortedSet elementSet(); +} diff --git a/sources/main/java/com/google/common/collect/SortedMultisets.java b/sources/main/java/com/google/common/collect/SortedMultisets.java new file mode 100644 index 0000000..04b6c18 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SortedMultisets.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.collect.BoundType.CLOSED; +import static com.google.common.collect.BoundType.OPEN; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.collect.Multiset.Entry; + +/** + * Provides static utility methods for creating and working with + * {@link SortedMultiset} instances. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +final class SortedMultisets { + private SortedMultisets() { + } + + /** + * A skeleton implementation for {@link SortedMultiset#elementSet}. + */ + static class ElementSet extends Multisets.ElementSet implements SortedSet { + private final SortedMultiset multiset; + + ElementSet(SortedMultiset multiset) { + this.multiset = multiset; + } + + @Override + final SortedMultiset multiset() { + return multiset; + } + + @Override + public Comparator comparator() { + return multiset().comparator(); + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + return multiset().subMultiset(fromElement, CLOSED, toElement, OPEN).elementSet(); + } + + @Override + public SortedSet headSet(E toElement) { + return multiset().headMultiset(toElement, OPEN).elementSet(); + } + + @Override + public SortedSet tailSet(E fromElement) { + return multiset().tailMultiset(fromElement, CLOSED).elementSet(); + } + + @Override + public E first() { + return getElementOrThrow(multiset().firstEntry()); + } + + @Override + public E last() { + return getElementOrThrow(multiset().lastEntry()); + } + } + + /** + * A skeleton navigable implementation for {@link SortedMultiset#elementSet}. + */ + @GwtIncompatible("Navigable") + static class NavigableElementSet extends ElementSet implements NavigableSet { + NavigableElementSet(SortedMultiset multiset) { + super(multiset); + } + + @Override + public E lower(E e) { + return getElementOrNull(multiset().headMultiset(e, OPEN).lastEntry()); + } + + @Override + public E floor(E e) { + return getElementOrNull(multiset().headMultiset(e, CLOSED).lastEntry()); + } + + @Override + public E ceiling(E e) { + return getElementOrNull(multiset().tailMultiset(e, CLOSED).firstEntry()); + } + + @Override + public E higher(E e) { + return getElementOrNull(multiset().tailMultiset(e, OPEN).firstEntry()); + } + + @Override + public NavigableSet descendingSet() { + return new NavigableElementSet(multiset().descendingMultiset()); + } + + @Override + public Iterator descendingIterator() { + return descendingSet().iterator(); + } + + @Override + public E pollFirst() { + return getElementOrNull(multiset().pollFirstEntry()); + } + + @Override + public E pollLast() { + return getElementOrNull(multiset().pollLastEntry()); + } + + @Override + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return new NavigableElementSet(multiset().subMultiset(fromElement, BoundType.forBoolean(fromInclusive), + toElement, BoundType.forBoolean(toInclusive))); + } + + @Override + public NavigableSet headSet(E toElement, boolean inclusive) { + return new NavigableElementSet(multiset().headMultiset(toElement, BoundType.forBoolean(inclusive))); + } + + @Override + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return new NavigableElementSet(multiset().tailMultiset(fromElement, BoundType.forBoolean(inclusive))); + } + } + + private static E getElementOrThrow(Entry entry) { + if (entry == null) { + throw new NoSuchElementException(); + } + return entry.getElement(); + } + + private static E getElementOrNull(@Nullable Entry entry) { + return (entry == null) ? null : entry.getElement(); + } +} diff --git a/sources/main/java/com/google/common/collect/SortedSetMultimap.java b/sources/main/java/com/google/common/collect/SortedSetMultimap.java new file mode 100644 index 0000000..50a8c2f --- /dev/null +++ b/sources/main/java/com/google/common/collect/SortedSetMultimap.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A {@code SetMultimap} whose set of values for a given key are kept sorted; + * that is, they comprise a {@link SortedSet}. It cannot hold duplicate + * key-value pairs; adding a key-value pair that's already in the multimap has + * no effect. This interface does not specify the ordering of the multimap's + * keys. See the {@link Multimap} documentation for information common to all + * multimaps. + * + *

+ * The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods each + * return a {@link SortedSet} of values, while {@link Multimap#entries()} + * returns a {@link Set} of map entries. Though the method signature doesn't say + * so explicitly, the map returned by {@link #asMap} has {@code SortedSet} + * values. + * + *

+ * See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +public interface SortedSetMultimap extends SetMultimap { + // Following Javadoc copied from Multimap. + + /** + * Returns a collection view of all values associated with a key. If no mappings + * in the multimap have the provided key, an empty collection is returned. + * + *

+ * Changes to the returned collection will update the underlying multimap, and + * vice versa. + * + *

+ * Because a {@code SortedSetMultimap} has unique sorted values for a given key, + * this method returns a {@link SortedSet}, instead of the + * {@link java.util.Collection} specified in the {@link Multimap} interface. + */ + @Override + SortedSet get(@Nullable K key); + + /** + * Removes all values associated with a given key. + * + *

+ * Because a {@code SortedSetMultimap} has unique sorted values for a given key, + * this method returns a {@link SortedSet}, instead of the + * {@link java.util.Collection} specified in the {@link Multimap} interface. + */ + @Override + SortedSet removeAll(@Nullable Object key); + + /** + * Stores a collection of values with the same key, replacing any existing + * values for that key. + * + *

+ * Because a {@code SortedSetMultimap} has unique sorted values for a given key, + * this method returns a {@link SortedSet}, instead of the + * {@link java.util.Collection} specified in the {@link Multimap} interface. + * + *

+ * Any duplicates in {@code values} will be stored in the multimap once. + */ + @Override + SortedSet replaceValues(K key, Iterable values); + + /** + * Returns a map view that associates each key with the corresponding values in + * the multimap. Changes to the returned map, such as element removal, will + * update the underlying multimap. The map does not support {@code setValue()} + * on its entries, {@code put}, or {@code putAll}. + * + *

+ * When passed a key that is present in the map, {@code + * asMap().get(Object)} has the same behavior as {@link #get}, returning a live + * collection. When passed a key that is not present, however, {@code + * asMap().get(Object)} returns {@code null} instead of an empty collection. + * + *

+ * Note: The returned map's values are guaranteed to be of type + * {@link SortedSet}. To obtain this map with the more specific generic type + * {@code Map>}, call {@link Multimaps#asMap(SortedSetMultimap)} + * instead. + */ + @Override + Map> asMap(); + + /** + * Returns the comparator that orders the multimap values, with {@code null} + * indicating that natural ordering is used. + */ + Comparator valueComparator(); +} diff --git a/sources/main/java/com/google/common/collect/SparseImmutableTable.java b/sources/main/java/com/google/common/collect/SparseImmutableTable.java new file mode 100644 index 0000000..c19ac74 --- /dev/null +++ b/sources/main/java/com/google/common/collect/SparseImmutableTable.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.annotation.concurrent.Immutable; + +import com.google.common.annotations.GwtCompatible; + +/** + * A {@code RegularImmutableTable} optimized for sparse data. + */ +@GwtCompatible +@Immutable +final class SparseImmutableTable extends RegularImmutableTable { + + private final ImmutableMap> rowMap; + private final ImmutableMap> columnMap; + private final int[] iterationOrderRow; + private final int[] iterationOrderColumn; + + SparseImmutableTable(ImmutableList> cellList, ImmutableSet rowSpace, ImmutableSet columnSpace) { + Map rowIndex = Maps.newHashMap(); + Map> rows = Maps.newLinkedHashMap(); + 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/sources/main/java/com/google/common/collect/StandardRowSortedTable.java b/sources/main/java/com/google/common/collect/StandardRowSortedTable.java new file mode 100644 index 0000000..737e999 --- /dev/null +++ b/sources/main/java/com/google/common/collect/StandardRowSortedTable.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Supplier; + +/** + * Implementation of {@code Table} whose iteration ordering across row keys is + * sorted by their natural ordering or by a supplied comparator. Note that + * iterations across the columns keys for a single row key may or may not be + * ordered, depending on the implementation. When rows and columns are both + * sorted, it's easier to use the {@link TreeBasedTable} subclass. + * + *

+ * The {@link #rowKeySet} method returns a {@link SortedSet} and the + * {@link #rowMap} method returns a {@link SortedMap}, instead of the + * {@link Set} and {@link Map} specified by the {@link Table} interface. + * + *

+ * Null keys and values are not supported. + * + *

+ * See the {@link StandardTable} superclass for more information about the + * behavior of this class. + * + * @author Jared Levy + */ +@GwtCompatible +class StandardRowSortedTable extends StandardTable implements RowSortedTable { + /* + * TODO(jlevy): Consider adding headTable, tailTable, and subTable methods, + * which return a Table view with rows keys in a given range. Create a + * RowSortedTable subinterface with the revised methods? + */ + + StandardRowSortedTable(SortedMap> backingMap, Supplier> factory) { + super(backingMap, factory); + } + + private SortedMap> sortedBackingMap() { + return (SortedMap>) backingMap; + } + + /** + * {@inheritDoc} + * + *

+ * This method returns a {@link SortedSet}, instead of the {@code Set} specified + * in the {@link Table} interface. + */ + @Override + public SortedSet rowKeySet() { + return (SortedSet) rowMap().keySet(); + } + + /** + * {@inheritDoc} + * + *

+ * This method returns a {@link SortedMap}, instead of the {@code Map} specified + * in the {@link Table} interface. + */ + @Override + public SortedMap> rowMap() { + return (SortedMap>) super.rowMap(); + } + + @Override + SortedMap> createRowMap() { + return new RowSortedMap(); + } + + private class RowSortedMap extends RowMap implements SortedMap> { + @Override + public SortedSet keySet() { + return (SortedSet) super.keySet(); + } + + @Override + SortedSet createKeySet() { + return new Maps.SortedKeySet>(this); + } + + @Override + public Comparator comparator() { + return sortedBackingMap().comparator(); + } + + @Override + public R firstKey() { + return sortedBackingMap().firstKey(); + } + + @Override + public R lastKey() { + return sortedBackingMap().lastKey(); + } + + @Override + public SortedMap> headMap(R toKey) { + checkNotNull(toKey); + return new StandardRowSortedTable(sortedBackingMap().headMap(toKey), factory).rowMap(); + } + + @Override + public SortedMap> subMap(R fromKey, R toKey) { + checkNotNull(fromKey); + checkNotNull(toKey); + return new StandardRowSortedTable(sortedBackingMap().subMap(fromKey, toKey), factory).rowMap(); + } + + @Override + public SortedMap> tailMap(R fromKey) { + checkNotNull(fromKey); + return new StandardRowSortedTable(sortedBackingMap().tailMap(fromKey), factory).rowMap(); + } + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/StandardTable.java b/sources/main/java/com/google/common/collect/StandardTable.java new file mode 100644 index 0000000..f3540b9 --- /dev/null +++ b/sources/main/java/com/google/common/collect/StandardTable.java @@ -0,0 +1,968 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.alwaysTrue; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.in; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Maps.safeContainsKey; +import static com.google.common.collect.Maps.safeGet; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Maps.ImprovedAbstractMap; +import com.google.common.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 + */ +@GwtCompatible +class StandardTable extends AbstractTable implements Serializable { + @GwtTransient + final Map> backingMap; + @GwtTransient + final Supplier> factory; + + StandardTable(Map> backingMap, Supplier> factory) { + this.backingMap = backingMap; + this.factory = factory; + } + + // Accessors + + @Override + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); + } + + @Override + public boolean containsColumn(@Nullable Object columnKey) { + if (columnKey == null) { + return false; + } + for (Map map : backingMap.values()) { + if (safeContainsKey(map, columnKey)) { + return true; + } + } + return false; + } + + @Override + public boolean containsRow(@Nullable Object rowKey) { + return rowKey != null && safeContainsKey(backingMap, rowKey); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return value != null && super.containsValue(value); + } + + @Override + public V get(@Nullable Object rowKey, @Nullable 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 = 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(@Nullable Object rowKey, @Nullable 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 Tables.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.apply(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 = factory.get(); + 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 + @SuppressWarnings("unchecked") + @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 + && Collections2.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. + @SuppressWarnings("unchecked") + @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. + @SuppressWarnings("unchecked") + 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/sources/main/java/com/google/common/collect/Synchronized.java b/sources/main/java/com/google/common/collect/Synchronized.java new file mode 100644 index 0000000..3b96d86 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Synchronized.java @@ -0,0 +1,1833 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Comparator; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Queue; +import java.util.RandomAccess; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; + +/** + * Synchronized collection views. The returned synchronized collection views are + * serializable if the backing collection and the mutex are serializable. + * + *

+ * If {@code null} is passed as the {@code mutex} parameter to any of this + * class's top-level methods or inner class constructors, the created object + * uses itself as the synchronization mutex. + * + *

+ * This class should be used by other collection classes only. + * + * @author Mike Bostock + * @author Jared Levy + */ +@GwtCompatible(emulated = true) +final class Synchronized { + private Synchronized() { + } + + static class SynchronizedObject implements Serializable { + final Object delegate; + final Object mutex; + + SynchronizedObject(Object delegate, @Nullable Object mutex) { + this.delegate = checkNotNull(delegate); + this.mutex = (mutex == null) ? this : mutex; + } + + Object delegate() { + return delegate; + } + + // No equals and hashCode; see ForwardingObject for details. + + @Override + public String toString() { + synchronized (mutex) { + return delegate.toString(); + } + } + + // Serialization invokes writeObject only when it's private. + // The SynchronizedObject subclasses don't need a writeObject method since + // they don't contain any non-transient member variables, while the + // following writeObject() handles the SynchronizedObject members. + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + synchronized (mutex) { + stream.defaultWriteObject(); + } + } + + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0; + } + + private static Collection collection(Collection collection, @Nullable Object mutex) { + return new SynchronizedCollection(collection, mutex); + } + + @VisibleForTesting + static class SynchronizedCollection extends SynchronizedObject implements Collection { + private SynchronizedCollection(Collection delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @SuppressWarnings("unchecked") + @Override + Collection delegate() { + return (Collection) super.delegate(); + } + + @Override + public boolean add(E e) { + synchronized (mutex) { + return delegate().add(e); + } + } + + @Override + public boolean addAll(Collection c) { + synchronized (mutex) { + return delegate().addAll(c); + } + } + + @Override + public void clear() { + synchronized (mutex) { + delegate().clear(); + } + } + + @Override + public boolean contains(Object o) { + synchronized (mutex) { + return delegate().contains(o); + } + } + + @Override + public boolean containsAll(Collection c) { + synchronized (mutex) { + return delegate().containsAll(c); + } + } + + @Override + public boolean isEmpty() { + synchronized (mutex) { + return delegate().isEmpty(); + } + } + + @Override + public Iterator iterator() { + return delegate().iterator(); // manually synchronized + } + + @Override + public boolean remove(Object o) { + synchronized (mutex) { + return delegate().remove(o); + } + } + + @Override + public boolean removeAll(Collection c) { + synchronized (mutex) { + return delegate().removeAll(c); + } + } + + @Override + public boolean retainAll(Collection c) { + synchronized (mutex) { + return delegate().retainAll(c); + } + } + + @Override + public int size() { + synchronized (mutex) { + return delegate().size(); + } + } + + @Override + public Object[] toArray() { + synchronized (mutex) { + return delegate().toArray(); + } + } + + @Override + public T[] toArray(T[] a) { + synchronized (mutex) { + return delegate().toArray(a); + } + } + + private static final long serialVersionUID = 0; + } + + @VisibleForTesting + static Set set(Set set, @Nullable Object mutex) { + return new SynchronizedSet(set, mutex); + } + + static class SynchronizedSet extends SynchronizedCollection implements Set { + + SynchronizedSet(Set delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + Set delegate() { + return (Set) super.delegate(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + private static SortedSet sortedSet(SortedSet set, @Nullable Object mutex) { + return new SynchronizedSortedSet(set, mutex); + } + + static class SynchronizedSortedSet extends SynchronizedSet implements SortedSet { + SynchronizedSortedSet(SortedSet delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + SortedSet delegate() { + return (SortedSet) super.delegate(); + } + + @Override + public Comparator comparator() { + synchronized (mutex) { + return delegate().comparator(); + } + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + synchronized (mutex) { + return sortedSet(delegate().subSet(fromElement, toElement), mutex); + } + } + + @Override + public SortedSet headSet(E toElement) { + synchronized (mutex) { + return sortedSet(delegate().headSet(toElement), mutex); + } + } + + @Override + public SortedSet tailSet(E fromElement) { + synchronized (mutex) { + return sortedSet(delegate().tailSet(fromElement), mutex); + } + } + + @Override + public E first() { + synchronized (mutex) { + return delegate().first(); + } + } + + @Override + public E last() { + synchronized (mutex) { + return delegate().last(); + } + } + + private static final long serialVersionUID = 0; + } + + private static List list(List list, @Nullable Object mutex) { + return (list instanceof RandomAccess) ? new SynchronizedRandomAccessList(list, mutex) + : new SynchronizedList(list, mutex); + } + + private static class SynchronizedList extends SynchronizedCollection implements List { + SynchronizedList(List delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + List delegate() { + return (List) super.delegate(); + } + + @Override + public void add(int index, E element) { + synchronized (mutex) { + delegate().add(index, element); + } + } + + @Override + public boolean addAll(int index, Collection c) { + synchronized (mutex) { + return delegate().addAll(index, c); + } + } + + @Override + public E get(int index) { + synchronized (mutex) { + return delegate().get(index); + } + } + + @Override + public int indexOf(Object o) { + synchronized (mutex) { + return delegate().indexOf(o); + } + } + + @Override + public int lastIndexOf(Object o) { + synchronized (mutex) { + return delegate().lastIndexOf(o); + } + } + + @Override + public ListIterator listIterator() { + return delegate().listIterator(); // manually synchronized + } + + @Override + public ListIterator listIterator(int index) { + return delegate().listIterator(index); // manually synchronized + } + + @Override + public E remove(int index) { + synchronized (mutex) { + return delegate().remove(index); + } + } + + @Override + public E set(int index, E element) { + synchronized (mutex) { + return delegate().set(index, element); + } + } + + @Override + public List subList(int fromIndex, int toIndex) { + synchronized (mutex) { + return list(delegate().subList(fromIndex, toIndex), mutex); + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + private static class SynchronizedRandomAccessList extends SynchronizedList implements RandomAccess { + SynchronizedRandomAccessList(List list, @Nullable Object mutex) { + super(list, mutex); + } + + private static final long serialVersionUID = 0; + } + + static Multiset multiset(Multiset multiset, @Nullable Object mutex) { + if (multiset instanceof SynchronizedMultiset || multiset instanceof ImmutableMultiset) { + return multiset; + } + return new SynchronizedMultiset(multiset, mutex); + } + + private static class SynchronizedMultiset extends SynchronizedCollection implements Multiset { + transient Set elementSet; + transient Set> entrySet; + + SynchronizedMultiset(Multiset delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + Multiset delegate() { + return (Multiset) super.delegate(); + } + + @Override + public int count(Object o) { + synchronized (mutex) { + return delegate().count(o); + } + } + + @Override + public int add(E e, int n) { + synchronized (mutex) { + return delegate().add(e, n); + } + } + + @Override + public int remove(Object o, int n) { + synchronized (mutex) { + return delegate().remove(o, n); + } + } + + @Override + public int setCount(E element, int count) { + synchronized (mutex) { + return delegate().setCount(element, count); + } + } + + @Override + public boolean setCount(E element, int oldCount, int newCount) { + synchronized (mutex) { + return delegate().setCount(element, oldCount, newCount); + } + } + + @Override + public Set elementSet() { + synchronized (mutex) { + if (elementSet == null) { + elementSet = typePreservingSet(delegate().elementSet(), mutex); + } + return elementSet; + } + } + + @Override + public Set> entrySet() { + synchronized (mutex) { + if (entrySet == null) { + entrySet = typePreservingSet(delegate().entrySet(), mutex); + } + return entrySet; + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + static Multimap multimap(Multimap multimap, @Nullable Object mutex) { + if (multimap instanceof SynchronizedMultimap || multimap instanceof ImmutableMultimap) { + return multimap; + } + return new SynchronizedMultimap(multimap, mutex); + } + + private static class SynchronizedMultimap extends SynchronizedObject implements Multimap { + transient Set keySet; + transient Collection valuesCollection; + transient Collection> entries; + transient Map> asMap; + transient Multiset keys; + + @SuppressWarnings("unchecked") + @Override + Multimap delegate() { + return (Multimap) super.delegate(); + } + + SynchronizedMultimap(Multimap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + public int size() { + synchronized (mutex) { + return delegate().size(); + } + } + + @Override + public boolean isEmpty() { + synchronized (mutex) { + return delegate().isEmpty(); + } + } + + @Override + public boolean containsKey(Object key) { + synchronized (mutex) { + return delegate().containsKey(key); + } + } + + @Override + public boolean containsValue(Object value) { + synchronized (mutex) { + return delegate().containsValue(value); + } + } + + @Override + public boolean containsEntry(Object key, Object value) { + synchronized (mutex) { + return delegate().containsEntry(key, value); + } + } + + @Override + public Collection get(K key) { + synchronized (mutex) { + return typePreservingCollection(delegate().get(key), mutex); + } + } + + @Override + public boolean put(K key, V value) { + synchronized (mutex) { + return delegate().put(key, value); + } + } + + @Override + public boolean putAll(K key, Iterable values) { + synchronized (mutex) { + return delegate().putAll(key, values); + } + } + + @Override + public boolean putAll(Multimap multimap) { + synchronized (mutex) { + return delegate().putAll(multimap); + } + } + + @Override + public Collection replaceValues(K key, Iterable values) { + synchronized (mutex) { + return delegate().replaceValues(key, values); // copy not synchronized + } + } + + @Override + public boolean remove(Object key, Object value) { + synchronized (mutex) { + return delegate().remove(key, value); + } + } + + @Override + public Collection removeAll(Object key) { + synchronized (mutex) { + return delegate().removeAll(key); // copy not synchronized + } + } + + @Override + public void clear() { + synchronized (mutex) { + delegate().clear(); + } + } + + @Override + public Set keySet() { + synchronized (mutex) { + if (keySet == null) { + keySet = typePreservingSet(delegate().keySet(), mutex); + } + return keySet; + } + } + + @Override + public Collection values() { + synchronized (mutex) { + if (valuesCollection == null) { + valuesCollection = collection(delegate().values(), mutex); + } + return valuesCollection; + } + } + + @Override + public Collection> entries() { + synchronized (mutex) { + if (entries == null) { + entries = typePreservingCollection(delegate().entries(), mutex); + } + return entries; + } + } + + @Override + public Map> asMap() { + synchronized (mutex) { + if (asMap == null) { + asMap = new SynchronizedAsMap(delegate().asMap(), mutex); + } + return asMap; + } + } + + @Override + public Multiset keys() { + synchronized (mutex) { + if (keys == null) { + keys = multiset(delegate().keys(), mutex); + } + return keys; + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + static ListMultimap listMultimap(ListMultimap multimap, @Nullable Object mutex) { + if (multimap instanceof SynchronizedListMultimap || multimap instanceof ImmutableListMultimap) { + return multimap; + } + return new SynchronizedListMultimap(multimap, mutex); + } + + private static class SynchronizedListMultimap extends SynchronizedMultimap + implements ListMultimap { + SynchronizedListMultimap(ListMultimap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + ListMultimap delegate() { + return (ListMultimap) super.delegate(); + } + + @Override + public List get(K key) { + synchronized (mutex) { + return list(delegate().get(key), mutex); + } + } + + @Override + public List removeAll(Object key) { + synchronized (mutex) { + return delegate().removeAll(key); // copy not synchronized + } + } + + @Override + public List replaceValues(K key, Iterable values) { + synchronized (mutex) { + return delegate().replaceValues(key, values); // copy not synchronized + } + } + + private static final long serialVersionUID = 0; + } + + static SetMultimap setMultimap(SetMultimap multimap, @Nullable Object mutex) { + if (multimap instanceof SynchronizedSetMultimap || multimap instanceof ImmutableSetMultimap) { + return multimap; + } + return new SynchronizedSetMultimap(multimap, mutex); + } + + private static class SynchronizedSetMultimap extends SynchronizedMultimap implements SetMultimap { + transient Set> entrySet; + + SynchronizedSetMultimap(SetMultimap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + SetMultimap delegate() { + return (SetMultimap) super.delegate(); + } + + @Override + public Set get(K key) { + synchronized (mutex) { + return set(delegate().get(key), mutex); + } + } + + @Override + public Set removeAll(Object key) { + synchronized (mutex) { + return delegate().removeAll(key); // copy not synchronized + } + } + + @Override + public Set replaceValues(K key, Iterable values) { + synchronized (mutex) { + return delegate().replaceValues(key, values); // copy not synchronized + } + } + + @Override + public Set> entries() { + synchronized (mutex) { + if (entrySet == null) { + entrySet = set(delegate().entries(), mutex); + } + return entrySet; + } + } + + private static final long serialVersionUID = 0; + } + + static SortedSetMultimap sortedSetMultimap(SortedSetMultimap multimap, @Nullable Object mutex) { + if (multimap instanceof SynchronizedSortedSetMultimap) { + return multimap; + } + return new SynchronizedSortedSetMultimap(multimap, mutex); + } + + private static class SynchronizedSortedSetMultimap extends SynchronizedSetMultimap + implements SortedSetMultimap { + SynchronizedSortedSetMultimap(SortedSetMultimap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + SortedSetMultimap delegate() { + return (SortedSetMultimap) super.delegate(); + } + + @Override + public SortedSet get(K key) { + synchronized (mutex) { + return sortedSet(delegate().get(key), mutex); + } + } + + @Override + public SortedSet removeAll(Object key) { + synchronized (mutex) { + return delegate().removeAll(key); // copy not synchronized + } + } + + @Override + public SortedSet replaceValues(K key, Iterable values) { + synchronized (mutex) { + return delegate().replaceValues(key, values); // copy not synchronized + } + } + + @Override + public Comparator valueComparator() { + synchronized (mutex) { + return delegate().valueComparator(); + } + } + + private static final long serialVersionUID = 0; + } + + private static Collection typePreservingCollection(Collection collection, @Nullable Object mutex) { + if (collection instanceof SortedSet) { + return sortedSet((SortedSet) collection, mutex); + } + if (collection instanceof Set) { + return set((Set) collection, mutex); + } + if (collection instanceof List) { + return list((List) collection, mutex); + } + return collection(collection, mutex); + } + + private static Set typePreservingSet(Set set, @Nullable Object mutex) { + if (set instanceof SortedSet) { + return sortedSet((SortedSet) set, mutex); + } else { + return set(set, mutex); + } + } + + private static class SynchronizedAsMapEntries extends SynchronizedSet>> { + SynchronizedAsMapEntries(Set>> delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + public Iterator>> iterator() { + // Must be manually synchronized. + final Iterator>> iterator = super.iterator(); + return new ForwardingIterator>>() { + @Override + protected Iterator>> delegate() { + return iterator; + } + + @Override + public Map.Entry> next() { + final Map.Entry> entry = super.next(); + return new ForwardingMapEntry>() { + @Override + protected Map.Entry> delegate() { + return entry; + } + + @Override + public Collection getValue() { + return typePreservingCollection(entry.getValue(), mutex); + } + }; + } + }; + } + + // See Collections.CheckedMap.CheckedEntrySet for details on attacks. + + @Override + public Object[] toArray() { + synchronized (mutex) { + return ObjectArrays.toArrayImpl(delegate()); + } + } + + @Override + public T[] toArray(T[] array) { + synchronized (mutex) { + return ObjectArrays.toArrayImpl(delegate(), array); + } + } + + @Override + public boolean contains(Object o) { + synchronized (mutex) { + return Maps.containsEntryImpl(delegate(), o); + } + } + + @Override + public boolean containsAll(Collection c) { + synchronized (mutex) { + return Collections2.containsAllImpl(delegate(), c); + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return Sets.equalsImpl(delegate(), o); + } + } + + @Override + public boolean remove(Object o) { + synchronized (mutex) { + return Maps.removeEntryImpl(delegate(), o); + } + } + + @Override + public boolean removeAll(Collection c) { + synchronized (mutex) { + return Iterators.removeAll(delegate().iterator(), c); + } + } + + @Override + public boolean retainAll(Collection c) { + synchronized (mutex) { + return Iterators.retainAll(delegate().iterator(), c); + } + } + + private static final long serialVersionUID = 0; + } + + @VisibleForTesting + static Map map(Map map, @Nullable Object mutex) { + return new SynchronizedMap(map, mutex); + } + + private static class SynchronizedMap extends SynchronizedObject implements Map { + transient Set keySet; + transient Collection values; + transient Set> entrySet; + + SynchronizedMap(Map delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @SuppressWarnings("unchecked") + @Override + Map delegate() { + return (Map) super.delegate(); + } + + @Override + public void clear() { + synchronized (mutex) { + delegate().clear(); + } + } + + @Override + public boolean containsKey(Object key) { + synchronized (mutex) { + return delegate().containsKey(key); + } + } + + @Override + public boolean containsValue(Object value) { + synchronized (mutex) { + return delegate().containsValue(value); + } + } + + @Override + public Set> entrySet() { + synchronized (mutex) { + if (entrySet == null) { + entrySet = set(delegate().entrySet(), mutex); + } + return entrySet; + } + } + + @Override + public V get(Object key) { + synchronized (mutex) { + return delegate().get(key); + } + } + + @Override + public boolean isEmpty() { + synchronized (mutex) { + return delegate().isEmpty(); + } + } + + @Override + public Set keySet() { + synchronized (mutex) { + if (keySet == null) { + keySet = set(delegate().keySet(), mutex); + } + return keySet; + } + } + + @Override + public V put(K key, V value) { + synchronized (mutex) { + return delegate().put(key, value); + } + } + + @Override + public void putAll(Map map) { + synchronized (mutex) { + delegate().putAll(map); + } + } + + @Override + public V remove(Object key) { + synchronized (mutex) { + return delegate().remove(key); + } + } + + @Override + public int size() { + synchronized (mutex) { + return delegate().size(); + } + } + + @Override + public Collection values() { + synchronized (mutex) { + if (values == null) { + values = collection(delegate().values(), mutex); + } + return values; + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + static SortedMap sortedMap(SortedMap sortedMap, @Nullable Object mutex) { + return new SynchronizedSortedMap(sortedMap, mutex); + } + + static class SynchronizedSortedMap extends SynchronizedMap implements SortedMap { + + SynchronizedSortedMap(SortedMap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + SortedMap delegate() { + return (SortedMap) super.delegate(); + } + + @Override + public Comparator comparator() { + synchronized (mutex) { + return delegate().comparator(); + } + } + + @Override + public K firstKey() { + synchronized (mutex) { + return delegate().firstKey(); + } + } + + @Override + public SortedMap headMap(K toKey) { + synchronized (mutex) { + return sortedMap(delegate().headMap(toKey), mutex); + } + } + + @Override + public K lastKey() { + synchronized (mutex) { + return delegate().lastKey(); + } + } + + @Override + public SortedMap subMap(K fromKey, K toKey) { + synchronized (mutex) { + return sortedMap(delegate().subMap(fromKey, toKey), mutex); + } + } + + @Override + public SortedMap tailMap(K fromKey) { + synchronized (mutex) { + return sortedMap(delegate().tailMap(fromKey), mutex); + } + } + + private static final long serialVersionUID = 0; + } + + static BiMap biMap(BiMap bimap, @Nullable Object mutex) { + if (bimap instanceof SynchronizedBiMap || bimap instanceof ImmutableBiMap) { + return bimap; + } + return new SynchronizedBiMap(bimap, mutex, null); + } + + @VisibleForTesting + static class SynchronizedBiMap extends SynchronizedMap implements BiMap, Serializable { + private transient Set valueSet; + private transient BiMap inverse; + + private SynchronizedBiMap(BiMap delegate, @Nullable Object mutex, @Nullable BiMap inverse) { + super(delegate, mutex); + this.inverse = inverse; + } + + @Override + BiMap delegate() { + return (BiMap) super.delegate(); + } + + @Override + public Set values() { + synchronized (mutex) { + if (valueSet == null) { + valueSet = set(delegate().values(), mutex); + } + return valueSet; + } + } + + @Override + public V forcePut(K key, V value) { + synchronized (mutex) { + return delegate().forcePut(key, value); + } + } + + @Override + public BiMap inverse() { + synchronized (mutex) { + if (inverse == null) { + inverse = new SynchronizedBiMap(delegate().inverse(), mutex, this); + } + return inverse; + } + } + + private static final long serialVersionUID = 0; + } + + private static class SynchronizedAsMap extends SynchronizedMap> { + transient Set>> asMapEntrySet; + transient Collection> asMapValues; + + SynchronizedAsMap(Map> delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + public Collection get(Object key) { + synchronized (mutex) { + Collection collection = super.get(key); + return (collection == null) ? null : typePreservingCollection(collection, mutex); + } + } + + @Override + public Set>> entrySet() { + synchronized (mutex) { + if (asMapEntrySet == null) { + asMapEntrySet = new SynchronizedAsMapEntries(delegate().entrySet(), mutex); + } + return asMapEntrySet; + } + } + + @Override + public Collection> values() { + synchronized (mutex) { + if (asMapValues == null) { + asMapValues = new SynchronizedAsMapValues(delegate().values(), mutex); + } + return asMapValues; + } + } + + @Override + public boolean containsValue(Object o) { + // values() and its contains() method are both synchronized. + return values().contains(o); + } + + private static final long serialVersionUID = 0; + } + + private static class SynchronizedAsMapValues extends SynchronizedCollection> { + SynchronizedAsMapValues(Collection> delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + public Iterator> iterator() { + // Must be manually synchronized. + final Iterator> iterator = super.iterator(); + return new ForwardingIterator>() { + @Override + protected Iterator> delegate() { + return iterator; + } + + @Override + public Collection next() { + return typePreservingCollection(super.next(), mutex); + } + }; + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible("NavigableSet") + @VisibleForTesting + static class SynchronizedNavigableSet extends SynchronizedSortedSet implements NavigableSet { + SynchronizedNavigableSet(NavigableSet delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + NavigableSet delegate() { + return (NavigableSet) super.delegate(); + } + + @Override + public E ceiling(E e) { + synchronized (mutex) { + return delegate().ceiling(e); + } + } + + @Override + public Iterator descendingIterator() { + return delegate().descendingIterator(); // manually synchronized + } + + transient NavigableSet descendingSet; + + @Override + public NavigableSet descendingSet() { + synchronized (mutex) { + if (descendingSet == null) { + NavigableSet dS = Synchronized.navigableSet(delegate().descendingSet(), mutex); + descendingSet = dS; + return dS; + } + return descendingSet; + } + } + + @Override + public E floor(E e) { + synchronized (mutex) { + return delegate().floor(e); + } + } + + @Override + public NavigableSet headSet(E toElement, boolean inclusive) { + synchronized (mutex) { + return Synchronized.navigableSet(delegate().headSet(toElement, inclusive), mutex); + } + } + + @Override + public E higher(E e) { + synchronized (mutex) { + return delegate().higher(e); + } + } + + @Override + public E lower(E e) { + synchronized (mutex) { + return delegate().lower(e); + } + } + + @Override + public E pollFirst() { + synchronized (mutex) { + return delegate().pollFirst(); + } + } + + @Override + public E pollLast() { + synchronized (mutex) { + return delegate().pollLast(); + } + } + + @Override + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + synchronized (mutex) { + return Synchronized.navigableSet(delegate().subSet(fromElement, fromInclusive, toElement, toInclusive), + mutex); + } + } + + @Override + public NavigableSet tailSet(E fromElement, boolean inclusive) { + synchronized (mutex) { + return Synchronized.navigableSet(delegate().tailSet(fromElement, inclusive), mutex); + } + } + + @Override + public SortedSet headSet(E toElement) { + return headSet(toElement, false); + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public SortedSet tailSet(E fromElement) { + return tailSet(fromElement, true); + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible("NavigableSet") + static NavigableSet navigableSet(NavigableSet navigableSet, @Nullable Object mutex) { + return new SynchronizedNavigableSet(navigableSet, mutex); + } + + @GwtIncompatible("NavigableSet") + static NavigableSet navigableSet(NavigableSet navigableSet) { + return navigableSet(navigableSet, null); + } + + @GwtIncompatible("NavigableMap") + static NavigableMap navigableMap(NavigableMap navigableMap) { + return navigableMap(navigableMap, null); + } + + @GwtIncompatible("NavigableMap") + static NavigableMap navigableMap(NavigableMap navigableMap, @Nullable Object mutex) { + return new SynchronizedNavigableMap(navigableMap, mutex); + } + + @GwtIncompatible("NavigableMap") + @VisibleForTesting + static class SynchronizedNavigableMap extends SynchronizedSortedMap implements NavigableMap { + + SynchronizedNavigableMap(NavigableMap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + NavigableMap delegate() { + return (NavigableMap) super.delegate(); + } + + @Override + public Entry ceilingEntry(K key) { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().ceilingEntry(key), mutex); + } + } + + @Override + public K ceilingKey(K key) { + synchronized (mutex) { + return delegate().ceilingKey(key); + } + } + + transient NavigableSet descendingKeySet; + + @Override + public NavigableSet descendingKeySet() { + synchronized (mutex) { + if (descendingKeySet == null) { + return descendingKeySet = Synchronized.navigableSet(delegate().descendingKeySet(), mutex); + } + return descendingKeySet; + } + } + + transient NavigableMap descendingMap; + + @Override + public NavigableMap descendingMap() { + synchronized (mutex) { + if (descendingMap == null) { + return descendingMap = navigableMap(delegate().descendingMap(), mutex); + } + return descendingMap; + } + } + + @Override + public Entry firstEntry() { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().firstEntry(), mutex); + } + } + + @Override + public Entry floorEntry(K key) { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().floorEntry(key), mutex); + } + } + + @Override + public K floorKey(K key) { + synchronized (mutex) { + return delegate().floorKey(key); + } + } + + @Override + public NavigableMap headMap(K toKey, boolean inclusive) { + synchronized (mutex) { + return navigableMap(delegate().headMap(toKey, inclusive), mutex); + } + } + + @Override + public Entry higherEntry(K key) { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().higherEntry(key), mutex); + } + } + + @Override + public K higherKey(K key) { + synchronized (mutex) { + return delegate().higherKey(key); + } + } + + @Override + public Entry lastEntry() { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().lastEntry(), mutex); + } + } + + @Override + public Entry lowerEntry(K key) { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().lowerEntry(key), mutex); + } + } + + @Override + public K lowerKey(K key) { + synchronized (mutex) { + return delegate().lowerKey(key); + } + } + + @Override + public Set keySet() { + return navigableKeySet(); + } + + transient NavigableSet navigableKeySet; + + @Override + public NavigableSet navigableKeySet() { + synchronized (mutex) { + if (navigableKeySet == null) { + return navigableKeySet = Synchronized.navigableSet(delegate().navigableKeySet(), mutex); + } + return navigableKeySet; + } + } + + @Override + public Entry pollFirstEntry() { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().pollFirstEntry(), mutex); + } + } + + @Override + public Entry pollLastEntry() { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().pollLastEntry(), mutex); + } + } + + @Override + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + synchronized (mutex) { + return navigableMap(delegate().subMap(fromKey, fromInclusive, toKey, toInclusive), mutex); + } + } + + @Override + public NavigableMap tailMap(K fromKey, boolean inclusive) { + synchronized (mutex) { + return navigableMap(delegate().tailMap(fromKey, inclusive), mutex); + } + } + + @Override + public SortedMap headMap(K toKey) { + return headMap(toKey, false); + } + + @Override + public SortedMap subMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public SortedMap tailMap(K fromKey) { + return tailMap(fromKey, true); + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible("works but is needed only for NavigableMap") + private static Entry nullableSynchronizedEntry(@Nullable Entry entry, @Nullable Object mutex) { + if (entry == null) { + return null; + } + return new SynchronizedEntry(entry, mutex); + } + + @GwtIncompatible("works but is needed only for NavigableMap") + private static class SynchronizedEntry extends SynchronizedObject implements Entry { + + SynchronizedEntry(Entry delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @SuppressWarnings("unchecked") // guaranteed by the constructor + @Override + Entry delegate() { + return (Entry) super.delegate(); + } + + @Override + public boolean equals(Object obj) { + synchronized (mutex) { + return delegate().equals(obj); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + @Override + public K getKey() { + synchronized (mutex) { + return delegate().getKey(); + } + } + + @Override + public V getValue() { + synchronized (mutex) { + return delegate().getValue(); + } + } + + @Override + public V setValue(V value) { + synchronized (mutex) { + return delegate().setValue(value); + } + } + + private static final long serialVersionUID = 0; + } + + static Queue queue(Queue queue, @Nullable Object mutex) { + return (queue instanceof SynchronizedQueue) ? queue : new SynchronizedQueue(queue, mutex); + } + + private static class SynchronizedQueue extends SynchronizedCollection implements Queue { + + SynchronizedQueue(Queue delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + Queue delegate() { + return (Queue) super.delegate(); + } + + @Override + public E element() { + synchronized (mutex) { + return delegate().element(); + } + } + + @Override + public boolean offer(E e) { + synchronized (mutex) { + return delegate().offer(e); + } + } + + @Override + public E peek() { + synchronized (mutex) { + return delegate().peek(); + } + } + + @Override + public E poll() { + synchronized (mutex) { + return delegate().poll(); + } + } + + @Override + public E remove() { + synchronized (mutex) { + return delegate().remove(); + } + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible("Deque") + static Deque deque(Deque deque, @Nullable Object mutex) { + return new SynchronizedDeque(deque, mutex); + } + + @GwtIncompatible("Deque") + private static final class SynchronizedDeque extends SynchronizedQueue implements Deque { + + SynchronizedDeque(Deque delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + @Override + Deque delegate() { + return (Deque) super.delegate(); + } + + @Override + public void addFirst(E e) { + synchronized (mutex) { + delegate().addFirst(e); + } + } + + @Override + public void addLast(E e) { + synchronized (mutex) { + delegate().addLast(e); + } + } + + @Override + public boolean offerFirst(E e) { + synchronized (mutex) { + return delegate().offerFirst(e); + } + } + + @Override + public boolean offerLast(E e) { + synchronized (mutex) { + return delegate().offerLast(e); + } + } + + @Override + public E removeFirst() { + synchronized (mutex) { + return delegate().removeFirst(); + } + } + + @Override + public E removeLast() { + synchronized (mutex) { + return delegate().removeLast(); + } + } + + @Override + public E pollFirst() { + synchronized (mutex) { + return delegate().pollFirst(); + } + } + + @Override + public E pollLast() { + synchronized (mutex) { + return delegate().pollLast(); + } + } + + @Override + public E getFirst() { + synchronized (mutex) { + return delegate().getFirst(); + } + } + + @Override + public E getLast() { + synchronized (mutex) { + return delegate().getLast(); + } + } + + @Override + public E peekFirst() { + synchronized (mutex) { + return delegate().peekFirst(); + } + } + + @Override + public E peekLast() { + synchronized (mutex) { + return delegate().peekLast(); + } + } + + @Override + public boolean removeFirstOccurrence(Object o) { + synchronized (mutex) { + return delegate().removeFirstOccurrence(o); + } + } + + @Override + public boolean removeLastOccurrence(Object o) { + synchronized (mutex) { + return delegate().removeLastOccurrence(o); + } + } + + @Override + public void push(E e) { + synchronized (mutex) { + delegate().push(e); + } + } + + @Override + public E pop() { + synchronized (mutex) { + return delegate().pop(); + } + } + + @Override + public Iterator descendingIterator() { + synchronized (mutex) { + return delegate().descendingIterator(); + } + } + + private static final long serialVersionUID = 0; + } +} diff --git a/sources/main/java/com/google/common/collect/Table.java b/sources/main/java/com/google/common/collect/Table.java new file mode 100644 index 0000000..1430d72 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Table.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + +/** + * 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 + */ +@GwtCompatible +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(@Nullable Object rowKey, @Nullable 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(@Nullable 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(@Nullable Object columnKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified + * value. + * + * @param value value to search for + */ + boolean containsValue(@Nullable 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(@Nullable Object rowKey, @Nullable 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(@Nullable 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(@Nullable Object rowKey, @Nullable 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(@Nullable 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/sources/main/java/com/google/common/collect/Tables.java b/sources/main/java/com/google/common/collect/Tables.java new file mode 100644 index 0000000..23954a2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/Tables.java @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Supplier; +import com.google.common.collect.Table.Cell; + +/** + * Provides static methods that involve a {@code Table}. + * + *

+ * See the Guava User Guide article on + * {@code Tables}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 7.0 + */ +@GwtCompatible +public final class Tables { + private Tables() { + } + + /** + * Returns an immutable cell with the specified row key, column key, and value. + * + *

+ * The returned cell is serializable. + * + * @param rowKey the row key to be associated with the returned cell + * @param columnKey the column key to be associated with the returned cell + * @param value the value to be associated with the returned cell + */ + public static Cell immutableCell(@Nullable R rowKey, @Nullable C columnKey, @Nullable 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(@Nullable R rowKey, @Nullable C columnKey, @Nullable 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 Objects.equal(getRowKey(), other.getRowKey()) + && Objects.equal(getColumnKey(), other.getColumnKey()) + && Objects.equal(getValue(), other.getValue()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getRowKey(), getColumnKey(), getValue()); + } + + @Override + public String toString() { + return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); + } + } + + /** + * Creates a transposed view of a given table that flips its row and column + * keys. In other words, calling {@code get(columnKey, rowKey)} on the generated + * table always returns the same value as calling {@code + * get(rowKey, columnKey)} on the original table. Updating the original table + * changes the contents of the transposed table and vice versa. + * + *

+ * The returned table supports update operations as long as the input table + * supports the analogous operation with swapped rows and columns. For example, + * in a {@link HashBasedTable} instance, {@code + * rowKeySet().iterator()} supports {@code remove()} but {@code + * columnKeySet().iterator()} doesn't. With a transposed {@link HashBasedTable}, + * it's the other way around. + */ + public static Table transpose(Table table) { + return (table instanceof TransposeTable) ? ((TransposeTable) table).original + : new TransposeTable(table); + } + + private static class TransposeTable extends AbstractTable { + final Table original; + + TransposeTable(Table original) { + this.original = checkNotNull(original); + } + + @Override + public void clear() { + original.clear(); + } + + @Override + public Map column(R columnKey) { + return original.row(columnKey); + } + + @Override + public Set columnKeySet() { + return original.rowKeySet(); + } + + @Override + public Map> columnMap() { + return original.rowMap(); + } + + @Override + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return original.contains(columnKey, rowKey); + } + + @Override + public boolean containsColumn(@Nullable Object columnKey) { + return original.containsRow(columnKey); + } + + @Override + public boolean containsRow(@Nullable Object rowKey) { + return original.containsColumn(rowKey); + } + + @Override + public boolean containsValue(@Nullable Object value) { + return original.containsValue(value); + } + + @Override + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + return original.get(columnKey, rowKey); + } + + @Override + public V put(C rowKey, R columnKey, V value) { + return original.put(columnKey, rowKey, value); + } + + @Override + public void putAll(Table table) { + original.putAll(transpose(table)); + } + + @Override + public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + return original.remove(columnKey, rowKey); + } + + @Override + public Map row(C rowKey) { + return original.column(rowKey); + } + + @Override + public Set rowKeySet() { + return original.columnKeySet(); + } + + @Override + public Map> rowMap() { + return original.columnMap(); + } + + @Override + public int size() { + return original.size(); + } + + @Override + public Collection values() { + return original.values(); + } + + // Will cast TRANSPOSE_CELL to a type that always succeeds + private static final Function, Cell> TRANSPOSE_CELL = new Function, Cell>() { + @Override + public Cell apply(Cell cell) { + return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue()); + } + }; + + @SuppressWarnings("unchecked") + @Override + Iterator> cellIterator() { + return Iterators.transform(original.cellSet().iterator(), (Function) TRANSPOSE_CELL); + } + } + + /** + * Creates a table that uses the specified backing map and factory. It can + * generate a table based on arbitrary {@link Map} classes. + * + *

+ * The {@code factory}-generated and {@code backingMap} classes determine the + * table iteration order. However, the table's {@code row()} method returns + * instances of a different class than {@code factory.get()} does. + * + *

+ * Call this method only when the simpler factory methods in classes like + * {@link HashBasedTable} and {@link TreeBasedTable} won't suffice. + * + *

+ * The views returned by the {@code Table} methods {@link Table#column}, + * {@link Table#columnKeySet}, and {@link Table#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. + * + *

+ * The table is serializable if {@code backingMap}, {@code factory}, the maps + * generated by {@code factory}, and the table contents are all serializable. + * + *

+ * Note: the table assumes complete ownership over of {@code backingMap} and the + * maps returned by {@code factory}. Those objects should not be manually + * updated and they should not use soft, weak, or phantom references. + * + * @param backingMap place to store the mapping from each row key to its + * corresponding column key / value map + * @param factory supplier of new, empty maps that will each hold all column + * key / value mappings for a given row key + * @throws IllegalArgumentException if {@code backingMap} is not empty + * @since 10.0 + */ + @Beta + public static Table newCustomTable(Map> backingMap, + Supplier> factory) { + checkArgument(backingMap.isEmpty()); + checkNotNull(factory); + // TODO(jlevy): Wrap factory to validate that the supplied maps are empty? + return new StandardTable(backingMap, factory); + } + + /** + * Returns a view of a table where each value is transformed by a function. All + * other properties of the table, such as iteration order, are left intact. + * + *

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

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

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

+ * The function is applied lazily, invoked when needed. This is necessary for + * the returned table to be a view, but it means that the function will be + * applied many times for bulk operations like {@link Table#containsValue} and + * {@code Table.toString()}. For this to perform well, {@code function} should + * be fast. To avoid lazy evaluation when the returned table doesn't need to be + * a view, copy the returned table into a new table of your choosing. + * + * @since 10.0 + */ + @Beta + public static Table transformValues(Table fromTable, + Function function) { + return new TransformedTable(fromTable, function); + } + + private static class TransformedTable extends AbstractTable { + final Table fromTable; + final Function function; + + TransformedTable(Table fromTable, Function function) { + this.fromTable = checkNotNull(fromTable); + this.function = checkNotNull(function); + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + return fromTable.contains(rowKey, columnKey); + } + + @Override + public V2 get(Object rowKey, Object columnKey) { + // The function is passed a null input only when the table contains a null + // value. + return contains(rowKey, columnKey) ? function.apply(fromTable.get(rowKey, columnKey)) : null; + } + + @Override + public int size() { + return fromTable.size(); + } + + @Override + public void clear() { + fromTable.clear(); + } + + @Override + public V2 put(R rowKey, C columnKey, V2 value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Table table) { + throw new UnsupportedOperationException(); + } + + @Override + public V2 remove(Object rowKey, Object columnKey) { + return contains(rowKey, columnKey) ? function.apply(fromTable.remove(rowKey, columnKey)) : null; + } + + @Override + public Map row(R rowKey) { + return Maps.transformValues(fromTable.row(rowKey), function); + } + + @Override + public Map column(C columnKey) { + return Maps.transformValues(fromTable.column(columnKey), function); + } + + Function, Cell> cellFunction() { + return new Function, Cell>() { + @Override + public Cell apply(Cell cell) { + return immutableCell(cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue())); + } + }; + } + + @Override + Iterator> cellIterator() { + return Iterators.transform(fromTable.cellSet().iterator(), cellFunction()); + } + + @Override + public Set rowKeySet() { + return fromTable.rowKeySet(); + } + + @Override + public Set columnKeySet() { + return fromTable.columnKeySet(); + } + + @Override + Collection createValues() { + return Collections2.transform(fromTable.values(), function); + } + + @Override + public Map> rowMap() { + Function, Map> rowFunction = new Function, Map>() { + @Override + public Map apply(Map row) { + return Maps.transformValues(row, function); + } + }; + return Maps.transformValues(fromTable.rowMap(), rowFunction); + } + + @Override + public Map> columnMap() { + Function, Map> columnFunction = new Function, Map>() { + @Override + public Map apply(Map column) { + return Maps.transformValues(column, function); + } + }; + return Maps.transformValues(fromTable.columnMap(), columnFunction); + } + } + + /** + * Returns an unmodifiable view of the specified table. This method allows + * modules to provide users with "read-only" access to internal tables. Query + * operations on the returned table "read through" to the specified table, and + * attempts to modify the returned table, whether direct or via its collection + * views, result in an {@code UnsupportedOperationException}. + * + *

+ * The returned table will be serializable if the specified table is + * serializable. + * + *

+ * Consider using an {@link ImmutableTable}, which is guaranteed never to + * change. + * + * @param table the table for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified table + * @since 11.0 + */ + public static Table unmodifiableTable(Table table) { + return new UnmodifiableTable(table); + } + + private static class UnmodifiableTable extends ForwardingTable implements Serializable { + final Table delegate; + + UnmodifiableTable(Table delegate) { + this.delegate = checkNotNull(delegate); + } + + @SuppressWarnings("unchecked") // safe, covariant cast + @Override + protected Table delegate() { + return (Table) delegate; + } + + @Override + public Set> cellSet() { + return Collections.unmodifiableSet(super.cellSet()); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Map column(@Nullable C columnKey) { + return Collections.unmodifiableMap(super.column(columnKey)); + } + + @Override + public Set columnKeySet() { + return Collections.unmodifiableSet(super.columnKeySet()); + } + + @Override + public Map> columnMap() { + Function, Map> wrapper = unmodifiableWrapper(); + return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper)); + } + + @Override + public V put(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Table table) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + throw new UnsupportedOperationException(); + } + + @Override + public Map row(@Nullable R rowKey) { + return Collections.unmodifiableMap(super.row(rowKey)); + } + + @Override + public Set rowKeySet() { + return Collections.unmodifiableSet(super.rowKeySet()); + } + + @Override + public Map> rowMap() { + Function, Map> wrapper = unmodifiableWrapper(); + return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper)); + } + + @Override + public Collection values() { + return Collections.unmodifiableCollection(super.values()); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an unmodifiable view of the specified row-sorted table. This method + * allows modules to provide users with "read-only" access to internal tables. + * Query operations on the returned table "read through" to the specified table, + * and attemps to modify the returned table, whether direct or via its + * collection views, result in an {@code UnsupportedOperationException}. + * + *

+ * The returned table will be serializable if the specified table is + * serializable. + * + * @param table the row-sorted table for which an unmodifiable view is to be + * returned + * @return an unmodifiable view of the specified table + * @since 11.0 + */ + @Beta + public static RowSortedTable unmodifiableRowSortedTable( + RowSortedTable table) { + /* + * It's not ? extends R, because it's technically not covariant in R. + * Specifically, table.rowMap().comparator() could return a comparator that only + * works for the ? extends R. Collections.unmodifiableSortedMap makes the same + * distinction. + */ + return new UnmodifiableRowSortedMap(table); + } + + static final class UnmodifiableRowSortedMap extends UnmodifiableTable + implements RowSortedTable { + + public UnmodifiableRowSortedMap(RowSortedTable delegate) { + super(delegate); + } + + @Override + protected RowSortedTable delegate() { + return (RowSortedTable) super.delegate(); + } + + @Override + public SortedMap> rowMap() { + Function, Map> wrapper = unmodifiableWrapper(); + return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper)); + } + + @Override + public SortedSet rowKeySet() { + return Collections.unmodifiableSortedSet(delegate().rowKeySet()); + } + + private static final long serialVersionUID = 0; + } + + @SuppressWarnings("unchecked") + private static Function, Map> unmodifiableWrapper() { + return (Function) UNMODIFIABLE_WRAPPER; + } + + private static final Function, ? extends Map> UNMODIFIABLE_WRAPPER = new Function, Map>() { + @Override + public Map apply(Map input) { + return Collections.unmodifiableMap(input); + } + }; + + static boolean equalsImpl(Table table, @Nullable Object obj) { + if (obj == table) { + return true; + } else if (obj instanceof Table) { + Table that = (Table) obj; + return table.cellSet().equals(that.cellSet()); + } else { + return false; + } + } +} diff --git a/sources/main/java/com/google/common/collect/TransformedIterator.java b/sources/main/java/com/google/common/collect/TransformedIterator.java new file mode 100644 index 0000000..55a6ab2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/TransformedIterator.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Iterator; + +import com.google.common.annotations.GwtCompatible; + +/** + * An iterator that transforms a backing iterator; for internal use. This avoids + * the object overhead of constructing a {@link Function} for internal methods. + * + * @author Louis Wasserman + */ +@GwtCompatible +abstract class TransformedIterator 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/sources/main/java/com/google/common/collect/TransformedListIterator.java b/sources/main/java/com/google/common/collect/TransformedListIterator.java new file mode 100644 index 0000000..b84d0a5 --- /dev/null +++ b/sources/main/java/com/google/common/collect/TransformedListIterator.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.ListIterator; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; + +/** + * An iterator that transforms a backing list iterator; for internal use. This + * avoids the object overhead of constructing a {@link Function} for internal + * methods. + * + * @author Louis Wasserman + */ +@GwtCompatible +abstract class TransformedListIterator extends TransformedIterator implements ListIterator { + TransformedListIterator(ListIterator backingIterator) { + super(backingIterator); + } + + private ListIterator backingIterator() { + return Iterators.cast(backingIterator); + } + + @Override + public final boolean hasPrevious() { + return backingIterator().hasPrevious(); + } + + @Override + public final T previous() { + return transform(backingIterator().previous()); + } + + @Override + public final int nextIndex() { + return backingIterator().nextIndex(); + } + + @Override + public final int previousIndex() { + return backingIterator().previousIndex(); + } + + @Override + public void set(T element) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(T element) { + throw new UnsupportedOperationException(); + } +} diff --git a/sources/main/java/com/google/common/collect/TreeBasedTable.java b/sources/main/java/com/google/common/collect/TreeBasedTable.java new file mode 100644 index 0000000..999acd7 --- /dev/null +++ b/sources/main/java/com/google/common/collect/TreeBasedTable.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Supplier; + +/** + * Implementation of {@code Table} whose row keys and column keys are ordered by + * their natural ordering or by supplied comparators. When constructing a + * {@code TreeBasedTable}, you may provide comparators for the row keys and the + * column keys, or you may use natural ordering for both. + * + *

+ * The {@link #rowKeySet} method returns a {@link SortedSet} and the + * {@link #rowMap} method returns a {@link SortedMap}, instead of the + * {@link Set} and {@link Map} specified by the {@link Table} interface. + * + *

+ * 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. + * + *

+ * Because a {@code TreeBasedTable} has unique sorted values for a given row, + * both {@code row(rowKey)} and {@code rowMap().get(rowKey)} are + * {@link SortedMap} instances, instead of the {@link Map} specified in the + * {@link Table} interface. + * + *

+ * 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. + * + *

+ * See the Guava User Guide article on + * {@code Table}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 7.0 + */ +@GwtCompatible(serializable = true) +@Beta +public class TreeBasedTable extends StandardRowSortedTable { + private final Comparator columnComparator; + + private static class Factory implements Supplier>, Serializable { + final Comparator comparator; + + Factory(Comparator comparator) { + this.comparator = comparator; + } + + @Override + public TreeMap get() { + return new TreeMap(comparator); + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates an empty {@code TreeBasedTable} that uses the natural orderings of + * both row and column keys. + * + *

+ * The method signature specifies {@code R extends Comparable} with a raw + * {@link Comparable}, instead of {@code R extends Comparable}, and + * the same for {@code C}. That's necessary to support classes defined without + * generics. + */ + public static TreeBasedTable create() { + return new TreeBasedTable(Ordering.natural(), Ordering.natural()); + } + + /** + * Creates an empty {@code TreeBasedTable} that is ordered by the specified + * comparators. + * + * @param rowComparator the comparator that orders the row keys + * @param columnComparator the comparator that orders the column keys + */ + public static TreeBasedTable create(Comparator rowComparator, + Comparator columnComparator) { + checkNotNull(rowComparator); + checkNotNull(columnComparator); + return new TreeBasedTable(rowComparator, columnComparator); + } + + /** + * Creates a {@code TreeBasedTable} with the same mappings and sort order as the + * specified {@code TreeBasedTable}. + */ + public static TreeBasedTable create(TreeBasedTable table) { + TreeBasedTable result = new TreeBasedTable(table.rowComparator(), table.columnComparator()); + result.putAll(table); + return result; + } + + TreeBasedTable(Comparator rowComparator, Comparator columnComparator) { + super(new TreeMap>(rowComparator), new Factory(columnComparator)); + this.columnComparator = columnComparator; + } + + // TODO(jlevy): Move to StandardRowSortedTable? + + /** + * Returns the comparator that orders the rows. With natural ordering, + * {@link Ordering#natural()} is returned. + */ + public Comparator rowComparator() { + return rowKeySet().comparator(); + } + + /** + * Returns the comparator that orders the columns. With natural ordering, + * {@link Ordering#natural()} is returned. + */ + public Comparator columnComparator() { + return columnComparator; + } + + // TODO(user): make column return a SortedMap + + /** + * {@inheritDoc} + * + *

+ * Because a {@code TreeBasedTable} has unique sorted values for a given row, + * this method returns a {@link SortedMap}, instead of the {@link Map} specified + * in the {@link Table} interface. + * + * @since 10.0 + * (mostly source-compatible since 7.0) + */ + @Override + public SortedMap row(R rowKey) { + return new TreeRow(rowKey); + } + + private class TreeRow extends Row implements SortedMap { + @Nullable + final C lowerBound; + @Nullable + final C upperBound; + + TreeRow(R rowKey) { + this(rowKey, null, null); + } + + TreeRow(R rowKey, @Nullable C lowerBound, @Nullable C upperBound) { + super(rowKey); + this.lowerBound = lowerBound; + this.upperBound = upperBound; + checkArgument(lowerBound == null || upperBound == null || compare(lowerBound, upperBound) <= 0); + } + + @Override + public SortedSet keySet() { + return new Maps.SortedKeySet(this); + } + + @Override + public Comparator comparator() { + return columnComparator(); + } + + int compare(Object a, Object b) { + // pretend we can compare anything + @SuppressWarnings({ "rawtypes", "unchecked" }) + Comparator cmp = (Comparator) comparator(); + return cmp.compare(a, b); + } + + boolean rangeContains(@Nullable Object o) { + return o != null && (lowerBound == null || compare(lowerBound, o) <= 0) + && (upperBound == null || compare(upperBound, o) > 0); + } + + @Override + public SortedMap subMap(C fromKey, C toKey) { + checkArgument(rangeContains(checkNotNull(fromKey)) && rangeContains(checkNotNull(toKey))); + return new TreeRow(rowKey, fromKey, toKey); + } + + @Override + public SortedMap headMap(C toKey) { + checkArgument(rangeContains(checkNotNull(toKey))); + return new TreeRow(rowKey, lowerBound, toKey); + } + + @Override + public SortedMap tailMap(C fromKey) { + checkArgument(rangeContains(checkNotNull(fromKey))); + return new TreeRow(rowKey, fromKey, upperBound); + } + + @Override + public C firstKey() { + SortedMap backing = backingRowMap(); + if (backing == null) { + throw new NoSuchElementException(); + } + return backingRowMap().firstKey(); + } + + @Override + public C lastKey() { + SortedMap backing = backingRowMap(); + if (backing == null) { + throw new NoSuchElementException(); + } + return backingRowMap().lastKey(); + } + + transient SortedMap wholeRow; + + /* + * If the row was previously empty, we check if there's a new row here every + * time we're queried. + */ + SortedMap wholeRow() { + if (wholeRow == null || (wholeRow.isEmpty() && backingMap.containsKey(rowKey))) { + wholeRow = (SortedMap) backingMap.get(rowKey); + } + return wholeRow; + } + + @Override + SortedMap backingRowMap() { + return (SortedMap) super.backingRowMap(); + } + + @Override + SortedMap computeBackingRowMap() { + SortedMap map = wholeRow(); + if (map != null) { + if (lowerBound != null) { + map = map.tailMap(lowerBound); + } + if (upperBound != null) { + map = map.headMap(upperBound); + } + return map; + } + return null; + } + + @Override + void maintainEmptyInvariant() { + if (wholeRow() != null && wholeRow.isEmpty()) { + backingMap.remove(rowKey); + wholeRow = null; + backingRowMap = null; + } + } + + @Override + public boolean containsKey(Object key) { + return rangeContains(key) && super.containsKey(key); + } + + @Override + public V put(C key, V value) { + checkArgument(rangeContains(checkNotNull(key))); + return super.put(key, value); + } + } + + // rowKeySet() and rowMap() are defined here so they appear in the Javadoc. + + @Override + public SortedSet rowKeySet() { + return super.rowKeySet(); + } + + @Override + public SortedMap> rowMap() { + return super.rowMap(); + } + + /** + * Overridden column iterator to return columns values in globally sorted order. + */ + @Override + Iterator createColumnKeyIterator() { + final Comparator comparator = columnComparator(); + + final Iterator merged = Iterators + .mergeSorted(Iterables.transform(backingMap.values(), new Function, Iterator>() { + @Override + public Iterator apply(Map input) { + return input.keySet().iterator(); + } + }), comparator); + + return new AbstractIterator() { + C lastValue; + + @Override + protected C computeNext() { + while (merged.hasNext()) { + C next = merged.next(); + boolean duplicate = lastValue != null && comparator.compare(next, lastValue) == 0; + + // Keep looping till we find a non-duplicate value. + if (!duplicate) { + lastValue = next; + return lastValue; + } + } + + lastValue = null; // clear reference to unused data + return endOfData(); + } + }; + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/TreeMultimap.java b/sources/main/java/com/google/common/collect/TreeMultimap.java new file mode 100644 index 0000000..ed1bf49 --- /dev/null +++ b/sources/main/java/com/google/common/collect/TreeMultimap.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Comparator; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +/** + * Implementation of {@code Multimap} whose keys and values are ordered by their + * natural ordering or by supplied comparators. In all cases, this + * implementation uses {@link Comparable#compareTo} or + * {@link Comparator#compare} instead of {@link Object#equals} to determine + * equivalence of instances. + * + *

+ * Warning: The comparators or comparables used must be consistent + * with equals as explained by the {@link Comparable} class specification. + * Otherwise, the resulting multiset will violate the general contract of + * {@link SetMultimap}, which it is specified in terms of {@link Object#equals}. + * + *

+ * The collections returned by {@code keySet} and {@code asMap} iterate through + * the keys according to the key comparator ordering or the natural ordering of + * the keys. Similarly, {@code get}, {@code removeAll}, and {@code + * replaceValues} return collections that iterate through the values according + * to the value comparator ordering or the natural ordering of the values. The + * collections generated by {@code entries}, {@code keys}, and {@code values} + * iterate across the keys according to the above key ordering, and for each key + * they iterate across the values according to the value ordering. + * + *

+ * The multimap does not store duplicate key-value pairs. Adding a new key-value + * pair equal to an existing key-value pair has no effect. + * + *

+ * Null keys and values are permitted (provided, of course, that the respective + * comparators support them). All optional multimap methods are supported, and + * all returned views are modifiable. + * + *

+ * This class is not threadsafe when any concurrent operations update the + * multimap. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap your multimap with a call to + * {@link Multimaps#synchronizedSortedSetMultimap}. + * + *

+ * See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(serializable = true, emulated = true) +public class TreeMultimap extends AbstractSortedKeySortedSetMultimap { + private transient Comparator keyComparator; + private transient Comparator valueComparator; + + /** + * Creates an empty {@code TreeMultimap} ordered by the natural ordering of its + * keys and values. + */ + public static TreeMultimap create() { + return new TreeMultimap(Ordering.natural(), Ordering.natural()); + } + + /** + * Creates an empty {@code TreeMultimap} instance using explicit comparators. + * Neither comparator may be null; use {@link Ordering#natural()} to specify + * natural order. + * + * @param keyComparator the comparator that determines the key ordering + * @param valueComparator the comparator that determines the value ordering + */ + public static TreeMultimap create(Comparator keyComparator, + Comparator valueComparator) { + return new TreeMultimap(checkNotNull(keyComparator), checkNotNull(valueComparator)); + } + + /** + * Constructs a {@code TreeMultimap}, ordered by the natural ordering of its + * keys and values, with the same mappings as the specified multimap. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static TreeMultimap create( + Multimap multimap) { + return new TreeMultimap(Ordering.natural(), Ordering.natural(), multimap); + } + + TreeMultimap(Comparator keyComparator, Comparator valueComparator) { + super(new TreeMap>(keyComparator)); + this.keyComparator = keyComparator; + this.valueComparator = valueComparator; + } + + private TreeMultimap(Comparator keyComparator, Comparator valueComparator, + Multimap multimap) { + this(keyComparator, valueComparator); + putAll(multimap); + } + + /** + * {@inheritDoc} + * + *

+ * Creates an empty {@code TreeSet} for a collection of values for one key. + * + * @return a new {@code TreeSet} containing a collection of values for one key + */ + @Override + SortedSet createCollection() { + return new TreeSet(valueComparator); + } + + @Override + Collection createCollection(@Nullable K key) { + if (key == null) { + keyComparator().compare(key, key); + } + return super.createCollection(key); + } + + /** + * Returns the comparator that orders the multimap keys. + */ + public Comparator keyComparator() { + return keyComparator; + } + + @Override + public Comparator valueComparator() { + return valueComparator; + } + + /* + * The following @GwtIncompatible methods override the methods in + * AbstractSortedKeySortedSetMultimap, so GWT will fall back to the ASKSSM + * implementations, which return SortedSets and SortedMaps. + */ + + @Override + @GwtIncompatible("NavigableMap") + NavigableMap> backingMap() { + return (NavigableMap>) super.backingMap(); + } + + /** + * @since 14.0 (present with return type {@code SortedSet} since 2.0) + */ + @Override + @GwtIncompatible("NavigableSet") + public NavigableSet get(@Nullable K key) { + return (NavigableSet) super.get(key); + } + + @Override + @GwtIncompatible("NavigableSet") + Collection unmodifiableCollectionSubclass(Collection collection) { + return Sets.unmodifiableNavigableSet((NavigableSet) collection); + } + + @Override + @GwtIncompatible("NavigableSet") + Collection wrapCollection(K key, Collection collection) { + return new WrappedNavigableSet(key, (NavigableSet) collection, null); + } + + /** + * {@inheritDoc} + * + *

+ * Because a {@code TreeMultimap} has unique sorted keys, this method returns a + * {@link NavigableSet}, instead of the {@link java.util.Set} specified in the + * {@link Multimap} interface. + * + * @since 14.0 (present with return type {@code SortedSet} since 2.0) + */ + @Override + @GwtIncompatible("NavigableSet") + public NavigableSet keySet() { + return (NavigableSet) super.keySet(); + } + + @Override + @GwtIncompatible("NavigableSet") + NavigableSet createKeySet() { + return new NavigableKeySet(backingMap()); + } + + /** + * {@inheritDoc} + * + *

+ * Because a {@code TreeMultimap} has unique sorted keys, this method returns a + * {@link NavigableMap}, instead of the {@link java.util.Map} specified in the + * {@link Multimap} interface. + * + * @since 14.0 (present with return type {@code SortedMap} since 2.0) + */ + @Override + @GwtIncompatible("NavigableMap") + public NavigableMap> asMap() { + return (NavigableMap>) super.asMap(); + } + + @Override + @GwtIncompatible("NavigableMap") + NavigableMap> createAsMap() { + return new NavigableAsMap(backingMap()); + } + + /** + * @serialData key comparator, value comparator, number of distinct keys, and + * then for each distinct key: the key, number of values for that + * key, and key values + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(keyComparator()); + stream.writeObject(valueComparator()); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyComparator = checkNotNull((Comparator) stream.readObject()); + valueComparator = checkNotNull((Comparator) stream.readObject()); + setMap(new TreeMap>(keyComparator)); + Serialization.populateMultimap(this, stream); + } + + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/TreeMultiset.java b/sources/main/java/com/google/common/collect/TreeMultiset.java new file mode 100644 index 0000000..31768fa --- /dev/null +++ b/sources/main/java/com/google/common/collect/TreeMultiset.java @@ -0,0 +1,989 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.CollectPreconditions.checkNonnegative; +import static com.google.common.collect.CollectPreconditions.checkRemove; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import javax.annotation.Nullable; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Objects; +import com.google.common.primitives.Ints; + +/** + * A multiset which maintains the ordering of its elements, according to either + * their natural order or an explicit {@link Comparator}. In all cases, this + * implementation uses {@link Comparable#compareTo} or + * {@link Comparator#compare} instead of {@link Object#equals} to determine + * equivalence of instances. + * + *

+ * Warning: The comparison must be consistent with equals as + * explained by the {@link Comparable} class specification. Otherwise, the + * resulting multiset will violate the {@link java.util.Collection} contract, + * which is specified in terms of {@link Object#equals}. + * + *

+ * See the Guava User Guide article on + * {@code Multiset}. + * + * @author Louis Wasserman + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible(emulated = true) +public final class TreeMultiset extends AbstractSortedMultiset implements Serializable { + + /** + * Creates a new, empty multiset, sorted according to the elements' natural + * order. All elements inserted into the multiset must implement the + * {@code Comparable} interface. Furthermore, all such elements must be + * mutually comparable: {@code e1.compareTo(e2)} must not throw a + * {@code ClassCastException} for any elements {@code e1} and {@code e2} in the + * multiset. If the user attempts to add an element to the multiset that + * violates this constraint (for example, the user attempts to add a string + * element to a set whose elements are integers), the {@code add(Object)} call + * will throw a {@code ClassCastException}. + * + *

+ * The type specification is {@code }, instead of the more + * specific {@code >}, to support classes + * defined without generics. + */ + public static TreeMultiset create() { + return new TreeMultiset(Ordering.natural()); + } + + /** + * Creates a new, empty multiset, sorted according to the specified comparator. + * All elements inserted into the multiset must be mutually comparable by + * the specified comparator: {@code comparator.compare(e1, + * e2)} must not throw a {@code ClassCastException} for any elements {@code e1} + * and {@code e2} in the multiset. If the user attempts to add an element to the + * multiset that violates this constraint, the {@code add(Object)} call will + * throw a {@code ClassCastException}. + * + * @param comparator the comparator that will be used to sort this multiset. A + * null value indicates that the elements' natural + * ordering should be used. + */ + @SuppressWarnings("unchecked") + public static TreeMultiset create(@Nullable Comparator comparator) { + return (comparator == null) ? new TreeMultiset((Comparator) Ordering.natural()) + : new TreeMultiset(comparator); + } + + /** + * Creates an empty multiset containing the given initial elements, sorted + * according to the elements' natural order. + * + *

+ * This implementation is highly efficient when {@code elements} is itself a + * {@link Multiset}. + * + *

+ * The type specification is {@code }, instead of the more + * specific {@code >}, to support classes + * defined without generics. + */ + public static TreeMultiset create(Iterable elements) { + TreeMultiset multiset = create(); + Iterables.addAll(multiset, elements); + return multiset; + } + + private final transient Reference> rootReference; + private final transient GeneralRange range; + private final transient AvlNode header; + + TreeMultiset(Reference> rootReference, GeneralRange range, AvlNode endLink) { + super(range.comparator()); + this.rootReference = rootReference; + this.range = range; + this.header = endLink; + } + + TreeMultiset(Comparator comparator) { + super(comparator); + this.range = GeneralRange.all(comparator); + this.header = new AvlNode(null, 1); + successor(header, header); + this.rootReference = new Reference>(); + } + + /** + * A function which can be summed across a subtree. + */ + private enum Aggregate { + SIZE { + @Override + int nodeAggregate(AvlNode node) { + return node.elemCount; + } + + @Override + long treeAggregate(@Nullable AvlNode root) { + return (root == null) ? 0 : root.totalCount; + } + }, + DISTINCT { + @Override + int nodeAggregate(AvlNode node) { + return 1; + } + + @Override + long treeAggregate(@Nullable AvlNode root) { + return (root == null) ? 0 : root.distinctElements; + } + }; + + abstract int nodeAggregate(AvlNode node); + + abstract long treeAggregate(@Nullable AvlNode root); + } + + private long aggregateForEntries(Aggregate aggr) { + AvlNode root = rootReference.get(); + long total = aggr.treeAggregate(root); + if (range.hasLowerBound()) { + total -= aggregateBelowRange(aggr, root); + } + if (range.hasUpperBound()) { + total -= aggregateAboveRange(aggr, root); + } + return total; + } + + private long aggregateBelowRange(Aggregate aggr, @Nullable AvlNode node) { + if (node == null) { + return 0; + } + int cmp = comparator().compare(range.getLowerEndpoint(), node.elem); + if (cmp < 0) { + return aggregateBelowRange(aggr, node.left); + } else if (cmp == 0) { + switch (range.getLowerBoundType()) { + case OPEN: + return aggr.nodeAggregate(node) + aggr.treeAggregate(node.left); + case CLOSED: + return aggr.treeAggregate(node.left); + default: + throw new AssertionError(); + } + } else { + return aggr.treeAggregate(node.left) + aggr.nodeAggregate(node) + aggregateBelowRange(aggr, node.right); + } + } + + private long aggregateAboveRange(Aggregate aggr, @Nullable AvlNode node) { + if (node == null) { + return 0; + } + int cmp = comparator().compare(range.getUpperEndpoint(), node.elem); + if (cmp > 0) { + return aggregateAboveRange(aggr, node.right); + } else if (cmp == 0) { + switch (range.getUpperBoundType()) { + case OPEN: + return aggr.nodeAggregate(node) + aggr.treeAggregate(node.right); + case CLOSED: + return aggr.treeAggregate(node.right); + default: + throw new AssertionError(); + } + } else { + return aggr.treeAggregate(node.right) + aggr.nodeAggregate(node) + aggregateAboveRange(aggr, node.left); + } + } + + @Override + public int size() { + return Ints.saturatedCast(aggregateForEntries(Aggregate.SIZE)); + } + + @Override + int distinctElements() { + return Ints.saturatedCast(aggregateForEntries(Aggregate.DISTINCT)); + } + + @Override + public int count(@Nullable Object element) { + try { + @SuppressWarnings("unchecked") + E e = (E) element; + AvlNode root = rootReference.get(); + if (!range.contains(e) || root == null) { + return 0; + } + return root.count(comparator(), e); + } catch (ClassCastException e) { + return 0; + } catch (NullPointerException e) { + return 0; + } + } + + @Override + public int add(@Nullable E element, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } + checkArgument(range.contains(element)); + AvlNode root = rootReference.get(); + if (root == null) { + comparator().compare(element, element); + AvlNode newRoot = new AvlNode(element, occurrences); + successor(header, newRoot, header); + rootReference.checkAndSet(root, newRoot); + return 0; + } + int[] result = new int[1]; // used as a mutable int reference to hold result + AvlNode newRoot = root.add(comparator(), element, occurrences, result); + rootReference.checkAndSet(root, newRoot); + return result[0]; + } + + @Override + public int remove(@Nullable Object element, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } + AvlNode root = rootReference.get(); + int[] result = new int[1]; // used as a mutable int reference to hold result + AvlNode newRoot; + try { + @SuppressWarnings("unchecked") + E e = (E) element; + if (!range.contains(e) || root == null) { + return 0; + } + newRoot = root.remove(comparator(), e, occurrences, result); + } catch (ClassCastException e) { + return 0; + } catch (NullPointerException e) { + return 0; + } + rootReference.checkAndSet(root, newRoot); + return result[0]; + } + + @Override + public int setCount(@Nullable E element, int count) { + checkNonnegative(count, "count"); + if (!range.contains(element)) { + checkArgument(count == 0); + return 0; + } + + AvlNode root = rootReference.get(); + if (root == null) { + if (count > 0) { + add(element, count); + } + return 0; + } + int[] result = new int[1]; // used as a mutable int reference to hold result + AvlNode newRoot = root.setCount(comparator(), element, count, result); + rootReference.checkAndSet(root, newRoot); + return result[0]; + } + + @Override + public boolean setCount(@Nullable E element, int oldCount, int newCount) { + checkNonnegative(newCount, "newCount"); + checkNonnegative(oldCount, "oldCount"); + checkArgument(range.contains(element)); + + AvlNode root = rootReference.get(); + if (root == null) { + if (oldCount == 0) { + if (newCount > 0) { + add(element, newCount); + } + return true; + } else { + return false; + } + } + int[] result = new int[1]; // used as a mutable int reference to hold result + AvlNode newRoot = root.setCount(comparator(), element, oldCount, newCount, result); + rootReference.checkAndSet(root, newRoot); + return result[0] == oldCount; + } + + private Entry wrapEntry(final AvlNode baseEntry) { + return new Multisets.AbstractEntry() { + @Override + public E getElement() { + return baseEntry.getElement(); + } + + @Override + public int getCount() { + int result = baseEntry.getCount(); + if (result == 0) { + return count(getElement()); + } else { + return result; + } + } + }; + } + + /** + * Returns the first node in the tree that is in range. + */ + @Nullable + private AvlNode firstNode() { + AvlNode root = rootReference.get(); + if (root == null) { + return null; + } + AvlNode node; + if (range.hasLowerBound()) { + E endpoint = range.getLowerEndpoint(); + node = rootReference.get().ceiling(comparator(), endpoint); + if (node == null) { + return null; + } + if (range.getLowerBoundType() == BoundType.OPEN && comparator().compare(endpoint, node.getElement()) == 0) { + node = node.succ; + } + } else { + node = header.succ; + } + return (node == header || !range.contains(node.getElement())) ? null : node; + } + + @Nullable + private AvlNode lastNode() { + AvlNode root = rootReference.get(); + if (root == null) { + return null; + } + AvlNode node; + if (range.hasUpperBound()) { + E endpoint = range.getUpperEndpoint(); + node = rootReference.get().floor(comparator(), endpoint); + if (node == null) { + return null; + } + if (range.getUpperBoundType() == BoundType.OPEN && comparator().compare(endpoint, node.getElement()) == 0) { + node = node.pred; + } + } else { + node = header.pred; + } + return (node == header || !range.contains(node.getElement())) ? null : node; + } + + @Override + Iterator> entryIterator() { + return new Iterator>() { + AvlNode current = firstNode(); + Entry prevEntry; + + @Override + public boolean hasNext() { + if (current == null) { + return false; + } else if (range.tooHigh(current.getElement())) { + current = null; + return false; + } else { + return true; + } + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Entry result = wrapEntry(current); + prevEntry = result; + if (current.succ == header) { + current = null; + } else { + current = current.succ; + } + return result; + } + + @Override + public void remove() { + checkRemove(prevEntry != null); + setCount(prevEntry.getElement(), 0); + prevEntry = null; + } + }; + } + + @Override + Iterator> descendingEntryIterator() { + return new Iterator>() { + AvlNode current = lastNode(); + Entry prevEntry = null; + + @Override + public boolean hasNext() { + if (current == null) { + return false; + } else if (range.tooLow(current.getElement())) { + current = null; + return false; + } else { + return true; + } + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Entry result = wrapEntry(current); + prevEntry = result; + if (current.pred == header) { + current = null; + } else { + current = current.pred; + } + return result; + } + + @Override + public void remove() { + checkRemove(prevEntry != null); + setCount(prevEntry.getElement(), 0); + prevEntry = null; + } + }; + } + + @Override + public SortedMultiset headMultiset(@Nullable E upperBound, BoundType boundType) { + return new TreeMultiset(rootReference, + range.intersect(GeneralRange.upTo(comparator(), upperBound, boundType)), header); + } + + @Override + public SortedMultiset tailMultiset(@Nullable E lowerBound, BoundType boundType) { + return new TreeMultiset(rootReference, + range.intersect(GeneralRange.downTo(comparator(), lowerBound, boundType)), header); + } + + static int distinctElements(@Nullable AvlNode node) { + return (node == null) ? 0 : node.distinctElements; + } + + private static final class Reference { + @Nullable + private T value; + + @Nullable + public T get() { + return value; + } + + public void checkAndSet(@Nullable T expected, T newValue) { + if (value != expected) { + throw new ConcurrentModificationException(); + } + value = newValue; + } + } + + private static final class AvlNode extends Multisets.AbstractEntry { + @Nullable + private final E elem; + + // elemCount is 0 iff this node has been deleted. + private int elemCount; + + private int distinctElements; + private long totalCount; + private int height; + private AvlNode left; + private AvlNode right; + private AvlNode pred; + private AvlNode succ; + + AvlNode(@Nullable E elem, int elemCount) { + checkArgument(elemCount > 0); + this.elem = elem; + this.elemCount = elemCount; + this.totalCount = elemCount; + this.distinctElements = 1; + this.height = 1; + this.left = null; + this.right = null; + } + + public int count(Comparator comparator, E e) { + int cmp = comparator.compare(e, elem); + if (cmp < 0) { + return (left == null) ? 0 : left.count(comparator, e); + } else if (cmp > 0) { + return (right == null) ? 0 : right.count(comparator, e); + } else { + return elemCount; + } + } + + private AvlNode addRightChild(E e, int count) { + right = new AvlNode(e, count); + successor(this, right, succ); + height = Math.max(2, height); + distinctElements++; + totalCount += count; + return this; + } + + private AvlNode addLeftChild(E e, int count) { + left = new AvlNode(e, count); + successor(pred, left, this); + height = Math.max(2, height); + distinctElements++; + totalCount += count; + return this; + } + + AvlNode add(Comparator comparator, @Nullable E e, int count, int[] result) { + /* + * It speeds things up considerably to unconditionally add count to totalCount + * here, but that destroys failure atomicity in the case of count overflow. =( + */ + int cmp = comparator.compare(e, elem); + if (cmp < 0) { + AvlNode initLeft = left; + if (initLeft == null) { + result[0] = 0; + return addLeftChild(e, count); + } + int initHeight = initLeft.height; + + left = initLeft.add(comparator, e, count, result); + if (result[0] == 0) { + distinctElements++; + } + this.totalCount += count; + return (left.height == initHeight) ? this : rebalance(); + } else if (cmp > 0) { + AvlNode initRight = right; + if (initRight == null) { + result[0] = 0; + return addRightChild(e, count); + } + int initHeight = initRight.height; + + right = initRight.add(comparator, e, count, result); + if (result[0] == 0) { + distinctElements++; + } + this.totalCount += count; + return (right.height == initHeight) ? this : rebalance(); + } + + // adding count to me! No rebalance possible. + result[0] = elemCount; + long resultCount = (long) elemCount + count; + checkArgument(resultCount <= Integer.MAX_VALUE); + this.elemCount += count; + this.totalCount += count; + return this; + } + + AvlNode remove(Comparator comparator, @Nullable E e, int count, int[] result) { + int cmp = comparator.compare(e, elem); + if (cmp < 0) { + AvlNode initLeft = left; + if (initLeft == null) { + result[0] = 0; + return this; + } + + left = initLeft.remove(comparator, e, count, result); + + if (result[0] > 0) { + if (count >= result[0]) { + this.distinctElements--; + this.totalCount -= result[0]; + } else { + this.totalCount -= count; + } + } + return (result[0] == 0) ? this : rebalance(); + } else if (cmp > 0) { + AvlNode initRight = right; + if (initRight == null) { + result[0] = 0; + return this; + } + + right = initRight.remove(comparator, e, count, result); + + if (result[0] > 0) { + if (count >= result[0]) { + this.distinctElements--; + this.totalCount -= result[0]; + } else { + this.totalCount -= count; + } + } + return rebalance(); + } + + // removing count from me! + result[0] = elemCount; + if (count >= elemCount) { + return deleteMe(); + } else { + this.elemCount -= count; + this.totalCount -= count; + return this; + } + } + + AvlNode setCount(Comparator comparator, @Nullable E e, int count, int[] result) { + int cmp = comparator.compare(e, elem); + if (cmp < 0) { + AvlNode initLeft = left; + if (initLeft == null) { + result[0] = 0; + return (count > 0) ? addLeftChild(e, count) : this; + } + + left = initLeft.setCount(comparator, e, count, result); + + if (count == 0 && result[0] != 0) { + this.distinctElements--; + } else if (count > 0 && result[0] == 0) { + this.distinctElements++; + } + + this.totalCount += count - result[0]; + return rebalance(); + } else if (cmp > 0) { + AvlNode initRight = right; + if (initRight == null) { + result[0] = 0; + return (count > 0) ? addRightChild(e, count) : this; + } + + right = initRight.setCount(comparator, e, count, result); + + if (count == 0 && result[0] != 0) { + this.distinctElements--; + } else if (count > 0 && result[0] == 0) { + this.distinctElements++; + } + + this.totalCount += count - result[0]; + return rebalance(); + } + + // setting my count + result[0] = elemCount; + if (count == 0) { + return deleteMe(); + } + this.totalCount += count - elemCount; + this.elemCount = count; + return this; + } + + AvlNode setCount(Comparator comparator, @Nullable E e, int expectedCount, int newCount, + int[] result) { + int cmp = comparator.compare(e, elem); + if (cmp < 0) { + AvlNode initLeft = left; + if (initLeft == null) { + result[0] = 0; + if (expectedCount == 0 && newCount > 0) { + return addLeftChild(e, newCount); + } + return this; + } + + left = initLeft.setCount(comparator, e, expectedCount, newCount, result); + + if (result[0] == expectedCount) { + if (newCount == 0 && result[0] != 0) { + this.distinctElements--; + } else if (newCount > 0 && result[0] == 0) { + this.distinctElements++; + } + this.totalCount += newCount - result[0]; + } + return rebalance(); + } else if (cmp > 0) { + AvlNode initRight = right; + if (initRight == null) { + result[0] = 0; + if (expectedCount == 0 && newCount > 0) { + return addRightChild(e, newCount); + } + return this; + } + + right = initRight.setCount(comparator, e, expectedCount, newCount, result); + + if (result[0] == expectedCount) { + if (newCount == 0 && result[0] != 0) { + this.distinctElements--; + } else if (newCount > 0 && result[0] == 0) { + this.distinctElements++; + } + this.totalCount += newCount - result[0]; + } + return rebalance(); + } + + // setting my count + result[0] = elemCount; + if (expectedCount == elemCount) { + if (newCount == 0) { + return deleteMe(); + } + this.totalCount += newCount - elemCount; + this.elemCount = newCount; + } + return this; + } + + private AvlNode deleteMe() { + int oldElemCount = this.elemCount; + this.elemCount = 0; + successor(pred, succ); + if (left == null) { + return right; + } else if (right == null) { + return left; + } else if (left.height >= right.height) { + AvlNode newTop = pred; + // newTop is the maximum node in my left subtree + newTop.left = left.removeMax(newTop); + newTop.right = right; + newTop.distinctElements = distinctElements - 1; + newTop.totalCount = totalCount - oldElemCount; + return newTop.rebalance(); + } else { + AvlNode newTop = succ; + newTop.right = right.removeMin(newTop); + newTop.left = left; + newTop.distinctElements = distinctElements - 1; + newTop.totalCount = totalCount - oldElemCount; + return newTop.rebalance(); + } + } + + // Removes the minimum node from this subtree to be reused elsewhere + private AvlNode removeMin(AvlNode node) { + if (left == null) { + return right; + } else { + left = left.removeMin(node); + distinctElements--; + totalCount -= node.elemCount; + return rebalance(); + } + } + + // Removes the maximum node from this subtree to be reused elsewhere + private AvlNode removeMax(AvlNode node) { + if (right == null) { + return left; + } else { + right = right.removeMax(node); + distinctElements--; + totalCount -= node.elemCount; + return rebalance(); + } + } + + private void recomputeMultiset() { + this.distinctElements = 1 + TreeMultiset.distinctElements(left) + TreeMultiset.distinctElements(right); + this.totalCount = elemCount + totalCount(left) + totalCount(right); + } + + private void recomputeHeight() { + this.height = 1 + Math.max(height(left), height(right)); + } + + private void recompute() { + recomputeMultiset(); + recomputeHeight(); + } + + private AvlNode rebalance() { + switch (balanceFactor()) { + case -2: + if (right.balanceFactor() > 0) { + right = right.rotateRight(); + } + return rotateLeft(); + case 2: + if (left.balanceFactor() < 0) { + left = left.rotateLeft(); + } + return rotateRight(); + default: + recomputeHeight(); + return this; + } + } + + private int balanceFactor() { + return height(left) - height(right); + } + + private AvlNode rotateLeft() { + checkState(right != null); + AvlNode newTop = right; + this.right = newTop.left; + newTop.left = this; + newTop.totalCount = this.totalCount; + newTop.distinctElements = this.distinctElements; + this.recompute(); + newTop.recomputeHeight(); + return newTop; + } + + private AvlNode rotateRight() { + checkState(left != null); + AvlNode newTop = left; + this.left = newTop.right; + newTop.right = this; + newTop.totalCount = this.totalCount; + newTop.distinctElements = this.distinctElements; + this.recompute(); + newTop.recomputeHeight(); + return newTop; + } + + private static long totalCount(@Nullable AvlNode node) { + return (node == null) ? 0 : node.totalCount; + } + + private static int height(@Nullable AvlNode node) { + return (node == null) ? 0 : node.height; + } + + @Nullable + private AvlNode ceiling(Comparator comparator, E e) { + int cmp = comparator.compare(e, elem); + if (cmp < 0) { + return (left == null) ? this : Objects.firstNonNull(left.ceiling(comparator, e), this); + } else if (cmp == 0) { + return this; + } else { + return (right == null) ? null : right.ceiling(comparator, e); + } + } + + @Nullable + private AvlNode floor(Comparator comparator, E e) { + int cmp = comparator.compare(e, elem); + if (cmp > 0) { + return (right == null) ? this : Objects.firstNonNull(right.floor(comparator, e), this); + } else if (cmp == 0) { + return this; + } else { + return (left == null) ? null : left.floor(comparator, e); + } + } + + @Override + public E getElement() { + return elem; + } + + @Override + public int getCount() { + return elemCount; + } + + @Override + public String toString() { + return Multisets.immutableEntry(getElement(), getCount()).toString(); + } + } + + private static void successor(AvlNode a, AvlNode b) { + a.succ = b; + b.pred = a; + } + + private static void successor(AvlNode a, AvlNode b, AvlNode c) { + successor(a, b); + successor(b, c); + } + + /* + * TODO(jlevy): Decide whether entrySet() should return entries with an equals() + * method that calls the comparator to compare the two keys. If that change is + * made, AbstractMultiset.equals() can simply check whether two multisets have + * equal entry sets. + */ + + /** + * @serialData the comparator, the number of distinct elements, the first + * element, its count, the second element, its count, and so on + */ + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(elementSet().comparator()); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + @SuppressWarnings("unchecked") + // reading data stored by writeObject + Comparator comparator = (Comparator) stream.readObject(); + Serialization.getFieldSetter(AbstractSortedMultiset.class, "comparator").set(this, comparator); + Serialization.getFieldSetter(TreeMultiset.class, "range").set(this, GeneralRange.all(comparator)); + Serialization.getFieldSetter(TreeMultiset.class, "rootReference").set(this, new Reference>()); + AvlNode header = new AvlNode(null, 1); + Serialization.getFieldSetter(TreeMultiset.class, "header").set(this, header); + successor(header, header); + Serialization.populateMultiset(this, stream); + } + + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 1; +} diff --git a/sources/main/java/com/google/common/collect/TreeRangeMap.java b/sources/main/java/com/google/common/collect/TreeRangeMap.java new file mode 100644 index 0000000..8760701 --- /dev/null +++ b/sources/main/java/com/google/common/collect/TreeRangeMap.java @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.compose; +import static com.google.common.base.Predicates.in; +import static com.google.common.base.Predicates.not; + +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Objects; +import com.google.common.base.Predicate; + +/** + * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting + * all optional operations. + * + *

+ * Like all {@code RangeMap} implementations, this supports neither null keys + * nor null values. + * + * @author Louis Wasserman + * @since 14.0 + */ +@Beta +@GwtIncompatible("NavigableMap") +public final class TreeRangeMap implements RangeMap { + + private final NavigableMap, RangeMapEntry> entriesByLowerBound; + + public static TreeRangeMap create() { + return new TreeRangeMap(); + } + + private TreeRangeMap() { + this.entriesByLowerBound = Maps.newTreeMap(); + } + + private static final class RangeMapEntry extends AbstractMapEntry, V> { + private final Range range; + private final V value; + + RangeMapEntry(Cut lowerBound, Cut upperBound, V value) { + this(Range.create(lowerBound, upperBound), value); + } + + RangeMapEntry(Range range, V value) { + this.range = range; + this.value = value; + } + + @Override + public Range getKey() { + return range; + } + + @Override + public V getValue() { + return value; + } + + public boolean contains(K value) { + return range.contains(value); + } + + Cut getLowerBound() { + return range.lowerBound; + } + + Cut getUpperBound() { + return range.upperBound; + } + } + + @Override + @Nullable + public V get(K key) { + Entry, V> entry = getEntry(key); + return (entry == null) ? null : entry.getValue(); + } + + @Override + @Nullable + public Entry, V> getEntry(K key) { + Map.Entry, RangeMapEntry> mapEntry = entriesByLowerBound.floorEntry(Cut.belowValue(key)); + if (mapEntry != null && mapEntry.getValue().contains(key)) { + return mapEntry.getValue(); + } else { + return null; + } + } + + @Override + public void put(Range range, V value) { + if (!range.isEmpty()) { + checkNotNull(value); + remove(range); + entriesByLowerBound.put(range.lowerBound, new RangeMapEntry(range, value)); + } + } + + @Override + public void putAll(RangeMap rangeMap) { + for (Map.Entry, V> entry : rangeMap.asMapOfRanges().entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void clear() { + entriesByLowerBound.clear(); + } + + @Override + public Range span() { + Entry, RangeMapEntry> firstEntry = entriesByLowerBound.firstEntry(); + Entry, RangeMapEntry> lastEntry = entriesByLowerBound.lastEntry(); + if (firstEntry == null) { + throw new NoSuchElementException(); + } + return Range.create(firstEntry.getValue().getKey().lowerBound, lastEntry.getValue().getKey().upperBound); + } + + private void putRangeMapEntry(Cut lowerBound, Cut upperBound, V value) { + entriesByLowerBound.put(lowerBound, new RangeMapEntry(lowerBound, upperBound, value)); + } + + @Override + public void remove(Range rangeToRemove) { + if (rangeToRemove.isEmpty()) { + return; + } + + /* + * The comments for this method will use [ ] to indicate the bounds of + * rangeToRemove and ( ) to indicate the bounds of ranges in the range map. + */ + Map.Entry, RangeMapEntry> mapEntryBelowToTruncate = entriesByLowerBound + .lowerEntry(rangeToRemove.lowerBound); + if (mapEntryBelowToTruncate != null) { + // we know ( [ + RangeMapEntry rangeMapEntry = mapEntryBelowToTruncate.getValue(); + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) { + // we know ( [ ) + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { + // we know ( [ ] ), so insert the range ] ) back into the map -- + // it's being split apart + putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), + mapEntryBelowToTruncate.getValue().getValue()); + } + // overwrite mapEntryToTruncateBelow with a truncated range + putRangeMapEntry(rangeMapEntry.getLowerBound(), rangeToRemove.lowerBound, + mapEntryBelowToTruncate.getValue().getValue()); + } + } + + Map.Entry, RangeMapEntry> mapEntryAboveToTruncate = entriesByLowerBound + .lowerEntry(rangeToRemove.upperBound); + if (mapEntryAboveToTruncate != null) { + // we know ( ] + RangeMapEntry rangeMapEntry = mapEntryAboveToTruncate.getValue(); + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { + // we know ( ] ), and since we dealt with truncating below already, + // we know [ ( ] ) + putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), + mapEntryAboveToTruncate.getValue().getValue()); + entriesByLowerBound.remove(rangeToRemove.lowerBound); + } + } + entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); + } + + @Override + public Map, V> asMapOfRanges() { + return new AsMapOfRanges(); + } + + private final class AsMapOfRanges extends AbstractMap, V> { + + @Override + public boolean containsKey(@Nullable Object key) { + return get(key) != null; + } + + @Override + public V get(@Nullable Object key) { + if (key instanceof Range) { + Range range = (Range) key; + RangeMapEntry rangeMapEntry = entriesByLowerBound.get(range.lowerBound); + if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) { + return rangeMapEntry.getValue(); + } + } + return null; + } + + @Override + public Set, V>> entrySet() { + return new AbstractSet, V>>() { + + @SuppressWarnings("unchecked") // it's safe to upcast iterators + @Override + public Iterator, V>> iterator() { + return (Iterator) entriesByLowerBound.values().iterator(); + } + + @Override + public int size() { + return entriesByLowerBound.size(); + } + }; + } + } + + @Override + public RangeMap subRangeMap(Range subRange) { + if (subRange.equals(Range.all())) { + return this; + } else { + return new SubRangeMap(subRange); + } + } + + @SuppressWarnings("unchecked") + private RangeMap emptySubRangeMap() { + return EMPTY_SUB_RANGE_MAP; + } + + private static final RangeMap EMPTY_SUB_RANGE_MAP = new RangeMap() { + @Override + @Nullable + public Object get(Comparable key) { + return null; + } + + @Override + @Nullable + public Entry getEntry(Comparable key) { + return null; + } + + @Override + public Range span() { + throw new NoSuchElementException(); + } + + @Override + public void put(Range range, Object value) { + checkNotNull(range); + throw new IllegalArgumentException("Cannot insert range " + range + " into an empty subRangeMap"); + } + + @Override + public void putAll(RangeMap rangeMap) { + if (!rangeMap.asMapOfRanges().isEmpty()) { + throw new IllegalArgumentException("Cannot putAll(nonEmptyRangeMap) into an empty " + "subRangeMap"); + } + } + + @Override + public void clear() { + } + + @Override + public void remove(Range range) { + checkNotNull(range); + } + + @Override + public Map asMapOfRanges() { + return Collections.emptyMap(); + } + + @Override + public RangeMap subRangeMap(Range range) { + checkNotNull(range); + return this; + } + }; + + private class SubRangeMap implements RangeMap { + + private final Range subRange; + + SubRangeMap(Range subRange) { + this.subRange = subRange; + } + + @Override + @Nullable + public V get(K key) { + return subRange.contains(key) ? TreeRangeMap.this.get(key) : null; + } + + @Override + @Nullable + public Entry, V> getEntry(K key) { + if (subRange.contains(key)) { + Entry, V> entry = TreeRangeMap.this.getEntry(key); + if (entry != null) { + return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); + } + } + return null; + } + + @Override + public Range span() { + Cut lowerBound; + Entry, RangeMapEntry> lowerEntry = entriesByLowerBound.floorEntry(subRange.lowerBound); + if (lowerEntry != null && lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) { + lowerBound = subRange.lowerBound; + } else { + lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound); + if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) { + throw new NoSuchElementException(); + } + } + + Cut upperBound; + Entry, RangeMapEntry> upperEntry = entriesByLowerBound.lowerEntry(subRange.upperBound); + if (upperEntry == null) { + throw new NoSuchElementException(); + } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) { + upperBound = subRange.upperBound; + } else { + upperBound = upperEntry.getValue().getUpperBound(); + } + return Range.create(lowerBound, upperBound); + } + + @Override + public void put(Range range, V value) { + checkArgument(subRange.encloses(range), "Cannot put range %s into a subRangeMap(%s)", range, subRange); + TreeRangeMap.this.put(range, value); + } + + @Override + public void putAll(RangeMap rangeMap) { + if (rangeMap.asMapOfRanges().isEmpty()) { + return; + } + Range span = rangeMap.span(); + checkArgument(subRange.encloses(span), "Cannot putAll rangeMap with span %s into a subRangeMap(%s)", span, + subRange); + TreeRangeMap.this.putAll(rangeMap); + } + + @Override + public void clear() { + TreeRangeMap.this.remove(subRange); + } + + @Override + public void remove(Range range) { + if (range.isConnected(subRange)) { + TreeRangeMap.this.remove(range.intersection(subRange)); + } + } + + @Override + public RangeMap subRangeMap(Range range) { + if (!range.isConnected(subRange)) { + return emptySubRangeMap(); + } else { + return TreeRangeMap.this.subRangeMap(range.intersection(subRange)); + } + } + + @Override + public Map, V> asMapOfRanges() { + return new SubRangeMapAsMap(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap) o; + return asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } + return false; + } + + @Override + public int hashCode() { + return asMapOfRanges().hashCode(); + } + + @Override + public String toString() { + return asMapOfRanges().toString(); + } + + class SubRangeMapAsMap extends AbstractMap, V> { + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public V get(Object key) { + try { + if (key instanceof Range) { + @SuppressWarnings("unchecked") // we catch ClassCastExceptions + Range r = (Range) key; + if (!subRange.encloses(r) || r.isEmpty()) { + return null; + } + RangeMapEntry candidate = null; + if (r.lowerBound.compareTo(subRange.lowerBound) == 0) { + // r could be truncated on the left + Entry, RangeMapEntry> entry = entriesByLowerBound.floorEntry(r.lowerBound); + if (entry != null) { + candidate = entry.getValue(); + } + } else { + candidate = entriesByLowerBound.get(r.lowerBound); + } + + if (candidate != null && candidate.getKey().isConnected(subRange) + && candidate.getKey().intersection(subRange).equals(r)) { + return candidate.getValue(); + } + } + } catch (ClassCastException e) { + return null; + } + return null; + } + + @Override + public V remove(Object key) { + V value = get(key); + if (value != null) { + @SuppressWarnings("unchecked") // it's definitely in the map, so safe + Range range = (Range) key; + TreeRangeMap.this.remove(range); + return value; + } + return null; + } + + @Override + public void clear() { + SubRangeMap.this.clear(); + } + + private boolean removeEntryIf(Predicate, V>> predicate) { + List> toRemove = Lists.newArrayList(); + for (Entry, V> entry : entrySet()) { + if (predicate.apply(entry)) { + toRemove.add(entry.getKey()); + } + } + for (Range range : toRemove) { + TreeRangeMap.this.remove(range); + } + return !toRemove.isEmpty(); + } + + @Override + public Set> keySet() { + return new Maps.KeySet, V>(SubRangeMapAsMap.this) { + @Override + public boolean remove(@Nullable Object o) { + return SubRangeMapAsMap.this.remove(o) != null; + } + + @Override + public boolean retainAll(Collection c) { + return removeEntryIf(compose(not(in(c)), Maps.>keyFunction())); + } + }; + } + + @Override + public Set, V>> entrySet() { + return new Maps.EntrySet, V>() { + @Override + Map, V> map() { + return SubRangeMapAsMap.this; + } + + @Override + public Iterator, V>> iterator() { + if (subRange.isEmpty()) { + return Iterators.emptyIterator(); + } + Cut cutToStart = Objects.firstNonNull(entriesByLowerBound.floorKey(subRange.lowerBound), + subRange.lowerBound); + final Iterator> backingItr = entriesByLowerBound.tailMap(cutToStart, true) + .values().iterator(); + return new AbstractIterator, V>>() { + + @Override + protected Entry, V> computeNext() { + while (backingItr.hasNext()) { + RangeMapEntry entry = backingItr.next(); + if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) { + break; + } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) { + // this might not be true e.g. at the start of the iteration + return Maps.immutableEntry(entry.getKey().intersection(subRange), + entry.getValue()); + } + } + return endOfData(); + } + }; + } + + @Override + public boolean retainAll(Collection c) { + return removeEntryIf(not(in(c))); + } + + @Override + public int size() { + return Iterators.size(iterator()); + } + + @Override + public boolean isEmpty() { + return !iterator().hasNext(); + } + }; + } + + @Override + public Collection values() { + return new Maps.Values, V>(this) { + @Override + public boolean removeAll(Collection c) { + return removeEntryIf(compose(in(c), Maps.valueFunction())); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntryIf(compose(not(in(c)), Maps.valueFunction())); + } + }; + } + } + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap) o; + return asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } + return false; + } + + @Override + public int hashCode() { + return asMapOfRanges().hashCode(); + } + + @Override + public String toString() { + return entriesByLowerBound.values().toString(); + } +} diff --git a/sources/main/java/com/google/common/collect/TreeRangeSet.java b/sources/main/java/com/google/common/collect/TreeRangeSet.java new file mode 100644 index 0000000..aec1eae --- /dev/null +++ b/sources/main/java/com/google/common/collect/TreeRangeSet.java @@ -0,0 +1,830 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeMap; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; + +/** + * An implementation of {@link RangeSet} backed by a {@link TreeMap}. + * + * @author Louis Wasserman + * @since 14.0 + */ +@Beta +@GwtIncompatible("uses NavigableMap") +public class TreeRangeSet> extends AbstractRangeSet { + + @VisibleForTesting + final NavigableMap, Range> rangesByLowerBound; + + /** + * Creates an empty {@code TreeRangeSet} instance. + */ + public static > TreeRangeSet create() { + return new TreeRangeSet(new TreeMap, Range>()); + } + + /** + * Returns a {@code TreeRangeSet} initialized with the ranges in the specified + * range set. + */ + public static > TreeRangeSet create(RangeSet rangeSet) { + TreeRangeSet result = create(); + result.addAll(rangeSet); + return result; + } + + private TreeRangeSet(NavigableMap, Range> rangesByLowerCut) { + this.rangesByLowerBound = rangesByLowerCut; + } + + private transient Set> asRanges; + + @Override + public Set> asRanges() { + Set> result = asRanges; + return (result == null) ? asRanges = new AsRanges() : result; + } + + final class AsRanges extends ForwardingCollection> implements Set> { + @Override + protected Collection> delegate() { + return rangesByLowerBound.values(); + } + + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @Override + public boolean equals(@Nullable Object o) { + return Sets.equalsImpl(this, o); + } + } + + @Override + @Nullable + public Range rangeContaining(C value) { + checkNotNull(value); + Entry, Range> floorEntry = rangesByLowerBound.floorEntry(Cut.belowValue(value)); + if (floorEntry != null && floorEntry.getValue().contains(value)) { + return floorEntry.getValue(); + } else { + // TODO(kevinb): revisit this design choice + return null; + } + } + + @Override + public boolean encloses(Range range) { + checkNotNull(range); + Entry, Range> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); + return floorEntry != null && floorEntry.getValue().encloses(range); + } + + @Nullable + private Range rangeEnclosing(Range range) { + checkNotNull(range); + Entry, Range> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); + return (floorEntry != null && floorEntry.getValue().encloses(range)) ? floorEntry.getValue() : null; + } + + @Override + public Range span() { + Entry, Range> firstEntry = rangesByLowerBound.firstEntry(); + Entry, Range> lastEntry = rangesByLowerBound.lastEntry(); + if (firstEntry == null) { + throw new NoSuchElementException(); + } + return Range.create(firstEntry.getValue().lowerBound, lastEntry.getValue().upperBound); + } + + @Override + public void add(Range rangeToAdd) { + checkNotNull(rangeToAdd); + + if (rangeToAdd.isEmpty()) { + return; + } + + // We will use { } to illustrate ranges currently in the range set, and < > + // to illustrate rangeToAdd. + Cut lbToAdd = rangeToAdd.lowerBound; + Cut ubToAdd = rangeToAdd.upperBound; + + Entry, Range> entryBelowLB = rangesByLowerBound.lowerEntry(lbToAdd); + if (entryBelowLB != null) { + // { < + Range rangeBelowLB = entryBelowLB.getValue(); + if (rangeBelowLB.upperBound.compareTo(lbToAdd) >= 0) { + // { < }, and we will need to coalesce + if (rangeBelowLB.upperBound.compareTo(ubToAdd) >= 0) { + // { < > } + ubToAdd = rangeBelowLB.upperBound; + /* + * TODO(cpovirk): can we just "return;" here? Or, can we remove this if() + * entirely? If not, add tests to demonstrate the problem with each approach + */ + } + lbToAdd = rangeBelowLB.lowerBound; + } + } + + Entry, Range> entryBelowUB = rangesByLowerBound.floorEntry(ubToAdd); + if (entryBelowUB != null) { + // { > + Range rangeBelowUB = entryBelowUB.getValue(); + if (rangeBelowUB.upperBound.compareTo(ubToAdd) >= 0) { + // { > }, and we need to coalesce + ubToAdd = rangeBelowUB.upperBound; + } + } + + // Remove ranges which are strictly enclosed. + rangesByLowerBound.subMap(lbToAdd, ubToAdd).clear(); + + replaceRangeWithSameLowerBound(Range.create(lbToAdd, ubToAdd)); + } + + @Override + public void remove(Range rangeToRemove) { + checkNotNull(rangeToRemove); + + if (rangeToRemove.isEmpty()) { + return; + } + + // We will use { } to illustrate ranges currently in the range set, and < > + // to illustrate rangeToRemove. + + Entry, Range> entryBelowLB = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); + if (entryBelowLB != null) { + // { < + Range rangeBelowLB = entryBelowLB.getValue(); + if (rangeBelowLB.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { + // { < }, and we will need to subdivide + if (rangeToRemove.hasUpperBound() && rangeBelowLB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { + // { < > } + replaceRangeWithSameLowerBound(Range.create(rangeToRemove.upperBound, rangeBelowLB.upperBound)); + } + replaceRangeWithSameLowerBound(Range.create(rangeBelowLB.lowerBound, rangeToRemove.lowerBound)); + } + } + + Entry, Range> entryBelowUB = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); + if (entryBelowUB != null) { + // { > + Range rangeBelowUB = entryBelowUB.getValue(); + if (rangeToRemove.hasUpperBound() && rangeBelowUB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { + // { > } + replaceRangeWithSameLowerBound(Range.create(rangeToRemove.upperBound, rangeBelowUB.upperBound)); + } + } + + rangesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); + } + + private void replaceRangeWithSameLowerBound(Range range) { + if (range.isEmpty()) { + rangesByLowerBound.remove(range.lowerBound); + } else { + rangesByLowerBound.put(range.lowerBound, range); + } + } + + private transient RangeSet complement; + + @Override + public RangeSet complement() { + RangeSet result = complement; + return (result == null) ? complement = new Complement() : result; + } + + @VisibleForTesting + static final class RangesByUpperBound> extends AbstractNavigableMap, Range> { + private final NavigableMap, Range> rangesByLowerBound; + + /** + * upperBoundWindow represents the headMap/subMap/tailMap view of the entire + * "ranges by upper bound" map; it's a constraint on the *keys*, and does not + * affect the values. + */ + private final Range> upperBoundWindow; + + RangesByUpperBound(NavigableMap, Range> rangesByLowerBound) { + this.rangesByLowerBound = rangesByLowerBound; + this.upperBoundWindow = Range.all(); + } + + private RangesByUpperBound(NavigableMap, Range> rangesByLowerBound, Range> upperBoundWindow) { + this.rangesByLowerBound = rangesByLowerBound; + this.upperBoundWindow = upperBoundWindow; + } + + private NavigableMap, Range> subMap(Range> window) { + if (window.isConnected(upperBoundWindow)) { + return new RangesByUpperBound(rangesByLowerBound, window.intersection(upperBoundWindow)); + } else { + return ImmutableSortedMap.of(); + } + } + + @Override + public NavigableMap, Range> subMap(Cut fromKey, boolean fromInclusive, Cut toKey, + boolean toInclusive) { + return subMap(Range.range(fromKey, BoundType.forBoolean(fromInclusive), toKey, + BoundType.forBoolean(toInclusive))); + } + + @Override + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + @Override + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + @Override + public Comparator> comparator() { + return Ordering.>natural(); + } + + @Override + public boolean containsKey(@Nullable Object key) { + return get(key) != null; + } + + @Override + public Range get(@Nullable Object key) { + if (key instanceof Cut) { + try { + @SuppressWarnings("unchecked") // we catch CCEs + Cut cut = (Cut) key; + if (!upperBoundWindow.contains(cut)) { + return null; + } + Entry, Range> candidate = rangesByLowerBound.lowerEntry(cut); + if (candidate != null && candidate.getValue().upperBound.equals(cut)) { + return candidate.getValue(); + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + @Override + Iterator, Range>> entryIterator() { + /* + * We want to start the iteration at the first range where the upper bound is in + * upperBoundWindow. + */ + final Iterator> backingItr; + if (!upperBoundWindow.hasLowerBound()) { + backingItr = rangesByLowerBound.values().iterator(); + } else { + Entry, Range> lowerEntry = rangesByLowerBound.lowerEntry(upperBoundWindow.lowerEndpoint()); + if (lowerEntry == null) { + backingItr = rangesByLowerBound.values().iterator(); + } else if (upperBoundWindow.lowerBound.isLessThan(lowerEntry.getValue().upperBound)) { + backingItr = rangesByLowerBound.tailMap(lowerEntry.getKey(), true).values().iterator(); + } else { + backingItr = rangesByLowerBound.tailMap(upperBoundWindow.lowerEndpoint(), true).values().iterator(); + } + } + return new AbstractIterator, Range>>() { + @Override + protected Entry, Range> computeNext() { + if (!backingItr.hasNext()) { + return endOfData(); + } + Range range = backingItr.next(); + if (upperBoundWindow.upperBound.isLessThan(range.upperBound)) { + return endOfData(); + } else { + return Maps.immutableEntry(range.upperBound, range); + } + } + }; + } + + @Override + Iterator, Range>> descendingEntryIterator() { + Collection> candidates; + if (upperBoundWindow.hasUpperBound()) { + candidates = rangesByLowerBound.headMap(upperBoundWindow.upperEndpoint(), false).descendingMap() + .values(); + } else { + candidates = rangesByLowerBound.descendingMap().values(); + } + final PeekingIterator> backingItr = Iterators.peekingIterator(candidates.iterator()); + if (backingItr.hasNext() && upperBoundWindow.upperBound.isLessThan(backingItr.peek().upperBound)) { + backingItr.next(); + } + return new AbstractIterator, Range>>() { + @Override + protected Entry, Range> computeNext() { + if (!backingItr.hasNext()) { + return endOfData(); + } + Range range = backingItr.next(); + return upperBoundWindow.lowerBound.isLessThan(range.upperBound) + ? Maps.immutableEntry(range.upperBound, range) + : endOfData(); + } + }; + } + + @Override + public int size() { + if (upperBoundWindow.equals(Range.all())) { + return rangesByLowerBound.size(); + } + return Iterators.size(entryIterator()); + } + + @Override + public boolean isEmpty() { + return upperBoundWindow.equals(Range.all()) ? rangesByLowerBound.isEmpty() : !entryIterator().hasNext(); + } + } + + private static final class ComplementRangesByLowerBound> + extends AbstractNavigableMap, Range> { + private final NavigableMap, Range> positiveRangesByLowerBound; + private final NavigableMap, Range> positiveRangesByUpperBound; + + /** + * complementLowerBoundWindow represents the headMap/subMap/tailMap view of the + * entire "complement ranges by lower bound" map; it's a constraint on the + * *keys*, and does not affect the values. + */ + private final Range> complementLowerBoundWindow; + + ComplementRangesByLowerBound(NavigableMap, Range> positiveRangesByLowerBound) { + this(positiveRangesByLowerBound, Range.>all()); + } + + private ComplementRangesByLowerBound(NavigableMap, Range> positiveRangesByLowerBound, + Range> window) { + this.positiveRangesByLowerBound = positiveRangesByLowerBound; + this.positiveRangesByUpperBound = new RangesByUpperBound(positiveRangesByLowerBound); + this.complementLowerBoundWindow = window; + } + + private NavigableMap, Range> subMap(Range> subWindow) { + if (!complementLowerBoundWindow.isConnected(subWindow)) { + return ImmutableSortedMap.of(); + } else { + subWindow = subWindow.intersection(complementLowerBoundWindow); + return new ComplementRangesByLowerBound(positiveRangesByLowerBound, subWindow); + } + } + + @Override + public NavigableMap, Range> subMap(Cut fromKey, boolean fromInclusive, Cut toKey, + boolean toInclusive) { + return subMap(Range.range(fromKey, BoundType.forBoolean(fromInclusive), toKey, + BoundType.forBoolean(toInclusive))); + } + + @Override + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + @Override + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + @Override + public Comparator> comparator() { + return Ordering.>natural(); + } + + @Override + Iterator, Range>> entryIterator() { + /* + * firstComplementRangeLowerBound is the first complement range lower bound + * inside complementLowerBoundWindow. Complement range lower bounds are either + * positive range upper bounds, or Cut.belowAll(). + * + * positiveItr starts at the first positive range with lower bound greater than + * firstComplementRangeLowerBound. (Positive range lower bounds correspond to + * complement range upper bounds.) + */ + Collection> positiveRanges; + if (complementLowerBoundWindow.hasLowerBound()) { + positiveRanges = positiveRangesByUpperBound.tailMap(complementLowerBoundWindow.lowerEndpoint(), + complementLowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values(); + } else { + positiveRanges = positiveRangesByUpperBound.values(); + } + final PeekingIterator> positiveItr = Iterators.peekingIterator(positiveRanges.iterator()); + final Cut firstComplementRangeLowerBound; + if (complementLowerBoundWindow.contains(Cut.belowAll()) + && (!positiveItr.hasNext() || positiveItr.peek().lowerBound != Cut.belowAll())) { + firstComplementRangeLowerBound = Cut.belowAll(); + } else if (positiveItr.hasNext()) { + firstComplementRangeLowerBound = positiveItr.next().upperBound; + } else { + return Iterators.emptyIterator(); + } + return new AbstractIterator, Range>>() { + Cut nextComplementRangeLowerBound = firstComplementRangeLowerBound; + + @Override + protected Entry, Range> computeNext() { + if (complementLowerBoundWindow.upperBound.isLessThan(nextComplementRangeLowerBound) + || nextComplementRangeLowerBound == Cut.aboveAll()) { + return endOfData(); + } + Range negativeRange; + if (positiveItr.hasNext()) { + Range positiveRange = positiveItr.next(); + negativeRange = Range.create(nextComplementRangeLowerBound, positiveRange.lowerBound); + nextComplementRangeLowerBound = positiveRange.upperBound; + } else { + negativeRange = Range.create(nextComplementRangeLowerBound, Cut.aboveAll()); + nextComplementRangeLowerBound = Cut.aboveAll(); + } + return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); + } + }; + } + + @Override + Iterator, Range>> descendingEntryIterator() { + Iterator> itr; + /* + * firstComplementRangeUpperBound is the upper bound of the last complement + * range with lower bound inside complementLowerBoundWindow. + * + * positiveItr starts at the first positive range with upper bound less than + * firstComplementRangeUpperBound. (Positive range upper bounds correspond to + * complement range lower bounds.) + */ + Cut startingPoint = complementLowerBoundWindow.hasUpperBound() + ? complementLowerBoundWindow.upperEndpoint() + : Cut.aboveAll(); + boolean inclusive = complementLowerBoundWindow.hasUpperBound() + && complementLowerBoundWindow.upperBoundType() == BoundType.CLOSED; + final PeekingIterator> positiveItr = Iterators.peekingIterator( + positiveRangesByUpperBound.headMap(startingPoint, inclusive).descendingMap().values().iterator()); + Cut cut; + if (positiveItr.hasNext()) { + cut = (positiveItr.peek().upperBound == Cut.aboveAll()) ? positiveItr.next().lowerBound + : positiveRangesByLowerBound.higherKey(positiveItr.peek().upperBound); + } else if (!complementLowerBoundWindow.contains(Cut.belowAll()) + || positiveRangesByLowerBound.containsKey(Cut.belowAll())) { + return Iterators.emptyIterator(); + } else { + cut = positiveRangesByLowerBound.higherKey(Cut.belowAll()); + } + final Cut firstComplementRangeUpperBound = Objects.firstNonNull(cut, Cut.aboveAll()); + return new AbstractIterator, Range>>() { + Cut nextComplementRangeUpperBound = firstComplementRangeUpperBound; + + @Override + protected Entry, Range> computeNext() { + if (nextComplementRangeUpperBound == Cut.belowAll()) { + return endOfData(); + } else if (positiveItr.hasNext()) { + Range positiveRange = positiveItr.next(); + Range negativeRange = Range.create(positiveRange.upperBound, nextComplementRangeUpperBound); + nextComplementRangeUpperBound = positiveRange.lowerBound; + if (complementLowerBoundWindow.lowerBound.isLessThan(negativeRange.lowerBound)) { + return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); + } + } else if (complementLowerBoundWindow.lowerBound.isLessThan(Cut.belowAll())) { + Range negativeRange = Range.create(Cut.belowAll(), nextComplementRangeUpperBound); + nextComplementRangeUpperBound = Cut.belowAll(); + return Maps.immutableEntry(Cut.belowAll(), negativeRange); + } + return endOfData(); + } + }; + } + + @Override + public int size() { + return Iterators.size(entryIterator()); + } + + @Override + @Nullable + public Range get(Object key) { + if (key instanceof Cut) { + try { + @SuppressWarnings("unchecked") + Cut cut = (Cut) key; + // tailMap respects the current window + Entry, Range> firstEntry = tailMap(cut, true).firstEntry(); + if (firstEntry != null && firstEntry.getKey().equals(cut)) { + return firstEntry.getValue(); + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + } + + private final class Complement extends TreeRangeSet { + Complement() { + super(new ComplementRangesByLowerBound(TreeRangeSet.this.rangesByLowerBound)); + } + + @Override + public void add(Range rangeToAdd) { + TreeRangeSet.this.remove(rangeToAdd); + } + + @Override + public void remove(Range rangeToRemove) { + TreeRangeSet.this.add(rangeToRemove); + } + + @Override + public boolean contains(C value) { + return !TreeRangeSet.this.contains(value); + } + + @Override + public RangeSet complement() { + return TreeRangeSet.this; + } + } + + private static final class SubRangeSetRangesByLowerBound> + extends AbstractNavigableMap, Range> { + /** + * lowerBoundWindow is the headMap/subMap/tailMap view; it only restricts the + * keys, and does not affect the values. + */ + private final Range> lowerBoundWindow; + + /** + * restriction is the subRangeSet view; ranges are truncated to their + * intersection with restriction. + */ + private final Range restriction; + + private final NavigableMap, Range> rangesByLowerBound; + private final NavigableMap, Range> rangesByUpperBound; + + private SubRangeSetRangesByLowerBound(Range> lowerBoundWindow, Range restriction, + NavigableMap, Range> rangesByLowerBound) { + this.lowerBoundWindow = checkNotNull(lowerBoundWindow); + this.restriction = checkNotNull(restriction); + this.rangesByLowerBound = checkNotNull(rangesByLowerBound); + this.rangesByUpperBound = new RangesByUpperBound(rangesByLowerBound); + } + + private NavigableMap, Range> subMap(Range> window) { + if (!window.isConnected(lowerBoundWindow)) { + return ImmutableSortedMap.of(); + } else { + return new SubRangeSetRangesByLowerBound(lowerBoundWindow.intersection(window), restriction, + rangesByLowerBound); + } + } + + @Override + public NavigableMap, Range> subMap(Cut fromKey, boolean fromInclusive, Cut toKey, + boolean toInclusive) { + return subMap(Range.range(fromKey, BoundType.forBoolean(fromInclusive), toKey, + BoundType.forBoolean(toInclusive))); + } + + @Override + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + @Override + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + @Override + public Comparator> comparator() { + return Ordering.>natural(); + } + + @Override + public boolean containsKey(@Nullable Object key) { + return get(key) != null; + } + + @Override + @Nullable + public Range get(@Nullable Object key) { + if (key instanceof Cut) { + try { + @SuppressWarnings("unchecked") // we catch CCE's + Cut cut = (Cut) key; + if (!lowerBoundWindow.contains(cut) || cut.compareTo(restriction.lowerBound) < 0 + || cut.compareTo(restriction.upperBound) >= 0) { + return null; + } else if (cut.equals(restriction.lowerBound)) { + // it might be present, truncated on the left + Range candidate = Maps.valueOrNull(rangesByLowerBound.floorEntry(cut)); + if (candidate != null && candidate.upperBound.compareTo(restriction.lowerBound) > 0) { + return candidate.intersection(restriction); + } + } else { + Range result = rangesByLowerBound.get(cut); + if (result != null) { + return result.intersection(restriction); + } + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + @Override + Iterator, Range>> entryIterator() { + if (restriction.isEmpty()) { + return Iterators.emptyIterator(); + } + final Iterator> completeRangeItr; + if (lowerBoundWindow.upperBound.isLessThan(restriction.lowerBound)) { + return Iterators.emptyIterator(); + } else if (lowerBoundWindow.lowerBound.isLessThan(restriction.lowerBound)) { + // starts at the first range with upper bound strictly greater than + // restriction.lowerBound + completeRangeItr = rangesByUpperBound.tailMap(restriction.lowerBound, false).values().iterator(); + } else { + // starts at the first range with lower bound above lowerBoundWindow.lowerBound + completeRangeItr = rangesByLowerBound.tailMap(lowerBoundWindow.lowerBound.endpoint(), + lowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values().iterator(); + } + final Cut> upperBoundOnLowerBounds = Ordering.natural().min(lowerBoundWindow.upperBound, + Cut.belowValue(restriction.upperBound)); + return new AbstractIterator, Range>>() { + @Override + protected Entry, Range> computeNext() { + if (!completeRangeItr.hasNext()) { + return endOfData(); + } + Range nextRange = completeRangeItr.next(); + if (upperBoundOnLowerBounds.isLessThan(nextRange.lowerBound)) { + return endOfData(); + } else { + nextRange = nextRange.intersection(restriction); + return Maps.immutableEntry(nextRange.lowerBound, nextRange); + } + } + }; + } + + @Override + Iterator, Range>> descendingEntryIterator() { + if (restriction.isEmpty()) { + return Iterators.emptyIterator(); + } + Cut> upperBoundOnLowerBounds = Ordering.natural().min(lowerBoundWindow.upperBound, + Cut.belowValue(restriction.upperBound)); + final Iterator> completeRangeItr = rangesByLowerBound + .headMap(upperBoundOnLowerBounds.endpoint(), + upperBoundOnLowerBounds.typeAsUpperBound() == BoundType.CLOSED) + .descendingMap().values().iterator(); + return new AbstractIterator, Range>>() { + @Override + protected Entry, Range> computeNext() { + if (!completeRangeItr.hasNext()) { + return endOfData(); + } + Range nextRange = completeRangeItr.next(); + if (restriction.lowerBound.compareTo(nextRange.upperBound) >= 0) { + return endOfData(); + } + nextRange = nextRange.intersection(restriction); + if (lowerBoundWindow.contains(nextRange.lowerBound)) { + return Maps.immutableEntry(nextRange.lowerBound, nextRange); + } else { + return endOfData(); + } + } + }; + } + + @Override + public int size() { + return Iterators.size(entryIterator()); + } + } + + @Override + public RangeSet subRangeSet(Range view) { + return view.equals(Range.all()) ? this : new SubRangeSet(view); + } + + private final class SubRangeSet extends TreeRangeSet { + private final Range restriction; + + SubRangeSet(Range restriction) { + super(new SubRangeSetRangesByLowerBound(Range.>all(), restriction, + TreeRangeSet.this.rangesByLowerBound)); + this.restriction = restriction; + } + + @Override + public boolean encloses(Range range) { + if (!restriction.isEmpty() && restriction.encloses(range)) { + Range enclosing = TreeRangeSet.this.rangeEnclosing(range); + return enclosing != null && !enclosing.intersection(restriction).isEmpty(); + } + return false; + } + + @Override + @Nullable + public Range rangeContaining(C value) { + if (!restriction.contains(value)) { + return null; + } + Range result = TreeRangeSet.this.rangeContaining(value); + return (result == null) ? null : result.intersection(restriction); + } + + @Override + public void add(Range rangeToAdd) { + checkArgument(restriction.encloses(rangeToAdd), "Cannot add range %s to subRangeSet(%s)", rangeToAdd, + restriction); + super.add(rangeToAdd); + } + + @Override + public void remove(Range rangeToRemove) { + if (rangeToRemove.isConnected(restriction)) { + TreeRangeSet.this.remove(rangeToRemove.intersection(restriction)); + } + } + + @Override + public boolean contains(C value) { + return restriction.contains(value) && TreeRangeSet.this.contains(value); + } + + @Override + public void clear() { + TreeRangeSet.this.remove(restriction); + } + + @Override + public RangeSet subRangeSet(Range view) { + if (view.encloses(restriction)) { + return this; + } else if (view.isConnected(restriction)) { + return new SubRangeSet(restriction.intersection(view)); + } else { + return ImmutableRangeSet.of(); + } + } + } +} diff --git a/sources/main/java/com/google/common/collect/TreeTraverser.java b/sources/main/java/com/google/common/collect/TreeTraverser.java new file mode 100644 index 0000000..e749bc2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/TreeTraverser.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.Queue; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Views elements of a type {@code T} as nodes in a tree, and provides methods + * to traverse the trees induced by this traverser. + * + *

+ * For example, the tree + * + *

+ *           {@code
+ *          h
+ *        / | \
+ *       /  e  \
+ *      d       g
+ *     /|\      |
+ *    / | \     f
+ *   a  b  c       }
+ * 
+ * + *

+ * can be iterated over in preorder (hdabcegf), postorder (abcdefgh), or + * breadth-first order (hdegabcf). + * + *

+ * Null nodes are strictly forbidden. + * + * @author Louis Wasserman + * @since 15.0 + */ +@Beta +@GwtCompatible(emulated = true) +public abstract class TreeTraverser { + // TODO(user): make this GWT-compatible when we've checked in ArrayDeque + // emulation + + /** + * Returns the children of the specified node. Must not contain null. + */ + public abstract Iterable children(T root); + + /** + * Returns an unmodifiable iterable over the nodes in a tree structure, using + * pre-order traversal. That is, each node's subtrees are traversed after the + * node itself is returned. + * + *

+ * No guarantees are made about the behavior of the traversal when nodes change + * while iteration is in progress or when the iterators generated by + * {@link #children} are advanced. + */ + public final FluentIterable preOrderTraversal(final T root) { + checkNotNull(root); + return new FluentIterable() { + @Override + public UnmodifiableIterator iterator() { + return preOrderIterator(root); + } + }; + } + + // overridden in BinaryTreeTraverser + UnmodifiableIterator preOrderIterator(T root) { + return new PreOrderIterator(root); + } + + private final class PreOrderIterator extends UnmodifiableIterator { + private final Deque> stack; + + PreOrderIterator(T root) { + this.stack = new ArrayDeque>(); + stack.addLast(Iterators.singletonIterator(checkNotNull(root))); + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } + + @Override + public T next() { + Iterator itr = stack.getLast(); // throws NSEE if empty + T result = checkNotNull(itr.next()); + if (!itr.hasNext()) { + stack.removeLast(); + } + Iterator childItr = children(result).iterator(); + if (childItr.hasNext()) { + stack.addLast(childItr); + } + return result; + } + } + + /** + * Returns an unmodifiable iterable over the nodes in a tree structure, using + * post-order traversal. That is, each node's subtrees are traversed before the + * node itself is returned. + * + *

+ * No guarantees are made about the behavior of the traversal when nodes change + * while iteration is in progress or when the iterators generated by + * {@link #children} are advanced. + */ + public final FluentIterable postOrderTraversal(final T root) { + checkNotNull(root); + return new FluentIterable() { + @Override + public UnmodifiableIterator iterator() { + return postOrderIterator(root); + } + }; + } + + // overridden in BinaryTreeTraverser + UnmodifiableIterator postOrderIterator(T root) { + return new PostOrderIterator(root); + } + + private static final class PostOrderNode { + final T root; + final Iterator childIterator; + + PostOrderNode(T root, Iterator childIterator) { + this.root = checkNotNull(root); + this.childIterator = checkNotNull(childIterator); + } + } + + private final class PostOrderIterator extends AbstractIterator { + private final ArrayDeque> stack; + + PostOrderIterator(T root) { + this.stack = new ArrayDeque>(); + stack.addLast(expand(root)); + } + + @Override + protected T computeNext() { + while (!stack.isEmpty()) { + PostOrderNode top = stack.getLast(); + if (top.childIterator.hasNext()) { + T child = top.childIterator.next(); + stack.addLast(expand(child)); + } else { + stack.removeLast(); + return top.root; + } + } + return endOfData(); + } + + private PostOrderNode expand(T t) { + return new PostOrderNode(t, children(t).iterator()); + } + } + + /** + * Returns an unmodifiable iterable over the nodes in a tree structure, using + * breadth-first traversal. That is, all the nodes of depth 0 are returned, then + * depth 1, then 2, and so on. + * + *

+ * No guarantees are made about the behavior of the traversal when nodes change + * while iteration is in progress or when the iterators generated by + * {@link #children} are advanced. + */ + public final FluentIterable breadthFirstTraversal(final T root) { + checkNotNull(root); + return new FluentIterable() { + @Override + public UnmodifiableIterator iterator() { + return new BreadthFirstIterator(root); + } + }; + } + + private final class BreadthFirstIterator extends UnmodifiableIterator implements PeekingIterator { + private final Queue queue; + + BreadthFirstIterator(T root) { + this.queue = new ArrayDeque(); + queue.add(root); + } + + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + + @Override + public T peek() { + return queue.element(); + } + + @Override + public T next() { + T result = queue.remove(); + Iterables.addAll(queue, children(result)); + return result; + } + } +} diff --git a/sources/main/java/com/google/common/collect/UnmodifiableIterator.java b/sources/main/java/com/google/common/collect/UnmodifiableIterator.java new file mode 100644 index 0000000..cc451fb --- /dev/null +++ b/sources/main/java/com/google/common/collect/UnmodifiableIterator.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Iterator; + +import com.google.common.annotations.GwtCompatible; + +/** + * An iterator that does not support {@link #remove}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +@GwtCompatible +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/sources/main/java/com/google/common/collect/UnmodifiableListIterator.java b/sources/main/java/com/google/common/collect/UnmodifiableListIterator.java new file mode 100644 index 0000000..68ff742 --- /dev/null +++ b/sources/main/java/com/google/common/collect/UnmodifiableListIterator.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.ListIterator; + +import com.google.common.annotations.GwtCompatible; + +/** + * A list iterator that does not support {@link #remove}, {@link #add}, or + * {@link #set}. + * + * @since 7.0 + * @author Louis Wasserman + */ +@GwtCompatible +public 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/sources/main/java/com/google/common/collect/UnmodifiableSortedMultiset.java b/sources/main/java/com/google/common/collect/UnmodifiableSortedMultiset.java new file mode 100644 index 0000000..49ee196 --- /dev/null +++ b/sources/main/java/com/google/common/collect/UnmodifiableSortedMultiset.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Comparator; +import java.util.NavigableSet; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.Multisets.UnmodifiableMultiset; + +/** + * Implementation of + * {@link Multisets#unmodifiableSortedMultiset(SortedMultiset)}, split out into + * its own file so it can be GWT emulated (to deal with the differing + * elementSet() types in GWT and non-GWT). + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +final class UnmodifiableSortedMultiset extends UnmodifiableMultiset implements SortedMultiset { + UnmodifiableSortedMultiset(SortedMultiset delegate) { + super(delegate); + } + + @Override + protected SortedMultiset delegate() { + return (SortedMultiset) super.delegate(); + } + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + NavigableSet createElementSet() { + return Sets.unmodifiableNavigableSet(delegate().elementSet()); + } + + @Override + public NavigableSet elementSet() { + return (NavigableSet) super.elementSet(); + } + + private transient UnmodifiableSortedMultiset descendingMultiset; + + @Override + public SortedMultiset descendingMultiset() { + UnmodifiableSortedMultiset result = descendingMultiset; + if (result == null) { + result = new UnmodifiableSortedMultiset(delegate().descendingMultiset()); + result.descendingMultiset = this; + return descendingMultiset = result; + } + return result; + } + + @Override + public Entry firstEntry() { + return delegate().firstEntry(); + } + + @Override + public Entry lastEntry() { + return delegate().lastEntry(); + } + + @Override + public Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public SortedMultiset headMultiset(E upperBound, BoundType boundType) { + return Multisets.unmodifiableSortedMultiset(delegate().headMultiset(upperBound, boundType)); + } + + @Override + public SortedMultiset subMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, + BoundType upperBoundType) { + return Multisets.unmodifiableSortedMultiset( + delegate().subMultiset(lowerBound, lowerBoundType, upperBound, upperBoundType)); + } + + @Override + public SortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return Multisets.unmodifiableSortedMultiset(delegate().tailMultiset(lowerBound, boundType)); + } + + private static final long serialVersionUID = 0; +} \ No newline at end of file diff --git a/sources/main/java/com/google/common/collect/UsingToStringOrdering.java b/sources/main/java/com/google/common/collect/UsingToStringOrdering.java new file mode 100644 index 0000000..b08e7d2 --- /dev/null +++ b/sources/main/java/com/google/common/collect/UsingToStringOrdering.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.io.Serializable; + +import com.google.common.annotations.GwtCompatible; + +/** + * An ordering that uses the natural order of the string representation of the + * values. + */ +@GwtCompatible(serializable = true) +final class UsingToStringOrdering extends Ordering implements Serializable { + static final UsingToStringOrdering INSTANCE = new UsingToStringOrdering(); + + @Override + public int compare(Object left, Object right) { + return left.toString().compareTo(right.toString()); + } + + // preserve singleton-ness, so equals() and hashCode() work correctly + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.usingToString()"; + } + + private UsingToStringOrdering() { + } + + private static final long serialVersionUID = 0; +} diff --git a/sources/main/java/com/google/common/collect/WellBehavedMap.java b/sources/main/java/com/google/common/collect/WellBehavedMap.java new file mode 100644 index 0000000..3213ff9 --- /dev/null +++ b/sources/main/java/com/google/common/collect/WellBehavedMap.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.google.common.annotations.GwtCompatible; + +/** + * Workaround for + * EnumMap + * bug. If you want to pass an {@code EnumMap}, with the intention of using + * its {@code entrySet()} method, you should wrap the {@code EnumMap} in this + * class instead. + * + *

+ * This class is not thread-safe even if the underlying map is. + * + * @author Dimitris Andreou + */ +@GwtCompatible +final class WellBehavedMap extends ForwardingMap { + private final Map delegate; + private Set> entrySet; + + private WellBehavedMap(Map delegate) { + this.delegate = delegate; + } + + /** + * Wraps the given map into a {@code WellBehavedEntriesMap}, which intercepts + * its {@code entrySet()} method by taking the {@code Set keySet()} and + * transforming it to {@code Set>}. All other invocations are + * delegated as-is. + */ + static WellBehavedMap wrap(Map delegate) { + return new WellBehavedMap(delegate); + } + + @Override + protected Map delegate() { + return delegate; + } + + @Override + public Set> entrySet() { + Set> es = entrySet; + if (es != null) { + return es; + } + return entrySet = new EntrySet(); + } + + private final class EntrySet extends Maps.EntrySet { + @Override + Map map() { + return WellBehavedMap.this; + } + + @Override + public Iterator> iterator() { + return new TransformedIterator>(keySet().iterator()) { + @Override + Entry transform(final K key) { + return new AbstractMapEntry() { + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return get(key); + } + + @Override + public V setValue(V value) { + return put(key, value); + } + }; + } + }; + } + } +} diff --git a/sources/main/java/com/google/common/collect/package-info.java b/sources/main/java/com/google/common/collect/package-info.java new file mode 100644 index 0000000..a80aeac --- /dev/null +++ b/sources/main/java/com/google/common/collect/package-info.java @@ -0,0 +1,224 @@ +/* + * 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. + */ + +/** + * This package contains generic collection interfaces and implementations, and + * other utilities for working with collections. It is a part of the open-source + * Guava libraries. + * + *

Collection Types

+ * + *
+ *
{@link com.google.common.collect.BiMap} + *
An extension of {@link java.util.Map} that guarantees the uniqueness of + * its values as well as that of its keys. This is sometimes called an + * "invertible map," since the restriction on values enables it to support an + * {@linkplain com.google.common.collect.BiMap#inverse inverse view} -- which is + * another instance of {@code BiMap}. + * + *
{@link com.google.common.collect.Multiset} + *
An extension of {@link java.util.Collection} that may contain duplicate + * values like a {@link java.util.List}, yet has order-independent equality like + * a {@link java.util.Set}. One typical use for a multiset is to represent a + * histogram. + * + *
{@link com.google.common.collect.Multimap} + *
A new type, which is similar to {@link java.util.Map}, but may contain + * multiple entries with the same key. Some behaviors of + * {@link com.google.common.collect.Multimap} are left unspecified and are + * provided only by the subtypes mentioned below. + * + *
{@link com.google.common.collect.ListMultimap} + *
An extension of {@link com.google.common.collect.Multimap} which permits + * duplicate entries, supports random access of values for a particular key, and + * has partially order-dependent equality as defined by + * {@link com.google.common.collect.ListMultimap#equals(Object)}. {@code + * ListMultimap} takes its name from the fact that the + * {@linkplain com.google.common.collect.ListMultimap#get collection of values} + * associated with a given key fulfills the {@link java.util.List} contract. + * + *
{@link com.google.common.collect.SetMultimap} + *
An extension of {@link com.google.common.collect.Multimap} which has + * order-independent equality and does not allow duplicate entries; that is, + * while a key may appear twice in a {@code SetMultimap}, each must map to a + * different value. {@code SetMultimap} takes its name from the fact that the + * {@linkplain com.google.common.collect.SetMultimap#get collection of values} + * associated with a given key fulfills the {@link java.util.Set} contract. + * + *
{@link com.google.common.collect.SortedSetMultimap} + *
An extension of {@link com.google.common.collect.SetMultimap} for which + * the {@linkplain com.google.common.collect.SortedSetMultimap#get collection + * values} associated with a given key is a {@link java.util.SortedSet}. + * + *
{@link com.google.common.collect.Table} + *
A new type, which is similar to {@link java.util.Map}, but which indexes + * its values by an ordered pair of keys, a row key and column key. + * + *
{@link com.google.common.collect.ClassToInstanceMap} + *
An extension of {@link java.util.Map} that associates a raw type with an + * instance of that type. + *
+ * + *

Collection Implementations

+ * + *

of {@link java.util.List}

+ *
    + *
  • {@link com.google.common.collect.ImmutableList} + *
+ * + *

of {@link java.util.Set}

+ *
    + *
  • {@link com.google.common.collect.ImmutableSet} + *
  • {@link com.google.common.collect.ImmutableSortedSet} + *
  • {@link com.google.common.collect.ContiguousSet} (see {@code Range}) + *
+ * + *

of {@link java.util.Map}

+ *
    + *
  • {@link com.google.common.collect.ImmutableMap} + *
  • {@link com.google.common.collect.ImmutableSortedMap} + *
  • {@link com.google.common.collect.MapMaker} + *
+ * + *

of {@link com.google.common.collect.BiMap}

+ *
    + *
  • {@link com.google.common.collect.ImmutableBiMap} + *
  • {@link com.google.common.collect.HashBiMap} + *
  • {@link com.google.common.collect.EnumBiMap} + *
  • {@link com.google.common.collect.EnumHashBiMap} + *
+ * + *

of {@link com.google.common.collect.Multiset}

+ *
    + *
  • {@link com.google.common.collect.ImmutableMultiset} + *
  • {@link com.google.common.collect.HashMultiset} + *
  • {@link com.google.common.collect.LinkedHashMultiset} + *
  • {@link com.google.common.collect.TreeMultiset} + *
  • {@link com.google.common.collect.EnumMultiset} + *
  • {@link com.google.common.collect.ConcurrentHashMultiset} + *
+ * + *

of {@link com.google.common.collect.Multimap}

+ *
    + *
  • {@link com.google.common.collect.ImmutableMultimap} + *
  • {@link com.google.common.collect.ImmutableListMultimap} + *
  • {@link com.google.common.collect.ImmutableSetMultimap} + *
  • {@link com.google.common.collect.ArrayListMultimap} + *
  • {@link com.google.common.collect.HashMultimap} + *
  • {@link com.google.common.collect.TreeMultimap} + *
  • {@link com.google.common.collect.LinkedHashMultimap} + *
  • {@link com.google.common.collect.LinkedListMultimap} + *
+ * + *

of {@link com.google.common.collect.Table}

+ *
    + *
  • {@link com.google.common.collect.ImmutableTable} + *
  • {@link com.google.common.collect.ArrayTable} + *
  • {@link com.google.common.collect.HashBasedTable} + *
  • {@link com.google.common.collect.TreeBasedTable} + *
+ * + *

of {@link com.google.common.collect.ClassToInstanceMap}

+ *
    + *
  • {@link com.google.common.collect.ImmutableClassToInstanceMap} + *
  • {@link com.google.common.collect.MutableClassToInstanceMap} + *
+ * + *

Classes of static utility methods

+ * + *
    + *
  • {@link com.google.common.collect.Collections2} + *
  • {@link com.google.common.collect.Iterators} + *
  • {@link com.google.common.collect.Iterables} + *
  • {@link com.google.common.collect.Lists} + *
  • {@link com.google.common.collect.Maps} + *
  • {@link com.google.common.collect.Queues} + *
  • {@link com.google.common.collect.Sets} + *
  • {@link com.google.common.collect.Multisets} + *
  • {@link com.google.common.collect.Multimaps} + *
  • {@link com.google.common.collect.Tables} + *
  • {@link com.google.common.collect.ObjectArrays} + *
+ * + *

Comparison

+ * + *
    + *
  • {@link com.google.common.collect.Ordering} + *
  • {@link com.google.common.collect.ComparisonChain} + *
+ * + *

Abstract implementations

+ * + *
    + *
  • {@link com.google.common.collect.AbstractIterator} + *
  • {@link com.google.common.collect.AbstractSequentialIterator} + *
  • {@link com.google.common.collect.ImmutableCollection} + *
  • {@link com.google.common.collect.UnmodifiableIterator} + *
  • {@link com.google.common.collect.UnmodifiableListIterator} + *
+ * + *

Ranges

+ * + *
    + *
  • {@link com.google.common.collect.Range} + *
  • {@link com.google.common.collect.RangeMap} + *
  • {@link com.google.common.collect.DiscreteDomain} + *
  • {@link com.google.common.collect.ContiguousSet} + *
+ * + *

Other

+ * + *
    + *
  • {@link com.google.common.collect.Interner}, + * {@link com.google.common.collect.Interners} + *
  • {@link com.google.common.collect.Constraint}, + * {@link com.google.common.collect.Constraints} + *
  • {@link com.google.common.collect.MapConstraint}, + * {@link com.google.common.collect.MapConstraints} + *
  • {@link com.google.common.collect.MapDifference}, + * {@link com.google.common.collect.SortedMapDifference} + *
  • {@link com.google.common.collect.MinMaxPriorityQueue} + *
  • {@link com.google.common.collect.PeekingIterator} + *
+ * + *

Forwarding collections

+ * + *
    + *
  • {@link com.google.common.collect.ForwardingCollection} + *
  • {@link com.google.common.collect.ForwardingConcurrentMap} + *
  • {@link com.google.common.collect.ForwardingIterator} + *
  • {@link com.google.common.collect.ForwardingList} + *
  • {@link com.google.common.collect.ForwardingListIterator} + *
  • {@link com.google.common.collect.ForwardingListMultimap} + *
  • {@link com.google.common.collect.ForwardingMap} + *
  • {@link com.google.common.collect.ForwardingMapEntry} + *
  • {@link com.google.common.collect.ForwardingMultimap} + *
  • {@link com.google.common.collect.ForwardingMultiset} + *
  • {@link com.google.common.collect.ForwardingNavigableMap} + *
  • {@link com.google.common.collect.ForwardingNavigableSet} + *
  • {@link com.google.common.collect.ForwardingObject} + *
  • {@link com.google.common.collect.ForwardingQueue} + *
  • {@link com.google.common.collect.ForwardingSet} + *
  • {@link com.google.common.collect.ForwardingSetMultimap} + *
  • {@link com.google.common.collect.ForwardingSortedMap} + *
  • {@link com.google.common.collect.ForwardingSortedMultiset} + *
  • {@link com.google.common.collect.ForwardingSortedSet} + *
  • {@link com.google.common.collect.ForwardingSortedSetMultimap} + *
  • {@link com.google.common.collect.ForwardingTable} + *
+ */ +@javax.annotation.ParametersAreNonnullByDefault +package com.google.common.collect; diff --git a/sources/main/java/com/google/common/escape/ArrayBasedCharEscaper.java b/sources/main/java/com/google/common/escape/ArrayBasedCharEscaper.java new file mode 100644 index 0000000..7f47559 --- /dev/null +++ b/sources/main/java/com/google/common/escape/ArrayBasedCharEscaper.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A {@link CharEscaper} that uses an array to quickly look up replacement + * characters for a given {@code char} value. An additional safe range is + * provided that determines whether {@code char} values without specific + * replacements are to be considered safe and left unescaped or should be + * escaped in a general way. + * + *

+ * A good example of usage of this class is for Java source code escaping where + * the replacement array contains information about special ASCII characters + * such as {@code \\t} and {@code \\n} while {@link #escapeUnsafe} is overridden + * to handle general escaping of the form {@code \\uxxxx}. + * + *

+ * The size of the data structure used by {@link ArrayBasedCharEscaper} is + * proportional to the highest valued character that requires escaping. For + * example a replacement map containing the single character + * '{@code \}{@code u1000}' will require approximately 16K of memory. If you + * need to create multiple escaper instances that have the same character + * replacement mapping consider using {@link ArrayBasedEscaperMap}. + * + * @author Sven Mawson + * @author David Beaumont + * @since 15.0 + */ +@Beta +@GwtCompatible +public abstract class ArrayBasedCharEscaper extends CharEscaper { + // The replacement array (see ArrayBasedEscaperMap). + private final char[][] replacements; + // The number of elements in the replacement array. + private final int replacementsLength; + // The first character in the safe range. + private final char safeMin; + // The last character in the safe range. + private final char safeMax; + + /** + * Creates a new ArrayBasedCharEscaper instance with the given replacement map + * and specified safe range. If {@code safeMax < safeMin} then no characters are + * considered safe. + * + *

+ * If a character has no mapped replacement then it is checked against the safe + * range. If it lies outside that, then {@link #escapeUnsafe} is called, + * otherwise no escaping is performed. + * + * @param replacementMap a map of characters to their escaped representations + * @param safeMin the lowest character value in the safe range + * @param safeMax the highest character value in the safe range + */ + protected ArrayBasedCharEscaper(Map replacementMap, char safeMin, char safeMax) { + + this(ArrayBasedEscaperMap.create(replacementMap), safeMin, safeMax); + } + + /** + * Creates a new ArrayBasedCharEscaper instance with the given replacement map + * and specified safe range. If {@code safeMax < safeMin} then no characters are + * considered safe. This initializer is useful when explicit instances of + * ArrayBasedEscaperMap are used to allow the sharing of large replacement + * mappings. + * + *

+ * If a character has no mapped replacement then it is checked against the safe + * range. If it lies outside that, then {@link #escapeUnsafe} is called, + * otherwise no escaping is performed. + * + * @param escaperMap the mapping of characters to be escaped + * @param safeMin the lowest character value in the safe range + * @param safeMax the highest character value in the safe range + */ + protected ArrayBasedCharEscaper(ArrayBasedEscaperMap escaperMap, char safeMin, char safeMax) { + + checkNotNull(escaperMap); // GWT specific check (do not optimize) + this.replacements = escaperMap.getReplacementArray(); + this.replacementsLength = replacements.length; + if (safeMax < safeMin) { + // If the safe range is empty, set the range limits to opposite extremes + // to ensure the first test of either value will (almost certainly) fail. + safeMax = Character.MIN_VALUE; + safeMin = Character.MAX_VALUE; + } + this.safeMin = safeMin; + this.safeMax = safeMax; + } + + /* + * This is overridden to improve performance. Rough benchmarking shows that this + * almost doubles the speed when processing strings that do not require any + * escaping. + */ + @Override + public final String escape(String s) { + checkNotNull(s); // GWT specific check (do not optimize). + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ((c < replacementsLength && replacements[c] != null) || c > safeMax || c < safeMin) { + return escapeSlow(s, i); + } + } + return s; + } + + /** + * Escapes a single character using the replacement array and safe range values. + * If the given character does not have an explicit replacement and lies outside + * the safe range then {@link #escapeUnsafe} is called. + */ + @Override + protected final char[] escape(char c) { + if (c < replacementsLength) { + char[] chars = replacements[c]; + if (chars != null) { + return chars; + } + } + if (c >= safeMin && c <= safeMax) { + return null; + } + return escapeUnsafe(c); + } + + /** + * Escapes a {@code char} value that has no direct explicit value in the + * replacement array and lies outside the stated safe range. Subclasses should + * override this method to provide generalized escaping for characters. + * + *

+ * Note that arrays returned by this method must not be modified once they have + * been returned. However it is acceptable to return the same array multiple + * times (even for different input characters). + * + * @param c the character to escape + * @return the replacement characters, or {@code null} if no escaping was + * required + */ + // TODO(user,cpovirk): Rename this something better once refactoring done + protected abstract char[] escapeUnsafe(char c); +} diff --git a/sources/main/java/com/google/common/escape/ArrayBasedEscaperMap.java b/sources/main/java/com/google/common/escape/ArrayBasedEscaperMap.java new file mode 100644 index 0000000..cc9406a --- /dev/null +++ b/sources/main/java/com/google/common/escape/ArrayBasedEscaperMap.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Map; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; + +/** + * An implementation-specific parameter class suitable for initializing + * {@link ArrayBasedCharEscaper} or {@link ArrayBasedUnicodeEscaper} instances. + * This class should be used when more than one escaper is created using the + * same character replacement mapping to allow the underlying (implementation + * specific) data structures to be shared. + * + *

+ * The size of the data structure used by ArrayBasedCharEscaper and + * ArrayBasedUnicodeEscaper is proportional to the highest valued character that + * has a replacement. For example a replacement map containing the single + * character '{@literal \}u1000' will require approximately 16K of memory. As + * such sharing this data structure between escaper instances is the primary + * goal of this class. + * + * @author David Beaumont + * @since 15.0 + */ +@Beta +@GwtCompatible +public final class ArrayBasedEscaperMap { + /** + * Returns a new ArrayBasedEscaperMap for creating ArrayBasedCharEscaper or + * ArrayBasedUnicodeEscaper instances. + * + * @param replacements a map of characters to their escaped representations + */ + public static ArrayBasedEscaperMap create(Map replacements) { + return new ArrayBasedEscaperMap(createReplacementArray(replacements)); + } + + // The underlying replacement array we can share between multiple escaper + // instances. + private final char[][] replacementArray; + + private ArrayBasedEscaperMap(char[][] replacementArray) { + this.replacementArray = replacementArray; + } + + // Returns the non-null array of replacements for fast lookup. + char[][] getReplacementArray() { + return replacementArray; + } + + // Creates a replacement array from the given map. The returned array is a + // linear lookup table of replacement character sequences indexed by the + // original character value. + @VisibleForTesting + static char[][] createReplacementArray(Map map) { + checkNotNull(map); // GWT specific check (do not optimize) + if (map.isEmpty()) { + return EMPTY_REPLACEMENT_ARRAY; + } + char max = Collections.max(map.keySet()); + char[][] replacements = new char[max + 1][]; + for (char c : map.keySet()) { + replacements[c] = map.get(c).toCharArray(); + } + return replacements; + } + + // Immutable empty array for when there are no replacements. + private static final char[][] EMPTY_REPLACEMENT_ARRAY = new char[0][0]; +} diff --git a/sources/main/java/com/google/common/escape/ArrayBasedUnicodeEscaper.java b/sources/main/java/com/google/common/escape/ArrayBasedUnicodeEscaper.java new file mode 100644 index 0000000..52444b1 --- /dev/null +++ b/sources/main/java/com/google/common/escape/ArrayBasedUnicodeEscaper.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * A {@link UnicodeEscaper} that uses an array to quickly look up replacement + * characters for a given code point. An additional safe range is provided that + * determines whether code points without specific replacements are to be + * considered safe and left unescaped or should be escaped in a general way. + * + *

+ * A good example of usage of this class is for HTML escaping where the + * replacement array contains information about the named HTML entities such as + * {@code &} and {@code "} while {@link #escapeUnsafe} is overridden to + * handle general escaping of the form {@code &#NNNNN;}. + * + *

+ * The size of the data structure used by {@link ArrayBasedUnicodeEscaper} is + * proportional to the highest valued code point that requires escaping. For + * example a replacement map containing the single character + * '{@code \}{@code u1000}' will require approximately 16K of memory. If you + * need to create multiple escaper instances that have the same character + * replacement mapping consider using {@link ArrayBasedEscaperMap}. + * + * @author David Beaumont + * @since 15.0 + */ +@Beta +@GwtCompatible +public abstract class ArrayBasedUnicodeEscaper extends UnicodeEscaper { + // The replacement array (see ArrayBasedEscaperMap). + private final char[][] replacements; + // The number of elements in the replacement array. + private final int replacementsLength; + // The first code point in the safe range. + private final int safeMin; + // The last code point in the safe range. + private final int safeMax; + + // Cropped values used in the fast path range checks. + private final char safeMinChar; + private final char safeMaxChar; + + /** + * Creates a new ArrayBasedUnicodeEscaper instance with the given replacement + * map and specified safe range. If {@code safeMax < safeMin} then no code + * points are considered safe. + * + *

+ * If a code point has no mapped replacement then it is checked against the safe + * range. If it lies outside that, then {@link #escapeUnsafe} is called, + * otherwise no escaping is performed. + * + * @param replacementMap a map of characters to their escaped representations + * @param safeMin the lowest character value in the safe range + * @param safeMax the highest character value in the safe range + * @param unsafeReplacement the default replacement for unsafe characters or + * null if no default replacement is required + */ + protected ArrayBasedUnicodeEscaper(Map replacementMap, int safeMin, int safeMax, + @Nullable String unsafeReplacement) { + + this(ArrayBasedEscaperMap.create(replacementMap), safeMin, safeMax, unsafeReplacement); + } + + /** + * Creates a new ArrayBasedUnicodeEscaper instance with the given replacement + * map and specified safe range. If {@code safeMax < safeMin} then no code + * points are considered safe. This initializer is useful when explicit + * instances of ArrayBasedEscaperMap are used to allow the sharing of large + * replacement mappings. + * + *

+ * If a code point has no mapped replacement then it is checked against the safe + * range. If it lies outside that, then {@link #escapeUnsafe} is called, + * otherwise no escaping is performed. + * + * @param escaperMap the map of replacements + * @param safeMin the lowest character value in the safe range + * @param safeMax the highest character value in the safe range + * @param unsafeReplacement the default replacement for unsafe characters or + * null if no default replacement is required + */ + protected ArrayBasedUnicodeEscaper(ArrayBasedEscaperMap escaperMap, int safeMin, int safeMax, + @Nullable String unsafeReplacement) { + + checkNotNull(escaperMap); // GWT specific check (do not optimize) + this.replacements = escaperMap.getReplacementArray(); + this.replacementsLength = replacements.length; + if (safeMax < safeMin) { + // If the safe range is empty, set the range limits to opposite extremes + // to ensure the first test of either value will fail. + safeMax = -1; + safeMin = Integer.MAX_VALUE; + } + this.safeMin = safeMin; + this.safeMax = safeMax; + + // This is a bit of a hack but lets us do quicker per-character checks in + // the fast path code. The safe min/max values are very unlikely to extend + // into the range of surrogate characters, but if they do we must not test + // any values in that range. To see why, consider the case where: + // safeMin <= {hi,lo} <= safeMax + // where {hi,lo} are characters forming a surrogate pair such that: + // codePointOf(hi, lo) > safeMax + // which would result in the surrogate pair being (wrongly) considered safe. + // If we clip the safe range used during the per-character tests so it is + // below the values of characters in surrogate pairs, this cannot occur. + // This approach does mean that we break out of the fast path code in cases + // where we don't strictly need to, but this situation will almost never + // occur in practice. + if (safeMin >= Character.MIN_HIGH_SURROGATE) { + // The safe range is empty or the all safe code points lie in or above the + // surrogate range. Either way the character range is empty. + this.safeMinChar = Character.MAX_VALUE; + this.safeMaxChar = 0; + } else { + // The safe range is non empty and contains values below the surrogate + // range but may extend above it. We may need to clip the maximum value. + this.safeMinChar = (char) safeMin; + this.safeMaxChar = (char) Math.min(safeMax, Character.MIN_HIGH_SURROGATE - 1); + } + } + + /* + * This is overridden to improve performance. Rough benchmarking shows that this + * almost doubles the speed when processing strings that do not require any + * escaping. + */ + @Override + public final String escape(String s) { + checkNotNull(s); // GWT specific check (do not optimize) + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ((c < replacementsLength && replacements[c] != null) || c > safeMaxChar || c < safeMinChar) { + return escapeSlow(s, i); + } + } + return s; + } + + /* Overridden for performance. */ + @Override + protected final int nextEscapeIndex(CharSequence csq, int index, int end) { + while (index < end) { + char c = csq.charAt(index); + if ((c < replacementsLength && replacements[c] != null) || c > safeMaxChar || c < safeMinChar) { + break; + } + index++; + } + return index; + } + + /** + * Escapes a single Unicode code point using the replacement array and safe + * range values. If the given character does not have an explicit replacement + * and lies outside the safe range then {@link #escapeUnsafe} is called. + */ + @Override + protected final char[] escape(int cp) { + if (cp < replacementsLength) { + char[] chars = replacements[cp]; + if (chars != null) { + return chars; + } + } + if (cp >= safeMin && cp <= safeMax) { + return null; + } + return escapeUnsafe(cp); + } + + /** + * Escapes a code point that has no direct explicit value in the replacement + * array and lies outside the stated safe range. Subclasses should override this + * method to provide generalized escaping for code points if required. + * + *

+ * Note that arrays returned by this method must not be modified once they have + * been returned. However it is acceptable to return the same array multiple + * times (even for different input characters). + * + * @param cp the Unicode code point to escape + * @return the replacement characters, or {@code null} if no escaping was + * required + */ + protected abstract char[] escapeUnsafe(int cp); +} diff --git a/sources/main/java/com/google/common/escape/CharEscaper.java b/sources/main/java/com/google/common/escape/CharEscaper.java new file mode 100644 index 0000000..1ab7f97 --- /dev/null +++ b/sources/main/java/com/google/common/escape/CharEscaper.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2006 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * An object that converts literal text into a format safe for inclusion in a + * particular context (such as an XML document). Typically (but not always), the + * inverse process of "unescaping" the text is performed automatically by the + * relevant parser. + * + *

+ * For example, an XML escaper would convert the literal string + * {@code "Foo"} into {@code + * "Foo<Bar>"} to prevent {@code ""} from being confused with an XML + * tag. When the resulting XML document is parsed, the parser API will return + * this text as the original literal string {@code "Foo"}. + * + *

+ * A {@code CharEscaper} instance is required to be stateless, and safe when + * used concurrently by multiple threads. + * + *

+ * Several popular escapers are defined as constants in classes like + * {@link com.google.common.html.HtmlEscapers}, + * {@link com.google.common.xml.XmlEscapers}, and {@link SourceCodeEscapers}. To + * create your own escapers extend this class and implement the + * {@link #escape(char)} method. + * + * @author Sven Mawson + * @since 15.0 + */ +@Beta +@GwtCompatible +public abstract class CharEscaper extends Escaper { + /** Constructor for use by subclasses. */ + protected CharEscaper() { + } + + /** + * Returns the escaped form of a given literal string. + * + * @param string the literal string to be escaped + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + */ + @Override + public String escape(String string) { + checkNotNull(string); // GWT specific check (do not optimize) + // Inlineable fast-path loop which hands off to escapeSlow() only if needed + int length = string.length(); + for (int index = 0; index < length; index++) { + if (escape(string.charAt(index)) != null) { + return escapeSlow(string, index); + } + } + return string; + } + + /** + * Returns the escaped form of a given literal string, starting at the given + * index. This method is called by the {@link #escape(String)} method when it + * discovers that escaping is required. It is protected to allow subclasses to + * override the fastpath escaping function to inline their escaping test. See + * {@link CharEscaperBuilder} for an example usage. + * + * @param s the literal string to be escaped + * @param index the index to start escaping from + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + */ + protected final String escapeSlow(String s, int index) { + int slen = s.length(); + + // Get a destination buffer and setup some loop variables. + char[] dest = Platform.charBufferFromThreadLocal(); + int destSize = dest.length; + int destIndex = 0; + int lastEscape = 0; + + // Loop through the rest of the string, replacing when needed into the + // destination buffer, which gets grown as needed as well. + for (; index < slen; index++) { + + // Get a replacement for the current character. + char[] r = escape(s.charAt(index)); + + // If no replacement is needed, just continue. + if (r == null) + continue; + + int rlen = r.length; + int charsSkipped = index - lastEscape; + + // This is the size needed to add the replacement, not the full size + // needed by the string. We only regrow when we absolutely must, and + // when we do grow, grow enough to avoid excessive growing. Grow. + int sizeNeeded = destIndex + charsSkipped + rlen; + if (destSize < sizeNeeded) { + destSize = sizeNeeded + DEST_PAD_MULTIPLIER * (slen - index); + dest = growBuffer(dest, destIndex, destSize); + } + + // If we have skipped any characters, we need to copy them now. + if (charsSkipped > 0) { + s.getChars(lastEscape, index, dest, destIndex); + destIndex += charsSkipped; + } + + // Copy the replacement string into the dest buffer as needed. + if (rlen > 0) { + System.arraycopy(r, 0, dest, destIndex, rlen); + destIndex += rlen; + } + lastEscape = index + 1; + } + + // Copy leftover characters if there are any. + int charsLeft = slen - lastEscape; + if (charsLeft > 0) { + int sizeNeeded = destIndex + charsLeft; + if (destSize < sizeNeeded) { + + // Regrow and copy, expensive! No padding as this is the final copy. + dest = growBuffer(dest, destIndex, sizeNeeded); + } + s.getChars(lastEscape, slen, dest, destIndex); + destIndex = sizeNeeded; + } + return new String(dest, 0, destIndex); + } + + /** + * Returns the escaped form of the given character, or {@code null} if this + * character does not need to be escaped. If an empty array is returned, this + * effectively strips the input character from the resulting text. + * + *

+ * If the character does not need to be escaped, this method should return + * {@code null}, rather than a one-character array containing the character + * itself. This enables the escaping algorithm to perform more efficiently. + * + *

+ * An escaper is expected to be able to deal with any {@code char} value, so + * this method should not throw any exceptions. + * + * @param c the character to escape if necessary + * @return the replacement characters, or {@code null} if no escaping was needed + */ + protected abstract char[] escape(char c); + + /** + * Helper method to grow the character buffer as needed, this only happens once + * in a while so it's ok if it's in a method call. If the index passed in is 0 + * then no copying will be done. + */ + private static char[] growBuffer(char[] dest, int index, int size) { + char[] copy = new char[size]; + if (index > 0) { + System.arraycopy(dest, 0, copy, 0, index); + } + return copy; + } + + /** + * The multiplier for padding to use when growing the escape buffer. + */ + private static final int DEST_PAD_MULTIPLIER = 2; +} diff --git a/sources/main/java/com/google/common/escape/CharEscaperBuilder.java b/sources/main/java/com/google/common/escape/CharEscaperBuilder.java new file mode 100644 index 0000000..f704af1 --- /dev/null +++ b/sources/main/java/com/google/common/escape/CharEscaperBuilder.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2006 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Simple helper class to build a "sparse" array of objects based on the indexes + * that were added to it. The array will be from 0 to the maximum index given. + * All non-set indexes will contain null (so it's not really a sparse array, + * just a pseudo sparse array). The builder can also return a CharEscaper based + * on the generated array. + * + * @author Sven Mawson + * @since 15.0 + */ +@Beta +@GwtCompatible +public final class CharEscaperBuilder { + /** + * Simple decorator that turns an array of replacement char[]s into a + * CharEscaper, this results in a very fast escape method. + */ + private static class CharArrayDecorator extends CharEscaper { + private final char[][] replacements; + private final int replaceLength; + + CharArrayDecorator(char[][] replacements) { + this.replacements = replacements; + this.replaceLength = replacements.length; + } + + /* + * Overriding escape method to be slightly faster for this decorator. We test + * the replacements array directly, saving a method call. + */ + @Override + public String escape(String s) { + int slen = s.length(); + for (int index = 0; index < slen; index++) { + char c = s.charAt(index); + if (c < replacements.length && replacements[c] != null) { + return escapeSlow(s, index); + } + } + return s; + } + + @Override + protected char[] escape(char c) { + return c < replaceLength ? replacements[c] : null; + } + } + + // Replacement mappings. + private final Map map; + + // The highest index we've seen so far. + private int max = -1; + + /** + * Construct a new sparse array builder. + */ + public CharEscaperBuilder() { + this.map = new HashMap(); + } + + /** + * Add a new mapping from an index to an object to the escaping. + */ + public CharEscaperBuilder addEscape(char c, String r) { + map.put(c, checkNotNull(r)); + if (c > max) { + max = c; + } + return this; + } + + /** + * Add multiple mappings at once for a particular index. + */ + public CharEscaperBuilder addEscapes(char[] cs, String r) { + checkNotNull(r); + for (char c : cs) { + addEscape(c, r); + } + return this; + } + + /** + * Convert this builder into an array of char[]s where the maximum index is the + * value of the highest character that has been seen. The array will be sparse + * in the sense that any unseen index will default to null. + * + * @return a "sparse" array that holds the replacement mappings. + */ + public char[][] toArray() { + char[][] result = new char[max + 1][]; + for (Map.Entry entry : map.entrySet()) { + result[entry.getKey()] = entry.getValue().toCharArray(); + } + return result; + } + + /** + * Convert this builder into a char escaper which is just a decorator around the + * underlying array of replacement char[]s. + * + * @return an escaper that escapes based on the underlying array. + */ + public Escaper toEscaper() { + return new CharArrayDecorator(toArray()); + } +} diff --git a/sources/main/java/com/google/common/escape/Escaper.java b/sources/main/java/com/google/common/escape/Escaper.java new file mode 100644 index 0000000..df2d086 --- /dev/null +++ b/sources/main/java/com/google/common/escape/Escaper.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; + +/** + * An object that converts literal text into a format safe for inclusion in a + * particular context (such as an XML document). Typically (but not always), the + * inverse process of "unescaping" the text is performed automatically by the + * relevant parser. + * + *

+ * For example, an XML escaper would convert the literal string + * {@code "Foo"} into {@code + * "Foo<Bar>"} to prevent {@code ""} from being confused with an XML + * tag. When the resulting XML document is parsed, the parser API will return + * this text as the original literal string {@code "Foo"}. + * + *

+ * An {@code Escaper} instance is required to be stateless, and safe when used + * concurrently by multiple threads. + * + *

+ * Because, in general, escaping operates on the code points of a string and not + * on its individual {@code char} values, it is not safe to assume that + * {@code escape(s)} is equivalent to + * {@code escape(s.substring(0, n)) + escape(s.substing(n))} for arbitrary + * {@code n}. This is because of the possibility of splitting a surrogate pair. + * The only case in which it is safe to escape strings and concatenate the + * results is if you can rule out this possibility, either by splitting an + * existing long string into short strings adaptively around + * {@linkplain Character#isHighSurrogate surrogate} + * {@linkplain Character#isLowSurrogate pairs}, or by starting with short + * strings already known to be free of unpaired surrogates. + * + *

+ * The two primary implementations of this interface are {@link CharEscaper} and + * {@link UnicodeEscaper}. They are heavily optimized for performance and + * greatly simplify the task of implementing new escapers. It is strongly + * recommended that when implementing a new escaper you extend one of these + * classes. If you find that you are unable to achieve the desired behavior + * using either of these classes, please contact the Java libraries team for + * advice. + * + *

+ * Several popular escapers are defined as constants in classes like + * {@link com.google.common.html.HtmlEscapers}, + * {@link com.google.common.xml.XmlEscapers}, and {@link SourceCodeEscapers}. To + * create your own escapers, use {@link CharEscaperBuilder}, or extend + * {@code CharEscaper} or {@code UnicodeEscaper}. + * + * @author David Beaumont + * @since 15.0 + */ +@Beta +@GwtCompatible +public abstract class Escaper { + // TODO(user): evaluate custom implementations, considering package private + // constructor. + /** Constructor for use by subclasses. */ + protected Escaper() { + } + + /** + * Returns the escaped form of a given literal string. + * + *

+ * Note that this method may treat input characters differently depending on the + * specific escaper implementation. + * + *

    + *
  • {@link UnicodeEscaper} handles + * UTF-16 correctly, including + * surrogate character pairs. If the input is badly formed the escaper should + * throw {@link IllegalArgumentException}. + *
  • {@link CharEscaper} handles Java characters independently and does not + * verify the input for well formed characters. A {@code CharEscaper} should not + * be used in situations where input is not guaranteed to be restricted to the + * Basic Multilingual Plane (BMP). + *
+ * + * @param string the literal string to be escaped + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + * @throws IllegalArgumentException if {@code string} contains badly formed + * UTF-16 or cannot be escaped for any other + * reason + */ + public abstract String escape(String string); + + private final Function asFunction = new Function() { + @Override + public String apply(String from) { + return escape(from); + } + }; + + /** + * Returns a {@link Function} that invokes {@link #escape(String)} on this + * escaper. + */ + public final Function asFunction() { + return asFunction; + } +} diff --git a/sources/main/java/com/google/common/escape/Escapers.java b/sources/main/java/com/google/common/escape/Escapers.java new file mode 100644 index 0000000..87e138b --- /dev/null +++ b/sources/main/java/com/google/common/escape/Escapers.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * Static utility methods pertaining to {@link Escaper} instances. + * + * @author Sven Mawson + * @author David Beaumont + * @since 15.0 + */ +@Beta +@GwtCompatible +public final class Escapers { + private Escapers() { + } + + /** + * Returns an {@link Escaper} that does no escaping, passing all character data + * through unchanged. + */ + public static Escaper nullEscaper() { + return NULL_ESCAPER; + } + + // An Escaper that efficiently performs no escaping. + // Extending CharEscaper (instead of Escaper) makes Escapers.compose() easier. + private static final Escaper NULL_ESCAPER = new CharEscaper() { + @Override + public String escape(String string) { + return checkNotNull(string); + } + + @Override + protected char[] escape(char c) { + // TODO: Fix tests not to call this directly and make it throw an error. + return null; + } + }; + + /** + * Returns a builder for creating simple, fast escapers. A builder instance can + * be reused and each escaper that is created will be a snapshot of the current + * builder state. Builders are not thread safe. + * + *

+ * The initial state of the builder is such that: + *

    + *
  • There are no replacement mappings + *
  • + *
  • {@code safeMin == Character.MIN_VALUE}
  • + *
  • {@code safeMax == Character.MAX_VALUE}
  • + *
  • {@code unsafeReplacement == null}
  • + *
+ *

+ * For performance reasons escapers created by this builder are not Unicode + * aware and will not validate the well-formedness of their input. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for simple, fast escapers. + * + *

+ * Typically an escaper needs to deal with the escaping of high valued + * characters or code points. In these cases it is necessary to extend either + * {@link ArrayBasedCharEscaper} or {@link ArrayBasedUnicodeEscaper} to provide + * the desired behavior. However this builder is suitable for creating escapers + * that replace a relative small set of characters. + * + * @author David Beaumont + * @since 15.0 + */ + @Beta + public static final class Builder { + private final Map replacementMap = new HashMap(); + private char safeMin = Character.MIN_VALUE; + private char safeMax = Character.MAX_VALUE; + private String unsafeReplacement = null; + + // The constructor is exposed via the builder() method above. + private Builder() { + } + + /** + * Sets the safe range of characters for the escaper. Characters in this range + * that have no explicit replacement are considered 'safe' and remain unescaped + * in the output. If {@code safeMax < safeMin} then the safe range is empty. + * + * @param safeMin the lowest 'safe' character + * @param safeMax the highest 'safe' character + * @return the builder instance + */ + public Builder setSafeRange(char safeMin, char safeMax) { + this.safeMin = safeMin; + this.safeMax = safeMax; + return this; + } + + /** + * Sets the replacement string for any characters outside the 'safe' range that + * have no explicit replacement. If {@code unsafeReplacement} is {@code null} + * then no replacement will occur, if it is {@code ""} then the unsafe + * characters are removed from the output. + * + * @param unsafeReplacement the string to replace unsafe chracters + * @return the builder instance + */ + public Builder setUnsafeReplacement(@Nullable String unsafeReplacement) { + this.unsafeReplacement = unsafeReplacement; + return this; + } + + /** + * Adds a replacement string for the given input character. The specified + * character will be replaced by the given string whenever it occurs in the + * input, irrespective of whether it lies inside or outside the 'safe' range. + * + * @param c the character to be replaced + * @param replacement the string to replace the given character + * @return the builder instance + * @throws NullPointerException if {@code replacement} is null + */ + public Builder addEscape(char c, String replacement) { + checkNotNull(replacement); + // This can replace an existing character (the builder is re-usable). + replacementMap.put(c, replacement); + return this; + } + + /** + * Returns a new escaper based on the current state of the builder. + */ + public Escaper build() { + return new ArrayBasedCharEscaper(replacementMap, safeMin, safeMax) { + private final char[] replacementChars = unsafeReplacement != null ? unsafeReplacement.toCharArray() + : null; + + @Override + protected char[] escapeUnsafe(char c) { + return replacementChars; + } + }; + } + } + + /** + * Returns a {@link UnicodeEscaper} equivalent to the given escaper instance. If + * the escaper is already a UnicodeEscaper then it is simply returned, otherwise + * it is wrapped in a UnicodeEscaper. + * + *

+ * When a {@link CharEscaper} escaper is wrapped by this method it acquires + * extra behavior with respect to the well-formedness of Unicode character + * sequences and will throw {@link IllegalArgumentException} when given bad + * input. + * + * @param escaper the instance to be wrapped + * @return a UnicodeEscaper with the same behavior as the given instance + * @throws NullPointerException if escaper is null + * @throws IllegalArgumentException if escaper is not a UnicodeEscaper or a + * CharEscaper + */ + static UnicodeEscaper asUnicodeEscaper(Escaper escaper) { + checkNotNull(escaper); + if (escaper instanceof UnicodeEscaper) { + return (UnicodeEscaper) escaper; + } else if (escaper instanceof CharEscaper) { + return wrap((CharEscaper) escaper); + } + // In practice this shouldn't happen because it would be very odd not to + // extend either CharEscaper or UnicodeEscaper for non trivial cases. + throw new IllegalArgumentException("Cannot create a UnicodeEscaper from: " + escaper.getClass().getName()); + } + + /** + * Returns a string that would replace the given character in the specified + * escaper, or {@code null} if no replacement should be made. This method is + * intended for use in tests through the {@code EscaperAsserts} class; + * production users of {@link CharEscaper} should limit themselves to its public + * interface. + * + * @param c the character to escape if necessary + * @return the replacement string, or {@code null} if no escaping was needed + */ + public static String computeReplacement(CharEscaper escaper, char c) { + return stringOrNull(escaper.escape(c)); + } + + /** + * Returns a string that would replace the given character in the specified + * escaper, or {@code null} if no replacement should be made. This method is + * intended for use in tests through the {@code EscaperAsserts} class; + * production users of {@link UnicodeEscaper} should limit themselves to its + * public interface. + * + * @param cp the Unicode code point to escape if necessary + * @return the replacement string, or {@code null} if no escaping was needed + */ + public static String computeReplacement(UnicodeEscaper escaper, int cp) { + return stringOrNull(escaper.escape(cp)); + } + + private static String stringOrNull(char[] in) { + return (in == null) ? null : new String(in); + } + + /** Private helper to wrap a CharEscaper as a UnicodeEscaper. */ + private static UnicodeEscaper wrap(final CharEscaper escaper) { + return new UnicodeEscaper() { + @Override + protected char[] escape(int cp) { + // If a code point maps to a single character, just escape that. + if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + return escaper.escape((char) cp); + } + // Convert the code point to a surrogate pair and escape them both. + // Note: This code path is horribly slow and typically allocates 4 new + // char[] each time it is invoked. However this avoids any + // synchronization issues and makes the escaper thread safe. + char[] surrogateChars = new char[2]; + Character.toChars(cp, surrogateChars, 0); + char[] hiChars = escaper.escape(surrogateChars[0]); + char[] loChars = escaper.escape(surrogateChars[1]); + + // If either hiChars or lowChars are non-null, the CharEscaper is trying + // to escape the characters of a surrogate pair separately. This is + // uncommon and applies only to escapers that assume UCS-2 rather than + // UTF-16. See: http://en.wikipedia.org/wiki/UTF-16/UCS-2 + if (hiChars == null && loChars == null) { + // We expect this to be the common code path for most escapers. + return null; + } + // Combine the characters and/or escaped sequences into a single array. + int hiCount = hiChars != null ? hiChars.length : 1; + int loCount = loChars != null ? loChars.length : 1; + char[] output = new char[hiCount + loCount]; + if (hiChars != null) { + // TODO: Is this faster than System.arraycopy() for small arrays? + for (int n = 0; n < hiChars.length; ++n) { + output[n] = hiChars[n]; + } + } else { + output[0] = surrogateChars[0]; + } + if (loChars != null) { + for (int n = 0; n < loChars.length; ++n) { + output[hiCount + n] = loChars[n]; + } + } else { + output[hiCount] = surrogateChars[1]; + } + return output; + } + }; + } +} diff --git a/sources/main/java/com/google/common/escape/Platform.java b/sources/main/java/com/google/common/escape/Platform.java new file mode 100644 index 0000000..f45a708 --- /dev/null +++ b/sources/main/java/com/google/common/escape/Platform.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import com.google.common.annotations.GwtCompatible; + +/** + * Methods factored out so that they can be emulated differently in GWT. + * + * @author Jesse Wilson + */ +@GwtCompatible(emulated = true) +final class Platform { + private Platform() { + } + + /** Returns a thread-local 1024-char array. */ + static char[] charBufferFromThreadLocal() { + return DEST_TL.get(); + } + + /** + * A thread-local destination buffer to keep us from creating new buffers. The + * starting size is 1024 characters. If we grow past this we don't put it back + * in the threadlocal, we just keep going and grow as needed. + */ + private static final ThreadLocal DEST_TL = new ThreadLocal() { + @Override + protected char[] initialValue() { + return new char[1024]; + } + }; +} diff --git a/sources/main/java/com/google/common/escape/UnicodeEscaper.java b/sources/main/java/com/google/common/escape/UnicodeEscaper.java new file mode 100644 index 0000000..735b599 --- /dev/null +++ b/sources/main/java/com/google/common/escape/UnicodeEscaper.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.escape; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +/** + * An {@link Escaper} that converts literal text into a format safe for + * inclusion in a particular context (such as an XML document). Typically (but + * not always), the inverse process of "unescaping" the text is performed + * automatically by the relevant parser. + * + *

+ * For example, an XML escaper would convert the literal string {@code + * "Foo"} into {@code "Foo<Bar>"} to prevent {@code ""} from + * being confused with an XML tag. When the resulting XML document is parsed, + * the parser API will return this text as the original literal string {@code + * "Foo"}. + * + *

+ * Note: This class is similar to {@link CharEscaper} but with one very + * important difference. A CharEscaper can only process Java + * UTF16 characters in + * isolation and may not cope when it encounters surrogate pairs. This class + * facilitates the correct escaping of all Unicode characters. + * + *

+ * As there are important reasons, including potential security issues, to + * handle Unicode correctly if you are considering implementing a new escaper + * you should favor using UnicodeEscaper wherever possible. + * + *

+ * A {@code UnicodeEscaper} instance is required to be stateless, and safe when + * used concurrently by multiple threads. + * + *

+ * Several popular escapers are defined as constants in classes like + * {@link com.google.common.html.HtmlEscapers}, + * {@link com.google.common.xml.XmlEscapers}, and {@link SourceCodeEscapers}. To + * create your own escapers extend this class and implement the + * {@link #escape(int)} method. + * + * @author David Beaumont + * @since 15.0 + */ +@Beta +@GwtCompatible +public abstract class UnicodeEscaper extends Escaper { + /** The amount of padding (chars) to use when growing the escape buffer. */ + private static final int DEST_PAD = 32; + + /** Constructor for use by subclasses. */ + protected UnicodeEscaper() { + } + + /** + * Returns the escaped form of the given Unicode code point, or {@code null} if + * this code point does not need to be escaped. When called as part of an + * escaping operation, the given code point is guaranteed to be in the range + * {@code 0 <= cp <= Character#MAX_CODE_POINT}. + * + *

+ * If an empty array is returned, this effectively strips the input character + * from the resulting text. + * + *

+ * If the character does not need to be escaped, this method should return + * {@code null}, rather than an array containing the character representation of + * the code point. This enables the escaping algorithm to perform more + * efficiently. + * + *

+ * If the implementation of this method cannot correctly handle a particular + * code point then it should either throw an appropriate runtime exception or + * return a suitable replacement character. It must never silently discard + * invalid input as this may constitute a security risk. + * + * @param cp the Unicode code point to escape if necessary + * @return the replacement characters, or {@code null} if no escaping was needed + */ + protected abstract char[] escape(int cp); + + /** + * Scans a sub-sequence of characters from a given {@link CharSequence}, + * returning the index of the next character that requires escaping. + * + *

+ * Note: When implementing an escaper, it is a good idea to override this + * method for efficiency. The base class implementation determines successive + * Unicode code points and invokes {@link #escape(int)} for each of them. If the + * semantics of your escaper are such that code points in the supplementary + * range are either all escaped or all unescaped, this method can be implemented + * more efficiently using {@link CharSequence#charAt(int)}. + * + *

+ * Note however that if your escaper does not escape characters in the + * supplementary range, you should either continue to validate the correctness + * of any surrogate characters encountered or provide a clear warning to users + * that your escaper does not validate its input. + * + *

+ * See {@link com.google.common.net.PercentEscaper} for an example. + * + * @param csq a sequence of characters + * @param start the index of the first character to be scanned + * @param end the index immediately after the last character to be scanned + * @throws IllegalArgumentException if the scanned sub-sequence of {@code csq} + * contains invalid surrogate pairs + */ + protected int nextEscapeIndex(CharSequence csq, int start, int end) { + int index = start; + while (index < end) { + int cp = codePointAt(csq, index, end); + if (cp < 0 || escape(cp) != null) { + break; + } + index += Character.isSupplementaryCodePoint(cp) ? 2 : 1; + } + return index; + } + + /** + * Returns the escaped form of a given literal string. + * + *

+ * If you are escaping input in arbitrary successive chunks, then it is not + * generally safe to use this method. If an input string ends with an unmatched + * high surrogate character, then this method will throw + * {@link IllegalArgumentException}. You should ensure your input is valid + * UTF-16 before calling this + * method. + * + *

+ * Note: When implementing an escaper it is a good idea to override this + * method for efficiency by inlining the implementation of + * {@link #nextEscapeIndex(CharSequence, int, int)} directly. Doing this for + * {@link com.google.common.net.PercentEscaper} more than doubled the + * performance for unescaped strings (as measured by + * {@link CharEscapersBenchmark}). + * + * @param string the literal string to be escaped + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + * @throws IllegalArgumentException if invalid surrogate characters are + * encountered + */ + @Override + public String escape(String string) { + checkNotNull(string); + int end = string.length(); + int index = nextEscapeIndex(string, 0, end); + return index == end ? string : escapeSlow(string, index); + } + + /** + * Returns the escaped form of a given literal string, starting at the given + * index. This method is called by the {@link #escape(String)} method when it + * discovers that escaping is required. It is protected to allow subclasses to + * override the fastpath escaping function to inline their escaping test. See + * {@link CharEscaperBuilder} for an example usage. + * + *

+ * This method is not reentrant and may only be invoked by the top level + * {@link #escape(String)} method. + * + * @param s the literal string to be escaped + * @param index the index to start escaping from + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + * @throws IllegalArgumentException if invalid surrogate characters are + * encountered + */ + protected final String escapeSlow(String s, int index) { + int end = s.length(); + + // Get a destination buffer and setup some loop variables. + char[] dest = Platform.charBufferFromThreadLocal(); + int destIndex = 0; + int unescapedChunkStart = 0; + + while (index < end) { + int cp = codePointAt(s, index, end); + if (cp < 0) { + throw new IllegalArgumentException("Trailing high surrogate at end of input"); + } + // It is possible for this to return null because nextEscapeIndex() may + // (for performance reasons) yield some false positives but it must never + // give false negatives. + char[] escaped = escape(cp); + int nextIndex = index + (Character.isSupplementaryCodePoint(cp) ? 2 : 1); + if (escaped != null) { + int charsSkipped = index - unescapedChunkStart; + + // This is the size needed to add the replacement, not the full + // size needed by the string. We only regrow when we absolutely must. + int sizeNeeded = destIndex + charsSkipped + escaped.length; + if (dest.length < sizeNeeded) { + int destLength = sizeNeeded + (end - index) + DEST_PAD; + dest = growBuffer(dest, destIndex, destLength); + } + // If we have skipped any characters, we need to copy them now. + if (charsSkipped > 0) { + s.getChars(unescapedChunkStart, index, dest, destIndex); + destIndex += charsSkipped; + } + if (escaped.length > 0) { + System.arraycopy(escaped, 0, dest, destIndex, escaped.length); + destIndex += escaped.length; + } + // If we dealt with an escaped character, reset the unescaped range. + unescapedChunkStart = nextIndex; + } + index = nextEscapeIndex(s, nextIndex, end); + } + + // Process trailing unescaped characters - no need to account for escaped + // length or padding the allocation. + int charsSkipped = end - unescapedChunkStart; + if (charsSkipped > 0) { + int endIndex = destIndex + charsSkipped; + if (dest.length < endIndex) { + dest = growBuffer(dest, destIndex, endIndex); + } + s.getChars(unescapedChunkStart, end, dest, destIndex); + destIndex = endIndex; + } + return new String(dest, 0, destIndex); + } + + /** + * Returns the Unicode code point of the character at the given index. + * + *

+ * Unlike {@link Character#codePointAt(CharSequence, int)} or + * {@link String#codePointAt(int)} this method will never fail silently when + * encountering an invalid surrogate pair. + * + *

+ * The behaviour of this method is as follows: + *

    + *
  1. If {@code index >= end}, {@link IndexOutOfBoundsException} is thrown. + *
  2. If the character at the specified index is not a surrogate, it is + * returned. + *
  3. If the first character was a high surrogate value, then an attempt is + * made to read the next character. + *
      + *
    1. If the end of the sequence was reached, the negated value of the + * trailing high surrogate is returned. + *
    2. If the next character was a valid low surrogate, the code point value + * of the high/low surrogate pair is returned. + *
    3. If the next character was not a low surrogate value, then + * {@link IllegalArgumentException} is thrown. + *
    + *
  4. If the first character was a low surrogate value, + * {@link IllegalArgumentException} is thrown. + *
+ * + * @param seq the sequence of characters from which to decode the code point + * @param index the index of the first character to decode + * @param end the index beyond the last valid character to decode + * @return the Unicode code point for the given index or the negated value of + * the trailing high surrogate character at the end of the sequence + */ + protected static int codePointAt(CharSequence seq, int index, int end) { + checkNotNull(seq); + if (index < end) { + char c1 = seq.charAt(index++); + if (c1 < Character.MIN_HIGH_SURROGATE || c1 > Character.MAX_LOW_SURROGATE) { + // Fast path (first test is probably all we need to do) + return c1; + } else if (c1 <= Character.MAX_HIGH_SURROGATE) { + // If the high surrogate was the last character, return its inverse + if (index == end) { + return -c1; + } + // Otherwise look for the low surrogate following it + char c2 = seq.charAt(index); + if (Character.isLowSurrogate(c2)) { + return Character.toCodePoint(c1, c2); + } + throw new IllegalArgumentException("Expected low surrogate but got char '" + c2 + "' with value " + + (int) c2 + " at index " + index + " in '" + seq + "'"); + } else { + throw new IllegalArgumentException("Unexpected low surrogate character '" + c1 + "' with value " + + (int) c1 + " at index " + (index - 1) + " in '" + seq + "'"); + } + } + throw new IndexOutOfBoundsException("Index exceeds specified range"); + } + + /** + * Helper method to grow the character buffer as needed, this only happens once + * in a while so it's ok if it's in a method call. If the index passed in is 0 + * then no copying will be done. + */ + private static char[] growBuffer(char[] dest, int index, int size) { + char[] copy = new char[size]; + if (index > 0) { + System.arraycopy(dest, 0, copy, 0, index); + } + return copy; + } +} diff --git a/sources/main/java/com/google/common/escape/package-info.java b/sources/main/java/com/google/common/escape/package-info.java new file mode 100644 index 0000000..aa05264 --- /dev/null +++ b/sources/main/java/com/google/common/escape/package-info.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Interfaces, utilities, and simple implementations of escapers and encoders. + * The primary type is {@link com.google.common.escape.Escaper}. + * + *

+ * Additional escapers implementations are found in the applicable packages: + * {@link com.google.common.html.HtmlEscapers} in + * {@code com.google.common.html}, {@link com.google.common.xml.XmlEscapers} in + * {@code com.google.common.xml}, and {@link com.google.common.net.UrlEscapers} + * in {@code com.google.common.net}. + * + *

+ * This package is a part of the open-source + * Guava libraries. + */ +@ParametersAreNonnullByDefault +package com.google.common.escape; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sources/main/java/com/google/common/hash/AbstractByteHasher.java b/sources/main/java/com/google/common/hash/AbstractByteHasher.java new file mode 100644 index 0000000..9db0b4e --- /dev/null +++ b/sources/main/java/com/google/common/hash/AbstractByteHasher.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.hash; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkPositionIndexes; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.google.common.primitives.Chars; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import com.google.common.primitives.Shorts; + +/** + * Abstract {@link Hasher} that handles converting primitives to bytes using a + * scratch {@code + * ByteBuffer} and streams all bytes to a sink to compute the hash. + * + * @author Colin Decker + */ +abstract class AbstractByteHasher extends AbstractHasher { + + private final ByteBuffer scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); + + /** + * Updates this hasher with the given byte. + */ + protected abstract void update(byte b); + + /** + * Updates this hasher with the given bytes. + */ + protected void update(byte[] b) { + update(b, 0, b.length); + } + + /** + * Updates this hasher with {@code len} bytes starting at {@code off} in the + * given buffer. + */ + protected void update(byte[] b, int off, int len) { + for (int i = off; i < off + len; i++) { + update(b[i]); + } + } + + @Override + public Hasher putByte(byte b) { + update(b); + return this; + } + + @Override + public Hasher putBytes(byte[] bytes) { + checkNotNull(bytes); + update(bytes); + return this; + } + + @Override + public Hasher putBytes(byte[] bytes, int off, int len) { + checkPositionIndexes(off, off + len, bytes.length); + update(bytes, off, len); + return this; + } + + /** + * Updates the sink with the given number of bytes from the buffer. + */ + private Hasher update(int bytes) { + try { + update(scratch.array(), 0, bytes); + } finally { + scratch.clear(); + } + return this; + } + + @Override + public Hasher putShort(short s) { + scratch.putShort(s); + return update(Shorts.BYTES); + } + + @Override + public Hasher putInt(int i) { + scratch.putInt(i); + return update(Ints.BYTES); + } + + @Override + public Hasher putLong(long l) { + scratch.putLong(l); + return update(Longs.BYTES); + } + + @Override + public Hasher putChar(char c) { + scratch.putChar(c); + return update(Chars.BYTES); + } + + @Override + public Hasher putObject(T instance, Funnel funnel) { + funnel.funnel(instance, this); + return this; + } +} diff --git a/sources/main/java/com/google/common/hash/AbstractCompositeHashFunction.java b/sources/main/java/com/google/common/hash/AbstractCompositeHashFunction.java new file mode 100644 index 0000000..7465afe --- /dev/null +++ b/sources/main/java/com/google/common/hash/AbstractCompositeHashFunction.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.hash; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.nio.charset.Charset; + +/** + * An abstract composition of multiple hash functions. {@linkplain #newHasher()} + * delegates to the {@code Hasher} objects of the delegate hash functions, and + * in the end, they are used by {@linkplain #makeHash(Hasher[])} that constructs + * the final {@code HashCode}. + * + * @author Dimitris Andreou + */ +abstract class AbstractCompositeHashFunction extends AbstractStreamingHashFunction { + final HashFunction[] functions; + + AbstractCompositeHashFunction(HashFunction... functions) { + for (HashFunction function : functions) { + checkNotNull(function); + } + this.functions = functions; + } + + /** + * Constructs a {@code HashCode} from the {@code Hasher} objects of the + * functions. Each of them has consumed the entire input and they are ready to + * output a {@code HashCode}. The order of the hashers are the same order as the + * functions given to the constructor. + */ + // this could be cleaner if it passed HashCode[], but that would create yet + // another array... + /* protected */ abstract HashCode makeHash(Hasher[] hashers); + + @Override + public Hasher newHasher() { + final Hasher[] hashers = new Hasher[functions.length]; + for (int i = 0; i < hashers.length; i++) { + hashers[i] = functions[i].newHasher(); + } + return new Hasher() { + @Override + public Hasher putByte(byte b) { + for (Hasher hasher : hashers) { + hasher.putByte(b); + } + return this; + } + + @Override + public Hasher putBytes(byte[] bytes) { + for (Hasher hasher : hashers) { + hasher.putBytes(bytes); + } + return this; + } + + @Override + public Hasher putBytes(byte[] bytes, int off, int len) { + for (Hasher hasher : hashers) { + hasher.putBytes(bytes, off, len); + } + return this; + } + + @Override + public Hasher putShort(short s) { + for (Hasher hasher : hashers) { + hasher.putShort(s); + } + return this; + } + + @Override + public Hasher putInt(int i) { + for (Hasher hasher : hashers) { + hasher.putInt(i); + } + return this; + } + + @Override + public Hasher putLong(long l) { + for (Hasher hasher : hashers) { + hasher.putLong(l); + } + return this; + } + + @Override + public Hasher putFloat(float f) { + for (Hasher hasher : hashers) { + hasher.putFloat(f); + } + return this; + } + + @Override + public Hasher putDouble(double d) { + for (Hasher hasher : hashers) { + hasher.putDouble(d); + } + return this; + } + + @Override + public Hasher putBoolean(boolean b) { + for (Hasher hasher : hashers) { + hasher.putBoolean(b); + } + return this; + } + + @Override + public Hasher putChar(char c) { + for (Hasher hasher : hashers) { + hasher.putChar(c); + } + return this; + } + + @Override + public Hasher putUnencodedChars(CharSequence chars) { + for (Hasher hasher : hashers) { + hasher.putUnencodedChars(chars); + } + return this; + } + + @Override + public Hasher putString(CharSequence chars, Charset charset) { + for (Hasher hasher : hashers) { + hasher.putString(chars, charset); + } + return this; + } + + @Override + public Hasher putObject(T instance, Funnel funnel) { + for (Hasher hasher : hashers) { + hasher.putObject(instance, funnel); + } + return this; + } + + @Override + public HashCode hash() { + return makeHash(hashers); + } + }; + } + + private static final long serialVersionUID = 0L; +} diff --git a/sources/main/java/com/google/common/hash/AbstractHasher.java b/sources/main/java/com/google/common/hash/AbstractHasher.java new file mode 100644 index 0000000..7b6eb2c --- /dev/null +++ b/sources/main/java/com/google/common/hash/AbstractHasher.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.hash; + +import java.nio.charset.Charset; + +/** + * An abstract hasher, implementing {@link #putBoolean(boolean)}, + * {@link #putDouble(double)}, {@link #putFloat(float)}, + * {@link #putUnencodedChars(CharSequence)}, and + * {@link #putString(CharSequence, Charset)} as prescribed by {@link Hasher}. + * + * @author Dimitris Andreou + */ +abstract class AbstractHasher implements Hasher { + @Override + public final Hasher putBoolean(boolean b) { + return putByte(b ? (byte) 1 : (byte) 0); + } + + @Override + public final Hasher putDouble(double d) { + return putLong(Double.doubleToRawLongBits(d)); + } + + @Override + public final Hasher putFloat(float f) { + return putInt(Float.floatToRawIntBits(f)); + } + + @Override + public Hasher putUnencodedChars(CharSequence charSequence) { + for (int i = 0, len = charSequence.length(); i < len; i++) { + putChar(charSequence.charAt(i)); + } + return this; + } + + @Override + public Hasher putString(CharSequence charSequence, Charset charset) { + return putBytes(charSequence.toString().getBytes(charset)); + } +} diff --git a/sources/main/java/com/google/common/hash/AbstractNonStreamingHashFunction.java b/sources/main/java/com/google/common/hash/AbstractNonStreamingHashFunction.java new file mode 100644 index 0000000..8ea1dbb --- /dev/null +++ b/sources/main/java/com/google/common/hash/AbstractNonStreamingHashFunction.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.hash; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; + +import com.google.common.base.Preconditions; + +/** + * Skeleton implementation of {@link HashFunction}, appropriate for + * non-streaming algorithms. All the hash computation done using + * {@linkplain #newHasher()} are delegated to the + * {@linkplain #hashBytes(byte[], int, int)} method. + * + * @author Dimitris Andreou + */ +abstract class AbstractNonStreamingHashFunction implements HashFunction { + @Override + public Hasher newHasher() { + return new BufferingHasher(32); + } + + @Override + public Hasher newHasher(int expectedInputSize) { + Preconditions.checkArgument(expectedInputSize >= 0); + return new BufferingHasher(expectedInputSize); + } + + @Override + public HashCode hashObject(T instance, Funnel funnel) { + return newHasher().putObject(instance, funnel).hash(); + } + + @Override + public HashCode hashUnencodedChars(CharSequence input) { + int len = input.length(); + Hasher hasher = newHasher(len * 2); + for (int i = 0; i < len; i++) { + hasher.putChar(input.charAt(i)); + } + return hasher.hash(); + } + + @Override + public HashCode hashString(CharSequence input, Charset charset) { + return hashBytes(input.toString().getBytes(charset)); + } + + @Override + public HashCode hashInt(int input) { + return newHasher(4).putInt(input).hash(); + } + + @Override + public HashCode hashLong(long input) { + return newHasher(8).putLong(input).hash(); + } + + @Override + public HashCode hashBytes(byte[] input) { + return hashBytes(input, 0, input.length); + } + + /** + * In-memory stream-based implementation of Hasher. + */ + private final class BufferingHasher extends AbstractHasher { + final ExposedByteArrayOutputStream stream; + static final int BOTTOM_BYTE = 0xFF; + + BufferingHasher(int expectedInputSize) { + this.stream = new ExposedByteArrayOutputStream(expectedInputSize); + } + + @Override + public Hasher putByte(byte b) { + stream.write(b); + return this; + } + + @Override + public Hasher putBytes(byte[] bytes) { + try { + stream.write(bytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + return this; + } + + @Override + public Hasher putBytes(byte[] bytes, int off, int len) { + stream.write(bytes, off, len); + return this; + } + + @Override + public Hasher putShort(short s) { + stream.write(s & BOTTOM_BYTE); + stream.write((s >>> 8) & BOTTOM_BYTE); + return this; + } + + @Override + public Hasher putInt(int i) { + stream.write(i & BOTTOM_BYTE); + stream.write((i >>> 8) & BOTTOM_BYTE); + stream.write((i >>> 16) & BOTTOM_BYTE); + stream.write((i >>> 24) & BOTTOM_BYTE); + return this; + } + + @Override + public Hasher putLong(long l) { + for (int i = 0; i < 64; i += 8) { + stream.write((byte) ((l >>> i) & BOTTOM_BYTE)); + } + return this; + } + + @Override + public Hasher putChar(char c) { + stream.write(c & BOTTOM_BYTE); + stream.write((c >>> 8) & BOTTOM_BYTE); + return this; + } + + @Override + public Hasher putObject(T instance, Funnel funnel) { + funnel.funnel(instance, this); + return this; + } + + @Override + public HashCode hash() { + return hashBytes(stream.byteArray(), 0, stream.length()); + } + } + + // Just to access the byte[] without introducing an unnecessary copy + private static final class ExposedByteArrayOutputStream extends ByteArrayOutputStream { + ExposedByteArrayOutputStream(int expectedInputSize) { + super(expectedInputSize); + } + + byte[] byteArray() { + return buf; + } + + int length() { + return count; + } + } +} diff --git a/sources/main/java/com/google/common/hash/AbstractStreamingHashFunction.java b/sources/main/java/com/google/common/hash/AbstractStreamingHashFunction.java new file mode 100644 index 0000000..ccece29 --- /dev/null +++ b/sources/main/java/com/google/common/hash/AbstractStreamingHashFunction.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.hash; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +import com.google.common.base.Preconditions; + +/** + * Skeleton implementation of {@link HashFunction}. Provides default + * implementations which invokes the appropriate method on {@link #newHasher()}, + * then return the result of {@link Hasher#hash}. + * + *

+ * Invocations of {@link #newHasher(int)} also delegate to + * {@linkplain #newHasher()}, ignoring the expected input size parameter. + * + * @author Kevin Bourrillion + */ +abstract class AbstractStreamingHashFunction implements HashFunction { + @Override + public HashCode hashObject(T instance, Funnel funnel) { + return newHasher().putObject(instance, funnel).hash(); + } + + @Override + public HashCode hashUnencodedChars(CharSequence input) { + return newHasher().putUnencodedChars(input).hash(); + } + + @Override + public HashCode hashString(CharSequence input, Charset charset) { + return newHasher().putString(input, charset).hash(); + } + + @Override + public HashCode hashInt(int input) { + return newHasher().putInt(input).hash(); + } + + @Override + public HashCode hashLong(long input) { + return newHasher().putLong(input).hash(); + } + + @Override + public HashCode hashBytes(byte[] input) { + return newHasher().putBytes(input).hash(); + } + + @Override + public HashCode hashBytes(byte[] input, int off, int len) { + return newHasher().putBytes(input, off, len).hash(); + } + + @Override + public Hasher newHasher(int expectedInputSize) { + Preconditions.checkArgument(expectedInputSize >= 0); + return newHasher(); + } + + /** + * A convenience base class for implementors of {@code Hasher}; handles + * accumulating data until an entire "chunk" (of implementation-dependent + * length) is ready to be hashed. + * + * @author Kevin Bourrillion + * @author Dimitris Andreou + */ + // TODO(kevinb): this class still needs some design-and-document-for-inheritance + // love + protected static abstract class AbstractStreamingHasher extends AbstractHasher { + /** Buffer via which we pass data to the hash algorithm (the implementor) */ + private final ByteBuffer buffer; + + /** Number of bytes to be filled before process() invocation(s). */ + private final int bufferSize; + + /** Number of bytes processed per process() invocation. */ + private final int chunkSize; + + /** + * Constructor for use by subclasses. This hasher instance will process chunks + * of the specified size. + * + * @param chunkSize the number of bytes available per + * {@link #process(ByteBuffer)} invocation; must be at least 4 + */ + protected AbstractStreamingHasher(int chunkSize) { + this(chunkSize, chunkSize); + } + + /** + * Constructor for use by subclasses. This hasher instance will process chunks + * of the specified size, using an internal buffer of {@code bufferSize} size, + * which must be a multiple of {@code chunkSize}. + * + * @param chunkSize the number of bytes available per + * {@link #process(ByteBuffer)} invocation; must be at least 4 + * @param bufferSize the size of the internal buffer. Must be a multiple of + * chunkSize + */ + protected AbstractStreamingHasher(int chunkSize, int bufferSize) { + // TODO(kevinb): check more preconditions (as bufferSize >= chunkSize) if this + // is ever public + checkArgument(bufferSize % chunkSize == 0); + + // TODO(user): benchmark performance difference with longer buffer + this.buffer = ByteBuffer.allocate(bufferSize + 7) // always space for a single primitive + .order(ByteOrder.LITTLE_ENDIAN); + this.bufferSize = bufferSize; + this.chunkSize = chunkSize; + } + + /** + * Processes the available bytes of the buffer (at most {@code chunk} bytes). + */ + protected abstract void process(ByteBuffer bb); + + /** + * This is invoked for the last bytes of the input, which are not enough to fill + * a whole chunk. The passed {@code ByteBuffer} is guaranteed to be non-empty. + * + *

+ * This implementation simply pads with zeros and delegates to + * {@link #process(ByteBuffer)}. + */ + protected void processRemaining(ByteBuffer bb) { + bb.position(bb.limit()); // move at the end + bb.limit(chunkSize + 7); // get ready to pad with longs + while (bb.position() < chunkSize) { + bb.putLong(0); + } + bb.limit(chunkSize); + bb.flip(); + process(bb); + } + + @Override + public final Hasher putBytes(byte[] bytes) { + return putBytes(bytes, 0, bytes.length); + } + + @Override + public final Hasher putBytes(byte[] bytes, int off, int len) { + return putBytes(ByteBuffer.wrap(bytes, off, len).order(ByteOrder.LITTLE_ENDIAN)); + } + + private Hasher putBytes(ByteBuffer readBuffer) { + // If we have room for all of it, this is easy + if (readBuffer.remaining() <= buffer.remaining()) { + buffer.put(readBuffer); + munchIfFull(); + return this; + } + + // First add just enough to fill buffer size, and munch that + int bytesToCopy = bufferSize - buffer.position(); + for (int i = 0; i < bytesToCopy; i++) { + buffer.put(readBuffer.get()); + } + munch(); // buffer becomes empty here, since chunkSize divides bufferSize + + // Now process directly from the rest of the input buffer + while (readBuffer.remaining() >= chunkSize) { + process(readBuffer); + } + + // Finally stick the remainder back in our usual buffer + buffer.put(readBuffer); + return this; + } + + @Override + public final Hasher putUnencodedChars(CharSequence charSequence) { + for (int i = 0; i < charSequence.length(); i++) { + putChar(charSequence.charAt(i)); + } + return this; + } + + @Override + public final Hasher putByte(byte b) { + buffer.put(b); + munchIfFull(); + return this; + } + + @Override + public final Hasher putShort(short s) { + buffer.putShort(s); + munchIfFull(); + return this; + } + + @Override + public final Hasher putChar(char c) { + buffer.putChar(c); + munchIfFull(); + return this; + } + + @Override + public final Hasher putInt(int i) { + buffer.putInt(i); + munchIfFull(); + return this; + } + + @Override + public final Hasher putLong(long l) { + buffer.putLong(l); + munchIfFull(); + return this; + } + + @Override + public final Hasher putObject(T instance, Funnel funnel) { + funnel.funnel(instance, this); + return this; + } + + @Override + public final HashCode hash() { + munch(); + buffer.flip(); + if (buffer.remaining() > 0) { + processRemaining(buffer); + } + return makeHash(); + } + + abstract HashCode makeHash(); + + // Process pent-up data in chunks + private void munchIfFull() { + if (buffer.remaining() < 8) { + // buffer is full; not enough room for a primitive. We have at least one full + // chunk. + munch(); + } + } + + private void munch() { + buffer.flip(); + while (buffer.remaining() >= chunkSize) { + // we could limit the buffer to ensure process() does not read more than + // chunkSize number of bytes, but we trust the implementations + process(buffer); + } + buffer.compact(); // preserve any remaining data that do not make a full chunk + } + } +} diff --git a/sources/main/java/com/google/common/hash/BloomFilter.java b/sources/main/java/com/google/common/hash/BloomFilter.java new file mode 100644 index 0000000..2c249a1 --- /dev/null +++ b/sources/main/java/com/google/common/hash/BloomFilter.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.hash; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; +import com.google.common.base.Predicate; +import com.google.common.hash.BloomFilterStrategies.BitArray; + +/** + * A Bloom filter for instances of {@code T}. A Bloom filter offers an + * approximate containment test with one-sided error: if it claims that an + * element is contained in it, this might be in error, but if it claims that an + * element is not contained in it, then this is definitely true. + * + *

+ * If you are unfamiliar with Bloom filters, this nice + * tutorial may + * help you understand how they work. + * + *

+ * The false positive probability ({@code FPP}) of a bloom filter is defined as + * the probability that {@linkplain #mightContain(Object)} will erroneously + * return {@code true} for an object that has not actually been put in the + * {@code BloomFilter}. + * + *

+ * Bloom filters are serializable. However, serial forms generated by newer + * versions of the code may not be readable by older versions of the code (e.g., + * a serialized bloom filter generated today may not be readable by a + * binary that was compiled 6 months ago). + * + * @param the type of instances that the {@code BloomFilter} accepts + * @author Dimitris Andreou + * @author Kevin Bourrillion + * @since 11.0 + */ +@Beta +public final class BloomFilter implements Predicate, Serializable { + /** + * A strategy to translate T instances, to {@code numHashFunctions} bit indexes. + * + *

+ * Implementations should be collections of pure functions (i.e. stateless). + */ + interface Strategy extends java.io.Serializable { + + /** + * Sets {@code numHashFunctions} bits of the given bit array, by hashing a user + * element. + * + *

+ * Returns whether any bits changed as a result of this operation. + */ + boolean put(T object, Funnel funnel, int numHashFunctions, BitArray bits); + + /** + * Queries {@code numHashFunctions} bits of the given bit array, by hashing a + * user element; returns {@code true} if and only if all selected bits are set. + */ + boolean mightContain(T object, Funnel funnel, int numHashFunctions, BitArray bits); + + /** + * Identifier used to encode this strategy, when marshalled as part of a + * BloomFilter. Only values in the [-128, 127] range are valid for the compact + * serial form. Non-negative values are reserved for enums defined in + * BloomFilterStrategies; negative values are reserved for any custom, stateful + * strategy we may define (e.g. any kind of strategy that would depend on user + * input). + */ + int ordinal(); + } + + /** The bit set of the BloomFilter (not necessarily power of 2!) */ + private final BitArray bits; + + /** Number of hashes per element */ + private final int numHashFunctions; + + /** The funnel to translate Ts to bytes */ + private final Funnel funnel; + + /** + * The strategy we employ to map an element T to {@code numHashFunctions} bit + * indexes. + */ + private final Strategy strategy; + + /** + * Creates a BloomFilter. + */ + private BloomFilter(BitArray bits, int numHashFunctions, Funnel funnel, Strategy strategy) { + checkArgument(numHashFunctions > 0, "numHashFunctions (%s) must be > 0", numHashFunctions); + checkArgument(numHashFunctions <= 255, "numHashFunctions (%s) must be <= 255", numHashFunctions); + this.bits = checkNotNull(bits); + this.numHashFunctions = numHashFunctions; + this.funnel = checkNotNull(funnel); + this.strategy = checkNotNull(strategy); + } + + /** + * Creates a new {@code BloomFilter} that's a copy of this instance. The new + * instance is equal to this instance but shares no mutable state. + * + * @since 12.0 + */ + public BloomFilter copy() { + return new BloomFilter(bits.copy(), numHashFunctions, funnel, strategy); + } + + /** + * Returns {@code true} if the element might have been put in this Bloom + * filter, {@code false} if this is definitely not the case. + */ + public boolean mightContain(T object) { + return strategy.mightContain(object, funnel, numHashFunctions, bits); + } + + /** + * @deprecated Provided only to satisfy the {@link Predicate} interface; use + * {@link #mightContain} instead. + */ + @Deprecated + @Override + public boolean apply(T input) { + return mightContain(input); + } + + /** + * Puts an element into this {@code BloomFilter}. Ensures that subsequent + * invocations of {@link #mightContain(Object)} with the same element will + * always return {@code true}. + * + * @return true if the bloom filter's bits changed as a result of this + * operation. If the bits changed, this is definitely the first + * time {@code object} has been added to the filter. If the bits haven't + * changed, this might be the first time {@code object} has been + * added to the filter. Note that {@code put(t)} always returns the + * opposite result to what {@code mightContain(t)} would have + * returned at the time it is called." + * @since 12.0 (present in 11.0 with {@code void} return type}) + */ + public boolean put(T object) { + return strategy.put(object, funnel, numHashFunctions, bits); + } + + /** + * Returns the probability that {@linkplain #mightContain(Object)} will + * erroneously return {@code true} for an object that has not actually been put + * in the {@code BloomFilter}. + * + *

+ * Ideally, this number should be close to the {@code fpp} parameter passed in + * {@linkplain #create(Funnel, int, double)}, or smaller. If it is significantly + * higher, it is usually the case that too many elements (more than expected) + * have been put in the {@code BloomFilter}, degenerating it. + * + * @since 14.0 (since 11.0 as expectedFalsePositiveProbability()) + */ + public double expectedFpp() { + // You down with FPP? (Yeah you know me!) Who's down with FPP? (Every last + // homie!) + return Math.pow((double) bits.bitCount() / bitSize(), numHashFunctions); + } + + /** + * Returns the number of bits in the underlying bit array. + */ + @VisibleForTesting + long bitSize() { + return bits.bitSize(); + } + + /** + * Determines whether a given bloom filter is compatible with this bloom filter. + * For two bloom filters to be compatible, they must: + * + *

+ * See the Guava User Guide article on + * {@code Splitter}. + * + * @author Julien Silland + * @author Jesse Wilson + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 1.0 + */ +@GwtCompatible(emulated = true) +public final class Splitter { + private final CharMatcher trimmer; + private final boolean omitEmptyStrings; + private final Strategy strategy; + private final int limit; + + private Splitter(Strategy strategy) { + this(strategy, false, CharMatcher.NONE, Integer.MAX_VALUE); + } + + private Splitter(Strategy strategy, boolean omitEmptyStrings, CharMatcher trimmer, int limit) { + this.strategy = strategy; + this.omitEmptyStrings = omitEmptyStrings; + this.trimmer = trimmer; + this.limit = limit; + } + + /** + * Returns a splitter that uses the given single-character separator. For + * example, {@code Splitter.on(',').split("foo,,bar")} returns an iterable + * containing {@code ["foo", "", "bar"]}. + * + * @param separator the character to recognize as a separator + * @return a splitter, with default settings, that recognizes that separator + */ + public static Splitter on(char separator) { + return on(CharMatcher.is(separator)); + } + + /** + * Returns a splitter that considers any single character matched by the given + * {@code CharMatcher} to be a separator. For example, {@code + * Splitter.on(CharMatcher.anyOf(";,")).split("foo,;bar,quux")} returns an + * iterable containing {@code ["foo", "", "bar", "quux"]}. + * + * @param separatorMatcher a {@link CharMatcher} that determines whether a + * character is a separator + * @return a splitter, with default settings, that uses this matcher + */ + public static Splitter on(final CharMatcher separatorMatcher) { + checkNotNull(separatorMatcher); + + return new Splitter(new Strategy() { + @Override + public SplittingIterator iterator(Splitter splitter, final CharSequence toSplit) { + return new SplittingIterator(splitter, toSplit) { + @Override + int separatorStart(int start) { + return separatorMatcher.indexIn(toSplit, start); + } + + @Override + int separatorEnd(int separatorPosition) { + return separatorPosition + 1; + } + }; + } + }); + } + + /** + * Returns a splitter that uses the given fixed string as a separator. For + * example, {@code Splitter.on(", ").split("foo, bar,baz")} returns an iterable + * containing {@code ["foo", "bar,baz"]}. + * + * @param separator the literal, nonempty string to recognize as a separator + * @return a splitter, with default settings, that recognizes that separator + */ + public static Splitter on(final String separator) { + checkArgument(separator.length() != 0, "The separator may not be the empty string."); + + return new Splitter(new Strategy() { + @Override + public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) { + return new SplittingIterator(splitter, toSplit) { + @Override + public int separatorStart(int start) { + int separatorLength = separator.length(); + + positions: for (int p = start, last = toSplit.length() - separatorLength; p <= last; p++) { + for (int i = 0; i < separatorLength; i++) { + if (toSplit.charAt(i + p) != separator.charAt(i)) { + continue positions; + } + } + return p; + } + return -1; + } + + @Override + public int separatorEnd(int separatorPosition) { + return separatorPosition + separator.length(); + } + }; + } + }); + } + + /** + * Returns a splitter that considers any subsequence matching {@code + * pattern} to be a separator. For example, {@code + * Splitter.on(Pattern.compile("\r?\n")).split(entireFile)} splits a string into + * lines whether it uses DOS-style or UNIX-style line terminators. + * + * @param separatorPattern the pattern that determines whether a subsequence is + * a separator. This pattern may not match the empty + * string. + * @return a splitter, with default settings, that uses this pattern + * @throws IllegalArgumentException if {@code separatorPattern} matches the + * empty string + */ + @GwtIncompatible("java.util.regex") + public static Splitter on(final Pattern separatorPattern) { + checkNotNull(separatorPattern); + checkArgument(!separatorPattern.matcher("").matches(), "The pattern may not match the empty string: %s", + separatorPattern); + + return new Splitter(new Strategy() { + @Override + public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) { + final Matcher matcher = separatorPattern.matcher(toSplit); + return new SplittingIterator(splitter, toSplit) { + @Override + public int separatorStart(int start) { + return matcher.find(start) ? matcher.start() : -1; + } + + @Override + public int separatorEnd(int separatorPosition) { + return matcher.end(); + } + }; + } + }); + } + + /** + * Returns a splitter that considers any subsequence matching a given pattern + * (regular expression) to be a separator. For example, {@code + * Splitter.onPattern("\r?\n").split(entireFile)} splits a string into lines + * whether it uses DOS-style or UNIX-style line terminators. This is equivalent + * to {@code Splitter.on(Pattern.compile(pattern))}. + * + * @param separatorPattern the pattern that determines whether a subsequence is + * a separator. This pattern may not match the empty + * string. + * @return a splitter, with default settings, that uses this pattern + * @throws java.util.regex.PatternSyntaxException if {@code separatorPattern} is + * a malformed expression + * @throws IllegalArgumentException if {@code separatorPattern} + * matches the empty string + */ + @GwtIncompatible("java.util.regex") + public static Splitter onPattern(String separatorPattern) { + return on(Pattern.compile(separatorPattern)); + } + + /** + * Returns a splitter that divides strings into pieces of the given length. For + * example, {@code Splitter.fixedLength(2).split("abcde")} returns an iterable + * containing {@code ["ab", "cd", "e"]}. The last piece can be smaller than + * {@code length} but will never be empty. + * + *