add files
This commit is contained in:
commit
784a776dbb
7211 changed files with 811080 additions and 0 deletions
154
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
Executable file
154
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/OpenGLObjects.java
Executable file
|
|
@ -0,0 +1,154 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
256
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java
Executable file
256
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformApplication.java
Executable file
|
|
@ -0,0 +1,256 @@
|
|||
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;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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) {
|
||||
EagRuntime.debugPrintStackTrace(var5);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setClipboard(String text) {
|
||||
glfwSetClipboardString(win, text);
|
||||
}
|
||||
|
||||
public static String getClipboard() {
|
||||
String str = glfwGetClipboardString(win);
|
||||
return str == null ? "" : str;
|
||||
}
|
||||
|
||||
public static void setLocalStorage(String name, byte[] data) {
|
||||
if(data == null) {
|
||||
(new File("_eagstorage."+name+".dat")).delete();
|
||||
}else {
|
||||
try(FileOutputStream f = new FileOutputStream(new File("_eagstorage."+name+".dat"))) {
|
||||
f.write(data);
|
||||
} catch (IOException e) {
|
||||
EagRuntime.debugPrintStackTrace(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 clearFileChooserResult() {
|
||||
fileChooserHasResult = false;
|
||||
fileChooserResultObject = null;
|
||||
}
|
||||
|
||||
public static void openCreditsPopup(String text) {
|
||||
|
||||
}
|
||||
|
||||
private static final File downloadsDirectory = new File("downloads");
|
||||
private static final Logger downloadsLogger = LogManager.getLogger("DownloadsFolder");
|
||||
|
||||
public static void downloadFileWithName(String fileName, byte[] fileContents) {
|
||||
if(!downloadsDirectory.isDirectory() && !downloadsDirectory.mkdirs()) {
|
||||
throw new RuntimeException("Could not create directory: " + downloadsDirectory.getAbsolutePath());
|
||||
}
|
||||
|
||||
File f = new File(downloadsDirectory, fileName);
|
||||
if(f.exists()) {
|
||||
String name = fileName;
|
||||
String ext = "";
|
||||
int i = fileName.lastIndexOf('.');
|
||||
if(i != -1) {
|
||||
name = fileName.substring(0, i);
|
||||
ext = fileName.substring(i);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
f = new File(downloadsDirectory, name + " (" + (++i) + ")" + ext);
|
||||
}while(f.exists());
|
||||
}
|
||||
|
||||
try(FileOutputStream fos = new FileOutputStream(f)) {
|
||||
fos.write(fileContents);
|
||||
}catch(IOException ex) {
|
||||
throw new RuntimeException("Could not save file: " + f.getAbsolutePath());
|
||||
}
|
||||
|
||||
downloadsLogger.info("Saved {} byte file to: {}", fileContents.length, f.getAbsolutePath());
|
||||
}
|
||||
|
||||
public static void addLogMessage(String logMessage, boolean isError) {
|
||||
|
||||
}
|
||||
|
||||
public static boolean isShowingDebugConsole() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void showDebugConsole() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
86
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java
Executable file
86
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAssets.java
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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)) {
|
||||
int i, j = 0;
|
||||
while(j < ret.length && (i = is.read(ret, j, ret.length - j)) != -1) {
|
||||
j += i;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
234
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java
Executable file
234
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformAudio.java
Executable file
|
|
@ -0,0 +1,234 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformAudio.IAudioCacheLoader;
|
||||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
public static void flushAudioCache() {
|
||||
|
||||
}
|
||||
|
||||
public static interface IAudioCacheLoader {
|
||||
byte[] loadFile(String filename);
|
||||
}
|
||||
|
||||
public static IAudioResource loadAudioDataNew(String filename, boolean holdInCache, IAudioCacheLoader loader) {
|
||||
throw new UnsupportedOperationException("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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
228
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java
Executable file
228
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformFilesystem.java
Executable file
|
|
@ -0,0 +1,228 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.LWJGLEntryPoint;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFSIterator2.BreakLoop;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.DesktopIntegratedServer;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class PlatformFilesystem {
|
||||
|
||||
public static final Logger logger = LogManager.getLogger("PlatformFilesystem");
|
||||
|
||||
public static final File filesystemRoot = (new File("filesystem/sp")).getAbsoluteFile();
|
||||
|
||||
public static void initialize() {
|
||||
assertThread();
|
||||
if(!filesystemRoot.isDirectory() && !filesystemRoot.mkdirs()) {
|
||||
throw new EaglerFileSystemException("Could not create directory for virtual filesystem: " + filesystemRoot.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertThread() {
|
||||
if(Thread.currentThread() != DesktopIntegratedServer.serverThread) {
|
||||
throw new UnsupportedOperationException("[DEBUG CHECK] VFS2 is currently only initialized for server contexts!");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean eaglerDelete(String pathName) {
|
||||
assertThread();
|
||||
File f = getJREFile(pathName);
|
||||
if(!f.exists()) {
|
||||
logger.warn("Tried to delete file that doesn't exist: \"{}\"", pathName);
|
||||
return false;
|
||||
}
|
||||
if(f.delete()) {
|
||||
deleteParentIfEmpty(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ByteBuffer eaglerRead(String pathName) {
|
||||
assertThread();
|
||||
File f = getJREFile(pathName);
|
||||
if(f.isFile()) {
|
||||
long fileSize = f.length();
|
||||
if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath());
|
||||
ByteBuffer buf = PlatformRuntime.allocateByteBuffer((int)fileSize);
|
||||
try(FileInputStream is = new FileInputStream(f)) {
|
||||
byte[] copyBuffer = new byte[4096];
|
||||
int i;
|
||||
while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) {
|
||||
buf.put(copyBuffer, 0, i);
|
||||
}
|
||||
if(buf.remaining() > 0) {
|
||||
throw new EaglerFileSystemException("ERROR: " + buf.remaining() + " bytes are remaining after reading: " + f.getAbsolutePath());
|
||||
}
|
||||
buf.flip();
|
||||
ByteBuffer tmp = buf;
|
||||
buf = null;
|
||||
return tmp;
|
||||
}catch (IOException e) {
|
||||
throw new EaglerFileSystemException("Failed to read: " + f.getAbsolutePath(), e);
|
||||
}catch(ArrayIndexOutOfBoundsException ex) {
|
||||
throw new EaglerFileSystemException("ERROR: Expected " + fileSize + " bytes, buffer overflow reading: " + f.getAbsolutePath(), ex);
|
||||
}finally {
|
||||
if(buf != null) {
|
||||
PlatformRuntime.freeByteBuffer(buf);
|
||||
}
|
||||
}
|
||||
}else {
|
||||
throw new EaglerFileSystemException("Not a file: " + f.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
public static void eaglerWrite(String pathName, ByteBuffer data) {
|
||||
assertThread();
|
||||
File f = getJREFile(pathName);
|
||||
File p = f.getParentFile();
|
||||
if(!p.isDirectory()) {
|
||||
if(!p.mkdirs()) {
|
||||
throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
try(FileOutputStream fos = new FileOutputStream(f)) {
|
||||
byte[] copyBuffer = new byte[4096];
|
||||
int i;
|
||||
while((i = data.remaining()) > 0) {
|
||||
if(i > copyBuffer.length) {
|
||||
i = copyBuffer.length;
|
||||
}
|
||||
data.get(copyBuffer, 0, i);
|
||||
fos.write(copyBuffer, 0, i);
|
||||
}
|
||||
}catch (IOException e) {
|
||||
throw new EaglerFileSystemException("Failed to write: " + f.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean eaglerExists(String pathName) {
|
||||
assertThread();
|
||||
return getJREFile(pathName).isFile();
|
||||
}
|
||||
|
||||
public static boolean eaglerMove(String pathNameOld, String pathNameNew) {
|
||||
assertThread();
|
||||
File f1 = getJREFile(pathNameOld);
|
||||
File f2 = getJREFile(pathNameNew);
|
||||
if(f2.exists()) {
|
||||
logger.warn("Tried to rename file \"{}\" to \"{}\" which already exists! File will be replaced");
|
||||
if(!f2.delete()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(f1.renameTo(f2)) {
|
||||
deleteParentIfEmpty(f1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int eaglerCopy(String pathNameOld, String pathNameNew) {
|
||||
assertThread();
|
||||
File f1 = getJREFile(pathNameOld);
|
||||
File f2 = getJREFile(pathNameNew);
|
||||
if(!f1.isFile()) {
|
||||
return -1;
|
||||
}
|
||||
if(f2.isDirectory()) {
|
||||
throw new EaglerFileSystemException("Destination file is a directory: " + f2.getAbsolutePath());
|
||||
}
|
||||
File p = f2.getParentFile();
|
||||
if(!p.isDirectory()) {
|
||||
if(!p.mkdirs()) {
|
||||
throw new EaglerFileSystemException("Could not create parent directory: " + p.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
int sz = 0;
|
||||
try(FileInputStream is = new FileInputStream(f1)) {
|
||||
try(FileOutputStream os = new FileOutputStream(f2)) {
|
||||
byte[] copyBuffer = new byte[4096];
|
||||
int i;
|
||||
while((i = is.read(copyBuffer, 0, copyBuffer.length)) != -1) {
|
||||
os.write(copyBuffer, 0, i);
|
||||
sz += i;
|
||||
}
|
||||
}
|
||||
}catch (IOException e) {
|
||||
throw new EaglerFileSystemException("Failed to copy \"" + f1.getAbsolutePath() + "\" to file \"" + f2.getAbsolutePath() + "\"", e);
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
public static int eaglerSize(String pathName) {
|
||||
assertThread();
|
||||
File f = getJREFile(pathName);
|
||||
if(f.isFile()) {
|
||||
long fileSize = f.length();
|
||||
if(fileSize > 2147483647L) throw new EaglerFileSystemException("Too large: " + fileSize + " @ " + f.getAbsolutePath());
|
||||
return (int)fileSize;
|
||||
}else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) {
|
||||
assertThread();
|
||||
try {
|
||||
iterateFile(pathName, getJREFile(pathName), itr, recursive);
|
||||
}catch(BreakLoop ex) {
|
||||
}
|
||||
}
|
||||
|
||||
private static void iterateFile(String pathName, File f, VFSFilenameIterator itr, boolean recursive) {
|
||||
if(!f.exists()) {
|
||||
return;
|
||||
}
|
||||
if(!f.isDirectory()) {
|
||||
itr.next(pathName);
|
||||
return;
|
||||
}
|
||||
File[] fa = f.listFiles();
|
||||
for(int i = 0; i < fa.length; ++i) {
|
||||
File ff = fa[i];
|
||||
String fn = pathName + "/" + ff.getName();
|
||||
if(ff.isDirectory()) {
|
||||
if(recursive) {
|
||||
iterateFile(fn, ff, itr, true);
|
||||
}
|
||||
}else {
|
||||
itr.next(fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static File getJREFile(String path) {
|
||||
return new File(filesystemRoot, path);
|
||||
}
|
||||
|
||||
private static void deleteParentIfEmpty(File f) {
|
||||
String[] s;
|
||||
while((f = f.getParentFile()) != null && (s = f.list()) != null && s.length == 0) {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
406
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java
Executable file
406
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformInput.java
Executable file
|
|
@ -0,0 +1,406 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class PlatformInput {
|
||||
|
||||
private static long win = 0l;
|
||||
|
||||
private static long cursorDefault = 0l;
|
||||
private static long cursorHand = 0l;
|
||||
private static long cursorText = 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<KeyboardEvent> 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;
|
||||
|
||||
public static boolean lockKeys = false;
|
||||
|
||||
private static final List<Character> 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<MouseEvent> 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 = "<null>";
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
cursorDefault = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
|
||||
cursorText = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
|
||||
glfwSetCursor(glfwWindow, cursorDefault);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static void toggleFullscreen() {
|
||||
//
|
||||
}
|
||||
|
||||
public static boolean isFullscreen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void showCursor(EnumCursorType cursor) {
|
||||
switch(cursor) {
|
||||
case DEFAULT:
|
||||
default:
|
||||
glfwSetCursor(win, cursorDefault);
|
||||
break;
|
||||
case HAND:
|
||||
glfwSetCursor(win, cursorHand);
|
||||
break;
|
||||
case TEXT:
|
||||
glfwSetCursor(win, cursorText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
151
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java
Executable file
151
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformNetworking.java
Executable file
|
|
@ -0,0 +1,151 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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<byte[]> playPackets = new LinkedList();
|
||||
|
||||
public static byte[] readPlayPacket() {
|
||||
synchronized(playPackets) {
|
||||
return playPackets.size() > 0 ? playPackets.remove(0) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<byte[]> readAllPacket() {
|
||||
synchronized(playPackets) {
|
||||
if(!playPackets.isEmpty()) {
|
||||
List<byte[]> ret = new ArrayList<>(playPackets);
|
||||
playPackets.clear();
|
||||
return ret;
|
||||
}else {
|
||||
return 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;
|
||||
}
|
||||
|
||||
}
|
||||
526
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java
Executable file
526
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformOpenGL.java
Executable file
|
|
@ -0,0 +1,526 @@
|
|||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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 _wglBlendColor(float r, float g, float b, float a) {
|
||||
glBlendColor(r, g, b, a);
|
||||
}
|
||||
|
||||
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 _wglTexImage3D(int target, int level, int internalFormat, int width, int height, int depth,
|
||||
int border, int format, int type, ByteBuffer data) {
|
||||
nglTexImage3D(target, level, internalFormat, width, height, depth, 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, 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 _wglTexImage2Du16(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 _wglTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height,
|
||||
int format, int type, ByteBuffer data) {
|
||||
nglTexSubImage2D(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) {
|
||||
nglTexSubImage2D(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 _wglTexSubImage2Du16(int target, int level, int xoffset, int yoffset, int width, int height,
|
||||
int format, int type, ByteBuffer 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 _wglTexStorage2D(int target, int levels, int internalFormat, int w, int h) {
|
||||
glTexStorage2D(target, levels, internalFormat, w, h);
|
||||
}
|
||||
|
||||
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 int _wglGetUniformBlockIndex(IProgramGL obj, String name) {
|
||||
return glGetUniformBlockIndex(((OpenGLObjects.ProgramGL) obj).ptr, name);
|
||||
}
|
||||
|
||||
public static final void _wglBindBufferRange(int target, int index, IBufferGL buffer, int offset, int size) {
|
||||
glBindBufferRange(target, index, ((OpenGLObjects.BufferGL) buffer).ptr, offset, size);
|
||||
}
|
||||
|
||||
public static final void _wglUniformBlockBinding(IProgramGL obj, int blockIndex, int bufferIndex) {
|
||||
glUniformBlockBinding(((OpenGLObjects.ProgramGL) obj).ptr, blockIndex, bufferIndex);
|
||||
}
|
||||
|
||||
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 _wglUniformMatrix3x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) {
|
||||
if (obj != null)
|
||||
nglUniformMatrix3x2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 6, 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 _wglUniformMatrix4x2fv(IUniformGL obj, boolean transpose, FloatBuffer mat) {
|
||||
if (obj != null)
|
||||
nglUniformMatrix4x2fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() >> 3, transpose,
|
||||
EaglerLWJGLAllocator.getAddress(mat));
|
||||
}
|
||||
|
||||
public static final void _wglUniformMatrix4x3fv(IUniformGL obj, boolean transpose, FloatBuffer mat) {
|
||||
if (obj != null)
|
||||
nglUniformMatrix4x3fv(((OpenGLObjects.UniformGL) obj).ptr, mat.remaining() / 12, transpose,
|
||||
EaglerLWJGLAllocator.getAddress(mat));
|
||||
}
|
||||
|
||||
public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) {
|
||||
if(framebuffer == null) {
|
||||
glBindFramebuffer(target, 0);
|
||||
}else {
|
||||
glBindFramebuffer(target, ((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 _wglFramebufferTextureLayer(int target, int attachment, ITextureGL texture, int level, int layer) {
|
||||
glFramebufferTextureLayer(target, attachment, ((OpenGLObjects.TextureGL) texture).ptr, level, layer);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public static final boolean checkHDRFramebufferSupport(int bits) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
471
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java
Executable file
471
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformRuntime.java
Executable file
|
|
@ -0,0 +1,471 @@
|
|||
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.Random;
|
||||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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);
|
||||
PlatformApplication.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<String> 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 boolean isOfflineDownloadURL() {
|
||||
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() {
|
||||
//
|
||||
}
|
||||
|
||||
private static final Random seedProvider = new Random();
|
||||
|
||||
public static long randomSeed() {
|
||||
synchronized(seedProvider) {
|
||||
return seedProvider.nextLong();
|
||||
}
|
||||
}
|
||||
|
||||
public static void getWindowXY(int[] x, int[] y) {
|
||||
glfwGetWindowPos(windowHandle, x, y);
|
||||
}
|
||||
|
||||
public static String currentThreadName() {
|
||||
return Thread.currentThread().getName();
|
||||
}
|
||||
}
|
||||
56
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java
Executable file
56
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformUpdateSvc.java
Executable file
|
|
@ -0,0 +1,56 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate;
|
||||
import net.lax1dude.eaglercraft.v1_8.update.UpdateProgressStruct;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class PlatformUpdateSvc {
|
||||
|
||||
private static final UpdateProgressStruct dummyStruct = new UpdateProgressStruct();
|
||||
|
||||
public static boolean supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
|
||||
}
|
||||
|
||||
public static byte[] getClientSignatureData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] getClientBundleData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void startClientUpdateFrom(UpdateCertificate clientUpdate) {
|
||||
|
||||
}
|
||||
|
||||
public static UpdateProgressStruct getUpdatingStatus() {
|
||||
return dummyStruct;
|
||||
}
|
||||
|
||||
public static void quine(String filename, byte[] cert, byte[] data, String date) {
|
||||
|
||||
}
|
||||
|
||||
public static void quine(UpdateCertificate clientUpdate, byte[] data) {
|
||||
|
||||
}
|
||||
}
|
||||
145
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java
Executable file
145
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/PlatformWebRTC.java
Executable file
|
|
@ -0,0 +1,145 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.lan.LANPeerEvent;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayQuery;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayServerSocket;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayWorldsQuery;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class PlatformWebRTC {
|
||||
|
||||
public static boolean supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static RelayServerSocket openRelayConnection(String addr, int timeout) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static RelayQuery openRelayQuery(String addr) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static RelayWorldsQuery openRelayWorldsQuery(String addr) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void startRTCLANClient() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static int clientLANReadyState() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void clientLANCloseConnection() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void clientLANSendPacket(byte[] pkt) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static byte[] clientLANReadPacket() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static List<byte[]> clientLANReadAllPacket() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void clientLANSetICEServersAndConnect(String[] servers) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void clearLANClientState() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static String clientLANAwaitICECandidate() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static String clientLANAwaitDescription() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static boolean clientLANAwaitChannel() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static boolean clientLANClosed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void clientLANSetICECandidate(String candidate) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void clientLANSetDescription(String description) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void startRTCLANServer() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void serverLANInitializeServer(String[] servers) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void serverLANCloseServer() {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static LANPeerEvent serverLANGetEvent(String clientId) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static List<LANPeerEvent> serverLANGetAllEvent(String clientId) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void serverLANWritePacket(String peer, byte[] data) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void serverLANCreatePeer(String peer) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void serverLANPeerICECandidates(String peer, String iceCandidates) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void serverLANPeerDescription(String peer, String description) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static void serverLANDisconnectPeer(String peer) {
|
||||
throw new UnsupportedOperationException("LAN not supported on desktop runtime!");
|
||||
}
|
||||
|
||||
public static int countPeers() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
import org.java_websocket.drafts.Draft;
|
||||
import org.java_websocket.drafts.Draft_6455;
|
||||
import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension;
|
||||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
class WebSocketPlayClient extends WebSocketClient {
|
||||
|
||||
private static final Draft perMessageDeflateDraft = new Draft_6455(new PerMessageDeflateExtension());
|
||||
|
||||
public static final Logger logger = LogManager.getLogger("WebSocket");
|
||||
|
||||
WebSocketPlayClient(URI serverUri) {
|
||||
super(serverUri, perMessageDeflateDraft);
|
||||
this.setConnectionLostTimeout(15);
|
||||
this.setTcpNoDelay(true);
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
}
|
||||
173
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketServerQuery.java
Executable file
173
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/WebSocketServerQuery.java
Executable file
|
|
@ -0,0 +1,173 @@
|
|||
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.drafts.Draft;
|
||||
import org.java_websocket.drafts.Draft_6455;
|
||||
import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension;
|
||||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
class WebSocketServerQuery extends WebSocketClient implements IServerQuery {
|
||||
|
||||
private static final Draft perMessageDeflateDraft = new Draft_6455(new PerMessageDeflateExtension());
|
||||
|
||||
public static final Logger logger = LogManager.getLogger("WebSocketQuery");
|
||||
|
||||
private final List<QueryResponse> queryResponses = new LinkedList();
|
||||
private final List<byte[]> 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, perMessageDeflateDraft);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
|
||||
|
||||
import org.lwjgl.system.jemalloc.JEmalloc;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,422 @@
|
|||
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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class DesktopClientConfigAdapter implements IClientConfigAdapter {
|
||||
|
||||
public static final IClientConfigAdapter instance = new DesktopClientConfigAdapter();
|
||||
|
||||
public final List<DefaultServer> defaultServers = new ArrayList();
|
||||
|
||||
@Override
|
||||
public String getDefaultLocale() {
|
||||
return "en_US";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DefaultServer> getDefaultServerList() {
|
||||
return defaultServers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerToJoin() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorldsDB() {
|
||||
return "desktop";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject dumpConfig() {
|
||||
return new JSONObject("{\"container\":null,\"worldsDB\":\"desktop\"}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RelayEntry> getRelays() {
|
||||
throw new UnsupportedOperationException("TODO");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkShaderGLErrors() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDemo() {
|
||||
return EaglercraftVersion.forceDemoMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowUpdateSvc() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowUpdateDL() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnableDownloadOfflineButton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadOfflineButtonLink() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useSpecialCursors() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogInvalidCerts() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCheckRelaysForUpdates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnableSignatureBadge() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
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.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.program.ShaderSource;
|
||||
import net.minecraft.client.main.Main;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class LWJGLEntryPoint {
|
||||
|
||||
public static Thread mainThread = null;
|
||||
|
||||
public static void main_(String[] args) {
|
||||
mainThread = Thread.currentThread();
|
||||
|
||||
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);
|
||||
|
||||
for(int i = 0; i < args.length; ++i) {
|
||||
if(args[i].equalsIgnoreCase("highp")) {
|
||||
ShaderSource.setHighP(true);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
|
||||
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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.internal.lwjgl;
|
||||
|
||||
public class MainClass {
|
||||
|
||||
public static void main(String[] args) {
|
||||
LWJGLEntryPoint.main_(args);
|
||||
}
|
||||
|
||||
}
|
||||
37
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/TestProgram.java
Executable file
37
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/lwjgl/TestProgram.java
Executable file
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
* <b><br>
|
||||
* <br>
|
||||
* This software is based on or using the LWJGL Lightweight Java Gaming Library
|
||||
* available from http://www.lwjgl.org/. </b><br>
|
||||
* <br>
|
||||
* LWJGL License: <br>
|
||||
* <i> Copyright (c) 2002-2008 Lightweight Java Game Library Project All rights
|
||||
* reserved. <br>
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* <br>
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer. <br>
|
||||
* * 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. <br>
|
||||
* * 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. <br>
|
||||
* 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. <br>
|
||||
* <br>
|
||||
* <br>
|
||||
* </i> <b><i> SoundSystem LibraryLWJGLOpenAL License:</b></i><br>
|
||||
* <b><br>
|
||||
* <b> 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: <br>
|
||||
* 1) You must abide by the conditions of the aforementioned LWJGL License. <br>
|
||||
* 2) You may not falsely claim to be the author of this library or any
|
||||
* unmodified portion of it. <br>
|
||||
* 3) You may not copyright this library or a modified version of it and then
|
||||
* sue me for copyright infringement. <br>
|
||||
* 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. <br>
|
||||
* 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 <br>
|
||||
* 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. <br>
|
||||
* 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. <br>
|
||||
* <br>
|
||||
* Author: Paul Lamb <br>
|
||||
* http://www.paulscode.com </b>
|
||||
*/
|
||||
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<byte[]> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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. <b><br>
|
||||
* <br>
|
||||
* This software is based on or using the LWJGL Lightweight Java Gaming Library
|
||||
* available from http://www.lwjgl.org/. </b><br>
|
||||
* <br>
|
||||
* LWJGL License: <br>
|
||||
* <i> Copyright (c) 2002-2008 Lightweight Java Game Library Project All rights
|
||||
* reserved. <br>
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* <br>
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer. <br>
|
||||
* * 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. <br>
|
||||
* * 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. <br>
|
||||
* 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. <br>
|
||||
* <br>
|
||||
* <br>
|
||||
* </i> <b><i> SoundSystem LibraryLWJGLOpenAL License:</b></i><br>
|
||||
* <b><br>
|
||||
* <b> 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: <br>
|
||||
* 1) You must abide by the conditions of the aforementioned LWJGL License. <br>
|
||||
* 2) You may not falsely claim to be the author of this library or any
|
||||
* unmodified portion of it. <br>
|
||||
* 3) You may not copyright this library or a modified version of it and then
|
||||
* sue me for copyright infringement. <br>
|
||||
* 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. <br>
|
||||
* 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 <br>
|
||||
* 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. <br>
|
||||
* 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. <br>
|
||||
* <br>
|
||||
* Author: Paul Lamb <br>
|
||||
* http://www.paulscode.com </b>
|
||||
*/
|
||||
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<String, IntBuffer> 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<String, IntBuffer>();
|
||||
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<String> keys = bufferMap.keySet();
|
||||
Iterator<String> 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<String, SoundBuffer>();
|
||||
importantMessage("Buffer Map was null in method 'loadSound'");
|
||||
}
|
||||
// Make sure the OpenAL buffer map exists:
|
||||
if (ALBufferMap == null) {
|
||||
ALBufferMap = new HashMap<String, IntBuffer>();
|
||||
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<String, SoundBuffer>();
|
||||
importantMessage("Buffer Map was null in method 'loadSound'");
|
||||
}
|
||||
// Make sure the OpenAL buffer map exists:
|
||||
if (ALBufferMap == null) {
|
||||
ALBufferMap = new HashMap<String, IntBuffer>();
|
||||
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<String, Source> srcMap) {
|
||||
if (srcMap == null)
|
||||
return;
|
||||
Set<String> keys = srcMap.keySet();
|
||||
Iterator<String> iter = keys.iterator();
|
||||
String sourcename;
|
||||
Source source;
|
||||
|
||||
// Make sure the buffer map exists:
|
||||
if (bufferMap == null) {
|
||||
bufferMap = new HashMap<String, SoundBuffer>();
|
||||
importantMessage("Buffer Map was null in method 'copySources'");
|
||||
}
|
||||
// Make sure the OpenAL buffer map exists:
|
||||
if (ALBufferMap == null) {
|
||||
ALBufferMap = new HashMap<String, IntBuffer>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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. <b><br>
|
||||
* <br>
|
||||
* This software is based on or using the LWJGL Lightweight Java Gaming Library
|
||||
* available from http://www.lwjgl.org/. </b><br>
|
||||
* <br>
|
||||
* LWJGL License: <br>
|
||||
* <i> Copyright (c) 2002-2008 Lightweight Java Game Library Project All rights
|
||||
* reserved. <br>
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* <br>
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer. <br>
|
||||
* * 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. <br>
|
||||
* * 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. <br>
|
||||
* 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. <br>
|
||||
* <br>
|
||||
* <br>
|
||||
* </i> <b><i> SoundSystem LibraryLWJGLOpenAL License:</b></i><br>
|
||||
* <b><br>
|
||||
* <b> 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: <br>
|
||||
* 1) You must abide by the conditions of the aforementioned LWJGL License. <br>
|
||||
* 2) You may not falsely claim to be the author of this library or any
|
||||
* unmodified portion of it. <br>
|
||||
* 3) You may not copyright this library or a modified version of it and then
|
||||
* sue me for copyright infringement. <br>
|
||||
* 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. <br>
|
||||
* 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 <br>
|
||||
* 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. <br>
|
||||
* 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. <br>
|
||||
* <br>
|
||||
* Author: Paul Lamb <br>
|
||||
* http://www.paulscode.com </b>
|
||||
*/
|
||||
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<byte[]> preLoadBuffers = new LinkedList<byte[]>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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<String> getResourceDomains() {
|
||||
return Sets.<String>newHashSet();
|
||||
}
|
||||
}
|
||||
42
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/vfs/SYS.java
Executable file
42
src/lwjgl/java/net/lax1dude/eaglercraft/v1_8/internal/vfs/SYS.java
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
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-2023 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class SYS {
|
||||
|
||||
public static final Object VFS = null;
|
||||
|
||||
public static final void loadRemoteResourcePack(String url, String hash, Consumer<String> cb, Consumer<Runnable> ast, Runnable loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static final boolean loadResourcePack(String name, InputStream data, String hash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final List<String> getResourcePackNames() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public static final void deleteResourcePack(String packName) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.sp.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.CrashScreenPopup;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.DesktopIntegratedServer;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.MemoryConnection;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class ClientPlatformSingleplayer {
|
||||
|
||||
private static CrashScreenPopup crashOverlay = null;
|
||||
|
||||
public static void startIntegratedServer() {
|
||||
DesktopIntegratedServer.startIntegratedServer();
|
||||
}
|
||||
|
||||
public static void sendPacket(IPCPacketData packet) {
|
||||
synchronized(MemoryConnection.clientToServerQueue) {
|
||||
MemoryConnection.clientToServerQueue.add(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public static IPCPacketData recievePacket() {
|
||||
synchronized(MemoryConnection.serverToClientQueue) {
|
||||
if(MemoryConnection.serverToClientQueue.size() > 0) {
|
||||
return MemoryConnection.serverToClientQueue.remove(0);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<IPCPacketData> recieveAllPacket() {
|
||||
synchronized(MemoryConnection.serverToClientQueue) {
|
||||
if(MemoryConnection.serverToClientQueue.size() == 0) {
|
||||
return null;
|
||||
}else {
|
||||
List<IPCPacketData> ret = new ArrayList(MemoryConnection.serverToClientQueue);
|
||||
MemoryConnection.serverToClientQueue.clear();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canKillWorker() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void killWorker() {
|
||||
throw new IllegalStateException("Cannot kill worker thread on desktop! (memleak)");
|
||||
}
|
||||
|
||||
public static boolean isRunningSingleThreadMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void showCrashReportOverlay(String report, int x, int y, int w, int h) {
|
||||
if(crashOverlay == null) {
|
||||
crashOverlay = new CrashScreenPopup();
|
||||
}
|
||||
int[] wx = new int[1];
|
||||
int[] wy = new int[1];
|
||||
PlatformRuntime.getWindowXY(wx, wy);
|
||||
crashOverlay.setBounds(wx[0] + x, wy[0] + y, w, h);
|
||||
crashOverlay.setCrashText(report);
|
||||
crashOverlay.setVisible(true);
|
||||
crashOverlay.requestFocus();
|
||||
}
|
||||
|
||||
public static void hideCrashReportOverlay() {
|
||||
crashOverlay.setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.sp.server.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem;
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.lwjgl.DesktopClientConfigAdapter;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.classes.EaglerServerBootstrap;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl.MemoryConnection;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2023-2024 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class ServerPlatformSingleplayer {
|
||||
|
||||
public static void initializeContext() {
|
||||
PlatformFilesystem.initialize();
|
||||
EaglerServerBootstrap.staticInit();
|
||||
}
|
||||
|
||||
public static void sendPacket(IPCPacketData packet) {
|
||||
synchronized(MemoryConnection.serverToClientQueue) {
|
||||
MemoryConnection.serverToClientQueue.add(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public static IPCPacketData recievePacket() {
|
||||
synchronized(MemoryConnection.clientToServerQueue) {
|
||||
if(MemoryConnection.clientToServerQueue.size() > 0) {
|
||||
return MemoryConnection.clientToServerQueue.remove(0);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<IPCPacketData> recieveAllPacket() {
|
||||
synchronized(MemoryConnection.clientToServerQueue) {
|
||||
if(MemoryConnection.clientToServerQueue.size() == 0) {
|
||||
return null;
|
||||
}else {
|
||||
List<IPCPacketData> ret = new ArrayList(MemoryConnection.clientToServerQueue);
|
||||
MemoryConnection.clientToServerQueue.clear();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IClientConfigAdapter getClientConfigAdapter() {
|
||||
return DesktopClientConfigAdapter.instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.BorderLayout;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import java.awt.Font;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Color;
|
||||
import java.awt.Window.Type;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class CrashScreenPopup extends JFrame {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private JPanel contentPane;
|
||||
private JTextArea txtrTest;
|
||||
|
||||
/**
|
||||
* Create the frame.
|
||||
*/
|
||||
public CrashScreenPopup() {
|
||||
setType(Type.UTILITY);
|
||||
setResizable(false);
|
||||
setIconImage(Toolkit.getDefaultToolkit().getImage("icon32.png"));
|
||||
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
setAlwaysOnTop(true);
|
||||
setTitle("EaglercraftX Integrated Server");
|
||||
setBounds(100, 100, 900, 600);
|
||||
setLocationByPlatform(true);
|
||||
contentPane = new JPanel();
|
||||
contentPane.setBorder(null);
|
||||
|
||||
setContentPane(contentPane);
|
||||
contentPane.setLayout(new BorderLayout(0, 0));
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane();
|
||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
contentPane.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
txtrTest = new JTextArea();
|
||||
txtrTest.setBackground(new Color(0, 0, 0));
|
||||
txtrTest.setForeground(new Color(255, 255, 255));
|
||||
txtrTest.setText("test");
|
||||
txtrTest.setFont(new Font("Monospaced", Font.BOLD, 18));
|
||||
txtrTest.setLineWrap(true);
|
||||
txtrTest.setWrapStyleWord(true);
|
||||
txtrTest.setEditable(false);
|
||||
scrollPane.setViewportView(txtrTest);
|
||||
}
|
||||
|
||||
public void setCrashText(String txt) {
|
||||
txtrTest.setText(txt);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
|
||||
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2023-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class DesktopIntegratedServer implements Runnable {
|
||||
|
||||
public static Thread serverThread = null;
|
||||
|
||||
public static void startIntegratedServer() {
|
||||
if(serverThread == null) {
|
||||
serverThread = new Thread(new DesktopIntegratedServer(), "IntegratedServer");
|
||||
serverThread.setDaemon(true);
|
||||
serverThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ServerPlatformSingleplayer.initializeContext();
|
||||
EaglerIntegratedServerWorker.serverMain();
|
||||
}finally {
|
||||
serverThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.lwjgl;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2023-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
public class MemoryConnection {
|
||||
|
||||
public static final List<IPCPacketData> clientToServerQueue = new LinkedList();
|
||||
public static final List<IPCPacketData> serverToClientQueue = new LinkedList();
|
||||
|
||||
}
|
||||
48
src/main/java/com/google/common/annotations/Beta.java
Executable file
48
src/main/java/com/google/common/annotations/Beta.java
Executable file
|
|
@ -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."
|
||||
*
|
||||
* <p>
|
||||
* It is generally safe for <i>applications</i> to depend on beta APIs, at the
|
||||
* cost of some extra work during upgrades. However it is generally inadvisable
|
||||
* for <i>libraries</i> (which get included on users' CLASSPATHs, outside the
|
||||
* library developers' control) to do so.
|
||||
*
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({ ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD,
|
||||
ElementType.TYPE })
|
||||
@Documented
|
||||
@GwtCompatible
|
||||
public @interface Beta {
|
||||
}
|
||||
92
src/main/java/com/google/common/annotations/GwtCompatible.java
Executable file
92
src/main/java/com/google/common/annotations/GwtCompatible.java
Executable file
|
|
@ -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 <a href="http://code.google.com/webtoolkit/">Google Web Toolkit</a>
|
||||
* (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,
|
||||
*
|
||||
* <pre style="code">
|
||||
* {@literal @}GwtCompatible
|
||||
* class Lists {
|
||||
* ...
|
||||
* {@literal @}GwtCompatible(serializable = true)
|
||||
* static <E> List<E> newArrayList(E... elements) {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* 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,
|
||||
*
|
||||
* <pre style="code">
|
||||
* {@literal @}GwtCompatible
|
||||
* interface ListFactory {
|
||||
* ...
|
||||
* {@literal @}GwtCompatible(serializable = true)
|
||||
* <E> List<E> newArrayList(E... elements);
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* The {@code newArrayList(E[])} method of all implementations of {@code
|
||||
* ListFactory} is expected to return a value with a GWT serializable type.
|
||||
*
|
||||
* <p>
|
||||
* 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 <a href=
|
||||
* "http://code.google.com/webtoolkit/doc/latest/DevGuideServerCommunication.html#DevGuideSerializableTypes">
|
||||
* Documentation about GWT serialization</a>
|
||||
*/
|
||||
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 <a href=
|
||||
* "http://code.google.com/webtoolkit/doc/latest/DevGuideOrganizingProjects.html#DevGuideModules">
|
||||
* Documentation about GWT emulated source</a>
|
||||
*/
|
||||
boolean emulated() default false;
|
||||
}
|
||||
51
src/main/java/com/google/common/annotations/GwtIncompatible.java
Executable file
51
src/main/java/com/google/common/annotations/GwtIncompatible.java
Executable file
|
|
@ -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
|
||||
* <em>not</em> be used with the
|
||||
* <a href="http://code.google.com/webtoolkit/">Google Web Toolkit</a> (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.
|
||||
*
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
27
src/main/java/com/google/common/annotations/VisibleForTesting.java
Executable file
27
src/main/java/com/google/common/annotations/VisibleForTesting.java
Executable file
|
|
@ -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 {
|
||||
}
|
||||
21
src/main/java/com/google/common/annotations/package-info.java
Executable file
21
src/main/java/com/google/common/annotations/package-info.java
Executable file
|
|
@ -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
|
||||
* <a href="http://guava-libraries.googlecode.com">Guava libraries</a>.
|
||||
*/
|
||||
package com.google.common.annotations;
|
||||
106
src/main/java/com/google/common/base/Absent.java
Executable file
106
src/main/java/com/google/common/base/Absent.java
Executable file
|
|
@ -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<T> extends Optional<T> {
|
||||
static final Absent<Object> INSTANCE = new Absent<Object>();
|
||||
|
||||
@SuppressWarnings("unchecked") // implementation is "fully variant"
|
||||
static <T> Optional<T> withType() {
|
||||
return (Optional<T>) 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<T> or(Optional<? extends T> secondChoice) {
|
||||
return (Optional<T>) checkNotNull(secondChoice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T or(Supplier<? extends T> 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<T> asSet() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Optional<V> transform(Function<? super T, V> function) {
|
||||
checkNotNull(function);
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
return object == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0x598df91c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Optional.absent()";
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
88
src/main/java/com/google/common/base/AbstractIterator.java
Executable file
88
src/main/java/com/google/common/base/AbstractIterator.java
Executable file
|
|
@ -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<T> implements Iterator<T> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
669
src/main/java/com/google/common/base/Ascii.java
Executable file
669
src/main/java/com/google/common/base/Ascii.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* ASCII utilities also exist in other classes of this package:
|
||||
* <ul>
|
||||
* <!-- TODO(kevinb): how can we make this not produce a warning when building
|
||||
* gwt javadoc? -->
|
||||
* <li>{@link Charsets#US_ASCII} specifies the {@code Charset} of ASCII
|
||||
* characters.
|
||||
* <li>{@link CharMatcher#ASCII} matches ASCII characters and provides text
|
||||
* processing methods which operate only on the ASCII characters of a string.
|
||||
* </ul>
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Examples:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* Ascii.truncate("foobar", 7, "..."); // returns "foobar"
|
||||
* Ascii.truncate("foobar", 5, "..."); // returns "fo..." }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b> This method <i>may</i> 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:
|
||||
*
|
||||
* <ul>
|
||||
* <li>it may split surrogate pairs</li>
|
||||
* <li>it may split characters and combining characters</li>
|
||||
* <li>it does not consider word boundaries</li>
|
||||
* <li>if truncating for display to users, there are other considerations that
|
||||
* must be taken into account</li>
|
||||
* <li>the appropriate truncation indicator may be locale-dependent</li>
|
||||
* <li>it is safe to use non-ASCII characters in the truncation indicator</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Note however that this method does not always behave identically to
|
||||
* expressions such as:
|
||||
* <ul>
|
||||
* <li>{@code string.toUpperCase().equals("UPPER CASE ASCII")}
|
||||
* <li>{@code string.toLowerCase().equals("lower case ascii")}
|
||||
* </ul>
|
||||
* <p>
|
||||
* 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');
|
||||
}
|
||||
}
|
||||
228
src/main/java/com/google/common/base/CaseFormat.java
Executable file
228
src/main/java/com/google/common/base/CaseFormat.java
Executable file
|
|
@ -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<String, String> converterTo(CaseFormat targetFormat) {
|
||||
return new StringConverter(this, targetFormat);
|
||||
}
|
||||
|
||||
private static final class StringConverter extends Converter<String, String> 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();
|
||||
}
|
||||
}
|
||||
1509
src/main/java/com/google/common/base/CharMatcher.java
Executable file
1509
src/main/java/com/google/common/base/CharMatcher.java
Executable file
File diff suppressed because it is too large
Load diff
58
src/main/java/com/google/common/base/Charsets.java
Executable file
58
src/main/java/com/google/common/base/Charsets.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* Assuming you're free to choose, note that <b>{@link #UTF_8} is widely
|
||||
* preferred</b>.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/StringsExplained#Charsets">
|
||||
* {@code Charsets}</a>.
|
||||
*
|
||||
* @author Mike Bostock
|
||||
* @since 1.0
|
||||
*/
|
||||
@GwtCompatible(emulated = true)
|
||||
public final class Charsets {
|
||||
private Charsets() {
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8: eight-bit UCS Transformation Format.
|
||||
*
|
||||
*/
|
||||
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
/*
|
||||
* Please do not add new Charset references to this class, unless those
|
||||
* character encodings are part of the set required to be supported by all Java
|
||||
* platform implementations! Any Charsets initialized here may cause unexpected
|
||||
* delays when this class is loaded. See the Charset Javadocs for the list of
|
||||
* built-in character encodings.
|
||||
*/
|
||||
}
|
||||
528
src/main/java/com/google/common/base/Converter.java
Executable file
528
src/main/java/com/google/common/base/Converter.java
Executable file
|
|
@ -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 <i>reverse</i>
|
||||
* function from {@code B} to {@code A}; used for converting back and forth
|
||||
* between <i>different representations of the same information</i>.
|
||||
*
|
||||
* <h3>Invertibility</h3>
|
||||
*
|
||||
* <p>
|
||||
* The reverse operation <b>may</b> be a strict <i>inverse</i> (meaning that
|
||||
* {@code
|
||||
* converter.reverse().convert(converter.convert(a)).equals(a)} is always true).
|
||||
* However, it is very common (perhaps <i>more</i> common) for round-trip
|
||||
* conversion to be <i>lossy</i>. Consider an example round-trip using
|
||||
* {@link com.google.common.primitives.Doubles#stringConverter}:
|
||||
*
|
||||
* <ol>
|
||||
* <li>{@code stringConverter().convert("1.00")} returns the {@code Double}
|
||||
* value {@code 1.0}
|
||||
* <li>{@code stringConverter().reverse().convert(1.0)} returns the string
|
||||
* {@code "1.0"} -- <i>not</i> the same string ({@code "1.00"}) we started with
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
* Note that it should still be the case that the round-tripped and original
|
||||
* objects are <i>similar</i>.
|
||||
*
|
||||
* <h3>Nullability</h3>
|
||||
*
|
||||
* <p>
|
||||
* 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 <i>missing</i>
|
||||
* 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}.
|
||||
*
|
||||
*
|
||||
* <h3>Common ways to use</h3>
|
||||
*
|
||||
* <p>
|
||||
* Getting a converter:
|
||||
*
|
||||
* <ul>
|
||||
* <li>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.
|
||||
* <li>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 <i>mock</i> the {@code Converter} type using a
|
||||
* mocking framework.
|
||||
* <li>Otherwise, extend this class and implement its {@link #doForward} and
|
||||
* {@link #doBackward} methods.
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Using a converter:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Convert one instance in the "forward" direction using
|
||||
* {@code converter.convert(a)}.
|
||||
* <li>Convert multiple instances "forward" using
|
||||
* {@code converter.convertAll(as)}.
|
||||
* <li>Convert in the "backward" direction using
|
||||
* {@code converter.reverse().convert(b)} or {@code
|
||||
* converter.reverse().convertAll(bs)}.
|
||||
* <li>Use {@code converter} or {@code converter.reverse()} anywhere a
|
||||
* {@link Function} is accepted
|
||||
* <li><b>Do not</b> call {@link #doForward} or {@link #doBackward} directly;
|
||||
* these exist only to be overridden.
|
||||
* </ul>
|
||||
*
|
||||
* @author Mike Ward
|
||||
* @author Kurt Alfred Kluever
|
||||
* @author Gregory Kick
|
||||
* @since 16.0
|
||||
*/
|
||||
@Beta
|
||||
@GwtCompatible
|
||||
public abstract class Converter<A, B> implements Function<A, B> {
|
||||
private final boolean handleNullAutomatically;
|
||||
|
||||
// We lazily cache the reverse view to avoid allocating on every call to
|
||||
// reverse().
|
||||
private transient Converter<B, A> 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; <b>must not</b> 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; <b>must not</b> 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
|
||||
* unimplement<i>able</i> (for example,
|
||||
* consider a
|
||||
* {@code Converter<Chicken, ChickenNugget>}),
|
||||
* 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 <i>if and only if</i> {@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.
|
||||
*
|
||||
* <p>
|
||||
* 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<B> convertAll(final Iterable<? extends A> fromIterable) {
|
||||
checkNotNull(fromIterable, "fromIterable");
|
||||
return new Iterable<B>() {
|
||||
@Override
|
||||
public Iterator<B> iterator() {
|
||||
return new Iterator<B>() {
|
||||
private final Iterator<? extends A> 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}.
|
||||
*
|
||||
* <p>
|
||||
* The returned converter is serializable if {@code this} converter is.
|
||||
*/
|
||||
// TODO(user): Make this method final
|
||||
public Converter<B, A> reverse() {
|
||||
Converter<B, A> result = reverse;
|
||||
return (result == null) ? reverse = new ReverseConverter<A, B>(this) : result;
|
||||
}
|
||||
|
||||
private static final class ReverseConverter<A, B> extends Converter<B, A> implements Serializable {
|
||||
final Converter<A, B> original;
|
||||
|
||||
ReverseConverter(Converter<A, B> 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<A, B> 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.
|
||||
*
|
||||
* <p>
|
||||
* The returned converter is serializable if {@code this} converter and
|
||||
* {@code secondConverter} are.
|
||||
*/
|
||||
public <C> Converter<A, C> andThen(Converter<B, C> secondConverter) {
|
||||
return new ConverterComposition<A, B, C>(this, checkNotNull(secondConverter));
|
||||
}
|
||||
|
||||
private static final class ConverterComposition<A, B, C> extends Converter<A, C> implements Serializable {
|
||||
final Converter<A, B> first;
|
||||
final Converter<B, C> second;
|
||||
|
||||
ConverterComposition(Converter<A, B> first, Converter<B, C> 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.
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* <i>interchangeable</i> with this one. "Interchangeable" <i>typically</i>
|
||||
* 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 <i>not</i> to be interchangeable.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
return super.equals(object);
|
||||
}
|
||||
|
||||
// Static converters
|
||||
|
||||
/**
|
||||
* Returns a converter based on <i>existing</i> forward and backward functions.
|
||||
* Note that it is unnecessary to create <i>new</i> 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.
|
||||
*
|
||||
* <p>
|
||||
* 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}).
|
||||
*
|
||||
* <p>
|
||||
* The returned converter is serializable if both provided functions are.
|
||||
*
|
||||
* @since 17.0
|
||||
*/
|
||||
public static <A, B> Converter<A, B> from(Function<? super A, ? extends B> forwardFunction,
|
||||
Function<? super B, ? extends A> backwardFunction) {
|
||||
return new FunctionBasedConverter<A, B>(forwardFunction, backwardFunction);
|
||||
}
|
||||
|
||||
private static final class FunctionBasedConverter<A, B> extends Converter<A, B> implements Serializable {
|
||||
private final Function<? super A, ? extends B> forwardFunction;
|
||||
private final Function<? super B, ? extends A> backwardFunction;
|
||||
|
||||
private FunctionBasedConverter(Function<? super A, ? extends B> forwardFunction,
|
||||
Function<? super B, ? extends A> 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 <T> Converter<T, T> identity() {
|
||||
return (IdentityConverter<T>) 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<T> extends Converter<T, T> 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<T> reverse() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> Converter<T, S> andThen(Converter<T, S> 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;
|
||||
}
|
||||
}
|
||||
68
src/main/java/com/google/common/base/Defaults.java
Executable file
68
src/main/java/com/google/common/base/Defaults.java
Executable file
|
|
@ -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<Class<?>, Object> DEFAULTS;
|
||||
|
||||
static {
|
||||
// Only add to this map via put(Map, Class<T>, T)
|
||||
Map<Class<?>, Object> map = new HashMap<Class<?>, 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 <T> void put(Map<Class<?>, Object> map, Class<T> 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> T defaultValue(Class<T> 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;
|
||||
}
|
||||
}
|
||||
197
src/main/java/com/google/common/base/Enums.java
Executable file
197
src/main/java/com/google/common/base/Enums.java
Executable file
|
|
@ -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 <T extends Enum<T>> Function<String, T> valueOfFunction(Class<T> enumClass) {
|
||||
return new ValueOfFunction<T>(enumClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Function} that maps an {@link Enum} name to the associated constant,
|
||||
* or {@code null} if the constant does not exist.
|
||||
*/
|
||||
private static final class ValueOfFunction<T extends Enum<T>> implements Function<String, T>, Serializable {
|
||||
|
||||
private final Class<T> enumClass;
|
||||
|
||||
private ValueOfFunction(Class<T> enumClass) {
|
||||
this.enumClass = checkNotNull(enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T apply(String value) {
|
||||
try {
|
||||
return Enum.valueOf(enumClass, value);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return obj instanceof ValueOfFunction && enumClass.equals(((ValueOfFunction) obj).enumClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return enumClass.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Enums.valueOf(" + enumClass + ")";
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional enum constant for the given type, using
|
||||
* {@link Enum#valueOf}. If the constant does not exist, {@link Optional#absent}
|
||||
* is returned. A common use case is for parsing user input or falling back to a
|
||||
* default enum constant. For example,
|
||||
* {@code Enums.getIfPresent(Country.class, countryInput).or(Country.DEFAULT);}
|
||||
*
|
||||
* @since 12.0
|
||||
*/
|
||||
public static <T extends Enum<T>> Optional<T> getIfPresent(Class<T> enumClass, String value) {
|
||||
checkNotNull(enumClass);
|
||||
checkNotNull(value);
|
||||
return Platform.getEnumIfPresent(enumClass, value);
|
||||
}
|
||||
|
||||
@GwtIncompatible("java.lang.ref.WeakReference")
|
||||
private static final Map<Class<? extends Enum<?>>, Map<String, WeakReference<? extends Enum<?>>>> enumConstantCache = new HashMap<Class<? extends Enum<?>>, Map<String, WeakReference<? extends Enum<?>>>>();
|
||||
|
||||
@GwtIncompatible("java.lang.ref.WeakReference")
|
||||
private static <T extends Enum<T>> Map<String, WeakReference<? extends Enum<?>>> populateCache(Class<T> enumClass) {
|
||||
Map<String, WeakReference<? extends Enum<?>>> result = new HashMap<String, WeakReference<? extends Enum<?>>>();
|
||||
for (T enumInstance : EnumSet.allOf(enumClass)) {
|
||||
result.put(enumInstance.name(), new WeakReference<Enum<?>>(enumInstance));
|
||||
}
|
||||
enumConstantCache.put(enumClass, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@GwtIncompatible("java.lang.ref.WeakReference")
|
||||
static <T extends Enum<T>> Map<String, WeakReference<? extends Enum<?>>> getEnumConstants(Class<T> enumClass) {
|
||||
synchronized (enumConstantCache) {
|
||||
Map<String, WeakReference<? extends Enum<?>>> 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 <T extends Enum<T>> Converter<String, T> stringConverter(final Class<T> enumClass) {
|
||||
return new StringConverter<T>(enumClass);
|
||||
}
|
||||
|
||||
private static final class StringConverter<T extends Enum<T>> extends Converter<String, T> implements Serializable {
|
||||
|
||||
private final Class<T> enumClass;
|
||||
|
||||
StringConverter(Class<T> 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;
|
||||
}
|
||||
}
|
||||
409
src/main/java/com/google/common/base/Equivalence.java
Executable file
409
src/main/java/com/google/common/base/Equivalence.java
Executable file
|
|
@ -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
|
||||
* (<a href="http://code.google.com/p/guava-libraries/wiki/Compatibility"
|
||||
* >mostly source-compatible</a> since 4.0)
|
||||
*/
|
||||
@GwtCompatible
|
||||
public abstract class Equivalence<T> {
|
||||
/**
|
||||
* Constructor for use by subclasses.
|
||||
*/
|
||||
protected Equivalence() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given objects are considered equivalent.
|
||||
*
|
||||
* <p>
|
||||
* The {@code equivalent} method implements an equivalence relation on object
|
||||
* references:
|
||||
*
|
||||
* <ul>
|
||||
* <li>It is <i>reflexive</i>: for any reference {@code x}, including null,
|
||||
* {@code
|
||||
* equivalent(x, x)} returns {@code true}.
|
||||
* <li>It is <i>symmetric</i>: for any references {@code x} and {@code y},
|
||||
* {@code
|
||||
* equivalent(x, y) == equivalent(y, x)}.
|
||||
* <li>It is <i>transitive</i>: 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}.
|
||||
* <li>It is <i>consistent</i>: 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).
|
||||
* </ul>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* 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}.
|
||||
*
|
||||
* <p>
|
||||
* The {@code hash} has the following properties:
|
||||
* <ul>
|
||||
* <li>It is <i>consistent</i>: 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.
|
||||
* <li>It is <i>distributable across equivalence</i>: for any references
|
||||
* {@code x} and {@code y}, if {@code equivalent(x, y)}, then
|
||||
* {@code hash(x) == hash(y)}. It is <i>not</i> necessary that the hash be
|
||||
* distributable across <i>inequivalence</i>. If {@code equivalence(x, y)} is
|
||||
* false, {@code hash(x) == hash(y)} may still be true.
|
||||
* <li>{@code hash(null)} is {@code 0}.
|
||||
* </ul>
|
||||
*/
|
||||
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}.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* For example:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* @code
|
||||
* Equivalence<Person> SAME_AGE = Equivalence.equals().onResultOf(GET_PERSON_AGE);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* {@code function} will never be invoked with a null value.
|
||||
*
|
||||
* <p>
|
||||
* Note that {@code function} must be consistent according to {@code this}
|
||||
* equivalence relation. That is, invoking {@link Function#apply} multiple times
|
||||
* for a given value must return equivalent results. For example,
|
||||
* {@code Equivalence.identity().onResultOf(Functions.toStringFunction())} is
|
||||
* broken because it's not guaranteed that {@link Object#toString}) always
|
||||
* returns the same string instance.
|
||||
*
|
||||
* @since 10.0
|
||||
*/
|
||||
public final <F> Equivalence<F> onResultOf(Function<F, ? extends T> function) {
|
||||
return new FunctionalEquivalence<F, T>(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 <S extends T> Wrapper<S> wrap(@Nullable S reference) {
|
||||
return new Wrapper<S>(this, reference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an object so that {@link #equals(Object)} and {@link #hashCode()}
|
||||
* delegate to an {@link Equivalence}.
|
||||
*
|
||||
* <p>
|
||||
* For example, given an {@link Equivalence} for {@link String strings} named
|
||||
* {@code equiv} that tests equivalence using their lengths:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* equiv.wrap("a").equals(equiv.wrap("b")) // true
|
||||
* equiv.wrap("a").equals(equiv.wrap("hello")) // false}
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Note in particular that an equivalence wrapper is never equal to the object
|
||||
* it wraps.
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* equiv.wrap(obj).equals(obj) // always false}
|
||||
* </pre>
|
||||
*
|
||||
* @since 10.0
|
||||
*/
|
||||
public static final class Wrapper<T> implements Serializable {
|
||||
private final Equivalence<? super T> equivalence;
|
||||
@Nullable
|
||||
private final T reference;
|
||||
|
||||
private Wrapper(Equivalence<? super T> 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<T>
|
||||
|
||||
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<Object> equivalence = (Equivalence<Object>) 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.
|
||||
*
|
||||
* <p>
|
||||
* 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 <S extends T> Equivalence<Iterable<S>> pairwise() {
|
||||
// Ideally, the returned equivalence would support Iterable<? extends T>.
|
||||
// However,
|
||||
// the need for this is so rare that it's not worth making callers deal with the
|
||||
// ugly wildcard.
|
||||
return new PairwiseEquivalence<S>(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<T> equivalentTo(@Nullable T target) {
|
||||
return new EquivalentToPredicate<T>(this, target);
|
||||
}
|
||||
|
||||
private static final class EquivalentToPredicate<T> implements Predicate<T>, Serializable {
|
||||
|
||||
private final Equivalence<T> equivalence;
|
||||
@Nullable
|
||||
private final T target;
|
||||
|
||||
EquivalentToPredicate(Equivalence<T> 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<Object> equals() {
|
||||
return Equals.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an equivalence that uses {@code ==} to compare values and
|
||||
* {@link System#identityHashCode(Object)} to compute the hash code.
|
||||
* {@link Equivalence#equivalent} returns {@code true} if {@code a == b},
|
||||
* including in the case that a and b are both null.
|
||||
*
|
||||
* @since 13.0
|
||||
* @since 4.0 (in Equivalences)
|
||||
*/
|
||||
public static Equivalence<Object> identity() {
|
||||
return Identity.INSTANCE;
|
||||
}
|
||||
|
||||
static final class Equals extends Equivalence<Object> implements Serializable {
|
||||
|
||||
static final Equals INSTANCE = new Equals();
|
||||
|
||||
@Override
|
||||
protected boolean doEquivalent(Object a, Object b) {
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int doHash(Object o) {
|
||||
return o.hashCode();
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
static final class Identity extends Equivalence<Object> implements Serializable {
|
||||
|
||||
static final Identity INSTANCE = new Identity();
|
||||
|
||||
@Override
|
||||
protected boolean doEquivalent(Object a, Object b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doHash(Object o) {
|
||||
return System.identityHashCode(o);
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
}
|
||||
45
src/main/java/com/google/common/base/FinalizablePhantomReference.java
Executable file
45
src/main/java/com/google/common/base/FinalizablePhantomReference.java
Executable file
|
|
@ -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}.
|
||||
*
|
||||
* <p>
|
||||
* 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<T> extends PhantomReference<T> 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();
|
||||
}
|
||||
}
|
||||
34
src/main/java/com/google/common/base/FinalizableReference.java
Executable file
34
src/main/java/com/google/common/base/FinalizableReference.java
Executable file
|
|
@ -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();
|
||||
}
|
||||
371
src/main/java/com/google/common/base/FinalizableReferenceQueue.java
Executable file
371
src/main/java/com/google/common/base/FinalizableReferenceQueue.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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 <em>could</em> use a finalizer to accomplish this,
|
||||
* but that has a number of well-known problems. Here is how you might use this
|
||||
* class instead:
|
||||
*
|
||||
* <pre>
|
||||
* 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();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @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<Object> queue;
|
||||
|
||||
final PhantomReference<Object> 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<Object>();
|
||||
frqRef = new PhantomReference<Object>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/main/java/com/google/common/base/FinalizableSoftReference.java
Executable file
41
src/main/java/com/google/common/base/FinalizableSoftReference.java
Executable file
|
|
@ -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<T> extends SoftReference<T> 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();
|
||||
}
|
||||
}
|
||||
41
src/main/java/com/google/common/base/FinalizableWeakReference.java
Executable file
41
src/main/java/com/google/common/base/FinalizableWeakReference.java
Executable file
|
|
@ -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<T> extends WeakReference<T> 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();
|
||||
}
|
||||
}
|
||||
74
src/main/java/com/google/common/base/Function.java
Executable file
74
src/main/java/com/google/common/base/Function.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* The {@link Functions} class provides common functions and related utilites.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/FunctionalExplained">the use
|
||||
* of {@code
|
||||
* Function}</a>.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@GwtCompatible
|
||||
public interface Function<F, T> {
|
||||
/**
|
||||
* Returns the result of applying this function to {@code input}. This method is
|
||||
* <i>generally expected</i>, but not absolutely required, to have the following
|
||||
* properties:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Its execution does not cause any observable side effects.
|
||||
* <li>The computation is <i>consistent with equals</i>; that is,
|
||||
* {@link Objects#equal Objects.equal}{@code (a, b)} implies that
|
||||
* {@code Objects.equal(function.apply(a),
|
||||
* function.apply(b))}.
|
||||
* </ul>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* <i>interchangeable</i> with this one. "Interchangeable" <i>typically</i>
|
||||
* 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 <i>not</i> to be
|
||||
* interchangeable.
|
||||
*/
|
||||
@Override
|
||||
boolean equals(@Nullable Object object);
|
||||
}
|
||||
79
src/main/java/com/google/common/base/FunctionalEquivalence.java
Executable file
79
src/main/java/com/google/common/base/FunctionalEquivalence.java
Executable file
|
|
@ -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<F, T> extends Equivalence<F> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
|
||||
private final Function<F, ? extends T> function;
|
||||
private final Equivalence<T> resultEquivalence;
|
||||
|
||||
FunctionalEquivalence(Function<F, ? extends T> function, Equivalence<T> 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 + ")";
|
||||
}
|
||||
}
|
||||
403
src/main/java/com/google/common/base/Functions.java
Executable file
403
src/main/java/com/google/common/base/Functions.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* All methods return serializable functions as long as they're given
|
||||
* serializable parameters.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/FunctionalExplained">the use
|
||||
* of {@code
|
||||
* Function}</a>.
|
||||
*
|
||||
* @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}.
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> The returned function may not be <i>consistent with
|
||||
* equals</i> (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<Object, String> toStringFunction() {
|
||||
return ToStringFunction.INSTANCE;
|
||||
}
|
||||
|
||||
// enum singleton pattern
|
||||
private enum ToStringFunction implements Function<Object, String> {
|
||||
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 <E> Function<E, E> identity() {
|
||||
return (Function<E, E>) IdentityFunction.INSTANCE;
|
||||
}
|
||||
|
||||
// enum singleton pattern
|
||||
private enum IdentityFunction implements Function<Object, Object> {
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* 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 <K, V> Function<K, V> forMap(Map<K, V> map) {
|
||||
return new FunctionForMapNoDefault<K, V>(map);
|
||||
}
|
||||
|
||||
private static class FunctionForMapNoDefault<K, V> implements Function<K, V>, Serializable {
|
||||
final Map<K, V> map;
|
||||
|
||||
FunctionForMapNoDefault(Map<K, V> 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 <K, V> Function<K, V> forMap(Map<K, ? extends V> map, @Nullable V defaultValue) {
|
||||
return new ForMapWithDefault<K, V>(map, defaultValue);
|
||||
}
|
||||
|
||||
private static class ForMapWithDefault<K, V> implements Function<K, V>, Serializable {
|
||||
final Map<K, ? extends V> map;
|
||||
final V defaultValue;
|
||||
|
||||
ForMapWithDefault(Map<K, ? extends V> 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 <a href="//en.wikipedia.org/wiki/Function_composition">function
|
||||
* composition</a>
|
||||
*/
|
||||
public static <A, B, C> Function<A, C> compose(Function<B, C> g, Function<A, ? extends B> f) {
|
||||
return new FunctionComposition<A, B, C>(g, f);
|
||||
}
|
||||
|
||||
private static class FunctionComposition<A, B, C> implements Function<A, C>, Serializable {
|
||||
private final Function<B, C> g;
|
||||
private final Function<A, ? extends B> f;
|
||||
|
||||
public FunctionComposition(Function<B, C> g, Function<A, ? extends B> 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.
|
||||
*
|
||||
* <p>
|
||||
* The returned function is <i>consistent with equals</i> (as documented at
|
||||
* {@link Function#apply}) if and only if {@code predicate} is itself consistent
|
||||
* with equals.
|
||||
*/
|
||||
public static <T> Function<T, Boolean> forPredicate(Predicate<T> predicate) {
|
||||
return new PredicateFunction<T>(predicate);
|
||||
}
|
||||
|
||||
/** @see Functions#forPredicate */
|
||||
private static class PredicateFunction<T> implements Function<T, Boolean>, Serializable {
|
||||
private final Predicate<T> predicate;
|
||||
|
||||
private PredicateFunction(Predicate<T> 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 <E> Function<Object, E> constant(@Nullable E value) {
|
||||
return new ConstantFunction<E>(value);
|
||||
}
|
||||
|
||||
private static class ConstantFunction<E> implements Function<Object, E>, 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 <T> Function<Object, T> forSupplier(Supplier<T> supplier) {
|
||||
return new SupplierFunction<T>(supplier);
|
||||
}
|
||||
|
||||
/** @see Functions#forSupplier */
|
||||
private static class SupplierFunction<T> implements Function<Object, T>, Serializable {
|
||||
|
||||
private final Supplier<T> supplier;
|
||||
|
||||
private SupplierFunction(Supplier<T> 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;
|
||||
}
|
||||
}
|
||||
501
src/main/java/com/google/common/base/Joiner.java
Executable file
501
src/main/java/com/google/common/base/Joiner.java
Executable file
|
|
@ -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:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* Joiner joiner = Joiner.on("; ").skipNulls();
|
||||
* . . .
|
||||
* return joiner.join("Harry", null, "Ron", "Hermione");}
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* This returns the string {@code "Harry; Ron; Hermione"}. Note that all input
|
||||
* elements are converted to strings using {@link Object#toString()} before
|
||||
* being appended.
|
||||
*
|
||||
* <p>
|
||||
* If neither {@link #skipNulls()} nor {@link #useForNull(String)} is specified,
|
||||
* the joining methods will throw {@link NullPointerException} if any given
|
||||
* element is null.
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning: joiner instances are always immutable</b>; 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.
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* @code
|
||||
*
|
||||
* // Bad! Do not do this!
|
||||
* Joiner joiner = Joiner.on(',');
|
||||
* joiner.skipNulls(); // does nothing!
|
||||
* return joiner.join("wrong", null, "wrong");
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/StringsExplained#Joiner">{@code Joiner}</a>.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@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 extends Appendable> 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 extends Appendable> 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 extends Appendable> 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 extends Appendable> 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 extends Appendable> 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.
|
||||
*
|
||||
* <p>
|
||||
* In addition to operating on {@code Map} instances, {@code MapJoiner} can
|
||||
* operate on {@code
|
||||
* Multimap} entries in two distinct modes:
|
||||
*
|
||||
* <ul>
|
||||
* <li>To output a separate entry for each key-value pair, pass
|
||||
* {@code multimap.entries()} to a {@code MapJoiner} method that accepts entries
|
||||
* as input, and receive output of the form {@code key1=A&key1=B&key2=C}.
|
||||
* <li>To output a single entry for each key, pass {@code multimap.asMap()} to a
|
||||
* {@code MapJoiner} method that accepts a map as input, and receive output of
|
||||
* the form {@code
|
||||
* key1=[A, B]&key2=C}.
|
||||
* </ul>
|
||||
*
|
||||
* @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 extends Appendable> 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 extends Appendable> A appendTo(A appendable, Iterable<? extends Entry<?, ?>> 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 extends Appendable> A appendTo(A appendable, Iterator<? extends Entry<?, ?>> 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<? extends Entry<?, ?>> 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<? extends Entry<?, ?>> 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<? extends Entry<?, ?>> 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<? extends Entry<?, ?>> 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<Object> iterable(final Object first, final Object second, final Object[] rest) {
|
||||
checkNotNull(rest);
|
||||
return new AbstractList<Object>() {
|
||||
@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];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
448
src/main/java/com/google/common/base/Objects.java
Executable file
448
src/main/java/com/google/common/base/Objects.java
Executable file
|
|
@ -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}.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/CommonObjectUtilitiesExplained">writing
|
||||
* {@code Object} methods with {@code Objects}</a>.
|
||||
*
|
||||
* @author Laurence Gonsalves
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@GwtCompatible
|
||||
public final class Objects {
|
||||
private Objects() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether two possibly-null objects are equal. Returns:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code true} if {@code a} and {@code b} are both null.
|
||||
* <li>{@code true} if {@code a} and {@code b} are both non-null and they are
|
||||
* equal according to {@link Object#equals(Object)}.
|
||||
* <li>{@code false} in all other situations.
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* public int hashCode() {
|
||||
* return Objects.hashCode(getX(), getY(), getZ());
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning</b>: 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}.
|
||||
*
|
||||
* <p>
|
||||
* This is helpful for implementing {@link Object#toString()}. Specification by
|
||||
* example:
|
||||
*
|
||||
* <pre>
|
||||
* {@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();
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 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()}.
|
||||
*
|
||||
* <p>
|
||||
* 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}.
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b> 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> 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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)}.
|
||||
*
|
||||
* <p>
|
||||
* After calling this method, you can keep adding more properties to later call
|
||||
* toString() again and get a more complete representation of the same object;
|
||||
* but properties cannot be removed, so this only allows limited reuse of the
|
||||
* helper instance. The helper allows duplication of properties (multiple
|
||||
* name/value pairs with the same name can be added).
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
// create a copy to keep it consistent in case value changes
|
||||
boolean omitNullValuesSnapshot = omitNullValues;
|
||||
String nextSeparator = "";
|
||||
StringBuilder builder = new StringBuilder(32).append(className).append('{');
|
||||
for (ValueHolder valueHolder = holderHead.next; valueHolder != null; valueHolder = valueHolder.next) {
|
||||
if (!omitNullValuesSnapshot || valueHolder.value != null) {
|
||||
builder.append(nextSeparator);
|
||||
nextSeparator = ", ";
|
||||
|
||||
if (valueHolder.name != null) {
|
||||
builder.append(valueHolder.name).append('=');
|
||||
}
|
||||
builder.append(valueHolder.value);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
265
src/main/java/com/google/common/base/Optional.java
Executable file
265
src/main/java/com/google/common/base/Optional.java
Executable file
|
|
@ -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}".
|
||||
*
|
||||
* <p>
|
||||
* A non-null {@code Optional<T>} 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.
|
||||
*
|
||||
* <p>
|
||||
* Some uses of this class include
|
||||
*
|
||||
* <ul>
|
||||
* <li>As a method return type, as an alternative to returning {@code null} to
|
||||
* indicate that no value was available
|
||||
* <li>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()})
|
||||
* <li>To wrap nullable references for storage in a collection that does not
|
||||
* support {@code null} (though there are <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/LivingWithNullHostileCollections">
|
||||
* several other approaches to this</a> that should be considered first)
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* A common alternative to using this class is to find or create a suitable
|
||||
* <a href="http://en.wikipedia.org/wiki/Null_Object_pattern">null object</a>
|
||||
* for the type in question.
|
||||
*
|
||||
* <p>
|
||||
* This class is not intended as a direct analogue of any existing "option" or
|
||||
* "maybe" construct from other programming environments, though it may bear
|
||||
* some similarities.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional">
|
||||
* using {@code Optional}</a>.
|
||||
*
|
||||
* @param <T> the type of instance that can be contained. {@code Optional} is
|
||||
* naturally covariant on this type, so it is safe to cast an
|
||||
* {@code Optional<T>} to {@code
|
||||
* Optional<S>} for any supertype {@code S} of {@code T}.
|
||||
* @author Kurt Alfred Kluever
|
||||
* @author Kevin Bourrillion
|
||||
* @since 10.0
|
||||
*/
|
||||
@GwtCompatible(serializable = true)
|
||||
public abstract class Optional<T> implements Serializable {
|
||||
/**
|
||||
* Returns an {@code Optional} instance with no contained reference.
|
||||
*/
|
||||
public static <T> Optional<T> absent() {
|
||||
return Absent.withType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code Optional} instance containing the given non-null reference.
|
||||
*/
|
||||
public static <T> Optional<T> of(T reference) {
|
||||
return new Present<T>(checkNotNull(reference));
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code nullableReference} is non-null, returns an {@code Optional}
|
||||
* instance containing that reference; otherwise returns
|
||||
* {@link Optional#absent}.
|
||||
*/
|
||||
public static <T> Optional<T> fromNullable(@Nullable T nullableReference) {
|
||||
return (nullableReference == null) ? Optional.<T>absent() : new Present<T>(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}.
|
||||
*
|
||||
* <p>
|
||||
* Note about generics: The signature {@code public T or(T defaultValue)} is
|
||||
* overly restrictive. However, the ideal signature,
|
||||
* {@code public <S super T> S or(S)}, is not legal Java. As a result, some
|
||||
* sensible operations involving subtypes are compile errors:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* Optional<Integer> optionalInt = getSomeOptionalInt();
|
||||
* Number value = optionalInt.or(0.5); // error
|
||||
*
|
||||
* FluentIterable<? extends Number> numbers = getSomeNumbers();
|
||||
* Optional<? extends Number> first = numbers.first();
|
||||
* Number value = first.or(0.5); // error}
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* As a workaround, it is always safe to cast an {@code Optional<? extends T>}
|
||||
* to {@code
|
||||
* Optional<T>}. Casting either of the above example {@code Optional} instances
|
||||
* to {@code
|
||||
* Optional<Number>} (where {@code Number} is the desired output type) solves
|
||||
* the problem:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* Optional<Number> optionalInt = (Optional) getSomeOptionalInt();
|
||||
* Number value = optionalInt.or(0.5); // fine
|
||||
*
|
||||
* FluentIterable<? extends Number> numbers = getSomeNumbers();
|
||||
* Optional<Number> first = (Optional) numbers.first();
|
||||
* Number value = first.or(0.5); // fine}
|
||||
* </pre>
|
||||
*/
|
||||
public abstract T or(T defaultValue);
|
||||
|
||||
/**
|
||||
* Returns this {@code Optional} if it has a value present; {@code secondChoice}
|
||||
* otherwise.
|
||||
*/
|
||||
public abstract Optional<T> or(Optional<? extends T> 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<? extends T> supplier);
|
||||
|
||||
/**
|
||||
* Returns the contained instance if it is present; {@code null} otherwise. If
|
||||
* the instance is known to be present, use {@link #get()} instead.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract T orNull();
|
||||
|
||||
/**
|
||||
* Returns an immutable singleton {@link Set} whose only element is the
|
||||
* contained instance if it is present; an empty immutable {@link Set}
|
||||
* otherwise.
|
||||
*
|
||||
* @since 11.0
|
||||
*/
|
||||
public abstract Set<T> asSet();
|
||||
|
||||
/**
|
||||
* If the instance is present, it is transformed with the given
|
||||
* {@link Function}; otherwise, {@link Optional#absent} is returned. If the
|
||||
* function returns {@code null}, a {@link NullPointerException} is thrown.
|
||||
*
|
||||
* @throws NullPointerException if the function returns {@code null}
|
||||
*
|
||||
* @since 12.0
|
||||
*/
|
||||
public abstract <V> Optional<V> transform(Function<? super T, V> function);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if {@code object} is an {@code Optional} instance, and
|
||||
* either the contained references are {@linkplain Object#equals equal} to each
|
||||
* other or both are absent. Note that {@code Optional} instances of differing
|
||||
* parameterized types can be equal.
|
||||
*/
|
||||
@Override
|
||||
public abstract boolean equals(@Nullable Object object);
|
||||
|
||||
/**
|
||||
* 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 <T> Iterable<T> presentInstances(final Iterable<? extends Optional<? extends T>> optionals) {
|
||||
checkNotNull(optionals);
|
||||
return new Iterable<T>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new AbstractIterator<T>() {
|
||||
private final Iterator<? extends Optional<? extends T>> iterator = checkNotNull(
|
||||
optionals.iterator());
|
||||
|
||||
@Override
|
||||
protected T computeNext() {
|
||||
while (iterator.hasNext()) {
|
||||
Optional<? extends T> optional = iterator.next();
|
||||
if (optional.isPresent()) {
|
||||
return optional.get();
|
||||
}
|
||||
}
|
||||
return endOfData();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
79
src/main/java/com/google/common/base/PairwiseEquivalence.java
Executable file
79
src/main/java/com/google/common/base/PairwiseEquivalence.java
Executable file
|
|
@ -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<T> extends Equivalence<Iterable<T>> implements Serializable {
|
||||
|
||||
final Equivalence<? super T> elementEquivalence;
|
||||
|
||||
PairwiseEquivalence(Equivalence<? super T> elementEquivalence) {
|
||||
this.elementEquivalence = Preconditions.checkNotNull(elementEquivalence);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doEquivalent(Iterable<T> iterableA, Iterable<T> iterableB) {
|
||||
Iterator<T> iteratorA = iterableA.iterator();
|
||||
Iterator<T> 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<T> 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;
|
||||
}
|
||||
46
src/main/java/com/google/common/base/Platform.java
Executable file
46
src/main/java/com/google/common/base/Platform.java
Executable file
|
|
@ -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 <T extends Enum<T>> Optional<T> getEnumIfPresent(Class<T> enumClass, String value) {
|
||||
WeakReference<? extends Enum<?>> ref = Enums.getEnumConstants(enumClass).get(value);
|
||||
return ref == null ? Optional.<T>absent() : Optional.of(enumClass.cast(ref.get()));
|
||||
}
|
||||
}
|
||||
505
src/main/java/com/google/common/base/Preconditions.java
Executable file
505
src/main/java/com/google/common/base/Preconditions.java
Executable file
|
|
@ -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 <i>preconditions</i> 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 <i>its</i> caller that <i>that</i> caller has made a mistake.
|
||||
* Example:
|
||||
*
|
||||
* <pre>
|
||||
* {@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);
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* In this example, {@code checkArgument} throws an
|
||||
* {@code IllegalArgumentException} to indicate that {@code exampleBadCaller}
|
||||
* made an error in <i>its</i> call to {@code sqrt}.
|
||||
*
|
||||
* <h3>Warning about performance</h3>
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* if (value < 0.0) {
|
||||
* throw new IllegalArgumentException("negative value: " + value);
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Other types of preconditions</h3>
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <h3>Non-preconditions</h3>
|
||||
*
|
||||
* <p>
|
||||
* It is of course possible to use the methods of this class to check for
|
||||
* invalid conditions which are <i>not the caller's fault</i>. Doing so is
|
||||
* <b>not recommended</b> because it is misleading to future readers of the code
|
||||
* and of stack traces. See <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/ConditionalFailuresExplained">Conditional
|
||||
* failures explained</a> in the Guava User Guide for more advice.
|
||||
*
|
||||
* <h3>{@code java.util.Objects.requireNonNull()}</h3>
|
||||
*
|
||||
* <p>
|
||||
* 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.)
|
||||
*
|
||||
* <h3>Only {@code %s} is supported</h3>
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <h3>More information</h3>
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/PreconditionsExplained">using
|
||||
* {@code
|
||||
* Preconditions}</a>.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@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> 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> 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> 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 <i>element</i> 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 <i>element</i> 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 <i>position</i> 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 <i>position</i> 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 <i>positions</i>
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
73
src/main/java/com/google/common/base/Predicate.java
Executable file
73
src/main/java/com/google/common/base/Predicate.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* The {@link Predicates} class provides common predicates and related
|
||||
* utilities.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/FunctionalExplained">the use
|
||||
* of {@code
|
||||
* Predicate}</a>.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@GwtCompatible
|
||||
public interface Predicate<T> {
|
||||
/**
|
||||
* Returns the result of applying this predicate to {@code input}. This method
|
||||
* is <i>generally expected</i>, but not absolutely required, to have the
|
||||
* following properties:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Its execution does not cause any observable side effects.
|
||||
* <li>The computation is <i>consistent with equals</i>; that is,
|
||||
* {@link Objects#equal Objects.equal}{@code (a, b)} implies that
|
||||
* {@code predicate.apply(a) ==
|
||||
* predicate.apply(b))}.
|
||||
* </ul>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* <i>interchangeable</i> with this one. "Interchangeable" <i>typically</i>
|
||||
* 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 <i>not</i> to be interchangeable.
|
||||
*/
|
||||
@Override
|
||||
boolean equals(@Nullable Object object);
|
||||
}
|
||||
710
src/main/java/com/google/common/base/Predicates.java
Executable file
710
src/main/java/com/google/common/base/Predicates.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* All methods returns serializable predicates as long as they're given
|
||||
* serializable parameters.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/FunctionalExplained">the use
|
||||
* of {@code Predicate}</a>.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@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 <T> Predicate<T> alwaysTrue() {
|
||||
return ObjectPredicate.ALWAYS_TRUE.withNarrowedType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a predicate that always evaluates to {@code false}.
|
||||
*/
|
||||
@GwtCompatible(serializable = true)
|
||||
public static <T> Predicate<T> 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 <T> Predicate<T> 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 <T> Predicate<T> notNull() {
|
||||
return ObjectPredicate.NOT_NULL.withNarrowedType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a predicate that evaluates to {@code true} if the given predicate
|
||||
* evaluates to {@code false}.
|
||||
*/
|
||||
public static <T> Predicate<T> not(Predicate<T> predicate) {
|
||||
return new NotPredicate<T>(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 <T> Predicate<T> and(Iterable<? extends Predicate<? super T>> components) {
|
||||
return new AndPredicate<T>(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 <T> Predicate<T> and(Predicate<? super T>... components) {
|
||||
return new AndPredicate<T>(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 <T> Predicate<T> and(Predicate<? super T> first, Predicate<? super T> second) {
|
||||
return new AndPredicate<T>(Predicates.<T>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 <T> Predicate<T> or(Iterable<? extends Predicate<? super T>> components) {
|
||||
return new OrPredicate<T>(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 <T> Predicate<T> or(Predicate<? super T>... components) {
|
||||
return new OrPredicate<T>(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 <T> Predicate<T> or(Predicate<? super T> first, Predicate<? super T> second) {
|
||||
return new OrPredicate<T>(Predicates.<T>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 <T> Predicate<T> equalTo(@Nullable T target) {
|
||||
return (target == null) ? Predicates.<T>isNull() : new IsEqualToPredicate<T>(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}.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> contrary to the typical assumptions about predicates (as
|
||||
* documented at {@link Predicate#apply}), the returned predicate may not be
|
||||
* <i>consistent with equals</i>. 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<Object> instanceOf(Class<?> clazz) {
|
||||
return new InstanceOfPredicate(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a predicate that evaluates to {@code true} if the class being tested
|
||||
* is assignable from the given class. The returned predicate does not allow
|
||||
* null inputs.
|
||||
*
|
||||
* @since 10.0
|
||||
*/
|
||||
@GwtIncompatible("Class.isAssignableFrom")
|
||||
@Beta
|
||||
public static Predicate<Class<?>> 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.<Object>in()}.
|
||||
*
|
||||
* @param target the collection that may contain the function input
|
||||
*/
|
||||
public static <T> Predicate<T> in(Collection<? extends T> target) {
|
||||
return new InPredicate<T>(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 <A, B> Predicate<A> compose(Predicate<B> predicate, Function<A, ? extends B> function) {
|
||||
return new CompositionPredicate<A, B>(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<CharSequence> 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<CharSequence> contains(Pattern pattern) {
|
||||
return new ContainsPatternPredicate(pattern);
|
||||
}
|
||||
|
||||
// End public API, begin private implementation classes.
|
||||
|
||||
// Package private for GWT serialization.
|
||||
enum ObjectPredicate implements Predicate<Object> {
|
||||
/** @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
|
||||
<T> Predicate<T> withNarrowedType() {
|
||||
return (Predicate<T>) this;
|
||||
}
|
||||
}
|
||||
|
||||
/** @see Predicates#not(Predicate) */
|
||||
private static class NotPredicate<T> implements Predicate<T>, Serializable {
|
||||
final Predicate<T> predicate;
|
||||
|
||||
NotPredicate(Predicate<T> 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<T> implements Predicate<T>, Serializable {
|
||||
private final List<? extends Predicate<? super T>> components;
|
||||
|
||||
private AndPredicate(List<? extends Predicate<? super T>> 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<T> implements Predicate<T>, Serializable {
|
||||
private final List<? extends Predicate<? super T>> components;
|
||||
|
||||
private OrPredicate(List<? extends Predicate<? super T>> 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<T> implements Predicate<T>, 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<Object>, 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<Class<?>>, 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<T> implements Predicate<T>, 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<A, B> implements Predicate<A>, Serializable {
|
||||
final Predicate<B> p;
|
||||
final Function<A, ? extends B> f;
|
||||
|
||||
private CompositionPredicate(Predicate<B> p, Function<A, ? extends B> 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<CharSequence>, 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 <T> List<Predicate<? super T>> asList(Predicate<? super T> first, Predicate<? super T> second) {
|
||||
// TODO(kevinb): understand why we still get a warning despite @SafeVarargs!
|
||||
return Arrays.<Predicate<? super T>>asList(first, second);
|
||||
}
|
||||
|
||||
private static <T> List<T> defensiveCopy(T... array) {
|
||||
return defensiveCopy(Arrays.asList(array));
|
||||
}
|
||||
|
||||
static <T> List<T> defensiveCopy(Iterable<T> iterable) {
|
||||
ArrayList<T> list = new ArrayList<T>();
|
||||
for (T element : iterable) {
|
||||
list.add(checkNotNull(element));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
103
src/main/java/com/google/common/base/Present.java
Executable file
103
src/main/java/com/google/common/base/Present.java
Executable file
|
|
@ -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<T> extends Optional<T> {
|
||||
private final T reference;
|
||||
|
||||
Present(T reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T or(T defaultValue) {
|
||||
checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)");
|
||||
return reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> or(Optional<? extends T> secondChoice) {
|
||||
checkNotNull(secondChoice);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T or(Supplier<? extends T> supplier) {
|
||||
checkNotNull(supplier);
|
||||
return reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T orNull() {
|
||||
return reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> asSet() {
|
||||
return Collections.singleton(reference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Optional<V> transform(Function<? super T, V> function) {
|
||||
return new Present<V>(checkNotNull(function.apply(reference),
|
||||
"the Function passed to Optional.transform() must not return null."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
if (object instanceof Present) {
|
||||
Present<?> other = (Present<?>) object;
|
||||
return reference.equals(other.reference);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0x598df91c + reference.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Optional.of(" + reference + ")";
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
155
src/main/java/com/google/common/base/SmallCharMatcher.java
Executable file
155
src/main/java/com/google/common/base/SmallCharMatcher.java
Executable file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
650
src/main/java/com/google/common/base/Splitter.java
Executable file
650
src/main/java/com/google/common/base/Splitter.java
Executable file
|
|
@ -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 <i>separator</i> sequence. This separator can be
|
||||
* specified as a single {@linkplain #on(char) character}, fixed
|
||||
* {@linkplain #on(String) string}, {@linkplain #onPattern regular expression}
|
||||
* or {@link #on(CharMatcher) CharMatcher} instance. Or, instead of using a
|
||||
* separator at all, a splitter can extract adjacent substrings of a given
|
||||
* {@linkplain #fixedLength fixed length}.
|
||||
*
|
||||
* <p>
|
||||
* For example, this expression:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* Splitter.on(',').split("foo,bar,qux")}
|
||||
* </pre>
|
||||
*
|
||||
* ... produces an {@code Iterable} containing {@code "foo"}, {@code "bar"} and
|
||||
* {@code "qux"}, in that order.
|
||||
*
|
||||
* <p>
|
||||
* By default, {@code Splitter}'s behavior is simplistic and unassuming. The
|
||||
* following expression:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* Splitter.on(',').split(" foo,,, bar ,")}
|
||||
* </pre>
|
||||
*
|
||||
* ... yields the substrings {@code [" foo", "", "", " bar ", ""]}. If this is
|
||||
* not the desired behavior, use configuration methods to obtain a <i>new</i>
|
||||
* splitter instance with modified behavior:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* @code
|
||||
*
|
||||
* private static final Splitter MY_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> Splitter instances are immutable. Invoking a configuration
|
||||
* method has no effect on the receiving instance; you must store and use the
|
||||
* new splitter instance it returns instead.
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* @code
|
||||
*
|
||||
* // Do NOT do this
|
||||
* Splitter splitter = Splitter.on('/');
|
||||
* splitter.trimResults(); // does nothing!
|
||||
* return splitter.split("wrong / wrong / wrong");
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* For separator-based splitters that do not use {@code omitEmptyStrings}, an
|
||||
* input string containing {@code n} occurrences of the separator naturally
|
||||
* yields an iterable of size {@code n + 1}. So if the separator does not occur
|
||||
* anywhere in the input, a single substring is returned containing the entire
|
||||
* input. Consequently, all splitters split the empty string to {@code [""]}
|
||||
* (note: even fixed-length splitters).
|
||||
*
|
||||
* <p>
|
||||
* Splitter instances are thread-safe immutable, and are therefore safe to store
|
||||
* as {@code static final} constants.
|
||||
*
|
||||
* <p>
|
||||
* The {@link Joiner} class provides the inverse operation to splitting, but
|
||||
* note that a round-trip between the two should be assumed to be lossy.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/StringsExplained#Splitter">
|
||||
* {@code Splitter}</a>.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>
|
||||
* <b>Exception:</b> for consistency with separator-based splitters, {@code
|
||||
* split("")} does not yield an empty iterable, but an iterable containing
|
||||
* {@code ""}. This is the only case in which {@code
|
||||
* Iterables.size(split(input))} does not equal {@code
|
||||
* IntMath.divide(input.length(), length, CEILING)}. To avoid this behavior, use
|
||||
* {@code omitEmptyStrings}.
|
||||
*
|
||||
* @param length the desired length of pieces after splitting, a positive
|
||||
* integer
|
||||
* @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"]}.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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<String> split(final CharSequence sequence) {
|
||||
checkNotNull(sequence);
|
||||
|
||||
return new Iterable<String>() {
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return splittingIterator(sequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Joiner.on(", ").appendTo(new StringBuilder().append('['), this).append(']').toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Iterator<String> 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<String> splitToList(CharSequence sequence) {
|
||||
checkNotNull(sequence);
|
||||
|
||||
Iterator<String> iterator = splittingIterator(sequence);
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
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, <code>
|
||||
* Splitter.on(';').trimResults().withKeyValueSeparator("=>")
|
||||
* .split("a=>b ; c=>b")
|
||||
* </code> will return a mapping from {@code "a"} to {@code "b"} and {@code "c"}
|
||||
* to {@code b}.
|
||||
*
|
||||
* <p>
|
||||
* 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<String, String> split(CharSequence sequence) {
|
||||
Map<String, String> map = new LinkedHashMap<String, String>();
|
||||
for (String entry : outerSplitter.split(sequence)) {
|
||||
Iterator<String> 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<String> iterator(Splitter splitter, CharSequence toSplit);
|
||||
}
|
||||
|
||||
private abstract static class SplittingIterator extends AbstractIterator<String> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
144
src/main/java/com/google/common/base/StandardSystemProperty.java
Executable file
144
src/main/java/com/google/common/base/StandardSystemProperty.java
Executable file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
240
src/main/java/com/google/common/base/Strings.java
Executable file
240
src/main/java/com/google/common/base/Strings.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* 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,
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code padStart("7", 3, '0')} returns {@code "007"}
|
||||
* <li>{@code padStart("2010", 3, '0')} returns {@code "2010"}
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* 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,
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code padEnd("4.", 5, '0')} returns {@code "4.000"}
|
||||
* <li>{@code padEnd("2010", 3, '!')} returns {@code "2010"}
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
38
src/main/java/com/google/common/base/Supplier.java
Executable file
38
src/main/java/com/google/common/base/Supplier.java
Executable file
|
|
@ -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<T> {
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
322
src/main/java/com/google/common/base/Suppliers.java
Executable file
322
src/main/java/com/google/common/base/Suppliers.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* 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 <F, T> Supplier<T> compose(Function<? super F, T> function, Supplier<F> supplier) {
|
||||
Preconditions.checkNotNull(function);
|
||||
Preconditions.checkNotNull(supplier);
|
||||
return new SupplierComposition<F, T>(function, supplier);
|
||||
}
|
||||
|
||||
private static class SupplierComposition<F, T> implements Supplier<T>, Serializable {
|
||||
final Function<? super F, T> function;
|
||||
final Supplier<F> supplier;
|
||||
|
||||
SupplierComposition(Function<? super F, T> function, Supplier<F> 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: <a href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* If {@code delegate} is an instance created by an earlier call to {@code
|
||||
* memoize}, it is returned directly.
|
||||
*/
|
||||
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
|
||||
return (delegate instanceof MemoizingSupplier) ? delegate
|
||||
: new MemoizingSupplier<T>(Preconditions.checkNotNull(delegate));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class MemoizingSupplier<T> implements Supplier<T>, Serializable {
|
||||
final Supplier<T> 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<T> 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:
|
||||
* <a href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
|
||||
*
|
||||
* <p>
|
||||
* 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 <T> Supplier<T> memoizeWithExpiration(Supplier<T> delegate, long duration, TimeUnit unit) {
|
||||
return new ExpiringMemoizingSupplier<T>(delegate, duration, unit);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class ExpiringMemoizingSupplier<T> implements Supplier<T>, Serializable {
|
||||
final Supplier<T> delegate;
|
||||
final long durationNanos;
|
||||
transient volatile T value;
|
||||
// The special value 0 means "not yet initialized".
|
||||
transient volatile long expirationNanos;
|
||||
|
||||
ExpiringMemoizingSupplier(Supplier<T> 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 <T> Supplier<T> ofInstance(@Nullable T instance) {
|
||||
return new SupplierOfInstance<T>(instance);
|
||||
}
|
||||
|
||||
private static class SupplierOfInstance<T> implements Supplier<T>, 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 <T> Supplier<T> synchronizedSupplier(Supplier<T> delegate) {
|
||||
return new ThreadSafeSupplier<T>(Preconditions.checkNotNull(delegate));
|
||||
}
|
||||
|
||||
private static class ThreadSafeSupplier<T> implements Supplier<T>, Serializable {
|
||||
final Supplier<T> delegate;
|
||||
|
||||
ThreadSafeSupplier(Supplier<T> 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 <T> Function<Supplier<T>, T> supplierFunction() {
|
||||
@SuppressWarnings("unchecked") // implementation is "fully variant"
|
||||
SupplierFunction<T> sf = (SupplierFunction<T>) SupplierFunctionImpl.INSTANCE;
|
||||
return sf;
|
||||
}
|
||||
|
||||
private interface SupplierFunction<T> extends Function<Supplier<T>, T> {
|
||||
}
|
||||
|
||||
private enum SupplierFunctionImpl implements SupplierFunction<Object> {
|
||||
INSTANCE;
|
||||
|
||||
// Note: This makes T a "pass-through type"
|
||||
@Override
|
||||
public Object apply(Supplier<Object> input) {
|
||||
return input.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Suppliers.supplierFunction()";
|
||||
}
|
||||
}
|
||||
}
|
||||
226
src/main/java/com/google/common/base/Throwables.java
Executable file
226
src/main/java/com/google/common/base/Throwables.java
Executable file
|
|
@ -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}.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide entry on
|
||||
* <a href= "http://code.google.com/p/guava-libraries/wiki/ThrowablesExplained">
|
||||
* Throwables</a>.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @author Ben Yu
|
||||
* @since 1.0
|
||||
*/
|
||||
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:
|
||||
*
|
||||
* <pre>
|
||||
* try {
|
||||
* someMethodThatCouldThrowAnything();
|
||||
* } catch (IKnowWhatToDoWithThisException e) {
|
||||
* handle(e);
|
||||
* } catch (Throwable t) {
|
||||
* Throwables.propagateIfInstanceOf(t, IOException.class);
|
||||
* Throwables.propagateIfInstanceOf(t, SQLException.class);
|
||||
* throw Throwables.propagate(t);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public static <X extends Throwable> void propagateIfInstanceOf(@Nullable Throwable throwable, Class<X> 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:
|
||||
*
|
||||
* <pre>
|
||||
* try {
|
||||
* someMethodThatCouldThrowAnything();
|
||||
* } catch (IKnowWhatToDoWithThisException e) {
|
||||
* handle(e);
|
||||
* } catch (Throwable t) {
|
||||
* Throwables.propagateIfPossible(t);
|
||||
* throw new RuntimeException("unexpected", t);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
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:
|
||||
*
|
||||
* <pre>
|
||||
* try {
|
||||
* someMethodThatCouldThrowAnything();
|
||||
* } catch (IKnowWhatToDoWithThisException e) {
|
||||
* handle(e);
|
||||
* } catch (Throwable t) {
|
||||
* Throwables.propagateIfPossible(t, OtherException.class);
|
||||
* throw new RuntimeException("unexpected", t);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param throwable the Throwable to possibly propagate
|
||||
* @param declaredType the single checked exception type declared by the calling
|
||||
* method
|
||||
*/
|
||||
public static <X extends Throwable> void propagateIfPossible(@Nullable Throwable throwable, Class<X> 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 <X1 extends Throwable, X2 extends Throwable> void propagateIfPossible(@Nullable Throwable throwable,
|
||||
Class<X1> declaredType1, Class<X2> 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.
|
||||
* <p>
|
||||
* 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:
|
||||
*
|
||||
* <pre>
|
||||
* T doSomething() {
|
||||
* try {
|
||||
* return someMethodThatCouldThrowAnything();
|
||||
* } catch (IKnowWhatToDoWithThisException e) {
|
||||
* return handle(e);
|
||||
* } catch (Throwable t) {
|
||||
* throw Throwables.propagate(t);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @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:
|
||||
*
|
||||
* <pre>
|
||||
* assertEquals("Unable to assign a customer id", Throwables.getRootCause(e).getMessage());
|
||||
* </pre>
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* Here's an example of how it can be used to find specific types of exceptions
|
||||
* in the cause chain:
|
||||
*
|
||||
* <pre>
|
||||
* Iterables.filter(Throwables.getCausalChain(e), IOException.class));
|
||||
* </pre>
|
||||
*
|
||||
* @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<Throwable> getCausalChain(Throwable throwable) {
|
||||
checkNotNull(throwable);
|
||||
List<Throwable> causes = new ArrayList<Throwable>(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);
|
||||
}
|
||||
}
|
||||
66
src/main/java/com/google/common/base/Ticker.java
Executable file
66
src/main/java/com/google/common/base/Ticker.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> this interface can only be used to measure elapsed time, not
|
||||
* wall time.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @since 10.0
|
||||
* (<a href="http://code.google.com/p/guava-libraries/wiki/Compatibility"
|
||||
* >mostly source-compatible</a> 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();
|
||||
}
|
||||
};
|
||||
}
|
||||
206
src/main/java/com/google/common/base/Utf8.java
Executable file
206
src/main/java/com/google/common/base/Utf8.java
Executable file
|
|
@ -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
|
||||
* <a href="http://www.unicode.org/versions/Unicode6.2.0/ch03.pdf">The Unicode
|
||||
* Standard Core Specification, Chapter 3</a>.
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
* <a href="http://www.unicode.org/versions/corrigendum1.html">"non-shortest
|
||||
* form"</a> 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 <i>well-formed</i> 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 <i>not</i> considered
|
||||
* well-formed.
|
||||
*
|
||||
* <p>
|
||||
* 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() {
|
||||
}
|
||||
}
|
||||
187
src/main/java/com/google/common/base/Verify.java
Executable file
187
src/main/java/com/google/common/base/Verify.java
Executable file
|
|
@ -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
|
||||
* <a href=
|
||||
* "http://docs.oracle.com/javase/7/docs/technotes/guides/language/assert.html">
|
||||
* assertions</a>, 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:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* @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());
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Comparison to alternatives</h3>
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b> In some cases the differences explained below can be subtle.
|
||||
* When it's unclear which approach to use, <b>don't worry</b> too much about
|
||||
* it; just pick something that seems reasonable and it will be fine.
|
||||
*
|
||||
* <ul>
|
||||
* <li>If checking whether the <i>caller</i> has violated your method or
|
||||
* constructor's contract (such as by passing an invalid argument), use the
|
||||
* utilities of the {@link Preconditions} class instead.
|
||||
*
|
||||
* <li>If checking an <i>impossible</i> condition (which <i>cannot</i> happen
|
||||
* unless your own class or its <i>trusted</i> 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."
|
||||
*
|
||||
* <li>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.
|
||||
*
|
||||
* <li>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.
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Warning about performance</h3>
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* @code
|
||||
*
|
||||
* Bill bill = remoteService.getLastUnpaidBill();
|
||||
* if (bill.status() != Status.UNPAID) {
|
||||
* throw new VerifyException("Unexpected bill status: " + bill.status());
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Only {@code %s} is supported</h3>
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <h3>More information</h3>
|
||||
*
|
||||
* See <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/ConditionalFailuresExplained">Conditional
|
||||
* failures explained</a> 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> 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> T verifyNotNull(@Nullable T reference, @Nullable String errorMessageTemplate,
|
||||
@Nullable Object... errorMessageArgs) {
|
||||
verify(reference != null, errorMessageTemplate, errorMessageArgs);
|
||||
return reference;
|
||||
}
|
||||
|
||||
// TODO(kevinb): consider <T> T verifySingleton(Iterable<T>) to take over for
|
||||
// Iterables.getOnlyElement()
|
||||
|
||||
private Verify() {
|
||||
}
|
||||
}
|
||||
41
src/main/java/com/google/common/base/VerifyException.java
Executable file
41
src/main/java/com/google/common/base/VerifyException.java
Executable file
|
|
@ -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 <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/ConditionalFailuresExplained">verification
|
||||
* check</a>, 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);
|
||||
}
|
||||
}
|
||||
67
src/main/java/com/google/common/base/package-info.java
Executable file
67
src/main/java/com/google/common/base/package-info.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* This package is a part of the open-source
|
||||
* <a href="http://guava-libraries.googlecode.com">Guava libraries</a>.
|
||||
*
|
||||
* <h2>Contents</h2>
|
||||
*
|
||||
* <h3>String-related utilities</h3>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link com.google.common.base.Ascii}
|
||||
* <li>{@link com.google.common.base.CaseFormat}
|
||||
* <li>{@link com.google.common.base.CharMatcher}
|
||||
* <li>{@link com.google.common.base.Charsets}
|
||||
* <li>{@link com.google.common.base.Joiner}
|
||||
* <li>{@link com.google.common.base.Splitter}
|
||||
* <li>{@link com.google.common.base.Strings}
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Function types</h3>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link com.google.common.base.Function},
|
||||
* {@link com.google.common.base.Functions}
|
||||
* <li>{@link com.google.common.base.Predicate},
|
||||
* {@link com.google.common.base.Predicates}
|
||||
* <li>{@link com.google.common.base.Equivalence}
|
||||
* <li>{@link com.google.common.base.Converter}
|
||||
* <li>{@link com.google.common.base.Supplier},
|
||||
* {@link com.google.common.base.Suppliers}
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Other</h3>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link com.google.common.base.Defaults}
|
||||
* <li>{@link com.google.common.base.Enums}
|
||||
* <li>{@link com.google.common.base.Objects}
|
||||
* <li>{@link com.google.common.base.Optional}
|
||||
* <li>{@link com.google.common.base.Preconditions}
|
||||
* <li>{@link com.google.common.base.Stopwatch}
|
||||
* <li>{@link com.google.common.base.Throwables}
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
package com.google.common.base;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
441
src/main/java/com/google/common/collect/AbstractBiMap.java
Executable file
441
src/main/java/com/google/common/collect/AbstractBiMap.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* 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<K, V> extends ForwardingMap<K, V> implements BiMap<K, V>, Serializable {
|
||||
|
||||
private transient Map<K, V> delegate;
|
||||
transient AbstractBiMap<V, K> inverse;
|
||||
|
||||
/** Package-private constructor for creating a map-backed bimap. */
|
||||
AbstractBiMap(Map<K, V> forward, Map<V, K> backward) {
|
||||
setDelegates(forward, backward);
|
||||
}
|
||||
|
||||
/** Private constructor for inverse bimap. */
|
||||
private AbstractBiMap(Map<K, V> backward, AbstractBiMap<V, K> forward) {
|
||||
delegate = backward;
|
||||
inverse = forward;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<K, V> 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<K, V> forward, Map<V, K> backward) {
|
||||
checkState(delegate == null);
|
||||
checkState(inverse == null);
|
||||
checkArgument(forward.isEmpty());
|
||||
checkArgument(backward.isEmpty());
|
||||
checkArgument(forward != backward);
|
||||
delegate = forward;
|
||||
inverse = new Inverse<V, K>(backward, this);
|
||||
}
|
||||
|
||||
void setInverse(AbstractBiMap<V, K> 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<? extends K, ? extends V> map) {
|
||||
for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
delegate.clear();
|
||||
inverse.delegate.clear();
|
||||
}
|
||||
|
||||
// Views
|
||||
|
||||
@Override
|
||||
public BiMap<V, K> inverse() {
|
||||
return inverse;
|
||||
}
|
||||
|
||||
private transient Set<K> keySet;
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
Set<K> result = keySet;
|
||||
return (result == null) ? keySet = new KeySet() : result;
|
||||
}
|
||||
|
||||
private class KeySet extends ForwardingSet<K> {
|
||||
@Override
|
||||
protected Set<K> 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<K> iterator() {
|
||||
return Maps.keyIterator(entrySet().iterator());
|
||||
}
|
||||
}
|
||||
|
||||
private transient Set<V> valueSet;
|
||||
|
||||
@Override
|
||||
public Set<V> 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<V> result = valueSet;
|
||||
return (result == null) ? valueSet = new ValueSet() : result;
|
||||
}
|
||||
|
||||
private class ValueSet extends ForwardingSet<V> {
|
||||
final Set<V> valuesDelegate = inverse.keySet();
|
||||
|
||||
@Override
|
||||
protected Set<V> delegate() {
|
||||
return valuesDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return Maps.valueIterator(entrySet().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return standardToArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] array) {
|
||||
return standardToArray(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return standardToString();
|
||||
}
|
||||
}
|
||||
|
||||
private transient Set<Entry<K, V>> entrySet;
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
Set<Entry<K, V>> result = entrySet;
|
||||
return (result == null) ? entrySet = new EntrySet() : result;
|
||||
}
|
||||
|
||||
private class EntrySet extends ForwardingSet<Entry<K, V>> {
|
||||
final Set<Entry<K, V>> esDelegate = delegate.entrySet();
|
||||
|
||||
@Override
|
||||
protected Set<Entry<K, V>> 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<Entry<K, V>> iterator() {
|
||||
final Iterator<Entry<K, V>> iterator = esDelegate.iterator();
|
||||
return new Iterator<Entry<K, V>>() {
|
||||
Entry<K, V> entry;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<K, V> next() {
|
||||
entry = iterator.next();
|
||||
final Entry<K, V> finalEntry = entry;
|
||||
|
||||
return new ForwardingMapEntry<K, V>() {
|
||||
@Override
|
||||
protected Entry<K, V> 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> 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<K, V> extends AbstractBiMap<K, V> {
|
||||
private Inverse(Map<K, V> backward, AbstractBiMap<V, K> 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<V, K>) 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;
|
||||
}
|
||||
108
src/main/java/com/google/common/collect/AbstractIndexedListIterator.java
Executable file
108
src/main/java/com/google/common/collect/AbstractIndexedListIterator.java
Executable file
|
|
@ -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<E> extends UnmodifiableListIterator<E> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
189
src/main/java/com/google/common/collect/AbstractIterator.java
Executable file
189
src/main/java/com/google/common/collect/AbstractIterator.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* {@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.
|
||||
*
|
||||
* <p>
|
||||
* Another example is an iterator that skips over null elements in a backing
|
||||
* iterator. This could be implemented as:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
*
|
||||
* public static Iterator<String> skipNulls(final Iterator<String> in) {
|
||||
* return new AbstractIterator<String>() {
|
||||
* protected String computeNext() {
|
||||
* while (in.hasNext()) {
|
||||
* String s = in.next();
|
||||
* if (s != null) {
|
||||
* return s;
|
||||
* }
|
||||
* }
|
||||
* return endOfData();
|
||||
* }
|
||||
* };
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 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<T> extends UnmodifiableIterator<T> {
|
||||
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. <b>Note:</b> 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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}.
|
||||
*
|
||||
* <p>
|
||||
* 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} <b>must</b> 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()}.
|
||||
*
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
136
src/main/java/com/google/common/collect/AbstractListMultimap.java
Executable file
136
src/main/java/com/google/common/collect/AbstractListMultimap.java
Executable file
|
|
@ -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<K, V> extends AbstractMapBasedMultimap<K, V> implements ListMultimap<K, V> {
|
||||
/**
|
||||
* 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<K, Collection<V>> map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract List<V> createCollection();
|
||||
|
||||
@Override
|
||||
List<V> createUnmodifiableEmptyCollection() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
// Following Javadoc copied from ListMultimap.
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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<V> get(@Nullable K key) {
|
||||
return (List<V>) super.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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<V> removeAll(@Nullable Object key) {
|
||||
return (List<V>) super.removeAll(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
|
||||
return (List<V>) 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}
|
||||
*
|
||||
* <p>
|
||||
* Though the method signature doesn't say so explicitly, the returned map has
|
||||
* {@link List} values.
|
||||
*/
|
||||
@Override
|
||||
public Map<K, Collection<V>> asMap() {
|
||||
return super.asMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the specified object to this multimap for equality.
|
||||
*
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
1591
src/main/java/com/google/common/collect/AbstractMapBasedMultimap.java
Executable file
1591
src/main/java/com/google/common/collect/AbstractMapBasedMultimap.java
Executable file
File diff suppressed because it is too large
Load diff
305
src/main/java/com/google/common/collect/AbstractMapBasedMultiset.java
Executable file
305
src/main/java/com/google/common/collect/AbstractMapBasedMultiset.java
Executable file
|
|
@ -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<E>} backed by an instance of {@code
|
||||
* Map<E, Count>}.
|
||||
*
|
||||
* <p>
|
||||
* For serialization to work, the subclass must specify explicit {@code
|
||||
* readObject} and {@code writeObject} methods.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
*/
|
||||
@GwtCompatible(emulated = true)
|
||||
abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> implements Serializable {
|
||||
|
||||
private transient Map<E, Count> 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<E, Count> backingMap) {
|
||||
this.backingMap = checkNotNull(backingMap);
|
||||
this.size = super.size();
|
||||
}
|
||||
|
||||
/** Used during deserialization only. The backing map must be empty. */
|
||||
void setBackingMap(Map<E, Count> backingMap) {
|
||||
this.backingMap = backingMap;
|
||||
}
|
||||
|
||||
// Required Implementations
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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<Multiset.Entry<E>> entrySet() {
|
||||
return super.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
Iterator<Entry<E>> entryIterator() {
|
||||
final Iterator<Map.Entry<E, Count>> backingEntries = backingMap.entrySet().iterator();
|
||||
return new Iterator<Multiset.Entry<E>>() {
|
||||
Map.Entry<E, Count> toRemove;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return backingEntries.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Multiset.Entry<E> next() {
|
||||
final Map.Entry<E, Count> mapEntry = backingEntries.next();
|
||||
toRemove = mapEntry;
|
||||
return new Multisets.AbstractEntry<E>() {
|
||||
@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<E> iterator() {
|
||||
return new MapBasedMultisetIterator();
|
||||
}
|
||||
|
||||
/*
|
||||
* Not subclassing AbstractMultiset$MultisetIterator because next() needs to
|
||||
* retrieve the Map.Entry<E, Count> entry, which can then be used for a more
|
||||
* efficient remove() call.
|
||||
*/
|
||||
private class MapBasedMultisetIterator implements Iterator<E> {
|
||||
final Iterator<Map.Entry<E, Count>> entryIterator;
|
||||
Map.Entry<E, Count> 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;
|
||||
}
|
||||
69
src/main/java/com/google/common/collect/AbstractMapEntry.java
Executable file
69
src/main/java/com/google/common/collect/AbstractMapEntry.java
Executable file
|
|
@ -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<K, V> implements Entry<K, V> {
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
249
src/main/java/com/google/common/collect/AbstractMultimap.java
Executable file
249
src/main/java/com/google/common/collect/AbstractMultimap.java
Executable file
|
|
@ -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<K, V> implements Multimap<K, V> {
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(@Nullable Object value) {
|
||||
for (Collection<V> collection : asMap().values()) {
|
||||
if (collection.contains(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntry(@Nullable Object key, @Nullable Object value) {
|
||||
Collection<V> collection = asMap().get(key);
|
||||
return collection != null && collection.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(@Nullable Object key, @Nullable Object value) {
|
||||
Collection<V> 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<? extends V> 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<? extends V> valueCollection = (Collection<? extends V>) values;
|
||||
return !valueCollection.isEmpty() && get(key).addAll(valueCollection);
|
||||
} else {
|
||||
Iterator<? extends V> valueItr = values.iterator();
|
||||
return valueItr.hasNext() && Iterators.addAll(get(key), valueItr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean putAll(Multimap<? extends K, ? extends V> multimap) {
|
||||
boolean changed = false;
|
||||
for (Map.Entry<? extends K, ? extends V> entry : multimap.entries()) {
|
||||
changed |= put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
|
||||
checkNotNull(values);
|
||||
Collection<V> result = removeAll(key);
|
||||
putAll(key, values);
|
||||
return result;
|
||||
}
|
||||
|
||||
private transient Collection<Entry<K, V>> entries;
|
||||
|
||||
@Override
|
||||
public Collection<Entry<K, V>> entries() {
|
||||
Collection<Entry<K, V>> result = entries;
|
||||
return (result == null) ? entries = createEntries() : result;
|
||||
}
|
||||
|
||||
Collection<Entry<K, V>> createEntries() {
|
||||
if (this instanceof SetMultimap) {
|
||||
return new EntrySet();
|
||||
} else {
|
||||
return new Entries();
|
||||
}
|
||||
}
|
||||
|
||||
private class Entries extends Multimaps.Entries<K, V> {
|
||||
@Override
|
||||
Multimap<K, V> multimap() {
|
||||
return AbstractMultimap.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
return entryIterator();
|
||||
}
|
||||
}
|
||||
|
||||
private class EntrySet extends Entries implements Set<Entry<K, V>> {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Sets.hashCodeImpl(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return Sets.equalsImpl(this, obj);
|
||||
}
|
||||
}
|
||||
|
||||
abstract Iterator<Entry<K, V>> entryIterator();
|
||||
|
||||
private transient Set<K> keySet;
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
Set<K> result = keySet;
|
||||
return (result == null) ? keySet = createKeySet() : result;
|
||||
}
|
||||
|
||||
Set<K> createKeySet() {
|
||||
return new Maps.KeySet<K, Collection<V>>(asMap());
|
||||
}
|
||||
|
||||
private transient Multiset<K> keys;
|
||||
|
||||
@Override
|
||||
public Multiset<K> keys() {
|
||||
Multiset<K> result = keys;
|
||||
return (result == null) ? keys = createKeys() : result;
|
||||
}
|
||||
|
||||
Multiset<K> createKeys() {
|
||||
return new Multimaps.Keys<K, V>(this);
|
||||
}
|
||||
|
||||
private transient Collection<V> values;
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
Collection<V> result = values;
|
||||
return (result == null) ? values = createValues() : result;
|
||||
}
|
||||
|
||||
Collection<V> createValues() {
|
||||
return new Values();
|
||||
}
|
||||
|
||||
class Values extends AbstractCollection<V> {
|
||||
@Override
|
||||
public Iterator<V> 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<V> valueIterator() {
|
||||
return Maps.valueIterator(entries().iterator());
|
||||
}
|
||||
|
||||
private transient Map<K, Collection<V>> asMap;
|
||||
|
||||
@Override
|
||||
public Map<K, Collection<V>> asMap() {
|
||||
Map<K, Collection<V>> result = asMap;
|
||||
return (result == null) ? asMap = createAsMap() : result;
|
||||
}
|
||||
|
||||
abstract Map<K, Collection<V>> createAsMap();
|
||||
|
||||
// Comparison and hashing
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
return Multimaps.equalsImpl(this, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash code for this multimap.
|
||||
*
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
241
src/main/java/com/google/common/collect/AbstractMultiset.java
Executable file
241
src/main/java/com/google/common/collect/AbstractMultiset.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* The {@link #count} and {@link #size} implementations all iterate across the
|
||||
* set returned by {@link Multiset#entrySet()}, as do many methods acting on the
|
||||
* set returned by {@link #elementSet()}. Override those methods for better
|
||||
* performance.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @author Louis Wasserman
|
||||
*/
|
||||
@GwtCompatible
|
||||
abstract class AbstractMultiset<E> extends AbstractCollection<E> implements Multiset<E> {
|
||||
// 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<E> iterator() {
|
||||
return Multisets.iteratorImpl(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count(@Nullable Object element) {
|
||||
for (Entry<E> 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}
|
||||
*
|
||||
* <p>
|
||||
* This implementation is highly efficient when {@code elementsToAdd} is itself
|
||||
* a {@link Multiset}.
|
||||
*/
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends E> 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<E> elementSet;
|
||||
|
||||
@Override
|
||||
public Set<E> elementSet() {
|
||||
Set<E> 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<E> createElementSet() {
|
||||
return new ElementSet();
|
||||
}
|
||||
|
||||
class ElementSet extends Multisets.ElementSet<E> {
|
||||
@Override
|
||||
Multiset<E> multiset() {
|
||||
return AbstractMultiset.this;
|
||||
}
|
||||
}
|
||||
|
||||
abstract Iterator<Entry<E>> entryIterator();
|
||||
|
||||
abstract int distinctElements();
|
||||
|
||||
private transient Set<Entry<E>> entrySet;
|
||||
|
||||
@Override
|
||||
public Set<Entry<E>> entrySet() {
|
||||
Set<Entry<E>> result = entrySet;
|
||||
return (result == null) ? entrySet = createEntrySet() : result;
|
||||
}
|
||||
|
||||
class EntrySet extends Multisets.EntrySet<E> {
|
||||
@Override
|
||||
Multiset<E> multiset() {
|
||||
return AbstractMultiset.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<E>> iterator() {
|
||||
return entryIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return distinctElements();
|
||||
}
|
||||
}
|
||||
|
||||
Set<Entry<E>> createEntrySet() {
|
||||
return new EntrySet();
|
||||
}
|
||||
|
||||
// Object methods
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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}
|
||||
*
|
||||
* <p>
|
||||
* This implementation returns the hash code of {@link Multiset#entrySet()}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return entrySet().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* This implementation returns the result of invoking {@code toString} on
|
||||
* {@link Multiset#entrySet()}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return entrySet().toString();
|
||||
}
|
||||
}
|
||||
198
src/main/java/com/google/common/collect/AbstractNavigableMap.java
Executable file
198
src/main/java/com/google/common/collect/AbstractNavigableMap.java
Executable file
|
|
@ -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<K, V> extends AbstractMap<K, V> implements NavigableMap<K, V> {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public abstract V get(@Nullable Object key);
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entry<K, V> firstEntry() {
|
||||
return Iterators.getNext(entryIterator(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entry<K, V> lastEntry() {
|
||||
return Iterators.getNext(descendingEntryIterator(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entry<K, V> pollFirstEntry() {
|
||||
return Iterators.pollNext(entryIterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entry<K, V> pollLastEntry() {
|
||||
return Iterators.pollNext(descendingEntryIterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public K firstKey() {
|
||||
Entry<K, V> entry = firstEntry();
|
||||
if (entry == null) {
|
||||
throw new NoSuchElementException();
|
||||
} else {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public K lastKey() {
|
||||
Entry<K, V> entry = lastEntry();
|
||||
if (entry == null) {
|
||||
throw new NoSuchElementException();
|
||||
} else {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entry<K, V> lowerEntry(K key) {
|
||||
return headMap(key, false).lastEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entry<K, V> floorEntry(K key) {
|
||||
return headMap(key, true).lastEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entry<K, V> ceilingEntry(K key) {
|
||||
return tailMap(key, true).firstEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Entry<K, V> higherEntry(K key) {
|
||||
return tailMap(key, false).firstEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K lowerKey(K key) {
|
||||
return Maps.keyOrNull(lowerEntry(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public K floorKey(K key) {
|
||||
return Maps.keyOrNull(floorEntry(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public K ceilingKey(K key) {
|
||||
return Maps.keyOrNull(ceilingEntry(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public K higherKey(K key) {
|
||||
return Maps.keyOrNull(higherEntry(key));
|
||||
}
|
||||
|
||||
abstract Iterator<Entry<K, V>> entryIterator();
|
||||
|
||||
abstract Iterator<Entry<K, V>> descendingEntryIterator();
|
||||
|
||||
@Override
|
||||
public SortedMap<K, V> subMap(K fromKey, K toKey) {
|
||||
return subMap(fromKey, true, toKey, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedMap<K, V> headMap(K toKey) {
|
||||
return headMap(toKey, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedMap<K, V> tailMap(K fromKey) {
|
||||
return tailMap(fromKey, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> navigableKeySet() {
|
||||
return new Maps.NavigableKeySet<K, V>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract int size();
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return new Maps.EntrySet<K, V>() {
|
||||
@Override
|
||||
Map<K, V> map() {
|
||||
return AbstractNavigableMap.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
return entryIterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<K> descendingKeySet() {
|
||||
return descendingMap().navigableKeySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<K, V> descendingMap() {
|
||||
return new DescendingMap();
|
||||
}
|
||||
|
||||
private final class DescendingMap extends Maps.DescendingMap<K, V> {
|
||||
@Override
|
||||
NavigableMap<K, V> forward() {
|
||||
return AbstractNavigableMap.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
Iterator<Entry<K, V>> entryIterator() {
|
||||
return descendingEntryIterator();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
103
src/main/java/com/google/common/collect/AbstractRangeSet.java
Executable file
103
src/main/java/com/google/common/collect/AbstractRangeSet.java
Executable file
|
|
@ -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<C extends Comparable> implements RangeSet<C> {
|
||||
AbstractRangeSet() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(C value) {
|
||||
return rangeContaining(value) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Range<C> rangeContaining(C value);
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return asRanges().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Range<C> range) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Range<C> range) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
remove(Range.<C>all());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enclosesAll(RangeSet<C> other) {
|
||||
for (Range<C> range : other.asRanges()) {
|
||||
if (!encloses(range)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(RangeSet<C> other) {
|
||||
for (Range<C> range : other.asRanges()) {
|
||||
add(range);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll(RangeSet<C> other) {
|
||||
for (Range<C> range : other.asRanges()) {
|
||||
remove(range);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean encloses(Range<C> 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();
|
||||
}
|
||||
}
|
||||
85
src/main/java/com/google/common/collect/AbstractSequentialIterator.java
Executable file
85
src/main/java/com/google/common/collect/AbstractSequentialIterator.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* Example:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* @code
|
||||
*
|
||||
* Iterator<Integer> powersOfTwo = new AbstractSequentialIterator<Integer>(1) {
|
||||
* protected Integer computeNext(Integer previous) {
|
||||
* return (previous == 1 << 30) ? null : previous * 2;
|
||||
* }
|
||||
* };
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Chris Povirk
|
||||
* @since 12.0 (in Guava as {@code AbstractLinkedIterator} since 8.0)
|
||||
*/
|
||||
@GwtCompatible
|
||||
public abstract class AbstractSequentialIterator<T> extends UnmodifiableIterator<T> {
|
||||
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 <i>future</i> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
150
src/main/java/com/google/common/collect/AbstractSetMultimap.java
Executable file
150
src/main/java/com/google/common/collect/AbstractSetMultimap.java
Executable file
|
|
@ -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<K, V> extends AbstractMapBasedMultimap<K, V> implements SetMultimap<K, V> {
|
||||
/**
|
||||
* 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<K, Collection<V>> map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract Set<V> createCollection();
|
||||
|
||||
@Override
|
||||
Set<V> createUnmodifiableEmptyCollection() {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
// Following Javadoc copied from SetMultimap.
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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<V> get(@Nullable K key) {
|
||||
return (Set<V>) super.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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<Map.Entry<K, V>> entries() {
|
||||
return (Set<Map.Entry<K, V>>) super.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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<V> removeAll(@Nullable Object key) {
|
||||
return (Set<V>) super.removeAll(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Any duplicates in {@code values} will be stored in the multimap once.
|
||||
*/
|
||||
@Override
|
||||
public Set<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
|
||||
return (Set<V>) super.replaceValues(key, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Though the method signature doesn't say so explicitly, the returned map has
|
||||
* {@link Set} values.
|
||||
*/
|
||||
@Override
|
||||
public Map<K, Collection<V>> 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.
|
||||
*
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -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<K, V> extends AbstractSortedSetMultimap<K, V> {
|
||||
|
||||
AbstractSortedKeySortedSetMultimap(SortedMap<K, Collection<V>> map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedMap<K, Collection<V>> asMap() {
|
||||
return (SortedMap<K, Collection<V>>) super.asMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
SortedMap<K, Collection<V>> backingMap() {
|
||||
return (SortedMap<K, Collection<V>>) super.backingMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<K> keySet() {
|
||||
return (SortedSet<K>) super.keySet();
|
||||
}
|
||||
|
||||
}
|
||||
147
src/main/java/com/google/common/collect/AbstractSortedMultiset.java
Executable file
147
src/main/java/com/google/common/collect/AbstractSortedMultiset.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* The {@link #count} and {@link #size} implementations all iterate across the
|
||||
* set returned by {@link Multiset#entrySet()}, as do many methods acting on the
|
||||
* set returned by {@link #elementSet()}. Override those methods for better
|
||||
* performance.
|
||||
*
|
||||
* @author Louis Wasserman
|
||||
*/
|
||||
@GwtCompatible(emulated = true)
|
||||
abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements SortedMultiset<E> {
|
||||
@GwtTransient
|
||||
final Comparator<? super E> comparator;
|
||||
|
||||
// needed for serialization
|
||||
@SuppressWarnings("unchecked")
|
||||
AbstractSortedMultiset() {
|
||||
this((Comparator) Ordering.natural());
|
||||
}
|
||||
|
||||
AbstractSortedMultiset(Comparator<? super E> comparator) {
|
||||
this.comparator = checkNotNull(comparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<E> elementSet() {
|
||||
return (NavigableSet<E>) super.elementSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
NavigableSet<E> createElementSet() {
|
||||
return new SortedMultisets.NavigableElementSet<E>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<? super E> comparator() {
|
||||
return comparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<E> firstEntry() {
|
||||
Iterator<Entry<E>> entryIterator = entryIterator();
|
||||
return entryIterator.hasNext() ? entryIterator.next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<E> lastEntry() {
|
||||
Iterator<Entry<E>> entryIterator = descendingEntryIterator();
|
||||
return entryIterator.hasNext() ? entryIterator.next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<E> pollFirstEntry() {
|
||||
Iterator<Entry<E>> entryIterator = entryIterator();
|
||||
if (entryIterator.hasNext()) {
|
||||
Entry<E> result = entryIterator.next();
|
||||
result = Multisets.immutableEntry(result.getElement(), result.getCount());
|
||||
entryIterator.remove();
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<E> pollLastEntry() {
|
||||
Iterator<Entry<E>> entryIterator = descendingEntryIterator();
|
||||
if (entryIterator.hasNext()) {
|
||||
Entry<E> result = entryIterator.next();
|
||||
result = Multisets.immutableEntry(result.getElement(), result.getCount());
|
||||
entryIterator.remove();
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedMultiset<E> subMultiset(@Nullable E fromElement, BoundType fromBoundType, @Nullable E toElement,
|
||||
BoundType toBoundType) {
|
||||
// These are checked elsewhere, but NullPointerTester wants them checked
|
||||
// eagerly.
|
||||
checkNotNull(fromBoundType);
|
||||
checkNotNull(toBoundType);
|
||||
return tailMultiset(fromElement, fromBoundType).headMultiset(toElement, toBoundType);
|
||||
}
|
||||
|
||||
abstract Iterator<Entry<E>> descendingEntryIterator();
|
||||
|
||||
Iterator<E> descendingIterator() {
|
||||
return Multisets.iteratorImpl(descendingMultiset());
|
||||
}
|
||||
|
||||
private transient SortedMultiset<E> descendingMultiset;
|
||||
|
||||
@Override
|
||||
public SortedMultiset<E> descendingMultiset() {
|
||||
SortedMultiset<E> result = descendingMultiset;
|
||||
return (result == null) ? descendingMultiset = createDescendingMultiset() : result;
|
||||
}
|
||||
|
||||
SortedMultiset<E> createDescendingMultiset() {
|
||||
return new DescendingMultiset<E>() {
|
||||
@Override
|
||||
SortedMultiset<E> forwardMultiset() {
|
||||
return AbstractSortedMultiset.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
Iterator<Entry<E>> entryIterator() {
|
||||
return descendingEntryIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return descendingIterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
146
src/main/java/com/google/common/collect/AbstractSortedSetMultimap.java
Executable file
146
src/main/java/com/google/common/collect/AbstractSortedSetMultimap.java
Executable file
|
|
@ -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<K, V> extends AbstractSetMultimap<K, V> implements SortedSetMultimap<K, V> {
|
||||
/**
|
||||
* 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<K, Collection<V>> map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract SortedSet<V> createCollection();
|
||||
|
||||
@Override
|
||||
SortedSet<V> createUnmodifiableEmptyCollection() {
|
||||
Comparator<? super V> comparator = valueComparator();
|
||||
if (comparator == null) {
|
||||
return Collections.unmodifiableSortedSet(createCollection());
|
||||
} else {
|
||||
return ImmutableSortedSet.emptySet(valueComparator());
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
*
|
||||
* <p>
|
||||
* Changes to the returned collection will update the underlying multimap, and
|
||||
* vice versa.
|
||||
*
|
||||
* <p>
|
||||
* 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<V> get(@Nullable K key) {
|
||||
return (SortedSet<V>) super.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all values associated with a given key. The returned collection is
|
||||
* immutable.
|
||||
*
|
||||
* <p>
|
||||
* 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<V> removeAll(@Nullable Object key) {
|
||||
return (SortedSet<V>) super.removeAll(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a collection of values with the same key, replacing any existing
|
||||
* values for that key. The returned collection is immutable.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Any duplicates in {@code values} will be stored in the multimap once.
|
||||
*/
|
||||
@Override
|
||||
public SortedSet<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
|
||||
return (SortedSet<V>) 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}.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Though the method signature doesn't say so explicitly, the returned map has
|
||||
* {@link SortedSet} values.
|
||||
*/
|
||||
@Override
|
||||
public Map<K, Collection<V>> asMap() {
|
||||
return super.asMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Consequently, the values do not follow their natural ordering or the ordering
|
||||
* of the value comparator.
|
||||
*/
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return super.values();
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 430848587173315748L;
|
||||
}
|
||||
220
src/main/java/com/google/common/collect/AbstractTable.java
Executable file
220
src/main/java/com/google/common/collect/AbstractTable.java
Executable file
|
|
@ -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<R, C, V> implements Table<R, C, V> {
|
||||
|
||||
@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<R> rowKeySet() {
|
||||
return rowMap().keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<C> columnKeySet() {
|
||||
return columnMap().keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(@Nullable Object value) {
|
||||
for (Map<C, V> row : rowMap().values()) {
|
||||
if (row.containsValue(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
|
||||
Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
|
||||
return row != null && Maps.safeContainsKey(row, columnKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
|
||||
Map<C, V> 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<C, V> 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<? extends R, ? extends C, ? extends V> table) {
|
||||
for (Table.Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
|
||||
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private transient Set<Cell<R, C, V>> cellSet;
|
||||
|
||||
@Override
|
||||
public Set<Cell<R, C, V>> cellSet() {
|
||||
Set<Cell<R, C, V>> result = cellSet;
|
||||
return (result == null) ? cellSet = createCellSet() : result;
|
||||
}
|
||||
|
||||
Set<Cell<R, C, V>> createCellSet() {
|
||||
return new CellSet();
|
||||
}
|
||||
|
||||
abstract Iterator<Table.Cell<R, C, V>> cellIterator();
|
||||
|
||||
class CellSet extends AbstractSet<Cell<R, C, V>> {
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
if (o instanceof Cell) {
|
||||
Cell<?, ?, ?> cell = (Cell<?, ?, ?>) o;
|
||||
Map<C, V> 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<C, V> 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<Table.Cell<R, C, V>> iterator() {
|
||||
return cellIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return AbstractTable.this.size();
|
||||
}
|
||||
}
|
||||
|
||||
private transient Collection<V> values;
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
Collection<V> result = values;
|
||||
return (result == null) ? values = createValues() : result;
|
||||
}
|
||||
|
||||
Collection<V> createValues() {
|
||||
return new Values();
|
||||
}
|
||||
|
||||
Iterator<V> valuesIterator() {
|
||||
return new TransformedIterator<Cell<R, C, V>, V>(cellSet().iterator()) {
|
||||
@Override
|
||||
V transform(Cell<R, C, V> cell) {
|
||||
return cell.getValue();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Values extends AbstractCollection<V> {
|
||||
@Override
|
||||
public Iterator<V> 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();
|
||||
}
|
||||
}
|
||||
66
src/main/java/com/google/common/collect/AllEqualOrdering.java
Executable file
66
src/main/java/com/google/common/collect/AllEqualOrdering.java
Executable file
|
|
@ -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<Object> implements Serializable {
|
||||
static final AllEqualOrdering INSTANCE = new AllEqualOrdering();
|
||||
|
||||
@Override
|
||||
public int compare(@Nullable Object left, @Nullable Object right) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> List<E> sortedCopy(Iterable<E> iterable) {
|
||||
return Lists.newArrayList(iterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> ImmutableList<E> immutableSortedCopy(Iterable<E> iterable) {
|
||||
return ImmutableList.copyOf(iterable);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <S> Ordering<S> reverse() {
|
||||
return (Ordering<S>) this;
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Ordering.allEqual()";
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
172
src/main/java/com/google/common/collect/ArrayListMultimap.java
Executable file
172
src/main/java/com/google/common/collect/ArrayListMultimap.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* Keys and values may be null. All optional multimap methods are supported, and
|
||||
* all returned views are modifiable.
|
||||
*
|
||||
* <p>
|
||||
* The lists returned by {@link #get}, {@link #removeAll}, and
|
||||
* {@link #replaceValues} all implement {@link net.lax1dude.eaglercraft.v1_8.RandomAccess}.
|
||||
*
|
||||
* <p>
|
||||
* 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}.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap">
|
||||
* {@code Multimap}</a>.
|
||||
*
|
||||
* @author Jared Levy
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@GwtCompatible(serializable = true, emulated = true)
|
||||
public final class ArrayListMultimap<K, V> extends AbstractListMultimap<K, V> {
|
||||
// Default from ArrayList
|
||||
private static final int DEFAULT_VALUES_PER_KEY = 3;
|
||||
|
||||
@VisibleForTesting
|
||||
transient int expectedValuesPerKey;
|
||||
|
||||
/**
|
||||
* Creates a new, empty {@code ArrayListMultimap} with the default initial
|
||||
* capacities.
|
||||
*/
|
||||
public static <K, V> ArrayListMultimap<K, V> create() {
|
||||
return new ArrayListMultimap<K, V>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <K, V> ArrayListMultimap<K, V> create(int expectedKeys, int expectedValuesPerKey) {
|
||||
return new ArrayListMultimap<K, V>(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 <K, V> ArrayListMultimap<K, V> create(Multimap<? extends K, ? extends V> multimap) {
|
||||
return new ArrayListMultimap<K, V>(multimap);
|
||||
}
|
||||
|
||||
private ArrayListMultimap() {
|
||||
super(new HashMap<K, Collection<V>>());
|
||||
expectedValuesPerKey = DEFAULT_VALUES_PER_KEY;
|
||||
}
|
||||
|
||||
private ArrayListMultimap(int expectedKeys, int expectedValuesPerKey) {
|
||||
super(Maps.<K, Collection<V>>newHashMapWithExpectedSize(expectedKeys));
|
||||
checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey");
|
||||
this.expectedValuesPerKey = expectedValuesPerKey;
|
||||
}
|
||||
|
||||
private ArrayListMultimap(Multimap<? extends K, ? extends V> 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<V> createCollection() {
|
||||
return new ArrayList<V>(expectedValuesPerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces the memory used by this {@code ArrayListMultimap}, if feasible.
|
||||
*/
|
||||
public void trimToSize() {
|
||||
for (Collection<V> collection : backingMap().values()) {
|
||||
ArrayList<V> arrayList = (ArrayList<V>) 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<K, Collection<V>> map = Maps.newHashMapWithExpectedSize(distinctKeys);
|
||||
setMap(map);
|
||||
Serialization.populateMultimap(this, stream, distinctKeys);
|
||||
}
|
||||
|
||||
@GwtIncompatible("Not needed in emulated source.")
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
793
src/main/java/com/google/common/collect/ArrayTable.java
Executable file
793
src/main/java/com/google/common/collect/ArrayTable.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* The allowed row and column keys must be supplied when the table is created.
|
||||
* The table always contains a mapping for every row key / column pair. The
|
||||
* value corresponding to a given row and column is null unless another value is
|
||||
* provided.
|
||||
*
|
||||
* <p>
|
||||
* The table's size is constant: the product of the number of supplied row keys
|
||||
* and the number of supplied column keys. The {@code remove} and {@code
|
||||
* clear} methods are not supported by the table or its views. The
|
||||
* {@link #erase} and {@link #eraseAll} methods may be used instead.
|
||||
*
|
||||
* <p>
|
||||
* The ordering of the row and column keys provided when the table is
|
||||
* constructed determines the iteration ordering across rows and columns in the
|
||||
* table's views. None of the view iterators support {@link Iterator#remove}. If
|
||||
* the table is modified after an iterator is created, the iterator remains
|
||||
* valid.
|
||||
*
|
||||
* <p>
|
||||
* This class requires less memory than the {@link HashBasedTable} and
|
||||
* {@link TreeBasedTable} implementations, except when the table is sparse.
|
||||
*
|
||||
* <p>
|
||||
* Null row keys or column keys are not permitted.
|
||||
*
|
||||
* <p>
|
||||
* This class provides methods involving the underlying array structure, where
|
||||
* the array indices correspond to the position of a row or column in the lists
|
||||
* of allowed keys and values. See the {@link #at}, {@link #set},
|
||||
* {@link #toArray}, {@link #rowKeyList}, and {@link #columnKeyList} methods for
|
||||
* more details.
|
||||
*
|
||||
* <p>
|
||||
* Note that this implementation is not synchronized. If multiple threads access
|
||||
* the same cell of an {@code ArrayTable} concurrently and one of the threads
|
||||
* modifies its value, there is no guarantee that the new value will be fully
|
||||
* visible to the other threads. To guarantee that modifications are visible,
|
||||
* synchronize access to the table. Unlike other {@code Table} implementations,
|
||||
* synchronization is unnecessary between a thread that writes to one cell and a
|
||||
* thread that reads from another.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table">
|
||||
* {@code Table}</a>.
|
||||
*
|
||||
* @author Jared Levy
|
||||
* @since 10.0
|
||||
*/
|
||||
@Beta
|
||||
@GwtCompatible(emulated = true)
|
||||
public final class ArrayTable<R, C, V> extends AbstractTable<R, C, V> 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 <R, C, V> ArrayTable<R, C, V> create(Iterable<? extends R> rowKeys,
|
||||
Iterable<? extends C> columnKeys) {
|
||||
return new ArrayTable<R, C, V>(rowKeys, columnKeys);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO(jlevy): Add factory methods taking an Enum class, instead of an
|
||||
* iterable, to specify the allowed row keys and/or column keys. Note that
|
||||
* custom serialization logic is needed to support different enum sizes during
|
||||
* serialization and deserialization.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates an {@code ArrayTable} with the mappings in the provided table.
|
||||
*
|
||||
* <p>
|
||||
* If {@code table} includes a mapping with row key {@code r} and a separate
|
||||
* mapping with column key {@code c}, the returned table contains a mapping with
|
||||
* row key {@code r} and column key {@code c}. If that row key / column key pair
|
||||
* in not in {@code table}, the pair maps to {@code null} in the generated
|
||||
* table.
|
||||
*
|
||||
* <p>
|
||||
* The returned table allows subsequent {@code put} calls with the row keys in
|
||||
* {@code table.rowKeySet()} and the column keys in {@code
|
||||
* table.columnKeySet()}. Calling {@link #put} with other keys leads to an
|
||||
* {@code IllegalArgumentException}.
|
||||
*
|
||||
* <p>
|
||||
* The ordering of {@code table.rowKeySet()} and {@code
|
||||
* table.columnKeySet()} determines the row and column iteration ordering of the
|
||||
* returned table.
|
||||
*
|
||||
* @throws NullPointerException if {@code table} has a null key
|
||||
* @throws IllegalArgumentException if the provided table is empty
|
||||
*/
|
||||
public static <R, C, V> ArrayTable<R, C, V> create(Table<R, C, V> table) {
|
||||
return (table instanceof ArrayTable<?, ?, ?>) ? new ArrayTable<R, C, V>((ArrayTable<R, C, V>) table)
|
||||
: new ArrayTable<R, C, V>(table);
|
||||
}
|
||||
|
||||
private final ImmutableList<R> rowList;
|
||||
private final ImmutableList<C> columnList;
|
||||
|
||||
// TODO(jlevy): Add getters returning rowKeyToIndex and columnKeyToIndex?
|
||||
private final ImmutableMap<R, Integer> rowKeyToIndex;
|
||||
private final ImmutableMap<C, Integer> columnKeyToIndex;
|
||||
private final V[][] array;
|
||||
|
||||
private ArrayTable(Iterable<? extends R> rowKeys, Iterable<? extends C> columnKeys) {
|
||||
this.rowList = ImmutableList.copyOf(rowKeys);
|
||||
this.columnList = ImmutableList.copyOf(columnKeys);
|
||||
checkArgument(!rowList.isEmpty());
|
||||
checkArgument(!columnList.isEmpty());
|
||||
|
||||
/*
|
||||
* TODO(jlevy): Support empty rowKeys or columnKeys? If we do, when columnKeys
|
||||
* is empty but rowKeys isn't, the table is empty but containsRow() can return
|
||||
* true and rowKeySet() isn't empty.
|
||||
*/
|
||||
rowKeyToIndex = index(rowList);
|
||||
columnKeyToIndex = index(columnList);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
V[][] tmpArray = (V[][]) new Object[rowList.size()][columnList.size()];
|
||||
array = tmpArray;
|
||||
// Necessary because in GWT the arrays are initialized with "undefined" instead
|
||||
// of null.
|
||||
eraseAll();
|
||||
}
|
||||
|
||||
private static <E> ImmutableMap<E, Integer> index(List<E> list) {
|
||||
ImmutableMap.Builder<E, Integer> columnBuilder = ImmutableMap.builder();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
columnBuilder.put(list.get(i), i);
|
||||
}
|
||||
return columnBuilder.build();
|
||||
}
|
||||
|
||||
private ArrayTable(Table<R, C, V> table) {
|
||||
this(table.rowKeySet(), table.columnKeySet());
|
||||
putAll(table);
|
||||
}
|
||||
|
||||
private ArrayTable(ArrayTable<R, C, V> table) {
|
||||
rowList = table.rowList;
|
||||
columnList = table.columnList;
|
||||
rowKeyToIndex = table.rowKeyToIndex;
|
||||
columnKeyToIndex = table.columnKeyToIndex;
|
||||
@SuppressWarnings("unchecked")
|
||||
V[][] copy = (V[][]) new Object[rowList.size()][columnList.size()];
|
||||
array = copy;
|
||||
// Necessary because in GWT the arrays are initialized with "undefined" instead
|
||||
// of null.
|
||||
eraseAll();
|
||||
for (int i = 0; i < rowList.size(); i++) {
|
||||
System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class ArrayMap<K, V> extends Maps.ImprovedAbstractMap<K, V> {
|
||||
private final ImmutableMap<K, Integer> keyIndex;
|
||||
|
||||
private ArrayMap(ImmutableMap<K, Integer> keyIndex) {
|
||||
this.keyIndex = keyIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return keyIndex.keySet();
|
||||
}
|
||||
|
||||
K getKey(int index) {
|
||||
return keyIndex.keySet().asList().get(index);
|
||||
}
|
||||
|
||||
abstract String getKeyRole();
|
||||
|
||||
@Nullable
|
||||
abstract V getValue(int index);
|
||||
|
||||
@Nullable
|
||||
abstract V setValue(int index, V newValue);
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return keyIndex.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return keyIndex.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Entry<K, V>> createEntrySet() {
|
||||
return new Maps.EntrySet<K, V>() {
|
||||
@Override
|
||||
Map<K, V> map() {
|
||||
return ArrayMap.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<K, V>> iterator() {
|
||||
return new AbstractIndexedListIterator<Entry<K, V>>(size()) {
|
||||
@Override
|
||||
protected Entry<K, V> get(final int index) {
|
||||
return new AbstractMapEntry<K, V>() {
|
||||
@Override
|
||||
public K getKey() {
|
||||
return ArrayMap.this.getKey(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return ArrayMap.this.getValue(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
return ArrayMap.this.setValue(index, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 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<R> rowKeyList() {
|
||||
return rowList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns, as an immutable list, the column keys provided when the table was
|
||||
* constructed, including those that are mapped to null values only.
|
||||
*/
|
||||
public ImmutableList<C> columnKeyList() {
|
||||
return columnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value corresponding to the specified row and column indices. The
|
||||
* same value is returned by {@code
|
||||
* get(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex))}, but this
|
||||
* method runs more quickly.
|
||||
*
|
||||
* @param rowIndex position of the row key in {@link #rowKeyList()}
|
||||
* @param columnIndex position of the row key in {@link #columnKeyList()}
|
||||
* @return the value with the specified row and column
|
||||
* @throws IndexOutOfBoundsException if either index is negative, {@code
|
||||
* rowIndex} is greater then or equal to the number of
|
||||
* allowed row keys, or {@code columnIndex} is
|
||||
* greater then or equal to the number of
|
||||
* allowed column keys
|
||||
*/
|
||||
public V at(int rowIndex, int columnIndex) {
|
||||
// In GWT array access never throws IndexOutOfBoundsException.
|
||||
checkElementIndex(rowIndex, rowList.size());
|
||||
checkElementIndex(columnIndex, columnList.size());
|
||||
return array[rowIndex][columnIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates {@code value} with the specified row and column indices. The logic
|
||||
* {@code
|
||||
* put(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex), value)} has
|
||||
* the same behavior, but this method runs more quickly.
|
||||
*
|
||||
* @param rowIndex position of the row key in {@link #rowKeyList()}
|
||||
* @param columnIndex position of the row key in {@link #columnKeyList()}
|
||||
* @param value value to store in the table
|
||||
* @return the previous value with the specified row and column
|
||||
* @throws IndexOutOfBoundsException if either index is negative, {@code
|
||||
* rowIndex} is greater then or equal to the number of
|
||||
* allowed row keys, or {@code columnIndex} is
|
||||
* greater then or equal to the number of
|
||||
* allowed column keys
|
||||
*/
|
||||
public V set(int rowIndex, int columnIndex, @Nullable V value) {
|
||||
// In GWT array access never throws IndexOutOfBoundsException.
|
||||
checkElementIndex(rowIndex, rowList.size());
|
||||
checkElementIndex(columnIndex, columnList.size());
|
||||
V oldValue = array[rowIndex][columnIndex];
|
||||
array[rowIndex][columnIndex] = value;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* 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<V> 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}
|
||||
*
|
||||
* <p>
|
||||
* If {@code table} is an {@code ArrayTable}, its null values will be stored in
|
||||
* this table, possibly replacing values that were previously non-null.
|
||||
*
|
||||
* @throws NullPointerException if {@code table} has a null key
|
||||
* @throws IllegalArgumentException if any of the provided table's row keys or
|
||||
* column keys is not in {@link #rowKeySet()}
|
||||
* or {@link #columnKeySet()}
|
||||
*/
|
||||
@Override
|
||||
public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* This method is equivalent to {@code put(rowKey, columnKey, null)} when both
|
||||
* provided keys are valid.
|
||||
*
|
||||
* @param rowKey row key of mapping to be erased
|
||||
* @param columnKey column key of mapping to be erased
|
||||
* @return the value previously associated with the keys, or {@code null} if no
|
||||
* mapping existed for the keys
|
||||
*/
|
||||
public V erase(@Nullable Object rowKey, @Nullable Object columnKey) {
|
||||
Integer rowIndex = rowKeyToIndex.get(rowKey);
|
||||
Integer columnIndex = columnKeyToIndex.get(columnKey);
|
||||
if (rowIndex == null || columnIndex == null) {
|
||||
return null;
|
||||
}
|
||||
return set(rowIndex, columnIndex, null);
|
||||
}
|
||||
|
||||
// TODO(jlevy): Add eraseRow and eraseColumn methods?
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return rowList.size() * columnList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable set of all row key / column key / value triplets.
|
||||
* Changes to the table will update the returned set.
|
||||
*
|
||||
* <p>
|
||||
* The returned set's iterator traverses the mappings with the first row key,
|
||||
* the mappings with the second row key, and so on.
|
||||
*
|
||||
* <p>
|
||||
* The value in the returned cells may change if the table subsequently changes.
|
||||
*
|
||||
* @return set of table cells consisting of row key / column key / value
|
||||
* triplets
|
||||
*/
|
||||
@Override
|
||||
public Set<Cell<R, C, V>> cellSet() {
|
||||
return super.cellSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
Iterator<Cell<R, C, V>> cellIterator() {
|
||||
return new AbstractIndexedListIterator<Cell<R, C, V>>(size()) {
|
||||
@Override
|
||||
protected Cell<R, C, V> get(final int index) {
|
||||
return new Tables.AbstractCell<R, C, V>() {
|
||||
final int rowIndex = index / columnList.size();
|
||||
final int columnIndex = index % columnList.size();
|
||||
|
||||
@Override
|
||||
public R getRowKey() {
|
||||
return rowList.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public C getColumnKey() {
|
||||
return columnList.get(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return at(rowIndex, columnIndex);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view of all mappings that have the given column key. If the column
|
||||
* key isn't in {@link #columnKeySet()}, an empty immutable map is returned.
|
||||
*
|
||||
* <p>
|
||||
* Otherwise, for each row key in {@link #rowKeySet()}, the returned map
|
||||
* associates the row key with the corresponding value in the table. Changes to
|
||||
* the returned map will update the underlying table, and vice versa.
|
||||
*
|
||||
* @param columnKey key of column to search for in the table
|
||||
* @return the corresponding map from row keys to values
|
||||
*/
|
||||
@Override
|
||||
public Map<R, V> column(C columnKey) {
|
||||
checkNotNull(columnKey);
|
||||
Integer columnIndex = columnKeyToIndex.get(columnKey);
|
||||
return (columnIndex == null) ? ImmutableMap.<R, V>of() : new Column(columnIndex);
|
||||
}
|
||||
|
||||
private class Column extends ArrayMap<R, V> {
|
||||
final int columnIndex;
|
||||
|
||||
Column(int columnIndex) {
|
||||
super(rowKeyToIndex);
|
||||
this.columnIndex = columnIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getKeyRole() {
|
||||
return "Row";
|
||||
}
|
||||
|
||||
@Override
|
||||
V getValue(int index) {
|
||||
return at(index, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
V setValue(int index, V newValue) {
|
||||
return set(index, columnIndex, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable set of the valid column keys, including those that are
|
||||
* associated with null values only.
|
||||
*
|
||||
* @return immutable set of column keys
|
||||
*/
|
||||
@Override
|
||||
public ImmutableSet<C> columnKeySet() {
|
||||
return columnKeyToIndex.keySet();
|
||||
}
|
||||
|
||||
private transient ColumnMap columnMap;
|
||||
|
||||
@Override
|
||||
public Map<C, Map<R, V>> columnMap() {
|
||||
ColumnMap map = columnMap;
|
||||
return (map == null) ? columnMap = new ColumnMap() : map;
|
||||
}
|
||||
|
||||
private class ColumnMap extends ArrayMap<C, Map<R, V>> {
|
||||
private ColumnMap() {
|
||||
super(columnKeyToIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getKeyRole() {
|
||||
return "Column";
|
||||
}
|
||||
|
||||
@Override
|
||||
Map<R, V> getValue(int index) {
|
||||
return new Column(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
Map<R, V> setValue(int index, Map<R, V> newValue) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<R, V> put(C key, Map<R, V> value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view of all mappings that have the given row key. If the row key
|
||||
* isn't in {@link #rowKeySet()}, an empty immutable map is returned.
|
||||
*
|
||||
* <p>
|
||||
* Otherwise, for each column key in {@link #columnKeySet()}, the returned map
|
||||
* associates the column key with the corresponding value in the table. Changes
|
||||
* to the returned map will update the underlying table, and vice versa.
|
||||
*
|
||||
* @param rowKey key of row to search for in the table
|
||||
* @return the corresponding map from column keys to values
|
||||
*/
|
||||
@Override
|
||||
public Map<C, V> row(R rowKey) {
|
||||
checkNotNull(rowKey);
|
||||
Integer rowIndex = rowKeyToIndex.get(rowKey);
|
||||
return (rowIndex == null) ? ImmutableMap.<C, V>of() : new Row(rowIndex);
|
||||
}
|
||||
|
||||
private class Row extends ArrayMap<C, V> {
|
||||
final int rowIndex;
|
||||
|
||||
Row(int rowIndex) {
|
||||
super(columnKeyToIndex);
|
||||
this.rowIndex = rowIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getKeyRole() {
|
||||
return "Column";
|
||||
}
|
||||
|
||||
@Override
|
||||
V getValue(int index) {
|
||||
return at(rowIndex, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
V setValue(int index, V newValue) {
|
||||
return set(rowIndex, index, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable set of the valid row keys, including those that are
|
||||
* associated with null values only.
|
||||
*
|
||||
* @return immutable set of row keys
|
||||
*/
|
||||
@Override
|
||||
public ImmutableSet<R> rowKeySet() {
|
||||
return rowKeyToIndex.keySet();
|
||||
}
|
||||
|
||||
private transient RowMap rowMap;
|
||||
|
||||
@Override
|
||||
public Map<R, Map<C, V>> rowMap() {
|
||||
RowMap map = rowMap;
|
||||
return (map == null) ? rowMap = new RowMap() : map;
|
||||
}
|
||||
|
||||
private class RowMap extends ArrayMap<R, Map<C, V>> {
|
||||
private RowMap() {
|
||||
super(rowKeyToIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getKeyRole() {
|
||||
return "Row";
|
||||
}
|
||||
|
||||
@Override
|
||||
Map<C, V> getValue(int index) {
|
||||
return new Row(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
Map<C, V> setValue(int index, Map<C, V> newValue) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<C, V> put(R key, Map<C, V> value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable collection of all values, which may contain
|
||||
* duplicates. Changes to the table will update the returned collection.
|
||||
*
|
||||
* <p>
|
||||
* The returned collection's iterator traverses the values of the first row key,
|
||||
* the values of the second row key, and so on.
|
||||
*
|
||||
* @return collection of values
|
||||
*/
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return super.values();
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
118
src/main/java/com/google/common/collect/BiMap.java
Executable file
118
src/main/java/com/google/common/collect/BiMap.java
Executable file
|
|
@ -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.
|
||||
*
|
||||
* <p>
|
||||
* See the Guava User Guide article on <a href=
|
||||
* "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap">
|
||||
* {@code BiMap}</a>.
|
||||
*
|
||||
* @author Kevin Bourrillion
|
||||
* @since 2.0 (imported from Google Collections Library)
|
||||
*/
|
||||
@GwtCompatible
|
||||
public interface BiMap<K, V> extends Map<K, V> {
|
||||
// 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.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> 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}
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> 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<? extends K, ? extends V> map);
|
||||
|
||||
// Views
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* 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<V> 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.
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b>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<V, K> inverse();
|
||||
}
|
||||
213
src/main/java/com/google/common/collect/BinaryTreeTraverser.java
Executable file
213
src/main/java/com/google/common/collect/BinaryTreeTraverser.java
Executable file
|
|
@ -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<T> extends TreeTraverser<T> {
|
||||
// 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<T> 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<T> rightChild(T root);
|
||||
|
||||
/**
|
||||
* Returns the children of this node, in left-to-right order.
|
||||
*/
|
||||
@Override
|
||||
public final Iterable<T> children(final T root) {
|
||||
checkNotNull(root);
|
||||
return new FluentIterable<T>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new AbstractIterator<T>() {
|
||||
boolean doneLeft;
|
||||
boolean doneRight;
|
||||
|
||||
@Override
|
||||
protected T computeNext() {
|
||||
if (!doneLeft) {
|
||||
doneLeft = true;
|
||||
Optional<T> left = leftChild(root);
|
||||
if (left.isPresent()) {
|
||||
return left.get();
|
||||
}
|
||||
}
|
||||
if (!doneRight) {
|
||||
doneRight = true;
|
||||
Optional<T> right = rightChild(root);
|
||||
if (right.isPresent()) {
|
||||
return right.get();
|
||||
}
|
||||
}
|
||||
return endOfData();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
UnmodifiableIterator<T> preOrderIterator(T root) {
|
||||
return new PreOrderIterator(root);
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimized implementation of preOrderIterator for binary trees.
|
||||
*/
|
||||
private final class PreOrderIterator extends UnmodifiableIterator<T> implements PeekingIterator<T> {
|
||||
private final Deque<T> stack;
|
||||
|
||||
PreOrderIterator(T root) {
|
||||
this.stack = new ArrayDeque<T>();
|
||||
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<T> postOrderIterator(T root) {
|
||||
return new PostOrderIterator(root);
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimized implementation of postOrderIterator for binary trees.
|
||||
*/
|
||||
private final class PostOrderIterator extends UnmodifiableIterator<T> {
|
||||
private final Deque<T> stack;
|
||||
private final BitSet hasExpanded;
|
||||
|
||||
PostOrderIterator(T root) {
|
||||
this.stack = new ArrayDeque<T>();
|
||||
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<T> inOrderTraversal(final T root) {
|
||||
checkNotNull(root);
|
||||
return new FluentIterable<T>() {
|
||||
@Override
|
||||
public UnmodifiableIterator<T> iterator() {
|
||||
return new InOrderIterator(root);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final class InOrderIterator extends AbstractIterator<T> {
|
||||
private final Deque<T> stack;
|
||||
private final BitSet hasExpandedLeft;
|
||||
|
||||
InOrderIterator(T root) {
|
||||
this.stack = new ArrayDeque<T>();
|
||||
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 <T> void pushIfPresent(Deque<T> stack, Optional<T> node) {
|
||||
if (node.isPresent()) {
|
||||
stack.addLast(node.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue