Conversion en projet Maven
This commit is contained in:
13
src/main/java/fr/pandacube/java/Pandacube.java
Normal file
13
src/main/java/fr/pandacube/java/Pandacube.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package fr.pandacube.java;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class Pandacube {
|
||||
|
||||
public static final Charset NETWORK_CHARSET = Charset.forName("UTF-8");
|
||||
|
||||
public static final int NETWORK_TCP_BUFFER_SIZE = 1024 * 1024;
|
||||
|
||||
public static final int NETWORK_TIMEOUT = 0; // no timeout (milli-seconds)
|
||||
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package fr.pandacube.java.external_tools;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OfflineUUID {
|
||||
public static void main(String[] args) {
|
||||
for (String arg : args)
|
||||
System.out.println("" + arg + ":" + getFromNickName(arg));
|
||||
if (args.length == 0)
|
||||
throw new IllegalArgumentException("no argument given. Please give at least one argument.");
|
||||
}
|
||||
|
||||
public static UUID getFromNickName(String nickname) {
|
||||
String str = "OfflinePlayer:" + nickname;
|
||||
byte[] from_str = str.getBytes(Charset.forName("UTF-8"));
|
||||
return UUID.nameUUIDFromBytes(from_str);
|
||||
}
|
||||
|
||||
public static UUID[] getFromNickName(String[] nicknames) {
|
||||
if (nicknames == null) throw new NullPointerException();
|
||||
|
||||
UUID[] uuids = new UUID[nicknames.length];
|
||||
for (int i = 0; i < nicknames.length; i++)
|
||||
uuids[i] = getFromNickName(nicknames[i]);
|
||||
return uuids;
|
||||
}
|
||||
}
|
47
src/main/java/fr/pandacube/java/util/BiMap.java
Normal file
47
src/main/java/fr/pandacube/java/util/BiMap.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class BiMap<K,V> {
|
||||
|
||||
HashMap<K, V> map = new HashMap<>();
|
||||
HashMap<V, K> inversedMap = new HashMap<>();
|
||||
|
||||
public synchronized void put(K k, V v) {
|
||||
if (map.containsKey(k) || inversedMap.containsKey(v)) {
|
||||
map.remove(k);
|
||||
inversedMap.remove(v);
|
||||
}
|
||||
map.put(k, v);
|
||||
inversedMap.put(v, k);
|
||||
}
|
||||
|
||||
public synchronized V get(K k) {
|
||||
return map.get(k);
|
||||
}
|
||||
|
||||
public synchronized K getKey(V v) {
|
||||
return inversedMap.get(v);
|
||||
}
|
||||
|
||||
public synchronized boolean containsKey(K k) {
|
||||
return map.containsKey(k);
|
||||
}
|
||||
|
||||
public synchronized boolean containsValue(V v) {
|
||||
return inversedMap.containsKey(v);
|
||||
}
|
||||
|
||||
public synchronized V remove(K k) {
|
||||
V v = map.remove(k);
|
||||
inversedMap.remove(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public synchronized K removeValue(V v) {
|
||||
K k = inversedMap.remove(v);
|
||||
map.remove(k);
|
||||
return k;
|
||||
}
|
||||
|
||||
}
|
6
src/main/java/fr/pandacube/java/util/Callback.java
Normal file
6
src/main/java/fr/pandacube/java/util/Callback.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Callback<T> {
|
||||
public void done(T arg);
|
||||
}
|
49
src/main/java/fr/pandacube/java/util/ColorUtil.java
Normal file
49
src/main/java/fr/pandacube/java/util/ColorUtil.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ColorUtil {
|
||||
|
||||
/*
|
||||
* Rainbow
|
||||
*/
|
||||
private static List<Color> rainbowColors = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Get a rainbow color
|
||||
* @param variation from 0 (include) to 1 (exclude).
|
||||
* 0 is red, 1/6 is yellow, 2/6 is green, 3/6 is cyan, 4/6 is blue, 5/6 is magenta
|
||||
* @return
|
||||
*/
|
||||
public static Color getRainbowColor(double variation) {
|
||||
synchronized (rainbowColors) {
|
||||
if (rainbowColors.isEmpty()) {
|
||||
for (int g=0; g<255; g++) rainbowColors.add(new Color(255, g, 0));
|
||||
for (int r=255; r>0; r--) rainbowColors.add(new Color(r, 255, 0));
|
||||
for (int b=0; b<255; b++) rainbowColors.add(new Color(0, 255, b));
|
||||
for (int g=255; g>0; g--) rainbowColors.add(new Color(0, g, 255));
|
||||
for (int r=0; r<255; r++) rainbowColors.add(new Color(r, 0, 255));
|
||||
for (int b=255; b>0; b--) rainbowColors.add(new Color(255, 0, b));
|
||||
}
|
||||
}
|
||||
|
||||
while (variation >= 1) variation -= 1d;
|
||||
while (variation < 0) variation += 1d;
|
||||
|
||||
return rainbowColors.get((int)(variation * rainbowColors.size()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
90
src/main/java/fr/pandacube/java/util/EnumUtil.java
Normal file
90
src/main/java/fr/pandacube/java/util/EnumUtil.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
public class EnumUtil {
|
||||
|
||||
/**
|
||||
* List all enum constants which are in the specified enum class.
|
||||
*
|
||||
* @param enumType the enum class.
|
||||
* @param separator a string which will be used as a separator
|
||||
* @return a string representation of the enum class.
|
||||
*/
|
||||
public static <T extends Enum<T>> String enumList(Class<T> enumType, String separator) {
|
||||
T[] elements = enumType.getEnumConstants();
|
||||
|
||||
String out = "";
|
||||
boolean first = true;
|
||||
for (T el : elements) {
|
||||
if (!first) out += separator;
|
||||
first = false;
|
||||
out += el.name();
|
||||
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all enum constants which are in the specified enum class. It is
|
||||
* equivalent to call
|
||||
* {@link #enumList(Class, String)} with the second parameter
|
||||
* <code>", "</code>
|
||||
*
|
||||
* @param enumType the enum class.
|
||||
* @return a string representation of the enum class.
|
||||
*/
|
||||
public static <T extends Enum<T>> String enumList(Class<T> enumType) {
|
||||
return enumList(enumType, ", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de rechercher l'existance d'un élément dans un enum, de façon
|
||||
* insensible à la casse
|
||||
*
|
||||
* @param enumType la classe correpondant à l'enum à lister
|
||||
* @param search l'élément à rechercher, insensible à la casse
|
||||
* @return l'élément de l'énumarétion, si elle a été trouvée, null sinon
|
||||
*/
|
||||
public static <T extends Enum<T>> T searchEnum(Class<T> enumType, String search) {
|
||||
T[] elements = enumType.getEnumConstants();
|
||||
|
||||
for (T el : elements)
|
||||
if (el.name().equalsIgnoreCase(search)) return el;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permet de rechercher l'existance d'un élément dans un enum, de façon
|
||||
* insensible à la casse
|
||||
* La validité de la classe passé en premier paramètre est vérifiée
|
||||
* dynamiquement et non
|
||||
* statiquement. Préférez l'utilisation de
|
||||
* {@link #searchEnum(Class, String)} quand c'est possible.
|
||||
*
|
||||
* @param enumType la classe correpondant à l'enum à lister
|
||||
* @param search l'élément à rechercher, insensible à la casse
|
||||
* @return l'élément de l'énumération, si elle a été trouvée et si la classe
|
||||
* passée en paramètre est un enum, null dans les autres cas
|
||||
*/
|
||||
public static Enum<?> searchUncheckedEnum(Class<?> enumType, String search) {
|
||||
if (!enumType.isEnum()) return null;
|
||||
Enum<?>[] elements = (Enum<?>[]) enumType.getEnumConstants();
|
||||
|
||||
for (Enum<?> el : elements)
|
||||
if (el.name().equalsIgnoreCase(search)) return el;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne une valeur aléatoire parmis les élément de l'Enum spécifié en
|
||||
* paramètre.
|
||||
*
|
||||
* @param enumType l'enum dans lequel piocher la valeur
|
||||
* @return une des valeurs de l'enum
|
||||
*/
|
||||
public static <T extends Enum<T>> T randomValue(Class<T> enumType) {
|
||||
T[] elements = enumType.getEnumConstants();
|
||||
|
||||
return elements[RandomUtil.rand.nextInt(elements.length)];
|
||||
}
|
||||
|
||||
}
|
787
src/main/java/fr/pandacube/java/util/GifDecoder.java
Normal file
787
src/main/java/fr/pandacube/java/util/GifDecoder.java
Normal file
@@ -0,0 +1,787 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Class GifDecoder - Decodes a GIF file into one or more frames.
|
||||
* <br><pre>
|
||||
* Example:
|
||||
* GifDecoder d = new GifDecoder();
|
||||
* d.read("sample.gif");
|
||||
* int n = d.getFrameCount();
|
||||
* for (int i = 0; i < n; i++) {
|
||||
* BufferedImage frame = d.getFrame(i); // frame i
|
||||
* int t = d.getDelay(i); // display duration of frame in milliseconds
|
||||
* // do something with frame
|
||||
* }
|
||||
* </pre>
|
||||
* No copyright asserted on the source code of this class. May be used for
|
||||
* any purpose, however, refer to the Unisys LZW patent for any additional
|
||||
* restrictions. Please forward any corrections to questions at fmsware.com.
|
||||
*
|
||||
* @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick.
|
||||
* @version 1.03 November 2003
|
||||
*
|
||||
*/
|
||||
|
||||
public class GifDecoder {
|
||||
|
||||
/**
|
||||
* File read status: No errors.
|
||||
*/
|
||||
public static final int STATUS_OK = 0;
|
||||
|
||||
/**
|
||||
* File read status: Error decoding file (may be partially decoded)
|
||||
*/
|
||||
public static final int STATUS_FORMAT_ERROR = 1;
|
||||
|
||||
/**
|
||||
* File read status: Unable to open source.
|
||||
*/
|
||||
public static final int STATUS_OPEN_ERROR = 2;
|
||||
|
||||
protected BufferedInputStream in;
|
||||
protected int status;
|
||||
|
||||
protected int width; // full image width
|
||||
protected int height; // full image height
|
||||
protected boolean gctFlag; // global color table used
|
||||
protected int gctSize; // size of global color table
|
||||
protected int loopCount = 1; // iterations; 0 = repeat forever
|
||||
|
||||
protected int[] gct; // global color table
|
||||
protected int[] lct; // local color table
|
||||
protected int[] act; // active color table
|
||||
|
||||
protected int bgIndex; // background color index
|
||||
protected int bgColor; // background color
|
||||
protected int lastBgColor; // previous bg color
|
||||
protected int pixelAspect; // pixel aspect ratio
|
||||
|
||||
protected boolean lctFlag; // local color table flag
|
||||
protected boolean interlace; // interlace flag
|
||||
protected int lctSize; // local color table size
|
||||
|
||||
protected int ix, iy, iw, ih; // current image rectangle
|
||||
protected Rectangle lastRect; // last image rect
|
||||
protected BufferedImage image; // current frame
|
||||
protected BufferedImage lastImage; // previous frame
|
||||
|
||||
protected byte[] block = new byte[256]; // current data block
|
||||
protected int blockSize = 0; // block size
|
||||
|
||||
// last graphic control extension info
|
||||
protected int dispose = 0;
|
||||
// 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
|
||||
protected int lastDispose = 0;
|
||||
protected boolean transparency = false; // use transparent color
|
||||
protected int delay = 0; // delay in milliseconds
|
||||
protected int transIndex; // transparent color index
|
||||
|
||||
protected static final int MaxStackSize = 4096;
|
||||
// max decoder pixel stack size
|
||||
|
||||
// LZW decoder working arrays
|
||||
protected short[] prefix;
|
||||
protected byte[] suffix;
|
||||
protected byte[] pixelStack;
|
||||
protected byte[] pixels;
|
||||
|
||||
protected ArrayList<GifFrame> frames; // frames read from current file
|
||||
protected int frameCount;
|
||||
|
||||
static class GifFrame {
|
||||
public GifFrame(BufferedImage im, int del) {
|
||||
image = im;
|
||||
delay = del;
|
||||
}
|
||||
public BufferedImage image;
|
||||
public int delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets display duration for specified frame.
|
||||
*
|
||||
* @param n int index of frame
|
||||
* @return delay in milliseconds
|
||||
*/
|
||||
public int getDelay(int n) {
|
||||
//
|
||||
delay = -1;
|
||||
if ((n >= 0) && (n < frameCount)) {
|
||||
delay = frames.get(n).delay;
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of frames read from file.
|
||||
* @return frame count
|
||||
*/
|
||||
public int getFrameCount() {
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first (or only) image read.
|
||||
*
|
||||
* @return BufferedImage containing first frame, or null if none.
|
||||
*/
|
||||
public BufferedImage getImage() {
|
||||
return getFrame(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "Netscape" iteration count, if any.
|
||||
* A count of 0 means repeat indefinitiely.
|
||||
*
|
||||
* @return iteration count if one was specified, else 1.
|
||||
*/
|
||||
public int getLoopCount() {
|
||||
return loopCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new frame image from current data (and previous
|
||||
* frames as specified by their disposition codes).
|
||||
*/
|
||||
protected void setPixels() {
|
||||
// expose destination image's pixels as int array
|
||||
int[] dest =
|
||||
((DataBufferInt) image.getRaster().getDataBuffer()).getData();
|
||||
|
||||
// fill in starting image contents based on last image's dispose code
|
||||
if (lastDispose > 0) {
|
||||
if (lastDispose == 3) {
|
||||
// use image before last
|
||||
int n = frameCount - 2;
|
||||
if (n > 0) {
|
||||
lastImage = getFrame(n - 1);
|
||||
} else {
|
||||
lastImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastImage != null) {
|
||||
int[] prev =
|
||||
((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
|
||||
System.arraycopy(prev, 0, dest, 0, width * height);
|
||||
// copy pixels
|
||||
|
||||
if (lastDispose == 2) {
|
||||
// fill last image rect area with background color
|
||||
Graphics2D g = image.createGraphics();
|
||||
Color c = null;
|
||||
if (transparency) {
|
||||
c = new Color(0, 0, 0, 0); // assume background is transparent
|
||||
} else {
|
||||
c = new Color(lastBgColor); // use given background color
|
||||
}
|
||||
g.setColor(c);
|
||||
g.setComposite(AlphaComposite.Src); // replace area
|
||||
g.fill(lastRect);
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy each source line to the appropriate place in the destination
|
||||
int pass = 1;
|
||||
int inc = 8;
|
||||
int iline = 0;
|
||||
for (int i = 0; i < ih; i++) {
|
||||
int line = i;
|
||||
if (interlace) {
|
||||
if (iline >= ih) {
|
||||
pass++;
|
||||
switch (pass) {
|
||||
case 2 :
|
||||
iline = 4;
|
||||
break;
|
||||
case 3 :
|
||||
iline = 2;
|
||||
inc = 4;
|
||||
break;
|
||||
case 4 :
|
||||
iline = 1;
|
||||
inc = 2;
|
||||
}
|
||||
}
|
||||
line = iline;
|
||||
iline += inc;
|
||||
}
|
||||
line += iy;
|
||||
if (line < height) {
|
||||
int k = line * width;
|
||||
int dx = k + ix; // start of line in dest
|
||||
int dlim = dx + iw; // end of dest line
|
||||
if ((k + width) < dlim) {
|
||||
dlim = k + width; // past dest edge
|
||||
}
|
||||
int sx = i * iw; // start of line in source
|
||||
while (dx < dlim) {
|
||||
// map color and insert in destination
|
||||
int index = (pixels[sx++]) & 0xff;
|
||||
int c = act[index];
|
||||
if (c != 0) {
|
||||
dest[dx] = c;
|
||||
}
|
||||
dx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image contents of frame n.
|
||||
*
|
||||
* @return BufferedImage representation of frame, or null if n is invalid.
|
||||
*/
|
||||
public BufferedImage getFrame(int n) {
|
||||
BufferedImage im = null;
|
||||
if ((n >= 0) && (n < frameCount)) {
|
||||
im = frames.get(n).image;
|
||||
}
|
||||
return im;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets image size.
|
||||
*
|
||||
* @return GIF image dimensions
|
||||
*/
|
||||
public Dimension getFrameSize() {
|
||||
return new Dimension(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads GIF image from stream
|
||||
*
|
||||
* @param BufferedInputStream containing GIF file.
|
||||
* @return read status code (0 = no errors)
|
||||
*/
|
||||
public int read(BufferedInputStream is) {
|
||||
init();
|
||||
if (is != null) {
|
||||
in = is;
|
||||
readHeader();
|
||||
if (!err()) {
|
||||
readContents();
|
||||
if (frameCount < 0) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
} else {
|
||||
status = STATUS_OPEN_ERROR;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads GIF image from stream
|
||||
*
|
||||
* @param InputStream containing GIF file.
|
||||
* @return read status code (0 = no errors)
|
||||
*/
|
||||
public int read(InputStream is) {
|
||||
init();
|
||||
if (is != null) {
|
||||
if (!(is instanceof BufferedInputStream))
|
||||
is = new BufferedInputStream(is);
|
||||
in = (BufferedInputStream) is;
|
||||
readHeader();
|
||||
if (!err()) {
|
||||
readContents();
|
||||
if (frameCount < 0) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
} else {
|
||||
status = STATUS_OPEN_ERROR;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads GIF file from specified file/URL source
|
||||
* (URL assumed if name contains ":/" or "file:")
|
||||
*
|
||||
* @param name String containing source
|
||||
* @return read status code (0 = no errors)
|
||||
*/
|
||||
public int read(String name) {
|
||||
status = STATUS_OK;
|
||||
try {
|
||||
name = name.trim().toLowerCase();
|
||||
if ((name.indexOf("file:") >= 0) ||
|
||||
(name.indexOf(":/") > 0)) {
|
||||
URL url = new URL(name);
|
||||
in = new BufferedInputStream(url.openStream());
|
||||
} else {
|
||||
in = new BufferedInputStream(new FileInputStream(name));
|
||||
}
|
||||
status = read(in);
|
||||
} catch (IOException e) {
|
||||
status = STATUS_OPEN_ERROR;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes LZW image data into pixel array.
|
||||
* Adapted from John Cristy's ImageMagick.
|
||||
*/
|
||||
protected void decodeImageData() {
|
||||
int NullCode = -1;
|
||||
int npix = iw * ih;
|
||||
int available,
|
||||
clear,
|
||||
code_mask,
|
||||
code_size,
|
||||
end_of_information,
|
||||
in_code,
|
||||
old_code,
|
||||
bits,
|
||||
code,
|
||||
count,
|
||||
i,
|
||||
datum,
|
||||
data_size,
|
||||
first,
|
||||
top,
|
||||
bi,
|
||||
pi;
|
||||
|
||||
if ((pixels == null) || (pixels.length < npix)) {
|
||||
pixels = new byte[npix]; // allocate new pixel array
|
||||
}
|
||||
if (prefix == null) prefix = new short[MaxStackSize];
|
||||
if (suffix == null) suffix = new byte[MaxStackSize];
|
||||
if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1];
|
||||
|
||||
// Initialize GIF data stream decoder.
|
||||
|
||||
data_size = read();
|
||||
clear = 1 << data_size;
|
||||
end_of_information = clear + 1;
|
||||
available = clear + 2;
|
||||
old_code = NullCode;
|
||||
code_size = data_size + 1;
|
||||
code_mask = (1 << code_size) - 1;
|
||||
for (code = 0; code < clear; code++) {
|
||||
prefix[code] = 0;
|
||||
suffix[code] = (byte) code;
|
||||
}
|
||||
|
||||
// Decode GIF pixel stream.
|
||||
|
||||
datum = bits = count = first = top = pi = bi = 0;
|
||||
|
||||
for (i = 0; i < npix;) {
|
||||
if (top == 0) {
|
||||
if (bits < code_size) {
|
||||
// Load bytes until there are enough bits for a code.
|
||||
if (count == 0) {
|
||||
// Read a new data block.
|
||||
count = readBlock();
|
||||
if (count <= 0)
|
||||
break;
|
||||
bi = 0;
|
||||
}
|
||||
datum += ((block[bi]) & 0xff) << bits;
|
||||
bits += 8;
|
||||
bi++;
|
||||
count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the next code.
|
||||
|
||||
code = datum & code_mask;
|
||||
datum >>= code_size;
|
||||
bits -= code_size;
|
||||
|
||||
// Interpret the code
|
||||
|
||||
if ((code > available) || (code == end_of_information))
|
||||
break;
|
||||
if (code == clear) {
|
||||
// Reset decoder.
|
||||
code_size = data_size + 1;
|
||||
code_mask = (1 << code_size) - 1;
|
||||
available = clear + 2;
|
||||
old_code = NullCode;
|
||||
continue;
|
||||
}
|
||||
if (old_code == NullCode) {
|
||||
pixelStack[top++] = suffix[code];
|
||||
old_code = code;
|
||||
first = code;
|
||||
continue;
|
||||
}
|
||||
in_code = code;
|
||||
if (code == available) {
|
||||
pixelStack[top++] = (byte) first;
|
||||
code = old_code;
|
||||
}
|
||||
while (code > clear) {
|
||||
pixelStack[top++] = suffix[code];
|
||||
code = prefix[code];
|
||||
}
|
||||
first = (suffix[code]) & 0xff;
|
||||
|
||||
// Add a new string to the string table,
|
||||
|
||||
if (available >= MaxStackSize) {
|
||||
pixelStack[top++] = (byte) first;
|
||||
continue;
|
||||
}
|
||||
pixelStack[top++] = (byte) first;
|
||||
prefix[available] = (short) old_code;
|
||||
suffix[available] = (byte) first;
|
||||
available++;
|
||||
if (((available & code_mask) == 0)
|
||||
&& (available < MaxStackSize)) {
|
||||
code_size++;
|
||||
code_mask += available;
|
||||
}
|
||||
old_code = in_code;
|
||||
}
|
||||
|
||||
// Pop a pixel off the pixel stack.
|
||||
|
||||
top--;
|
||||
pixels[pi++] = pixelStack[top];
|
||||
i++;
|
||||
}
|
||||
|
||||
for (i = pi; i < npix; i++) {
|
||||
pixels[i] = 0; // clear missing pixels
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an error was encountered during reading/decoding
|
||||
*/
|
||||
protected boolean err() {
|
||||
return status != STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes or re-initializes reader
|
||||
*/
|
||||
protected void init() {
|
||||
status = STATUS_OK;
|
||||
frameCount = 0;
|
||||
frames = new ArrayList<>();
|
||||
gct = null;
|
||||
lct = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single byte from the input stream.
|
||||
*/
|
||||
protected int read() {
|
||||
int curByte = 0;
|
||||
try {
|
||||
curByte = in.read();
|
||||
} catch (IOException e) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
return curByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads next variable length block from input.
|
||||
*
|
||||
* @return number of bytes stored in "buffer"
|
||||
*/
|
||||
protected int readBlock() {
|
||||
blockSize = read();
|
||||
int n = 0;
|
||||
if (blockSize > 0) {
|
||||
try {
|
||||
int count = 0;
|
||||
while (n < blockSize) {
|
||||
count = in.read(block, n, blockSize - n);
|
||||
if (count == -1)
|
||||
break;
|
||||
n += count;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
if (n < blockSize) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads color table as 256 RGB integer values
|
||||
*
|
||||
* @param ncolors int number of colors to read
|
||||
* @return int array containing 256 colors (packed ARGB with full alpha)
|
||||
*/
|
||||
protected int[] readColorTable(int ncolors) {
|
||||
int nbytes = 3 * ncolors;
|
||||
int[] tab = null;
|
||||
byte[] c = new byte[nbytes];
|
||||
int n = 0;
|
||||
try {
|
||||
n = in.read(c);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if (n < nbytes) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
} else {
|
||||
tab = new int[256]; // max size to avoid bounds checks
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (i < ncolors) {
|
||||
int r = (c[j++]) & 0xff;
|
||||
int g = (c[j++]) & 0xff;
|
||||
int b = (c[j++]) & 0xff;
|
||||
tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
return tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main file parser. Reads GIF content blocks.
|
||||
*/
|
||||
protected void readContents() {
|
||||
// read GIF file content blocks
|
||||
boolean done = false;
|
||||
while (!(done || err())) {
|
||||
int code = read();
|
||||
switch (code) {
|
||||
|
||||
case 0x2C : // image separator
|
||||
readImage();
|
||||
break;
|
||||
|
||||
case 0x21 : // extension
|
||||
code = read();
|
||||
switch (code) {
|
||||
case 0xf9 : // graphics control extension
|
||||
readGraphicControlExt();
|
||||
break;
|
||||
|
||||
case 0xff : // application extension
|
||||
readBlock();
|
||||
String app = "";
|
||||
for (int i = 0; i < 11; i++) {
|
||||
app += (char) block[i];
|
||||
}
|
||||
if (app.equals("NETSCAPE2.0")) {
|
||||
readNetscapeExt();
|
||||
}
|
||||
else
|
||||
skip(); // don't care
|
||||
break;
|
||||
|
||||
default : // uninteresting extension
|
||||
skip();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x3b : // terminator
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case 0x00 : // bad byte, but keep going and see what happens
|
||||
break;
|
||||
|
||||
default :
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads Graphics Control Extension values
|
||||
*/
|
||||
protected void readGraphicControlExt() {
|
||||
read(); // block size
|
||||
int packed = read(); // packed fields
|
||||
dispose = (packed & 0x1c) >> 2; // disposal method
|
||||
if (dispose == 0) {
|
||||
dispose = 1; // elect to keep old image if discretionary
|
||||
}
|
||||
transparency = (packed & 1) != 0;
|
||||
delay = readShort() * 10; // delay in milliseconds
|
||||
transIndex = read(); // transparent color index
|
||||
read(); // block terminator
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads GIF file header information.
|
||||
*/
|
||||
protected void readHeader() {
|
||||
String id = "";
|
||||
for (int i = 0; i < 6; i++) {
|
||||
id += (char) read();
|
||||
}
|
||||
if (!id.startsWith("GIF")) {
|
||||
status = STATUS_FORMAT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
readLSD();
|
||||
if (gctFlag && !err()) {
|
||||
gct = readColorTable(gctSize);
|
||||
bgColor = gct[bgIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads next frame image
|
||||
*/
|
||||
protected void readImage() {
|
||||
ix = readShort(); // (sub)image position & size
|
||||
iy = readShort();
|
||||
iw = readShort();
|
||||
ih = readShort();
|
||||
|
||||
int packed = read();
|
||||
lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
|
||||
interlace = (packed & 0x40) != 0; // 2 - interlace flag
|
||||
// 3 - sort flag
|
||||
// 4-5 - reserved
|
||||
lctSize = 2 << (packed & 7); // 6-8 - local color table size
|
||||
|
||||
if (lctFlag) {
|
||||
lct = readColorTable(lctSize); // read table
|
||||
act = lct; // make local table active
|
||||
} else {
|
||||
act = gct; // make global table active
|
||||
if (bgIndex == transIndex)
|
||||
bgColor = 0;
|
||||
}
|
||||
int save = 0;
|
||||
if (transparency) {
|
||||
save = act[transIndex];
|
||||
act[transIndex] = 0; // set transparent color if specified
|
||||
}
|
||||
|
||||
if (act == null) {
|
||||
status = STATUS_FORMAT_ERROR; // no color table defined
|
||||
}
|
||||
|
||||
if (err()) return;
|
||||
|
||||
decodeImageData(); // decode pixel data
|
||||
skip();
|
||||
|
||||
if (err()) return;
|
||||
|
||||
frameCount++;
|
||||
|
||||
// create new image to receive frame data
|
||||
image =
|
||||
new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
|
||||
setPixels(); // transfer pixel data to image
|
||||
|
||||
frames.add(new GifFrame(image, delay)); // add image to frame list
|
||||
|
||||
if (transparency) {
|
||||
act[transIndex] = save;
|
||||
}
|
||||
resetFrame();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads Logical Screen Descriptor
|
||||
*/
|
||||
protected void readLSD() {
|
||||
|
||||
// logical screen size
|
||||
width = readShort();
|
||||
height = readShort();
|
||||
|
||||
// packed fields
|
||||
int packed = read();
|
||||
gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
|
||||
// 2-4 : color resolution
|
||||
// 5 : gct sort flag
|
||||
gctSize = 2 << (packed & 7); // 6-8 : gct size
|
||||
|
||||
bgIndex = read(); // background color index
|
||||
pixelAspect = read(); // pixel aspect ratio
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads Netscape extenstion to obtain iteration count
|
||||
*/
|
||||
protected void readNetscapeExt() {
|
||||
do {
|
||||
readBlock();
|
||||
if (block[0] == 1) {
|
||||
// loop count sub-block
|
||||
int b1 = (block[1]) & 0xff;
|
||||
int b2 = (block[2]) & 0xff;
|
||||
loopCount = (b2 << 8) | b1;
|
||||
}
|
||||
} while ((blockSize > 0) && !err());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads next 16-bit value, LSB first
|
||||
*/
|
||||
protected int readShort() {
|
||||
// read 16-bit value, LSB first
|
||||
return read() | (read() << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets frame state for reading next image.
|
||||
*/
|
||||
protected void resetFrame() {
|
||||
lastDispose = dispose;
|
||||
lastRect = new Rectangle(ix, iy, iw, ih);
|
||||
lastImage = image;
|
||||
lastBgColor = bgColor;
|
||||
/*int dispose = 0;
|
||||
boolean transparency = false;
|
||||
int delay = 0;*/
|
||||
lct = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips variable length blocks up to and including
|
||||
* next zero length block.
|
||||
*/
|
||||
protected void skip() {
|
||||
do {
|
||||
readBlock();
|
||||
} while ((blockSize > 0) && !err());
|
||||
}
|
||||
}
|
762
src/main/java/fr/pandacube/java/util/JArithmeticInterpreter.java
Normal file
762
src/main/java/fr/pandacube/java/util/JArithmeticInterpreter.java
Normal file
@@ -0,0 +1,762 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
//******************************************************************************
|
||||
//***
|
||||
//*** INTERPRETEUR ARITHMETIQUE version 2.1 Version Java
|
||||
//***
|
||||
//***
|
||||
//***
|
||||
//******************************************************************************
|
||||
|
||||
/*
|
||||
Historique:
|
||||
|
||||
2.1:
|
||||
- Version Java disponible:
|
||||
# les operateurs mathematiques disponibles sont ceux de Java donc certains manquent.
|
||||
|
||||
2.0:
|
||||
- Portage en C++ et debut en Java
|
||||
|
||||
Version C++:
|
||||
|
||||
- Meilleure gestion memoire lors de la construction de l'expression.
|
||||
- Acceleration de certaines operations.
|
||||
|
||||
Version Java:
|
||||
|
||||
- Premiere version. Normalement ca doit marcher
|
||||
|
||||
1.3b: ajoute les fonctions suivantes: (NON DISTRIBUEE)
|
||||
- reconnaissance du symbole <20>
|
||||
- ecriture formatee d'expression (<28> ameliorer)
|
||||
|
||||
1.2: corrige les bugs suivants:
|
||||
- erreur sur l'interpretation de fonctions unaires imbriquees telles que ln(exp(x)).
|
||||
- la fonction puissance (pour les puissances entieres).
|
||||
ajoute:
|
||||
- la verification de la chaine entree (voir code d'erreur)
|
||||
- la verification de l'existence de l'evaluation (voir code d'erreur)
|
||||
|
||||
|
||||
1.1: corrige un bug au niveau de l'interpretation des fonctions du type:
|
||||
|
||||
exp(-(x+y))
|
||||
|
||||
1.0: premiere version
|
||||
|
||||
Le code source peut etre librement modifie et distribue.
|
||||
|
||||
Puisqu'il s'agit d'un essai en Java, le code ne doit pas etre super genial et merite sans doute
|
||||
quelques modifications.En particulier, il serait interessant de rajouter le support des Exceptions
|
||||
pour les calculs (division par zero, etc...).
|
||||
|
||||
*/
|
||||
|
||||
// Classe servant <20> palier l'absence de passage par variables ou reference
|
||||
|
||||
class VariableInt {
|
||||
public int mValue;
|
||||
}
|
||||
|
||||
// Classe principale
|
||||
|
||||
public class JArithmeticInterpreter {
|
||||
|
||||
// Variables
|
||||
|
||||
int mOperator;
|
||||
double mValue;
|
||||
JArithmeticInterpreter fg, fd;
|
||||
|
||||
// Methods
|
||||
|
||||
// ....................................................................................
|
||||
// Node
|
||||
|
||||
private JArithmeticInterpreter() {
|
||||
this(0, 0, null, null);
|
||||
}
|
||||
|
||||
// ....................................................................................
|
||||
// Node
|
||||
|
||||
private JArithmeticInterpreter(int Operator, double Value, JArithmeticInterpreter Fg, JArithmeticInterpreter Fd) {
|
||||
mOperator = Operator;
|
||||
mValue = Value;
|
||||
fg = Fg;
|
||||
fd = Fd;
|
||||
}
|
||||
|
||||
private JArithmeticInterpreter(int Operator, double Value) {
|
||||
this(Operator, Value, null, null);
|
||||
}
|
||||
|
||||
// ....................................................................................
|
||||
// Construct_Tree
|
||||
|
||||
private static JArithmeticInterpreter constructTree(StringBuffer string, int length, int error) {
|
||||
int imbric, Bimbric;
|
||||
int priorite, ope;
|
||||
int position, positionv, i, j;
|
||||
int opetemp = 0;
|
||||
int espa = 0, espat = 0;
|
||||
int caspp = 0;
|
||||
|
||||
JArithmeticInterpreter node;
|
||||
|
||||
// Initialisation des variables
|
||||
|
||||
if (length <= 0) {
|
||||
error = 3;
|
||||
return null;
|
||||
}
|
||||
|
||||
ope = 0;
|
||||
imbric = 0;
|
||||
Bimbric = 128;
|
||||
priorite = 6;
|
||||
i = 0;
|
||||
positionv = position = 0;
|
||||
|
||||
// Mise en place des donnees sur le morceau de chaine
|
||||
|
||||
while (i < length)
|
||||
if (((string.charAt(i) > 47) && (string.charAt(i) < 58)) || (string.charAt(i) == '<27>')) {
|
||||
if (priorite > 5) {
|
||||
priorite = 5;
|
||||
positionv = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else if ((string.charAt(i) > 96) && (string.charAt(i) < 117)) {
|
||||
VariableInt Vopetemp, Vespat;
|
||||
|
||||
Vopetemp = new VariableInt();
|
||||
Vespat = new VariableInt();
|
||||
|
||||
Vopetemp.mValue = opetemp;
|
||||
Vespat.mValue = espat;
|
||||
|
||||
FindOperator(Vopetemp, Vespat, string, i);
|
||||
|
||||
opetemp = Vopetemp.mValue;
|
||||
espat = Vespat.mValue;
|
||||
|
||||
if (opetemp >= 0) {
|
||||
if (imbric < Bimbric) {
|
||||
Bimbric = imbric;
|
||||
ope = opetemp;
|
||||
position = i;
|
||||
priorite = 4;
|
||||
espa = espat;
|
||||
}
|
||||
else if ((imbric == Bimbric) && (priorite >= 4)) {
|
||||
ope = opetemp;
|
||||
position = i;
|
||||
priorite = 4;
|
||||
espa = espat;
|
||||
}
|
||||
j = i + 1;
|
||||
i += espat;
|
||||
while (j < i)
|
||||
j++;
|
||||
|
||||
}
|
||||
else if (string.charAt(i) == 't') {
|
||||
if (priorite == 6) ope = -1;
|
||||
i++;
|
||||
}
|
||||
else if (string.charAt(i) == 'p') {
|
||||
if (priorite == 6) ope = -2;
|
||||
i++;
|
||||
}
|
||||
else if (string.charAt(i) == 'r') {
|
||||
if (priorite == 6) ope = -2;
|
||||
i++;
|
||||
}
|
||||
else if (string.charAt(i) == 'n') {
|
||||
if (priorite == 6) ope = -1;
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
error = 2; // symbole non reconnu
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
switch (string.charAt(i)) {
|
||||
case '(':
|
||||
imbric++;
|
||||
i++;
|
||||
break;
|
||||
case ')':
|
||||
imbric--;
|
||||
i++;
|
||||
break;
|
||||
case '+':
|
||||
if (imbric < Bimbric) {
|
||||
Bimbric = imbric;
|
||||
priorite = 1;
|
||||
ope = 1;
|
||||
position = i;
|
||||
caspp = 0;
|
||||
}
|
||||
else if ((imbric == Bimbric) && (priorite >= 1)) {
|
||||
priorite = 1;
|
||||
ope = 1;
|
||||
position = i;
|
||||
caspp = 0;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case '-':
|
||||
if (imbric < Bimbric) {
|
||||
if ((i - 1) < 0) {
|
||||
if (priorite > 5) {
|
||||
priorite = 1;
|
||||
position = i;
|
||||
ope = 2;
|
||||
Bimbric = imbric;
|
||||
caspp = 1;
|
||||
}
|
||||
}
|
||||
else if (string.charAt(i - 1) == '(') {
|
||||
if (priorite > 1) {
|
||||
priorite = 1;
|
||||
position = i;
|
||||
Bimbric = imbric;
|
||||
caspp = 1;
|
||||
ope = 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Bimbric = imbric;
|
||||
priorite = 1;
|
||||
ope = 2;
|
||||
position = i;
|
||||
caspp = 0;
|
||||
}
|
||||
}
|
||||
else if ((imbric == Bimbric) && (priorite >= 1)) if ((i - 1) < 0) {
|
||||
if (priorite > 5) {
|
||||
priorite = 1;
|
||||
position = i;
|
||||
ope = 2;
|
||||
caspp = 1;
|
||||
}
|
||||
}
|
||||
else if (string.charAt(i - 1) == '(') {
|
||||
if (priorite > 1) {
|
||||
priorite = 1;
|
||||
position = i;
|
||||
caspp = 1;
|
||||
ope = 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
priorite = 1;
|
||||
ope = 2;
|
||||
position = i;
|
||||
caspp = 0;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case '*':
|
||||
if (imbric < Bimbric) {
|
||||
Bimbric = imbric;
|
||||
priorite = 2;
|
||||
ope = 3;
|
||||
position = i;
|
||||
}
|
||||
else if ((imbric == Bimbric) && (priorite >= 2)) {
|
||||
priorite = 2;
|
||||
ope = 3;
|
||||
position = i;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case '/':
|
||||
if (imbric < Bimbric) {
|
||||
Bimbric = imbric;
|
||||
priorite = 2;
|
||||
ope = 4;
|
||||
position = i;
|
||||
}
|
||||
else if ((imbric == Bimbric) && (priorite >= 2)) {
|
||||
priorite = 2;
|
||||
ope = 4;
|
||||
position = i;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case '^':
|
||||
if (imbric < Bimbric) {
|
||||
Bimbric = imbric;
|
||||
priorite = 3;
|
||||
ope = 5;
|
||||
position = i;
|
||||
}
|
||||
else if ((imbric == Bimbric) && (priorite >= 3)) {
|
||||
priorite = 3;
|
||||
ope = 5;
|
||||
position = i;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case '.':
|
||||
i++;
|
||||
break;
|
||||
default:
|
||||
error = 2; // symbole non reconnu
|
||||
return null;
|
||||
}
|
||||
|
||||
if (imbric != 0) {
|
||||
error = 1; // erreur de "parenthesage"
|
||||
return null;
|
||||
}
|
||||
|
||||
// Traitement des donnees
|
||||
|
||||
if (priorite == 6) {
|
||||
node = new JArithmeticInterpreter(ope, 0.0);
|
||||
return node;
|
||||
}
|
||||
else if (caspp == 1) {
|
||||
node = new JArithmeticInterpreter(2, 0);
|
||||
|
||||
node.fg = new JArithmeticInterpreter(0, 0);
|
||||
node.fd = new JArithmeticInterpreter();
|
||||
|
||||
if ((length - position - 1 - Bimbric) == 0) { // argument absent
|
||||
error = 3;
|
||||
return null;
|
||||
}
|
||||
StringBuffer temp = CopyPartialString(string, (position + 1), (length - 1 - Bimbric));
|
||||
node.fd = constructTree(temp, (length - position - 1 - Bimbric), error);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
else if (priorite == 5) {
|
||||
node = new JArithmeticInterpreter(0, calc_const(string, positionv), null, null);
|
||||
|
||||
return node;
|
||||
}
|
||||
else if (ope > 5) {
|
||||
node = new JArithmeticInterpreter(ope, 0, null, null);
|
||||
|
||||
if ((length - position - espa - Bimbric) == 0) { // argument absent
|
||||
error = 3;
|
||||
return null;
|
||||
}
|
||||
StringBuffer temp = CopyPartialString(string, (position + espa), (length - 1));
|
||||
node.fg = constructTree(temp, (length - position - espa - Bimbric), error);
|
||||
return node;
|
||||
}
|
||||
else {
|
||||
node = new JArithmeticInterpreter(ope, 0, null, null);
|
||||
|
||||
if ((position - Bimbric) == 0) { // argument absent
|
||||
error = 3;
|
||||
return null;
|
||||
}
|
||||
StringBuffer temp = CopyPartialString(string, Bimbric, (position - 1));
|
||||
node.fg = constructTree(temp, (position - Bimbric), error);
|
||||
if ((length - position - 1 - Bimbric) == 0) { // argument absent
|
||||
error = 3;
|
||||
return null;
|
||||
}
|
||||
temp = CopyPartialString(string, (position + 1), (length - 1 - Bimbric));
|
||||
node.fd = constructTree(temp, (length - position - 1 - Bimbric), error);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
// ....................................................................................
|
||||
|
||||
private double computeTree() {
|
||||
if (mOperator == 0) return mValue;
|
||||
int error = 0;
|
||||
|
||||
double valueL = fg.computeTree();
|
||||
|
||||
if (error != 0) return 0;
|
||||
double valueR = 0;
|
||||
|
||||
if (fd != null) valueR = fd.computeTree();
|
||||
if (error != 0) return 0;
|
||||
|
||||
switch (mOperator) {
|
||||
case 1: // +
|
||||
return (valueL + valueR);
|
||||
case 2: // -
|
||||
return (valueL - valueR);
|
||||
case 3: // *
|
||||
return (valueL * valueR);
|
||||
case 4: // -
|
||||
if (valueR == 0) {
|
||||
error = 1;
|
||||
return 0;
|
||||
}
|
||||
return (valueL / valueR);
|
||||
case 5: // ^
|
||||
return Math.pow(valueL, valueR);
|
||||
case 6: // exp
|
||||
return Math.exp(valueL);
|
||||
case 7: // ln
|
||||
if (valueL <= 0) {
|
||||
if (valueL < 0) error = 2;
|
||||
else
|
||||
error = 1;
|
||||
return 0;
|
||||
}
|
||||
return (Math.log(valueL) / Math.log(2));
|
||||
case 8: // log
|
||||
if (valueL <= 0) {
|
||||
if (valueL < 0) error = 2;
|
||||
else
|
||||
error = 1;
|
||||
return 0;
|
||||
}
|
||||
return Math.log(valueL);
|
||||
case 9: // sqrt
|
||||
if (valueL < 0) {
|
||||
error = 2;
|
||||
return 0;
|
||||
}
|
||||
return Math.sqrt(valueL);
|
||||
case 10: // abs
|
||||
return Math.abs(valueL);
|
||||
case 11:
|
||||
return Math.sin(valueL); // sin
|
||||
case 12:
|
||||
return Math.cos(valueL); // cos
|
||||
case 13:
|
||||
return Math.tan(valueL); // tan
|
||||
case 14:
|
||||
return Math.asin(valueL); // asin
|
||||
case 15:
|
||||
return Math.acos(valueL); // acos
|
||||
case 16:
|
||||
return Math.atan(valueL); // atan
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ....................................................................................
|
||||
// Write_Tree
|
||||
|
||||
private void writeTree(StringBuffer string) {
|
||||
boolean parenthese = false;
|
||||
|
||||
switch (mOperator) {
|
||||
case 0:
|
||||
string.append(StringUtil.formatDouble(mValue));
|
||||
break;
|
||||
case 1:
|
||||
fg.writeTree(string);
|
||||
string.append('+');
|
||||
fd.writeTree(string);
|
||||
break;
|
||||
case 2:
|
||||
if ((fg.mOperator == 0) && (fg.mValue == 0)) ;
|
||||
else
|
||||
fg.writeTree(string);
|
||||
string.append('-');
|
||||
if ((fd.mOperator == 1) || (fd.mOperator == 2)) {
|
||||
parenthese = true;
|
||||
string.append('(');
|
||||
}
|
||||
fd.writeTree(string);
|
||||
if (parenthese == true) string.append(')');
|
||||
break;
|
||||
case 3:
|
||||
if ((fg.mOperator == 1) || (fg.mOperator == 2)) {
|
||||
parenthese = true;
|
||||
string.append('(');
|
||||
}
|
||||
fg.writeTree(string);
|
||||
if (parenthese == true) string.append(')');
|
||||
parenthese = false;
|
||||
string.append('*');
|
||||
if ((fd.mOperator == 1) || (fd.mOperator == 2)) {
|
||||
parenthese = true;
|
||||
string.append('(');
|
||||
}
|
||||
fd.writeTree(string);
|
||||
if (parenthese == true) string.append(')');
|
||||
break;
|
||||
case 4:
|
||||
if ((fg.mOperator == 1) || (fg.mOperator == 2)) {
|
||||
parenthese = true;
|
||||
string.append('(');
|
||||
}
|
||||
fg.writeTree(string);
|
||||
if (parenthese == true) string.append(')');
|
||||
parenthese = false;
|
||||
string.append('/');
|
||||
if ((fd.mOperator > 0) && (fd.mOperator < 5)) {
|
||||
parenthese = true;
|
||||
string.append('(');
|
||||
}
|
||||
fd.writeTree(string);
|
||||
if (parenthese == true) string.append(')');
|
||||
break;
|
||||
case 5:
|
||||
if ((fg.mOperator > 0) && (fg.mOperator < 5)) {
|
||||
parenthese = true;
|
||||
string.append('(');
|
||||
}
|
||||
fg.writeTree(string);
|
||||
if (parenthese == true) string.append(')');
|
||||
parenthese = false;
|
||||
string.append('^');
|
||||
if ((fd.mOperator > 0) && (fd.mOperator < 5)) {
|
||||
parenthese = true;
|
||||
string.append('(');
|
||||
}
|
||||
fd.writeTree(string);
|
||||
if (parenthese == true) string.append(')');
|
||||
break;
|
||||
case 6:
|
||||
string.append("exp(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 7:
|
||||
string.append("log(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 8:
|
||||
string.append("ln(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 9:
|
||||
string.append("sqrt(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 10:
|
||||
string.append("|");
|
||||
fg.writeTree(string);
|
||||
string.append('|');
|
||||
break;
|
||||
case 11:
|
||||
string.append("sin(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 12:
|
||||
string.append("cos(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 13:
|
||||
string.append("tan(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 14:
|
||||
string.append("asin(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 15:
|
||||
string.append("acos(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
case 16:
|
||||
string.append("atan(");
|
||||
fg.writeTree(string);
|
||||
string.append(')');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ....................................................................................
|
||||
// calc_const
|
||||
|
||||
private static double calc_const(StringBuffer chaine, int pos) {
|
||||
int i = pos, j;
|
||||
double temp = 0;
|
||||
int signe = 1;
|
||||
int longueur = chaine.length();
|
||||
|
||||
if (chaine.charAt(i) == '-') {
|
||||
signe = -1;
|
||||
i++;
|
||||
}
|
||||
if (chaine.charAt(i) == 'π') return signe * Math.PI;
|
||||
|
||||
while (i < longueur && chaine.charAt(i) > 47 && chaine.charAt(i) < 58) {
|
||||
temp = temp * 10 + (chaine.charAt(i) - 48);
|
||||
i++;
|
||||
}
|
||||
if (i < longueur && chaine.charAt(i) == '.') {
|
||||
i++;
|
||||
j = 1;
|
||||
while (i < longueur && chaine.charAt(i) > 47 && chaine.charAt(i) < 58) {
|
||||
temp = temp + (chaine.charAt(i) - 48) * Math.exp(-j * 2.30258509);
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return (signe * temp);
|
||||
}
|
||||
|
||||
// ....................................................................................
|
||||
// FindOperator
|
||||
|
||||
private static void FindOperator(VariableInt oper, VariableInt esp, StringBuffer chaine, int pos) {
|
||||
switch (chaine.charAt(pos)) {
|
||||
case 'a':
|
||||
switch (chaine.charAt(pos + 1)) {
|
||||
case 'b':
|
||||
esp.mValue = 3;
|
||||
oper.mValue = 10;
|
||||
break;
|
||||
case 'c':
|
||||
esp.mValue = 4;
|
||||
oper.mValue = 15;
|
||||
break;
|
||||
case 's':
|
||||
esp.mValue = 4;
|
||||
oper.mValue = 14;
|
||||
break;
|
||||
case 't':
|
||||
esp.mValue = 4;
|
||||
oper.mValue = 16;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (chaine.charAt(pos + 1) == 'h') {
|
||||
esp.mValue = 2;
|
||||
oper.mValue = 18;
|
||||
}
|
||||
else if ((chaine.charAt(pos + 1) == 'o') && (chaine.charAt(pos + 2) == 's'))
|
||||
if (chaine.charAt(pos + 3) == 'h') {
|
||||
esp.mValue = 4;
|
||||
oper.mValue = 18;
|
||||
}
|
||||
else {
|
||||
esp.mValue = 3;
|
||||
oper.mValue = 12;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if ((chaine.charAt(pos + 1) == 'x') && (chaine.charAt(pos + 2) == 'p')) {
|
||||
esp.mValue = 3;
|
||||
oper.mValue = 6;
|
||||
}
|
||||
else
|
||||
oper.mValue = -10;
|
||||
break;
|
||||
case 'l':
|
||||
if (chaine.charAt(pos + 1) == 'n') {
|
||||
esp.mValue = 2;
|
||||
oper.mValue = 7;
|
||||
}
|
||||
else if ((chaine.charAt(pos + 1) == 'o') && (chaine.charAt(pos + 2) == 'g')) {
|
||||
esp.mValue = 3;
|
||||
oper.mValue = 8;
|
||||
}
|
||||
else
|
||||
oper.mValue = -10;
|
||||
break;
|
||||
case 's':
|
||||
if (chaine.charAt(pos + 1) == 'h') {
|
||||
esp.mValue = 2;
|
||||
oper.mValue = 17;
|
||||
}
|
||||
else if (chaine.charAt(pos + 1) == 'q') {
|
||||
esp.mValue = 4;
|
||||
oper.mValue = 9;
|
||||
}
|
||||
else if (chaine.charAt(pos + 3) == 'h') {
|
||||
esp.mValue = 4;
|
||||
oper.mValue = 17;
|
||||
}
|
||||
else {
|
||||
esp.mValue = 3;
|
||||
oper.mValue = 11;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (chaine.charAt(pos + 1) == 'h') {
|
||||
esp.mValue = 2;
|
||||
oper.mValue = 19;
|
||||
}
|
||||
else if ((chaine.charAt(pos + 1) == 'a') && (chaine.charAt(pos + 2) == 'n')) {
|
||||
if (chaine.charAt(pos + 3) == 'h') {
|
||||
esp.mValue = 4;
|
||||
oper.mValue = 19;
|
||||
}
|
||||
else {
|
||||
esp.mValue = 3;
|
||||
oper.mValue = 13;
|
||||
}
|
||||
}
|
||||
else
|
||||
oper.mValue = -10;
|
||||
break;
|
||||
default:
|
||||
oper.mValue = -10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ....................................................................................
|
||||
// CopyPartialString
|
||||
|
||||
private static StringBuffer CopyPartialString(StringBuffer chaine, int debut, int fin) {
|
||||
StringBuffer chartemp;
|
||||
int a = fin - debut + 1;
|
||||
chartemp = new StringBuffer(a + 1);
|
||||
|
||||
for (int i = 0; i < a; i++)
|
||||
chartemp.append(chaine.charAt(debut + i));
|
||||
|
||||
return chartemp;
|
||||
}
|
||||
|
||||
public static double getResultFromExpression(String expr, StringBuffer writeTree) {
|
||||
StringBuffer input = new StringBuffer(expr);
|
||||
|
||||
JArithmeticInterpreter jai = null;
|
||||
|
||||
try {
|
||||
jai = JArithmeticInterpreter.constructTree(input, input.length(), 0);
|
||||
} catch (Exception e) {}
|
||||
|
||||
if (jai == null) throw new IllegalArgumentException("Le calcul passé en paramètre est invalide");
|
||||
|
||||
if (writeTree != null) {
|
||||
writeTree.setLength(0);
|
||||
jai.writeTree(writeTree);
|
||||
}
|
||||
|
||||
return jai.computeTree();
|
||||
}
|
||||
|
||||
public static double getResultFromExpression(String expr) {
|
||||
return getResultFromExpression(expr, null);
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
|
||||
StringBuffer b = new StringBuffer(0);
|
||||
|
||||
String disp_res = StringUtil.formatDouble(JArithmeticInterpreter.getResultFromExpression("1245.25*2", b));
|
||||
|
||||
System.out.println(disp_res);
|
||||
System.out.println(b);
|
||||
} // */
|
||||
|
||||
}
|
71
src/main/java/fr/pandacube/java/util/Log.java
Normal file
71
src/main/java/fr/pandacube/java/util/Log.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class Log {
|
||||
|
||||
private static Logger logger;
|
||||
private static AtomicBoolean logDebug = new AtomicBoolean(false);
|
||||
|
||||
public static void setDebugState(boolean newVal) {
|
||||
logDebug.set(newVal);
|
||||
}
|
||||
|
||||
public static boolean getDebugState() {
|
||||
return logDebug.get();
|
||||
}
|
||||
|
||||
public static Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public static void setLogger(Logger l) {
|
||||
logger = l;
|
||||
}
|
||||
|
||||
public static void info(String message) {
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
public static void warning(String message, Throwable t) {
|
||||
logger.log(Level.WARNING, message, t);
|
||||
}
|
||||
|
||||
public static void warning(Throwable t) {
|
||||
logger.log(Level.WARNING, "", t);
|
||||
}
|
||||
|
||||
public static void warning(String message) {
|
||||
logger.warning(message);
|
||||
}
|
||||
|
||||
public static void severe(String message, Throwable t) {
|
||||
logger.log(Level.SEVERE, message, t);
|
||||
}
|
||||
|
||||
public static void severe(Throwable t) {
|
||||
logger.log(Level.SEVERE, "", t);
|
||||
}
|
||||
|
||||
public static void severe(String message) {
|
||||
logger.severe(message);
|
||||
}
|
||||
|
||||
public static void debug(String message, Throwable t) {
|
||||
if (!logDebug.get()) return;
|
||||
logger.log(Level.INFO, message, t);
|
||||
}
|
||||
|
||||
public static void debug(Throwable t) {
|
||||
if (!logDebug.get()) return;
|
||||
logger.log(Level.INFO, "", t);
|
||||
}
|
||||
|
||||
public static void debug(String message) {
|
||||
if (!logDebug.get()) return;
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
}
|
84
src/main/java/fr/pandacube/java/util/MinecraftVersion.java
Normal file
84
src/main/java/fr/pandacube/java/util/MinecraftVersion.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public enum MinecraftVersion {
|
||||
v1_7_2_to_1_7_5(4, "1.7.2-1.7.5"),
|
||||
v1_7_6_to_1_7_10(5, "1.7.6-1.7.10"),
|
||||
v1_8(47, "1.8.x"),
|
||||
v1_9(107, "1.9"),
|
||||
v1_9_1(108, "1.9.1"),
|
||||
v1_9_2(109, "1.9.2"),
|
||||
v1_9_3_to_1_9_4(110, "1.9.3-1.9.4"),
|
||||
v1_10(210, "1.10.x"),
|
||||
v1_11(315, "1.11");
|
||||
|
||||
public final int versionNumber;
|
||||
public final String versionDisplay;
|
||||
|
||||
private MinecraftVersion(int v, String d) {
|
||||
versionNumber = v;
|
||||
versionDisplay = d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return versionDisplay;
|
||||
}
|
||||
|
||||
public static MinecraftVersion getVersion(int v) {
|
||||
for (MinecraftVersion mcV : MinecraftVersion.values())
|
||||
if (mcV.versionNumber == v) return mcV;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String displayOptimizedListOfVersions(List<MinecraftVersion> versions) {
|
||||
return String.join(", ", getVersionsDisplayList(versions));
|
||||
}
|
||||
|
||||
|
||||
public static final List<String> getVersionsDisplayList(List<MinecraftVersion> versions) {
|
||||
versions = new ArrayList<>(new HashSet<>(versions));
|
||||
versions.sort((a, b) -> Integer.compare(a.versionNumber, b.versionNumber));
|
||||
|
||||
List<String> ret = new ArrayList<>();
|
||||
|
||||
// grouping 1.7 versions
|
||||
if (versions.contains(v1_7_2_to_1_7_5) && versions.contains(v1_7_6_to_1_7_10)) {
|
||||
versions.remove(v1_7_2_to_1_7_5);
|
||||
versions.remove(v1_7_6_to_1_7_10);
|
||||
ret.add("1.7");
|
||||
}
|
||||
// grouping 1.8 versions
|
||||
if (versions.contains(v1_8)) {
|
||||
versions.remove(v1_8);
|
||||
ret.add("1.8");
|
||||
}
|
||||
// grouping 1.9 versions
|
||||
if (versions.contains(v1_9) && versions.contains(v1_9_1) && versions.contains(v1_9_2)
|
||||
&& versions.contains(v1_9_3_to_1_9_4)) {
|
||||
versions.remove(v1_9);
|
||||
versions.remove(v1_9_1);
|
||||
versions.remove(v1_9_2);
|
||||
versions.remove(v1_9_3_to_1_9_4);
|
||||
ret.add("1.9");
|
||||
}
|
||||
// grouping 1.10 versions
|
||||
if (versions.contains(v1_10)) {
|
||||
versions.remove(v1_10);
|
||||
ret.add("1.10");
|
||||
}
|
||||
|
||||
for (MinecraftVersion v : versions)
|
||||
ret.add(v.versionDisplay);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
}
|
93
src/main/java/fr/pandacube/java/util/MinecraftWebUtil.java
Normal file
93
src/main/java/fr/pandacube/java/util/MinecraftWebUtil.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
public class MinecraftWebUtil {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Convert a legacy Minecraft color coded String into HTML Format.
|
||||
|
||||
@param
|
||||
*/
|
||||
public static String fromMinecraftColorCodeToHTML(char code_prefix, String ligne)
|
||||
{
|
||||
String color_char = "0123456789abcdefr";
|
||||
|
||||
String builder = "";
|
||||
char currentColor = 'r';
|
||||
boolean bold = false, italic = false, underlined = false, strikethrough = false;
|
||||
|
||||
for (int i=0; i<ligne.length(); i++) {
|
||||
char c = ligne.charAt(i);
|
||||
|
||||
if (c == code_prefix && (i<ligne.length()-1)) {
|
||||
i++;
|
||||
c = ligne.charAt(i);
|
||||
if (color_char.contains(new Character(c).toString().toLowerCase())) {
|
||||
if (bold) {
|
||||
builder += "</span>";
|
||||
bold = false;
|
||||
}
|
||||
if (italic) {
|
||||
builder += "</span>";
|
||||
italic = false;
|
||||
}
|
||||
if (underlined) {
|
||||
builder += "</span>";
|
||||
underlined = false;
|
||||
}
|
||||
if (strikethrough) {
|
||||
builder += "</span>";
|
||||
strikethrough = false;
|
||||
}
|
||||
if (Character.toLowerCase(c) != currentColor) {
|
||||
if (currentColor != 'r')
|
||||
builder += "</span>";
|
||||
if (Character.toLowerCase(c) != 'r')
|
||||
builder += "<span class=\"c" + Character.toUpperCase(c) + "\">";
|
||||
currentColor = Character.toLowerCase(c);
|
||||
}
|
||||
|
||||
}
|
||||
else if (Character.toLowerCase(c) == 'l') {
|
||||
if (!bold) {
|
||||
builder += "<span class=\"cL\">";
|
||||
bold = true;
|
||||
}
|
||||
}
|
||||
else if (Character.toLowerCase(c) == 'm') {
|
||||
if (!strikethrough) {
|
||||
builder += "<span class=\"cM\">";
|
||||
strikethrough = true;
|
||||
}
|
||||
}
|
||||
else if (Character.toLowerCase(c) == 'n') {
|
||||
if (!underlined) {
|
||||
builder += "<span class=\"cN\">";
|
||||
underlined = true;
|
||||
}
|
||||
}
|
||||
else if (Character.toLowerCase(c) == 'o') {
|
||||
if (!italic) {
|
||||
builder += "<span class=\"cO\">";
|
||||
italic = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
builder += code_prefix + c;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
builder += c;
|
||||
}
|
||||
|
||||
return builder;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
208
src/main/java/fr/pandacube/java/util/PlayerFinder.java
Normal file
208
src/main/java/fr/pandacube/java/util/PlayerFinder.java
Normal file
@@ -0,0 +1,208 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.SQLLoginHistory;
|
||||
import fr.pandacube.java.util.db.SQLPlayer;
|
||||
import fr.pandacube.java.util.db.SQLUUIDPlayer;
|
||||
import fr.pandacube.java.util.db.sql_tools.ORM;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLOrderBy;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereLike;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLOrderBy.Direction;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
import net.alpenblock.bungeeperms.BungeePerms;
|
||||
|
||||
/*
|
||||
* Etape de recherche de joueur :
|
||||
* Passer par bungeeperms (si accessible)
|
||||
* utiliser directement la table pseudo <-> uuid
|
||||
* chercher dans l'historique de login
|
||||
*/
|
||||
public class PlayerFinder {
|
||||
|
||||
private static BungeePerms getPermPlugin() {
|
||||
try {
|
||||
return BungeePerms.getInstance();
|
||||
} catch (NoClassDefFoundError | Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String getLastKnownName(UUID id) {
|
||||
if (id == null) return null;
|
||||
|
||||
// on passe par le plugin de permission (mise en cache ? )
|
||||
BungeePerms pl = getPermPlugin();
|
||||
if (pl != null) return pl.getPermissionsManager().getUUIDPlayerDB().getPlayerName(id);
|
||||
|
||||
// on tente en accédant directement à la table des identifiants
|
||||
try {
|
||||
SQLUUIDPlayer el = ORM.getFirst(SQLUUIDPlayer.class,
|
||||
new SQLWhereComp(SQLUUIDPlayer.uuid, SQLComparator.EQ, id.toString()), null);
|
||||
if (el != null) return el.get(SQLUUIDPlayer.player);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Can't search for player name from uuid in database", e);
|
||||
}
|
||||
|
||||
// le pseudo est introuvable
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<String> getLocalNameHistory(UUID id) {
|
||||
List<String> ret = new ArrayList<>();
|
||||
|
||||
if (id == null) return ret;
|
||||
|
||||
String last = getLastKnownName(id);
|
||||
if (last != null) ret.add(last);
|
||||
|
||||
try {
|
||||
List<SQLLoginHistory> els = ORM.getAll(SQLLoginHistory.class,
|
||||
new SQLWhereComp(SQLLoginHistory.playerId, SQLComparator.EQ, id.toString()),
|
||||
new SQLOrderBy().addField(SQLLoginHistory.time, Direction.DESC), null, null);
|
||||
|
||||
for (SQLLoginHistory el : els) {
|
||||
String name = el.get(SQLLoginHistory.playerName);
|
||||
if (ret.contains(name)) continue;
|
||||
ret.add(name);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe("Can't search for olds players names from uuid in database", e);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cherche un identifiant de compte en se basant sur le pseudo passé en
|
||||
* paramètre. La méthode
|
||||
* cherchera d'abord dans les derniers pseudos connus. Puis, cherchera la
|
||||
* dernière personne à
|
||||
* s'être connecté avec ce pseudo sur le serveur.
|
||||
*
|
||||
* @param exactName le pseudo complet, insensible à la casse, et dans un
|
||||
* format de pseudo valide
|
||||
* @param old si on doit chercher dans les anciens pseudos de joueurs
|
||||
* @return l'UUID du joueur si trouvé, null sinon
|
||||
*/
|
||||
public static UUID getPlayerId(String exactName, boolean old) {
|
||||
if (!isValidPlayerName(exactName)) return null; // évite une recherche
|
||||
// inutile dans la base
|
||||
// de donnée
|
||||
|
||||
// on tente d'abord via le plugin de permission
|
||||
BungeePerms pl = getPermPlugin();
|
||||
if (pl != null) return pl.getPermissionsManager().getUUIDPlayerDB().getUUID(exactName);
|
||||
|
||||
// on tente en accédant directement à la table des identifiants
|
||||
try {
|
||||
SQLUUIDPlayer el = ORM.getFirst(SQLUUIDPlayer.class,
|
||||
new SQLWhereLike(SQLUUIDPlayer.player, exactName.replace("_", "\\_")), null);
|
||||
if (el != null) return el.getUUID();
|
||||
} catch (Exception e) {
|
||||
Log.severe("Can't search for uuid from player name in database", e);
|
||||
}
|
||||
|
||||
if (!old) return null;
|
||||
|
||||
// on recherche dans les anciens pseudos
|
||||
try {
|
||||
SQLLoginHistory el = ORM.getFirst(SQLLoginHistory.class,
|
||||
new SQLWhereLike(SQLLoginHistory.playerName, exactName.replace("_", "\\_")),
|
||||
new SQLOrderBy().addField(SQLLoginHistory.time, Direction.DESC));
|
||||
if (el != null) return el.getPlayerId();
|
||||
} catch (Exception e) {
|
||||
Log.severe("Can't search for uuid from old player name in database", e);
|
||||
}
|
||||
|
||||
// on a pas trouvé
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param query le pseudo, partiel ou complet, insensible à la casse, qu'on
|
||||
* recherche
|
||||
* @param old si on cherche aussi dans les anciens pseudos
|
||||
* @return
|
||||
*/
|
||||
public static List<PlayerSearchResult> searchForPlayers(String query, boolean old) {
|
||||
List<PlayerSearchResult> res = new ArrayList<>();
|
||||
|
||||
if (!isValidPlayerName(query)) return res;
|
||||
|
||||
// rechercher parmis les derniers pseudos connus de chaque joueurs
|
||||
try {
|
||||
List<SQLUUIDPlayer> els = ORM.getAll(SQLUUIDPlayer.class,
|
||||
new SQLWhereLike(SQLUUIDPlayer.player, "%" + query.replace("_", "\\_") + "%"), null, null, null);
|
||||
|
||||
for (SQLUUIDPlayer el : els)
|
||||
res.add(new PlayerSearchResult(el.getUUID(), el.get(SQLUUIDPlayer.player), null));
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe("Can't search for players names in database", e);
|
||||
}
|
||||
|
||||
if (!old) return res;
|
||||
|
||||
// rechercher parmi les anciens pseudos de joueurs
|
||||
try {
|
||||
List<SQLLoginHistory> els = ORM.getAll(SQLLoginHistory.class,
|
||||
new SQLWhereLike(SQLLoginHistory.playerName, "%" + query.replace("_", "\\_") + "%"),
|
||||
new SQLOrderBy().addField(SQLLoginHistory.time, Direction.DESC), null, null);
|
||||
|
||||
for (SQLLoginHistory el : els) {
|
||||
if (res.contains(new PlayerSearchResult(el.getPlayerId(), null, null))) continue;
|
||||
res.add(new PlayerSearchResult(el.getPlayerId(), getLastKnownName(el.getPlayerId()),
|
||||
el.get(SQLLoginHistory.playerName)));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe("Can't search for uuid from player name in database", e);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static class PlayerSearchResult {
|
||||
public final UUID uuid;
|
||||
public String lastName;
|
||||
public final String nameFound;
|
||||
|
||||
PlayerSearchResult(UUID id, String last, String found) {
|
||||
uuid = id;
|
||||
lastName = last;
|
||||
nameFound = found;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof PlayerSearchResult)) return false;
|
||||
return uuid.equals(((PlayerSearchResult) o).uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uuid.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isValidPlayerName(String name) {
|
||||
if (name == null) return false;
|
||||
return name.matches("[0-9a-zA-Z_]{2,16}");
|
||||
}
|
||||
|
||||
public static SQLPlayer getDBPlayer(UUID id) throws Exception {
|
||||
if (id == null) return null;
|
||||
return ORM.getFirst(SQLPlayer.class, new SQLWhereComp(SQLPlayer.playerId, SQLComparator.EQ, id.toString()),
|
||||
null);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,145 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This class performs a name lookup for a player and gets back all the name
|
||||
* changes of the player (if any).
|
||||
* <br/>
|
||||
* <a href="https://bukkit.org/threads/player-name-history-lookup.412679/">https
|
||||
* ://bukkit.org/threads/player-name-history-lookup.412679/</a>
|
||||
*
|
||||
* @since 25-3-2016
|
||||
* @author mine-care (AKA fillpant)
|
||||
*
|
||||
*/
|
||||
public class PlayerNameHistoryLookup {
|
||||
|
||||
/**
|
||||
* The URL from Mojang API that provides the JSON String in response.
|
||||
*/
|
||||
private static final String LOOKUP_URL = "https://api.mojang.com/user/profiles/%s/names";
|
||||
|
||||
private static final Gson JSON_PARSER = new Gson();
|
||||
|
||||
/**
|
||||
* <h1>NOTE: Avoid running this method <i>Synchronously</i> with the main
|
||||
* thread!It blocks while attempting to get a response from Mojang servers!
|
||||
* </h1>
|
||||
*
|
||||
* @param player The UUID of the player to be looked up.
|
||||
* @return Returns an array of {@link PreviousPlayerNameEntry} objects, or
|
||||
* null if the response couldn't be interpreted.
|
||||
* @throws IOException {@link #getPlayerPreviousNames(String)}
|
||||
*/
|
||||
public static PreviousPlayerNameEntry[] getPlayerPreviousNames(UUID player) throws IOException {
|
||||
return getPlayerPreviousNames(player.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1>NOTE: Avoid running this method <i>Synchronously</i> with the main
|
||||
* thread! It blocks while attempting to get a response from Mojang servers!
|
||||
* </h1>
|
||||
* Alternative method accepting an {@link OfflinePlayer} (and therefore
|
||||
* {@link Player}) objects as parameter.
|
||||
*
|
||||
* @param uuid The UUID String to lookup
|
||||
* @return Returns an array of {@link PreviousPlayerNameEntry} objects, or
|
||||
* null if the response couldn't be interpreted.
|
||||
* @throws IOException {@link #getRawJsonResponse(String)}
|
||||
*/
|
||||
public static PreviousPlayerNameEntry[] getPlayerPreviousNames(String uuid) throws IOException {
|
||||
if (uuid == null || uuid.isEmpty()) return null;
|
||||
uuid = uuid.replace("-", "");
|
||||
String response = getRawJsonResponse(new URL(String.format(LOOKUP_URL, uuid)));
|
||||
PreviousPlayerNameEntry[] names = JSON_PARSER.fromJson(response, PreviousPlayerNameEntry[].class);
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method used to read the response of Mojang's API
|
||||
* webservers.
|
||||
*
|
||||
* @param u the URL to connect to
|
||||
* @return a String with the data read.
|
||||
* @throws IOException Inherited by {@link BufferedReader#readLine()},
|
||||
* {@link BufferedReader#close()}, {@link URL},
|
||||
* {@link HttpURLConnection#getInputStream()}
|
||||
*/
|
||||
private static String getRawJsonResponse(URL u) throws IOException {
|
||||
HttpURLConnection con = (HttpURLConnection) u.openConnection();
|
||||
con.setDoInput(true);
|
||||
con.setConnectTimeout(2000);
|
||||
con.setReadTimeout(2000);
|
||||
con.connect();
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||
String response = in.readLine();
|
||||
in.close();
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents the typical response expected by Mojang servers
|
||||
* when requesting the name history of a player.
|
||||
*/
|
||||
public class PreviousPlayerNameEntry {
|
||||
private String name;
|
||||
@SerializedName("changedToAt")
|
||||
private long changeTime;
|
||||
|
||||
/**
|
||||
* Gets the player name of this entry.
|
||||
*
|
||||
* @return The name of the player.
|
||||
*/
|
||||
public String getPlayerName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time of change of the name.
|
||||
* <br>
|
||||
* <b>Note: This will return 0 if the name is the original (initial)
|
||||
* name of the player! Make sure you check if it is 0 before handling!
|
||||
* <br>
|
||||
* Parsing 0 to a Date will result in the date "01/01/1970".</b>
|
||||
*
|
||||
* @return a timestamp in miliseconds that you can turn into a date or
|
||||
* handle however you want :)
|
||||
*/
|
||||
public long getChangeTime() {
|
||||
return changeTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this name is the name used to register the account (the
|
||||
* initial/original name)
|
||||
*
|
||||
* @return a boolean, true if it is the the very first name of the
|
||||
* player, otherwise false.
|
||||
*/
|
||||
public boolean isPlayersInitialName() {
|
||||
return getChangeTime() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Name: " + name + " Date of change: " + new Date(changeTime).toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
System.out.println(Arrays.toString(getPlayerPreviousNames("a18d9b2c-e18f-4933-9e15-36452bc36857")));
|
||||
}
|
||||
|
||||
}
|
17
src/main/java/fr/pandacube/java/util/RandomUtil.java
Normal file
17
src/main/java/fr/pandacube/java/util/RandomUtil.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomUtil {
|
||||
|
||||
public static Random rand = new Random();
|
||||
|
||||
public static int nextIntBetween(int minInclu, int maxExclu) {
|
||||
return rand.nextInt(maxExclu - minInclu) + minInclu;
|
||||
}
|
||||
|
||||
public static double nextDoubleBetween(double minInclu, double maxExclu) {
|
||||
return rand.nextDouble() * (maxExclu - minInclu) + minInclu;
|
||||
}
|
||||
|
||||
}
|
110
src/main/java/fr/pandacube/java/util/ServerPropertyFile.java
Normal file
110
src/main/java/fr/pandacube/java/util/ServerPropertyFile.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class ServerPropertyFile {
|
||||
|
||||
private transient File file;
|
||||
|
||||
private String name = "default_name";
|
||||
private String memory = "512M";
|
||||
private String javaArgs = "";
|
||||
private String MinecraftArgs = "";
|
||||
private String jarFile = "";
|
||||
|
||||
public ServerPropertyFile(File f) {
|
||||
if (f == null) throw new IllegalArgumentException("f ne doit pas être null");
|
||||
file = f;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge le fichier de configuration dans cette instance de la classe
|
||||
*
|
||||
* @return true si le chargement a réussi, false sinon
|
||||
*/
|
||||
public boolean loadFromFile() {
|
||||
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
|
||||
|
||||
ServerPropertyFile dataFile = new Gson().fromJson(in, getClass());
|
||||
|
||||
name = dataFile.name;
|
||||
memory = dataFile.memory;
|
||||
javaArgs = dataFile.javaArgs;
|
||||
MinecraftArgs = dataFile.MinecraftArgs;
|
||||
jarFile = dataFile.jarFile;
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.severe(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean save() {
|
||||
try (BufferedWriter out = new BufferedWriter(new FileWriter(file, false))) {
|
||||
|
||||
new Gson().toJson(this, out);
|
||||
out.flush();
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.severe(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getMemory() {
|
||||
return memory;
|
||||
}
|
||||
|
||||
public String getJavaArgs() {
|
||||
return javaArgs;
|
||||
}
|
||||
|
||||
public String getMinecraftArgs() {
|
||||
return MinecraftArgs;
|
||||
}
|
||||
|
||||
public String getJarFile() {
|
||||
return jarFile;
|
||||
}
|
||||
|
||||
public void setName(String n) {
|
||||
if (n == null || !n.matches("^[a-zA-Z]$")) throw new IllegalArgumentException();
|
||||
name = n;
|
||||
}
|
||||
|
||||
public void setMemory(String m) {
|
||||
if (m == null || !m.matches("^[0-9]+[mgMG]$")) throw new IllegalArgumentException();
|
||||
memory = m;
|
||||
}
|
||||
|
||||
public void setJavaArgs(String ja) {
|
||||
if (ja == null) throw new IllegalArgumentException();
|
||||
javaArgs = ja;
|
||||
}
|
||||
|
||||
public void setMinecraftArgs(String ma) {
|
||||
if (ma == null) throw new IllegalArgumentException();
|
||||
MinecraftArgs = ma;
|
||||
}
|
||||
|
||||
public void setJarFile(String j) {
|
||||
if (j == null) throw new IllegalArgumentException();
|
||||
jarFile = j;
|
||||
}
|
||||
|
||||
}
|
22
src/main/java/fr/pandacube/java/util/StringUtil.java
Normal file
22
src/main/java/fr/pandacube/java/util/StringUtil.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package fr.pandacube.java.util;
|
||||
|
||||
public class StringUtil {
|
||||
public static String formatDouble(double d) {
|
||||
if (d == (long) d)
|
||||
return String.format("%d", (long) d);
|
||||
return String.valueOf(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s Chaine de caractère à parcourir
|
||||
* @param c_match le caractère dont on doit retourner le nombre d'occurence
|
||||
* @return nombre d'occurence de <b>c_match</b> dans <b>s</b>
|
||||
*/
|
||||
public static int char_count(CharSequence s, char c_match) {
|
||||
char[] chars = s.toString().toCharArray();
|
||||
int count = 0;
|
||||
for (char c : chars)
|
||||
if (c == c_match) count++;
|
||||
return count;
|
||||
}
|
||||
}
|
161
src/main/java/fr/pandacube/java/util/chat_display/Display.java
Normal file
161
src/main/java/fr/pandacube/java/util/chat_display/Display.java
Normal file
@@ -0,0 +1,161 @@
|
||||
package fr.pandacube.java.util.chat_display;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
||||
public class Display {
|
||||
|
||||
private BaseComponent first = new TextComponent("");
|
||||
|
||||
private BaseComponent current = null;
|
||||
|
||||
public Display() {}
|
||||
|
||||
/**
|
||||
* Après l'appel de ce contructeur, vous devez appeler nextComponent() pour
|
||||
* initialiser la composante suivante
|
||||
*/
|
||||
public Display(String legacyText) {
|
||||
convertAndAddLegacy(legacyText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit un message en mettant à la ligne après chaque chaine passé en
|
||||
* paramètre.<br/>
|
||||
* Après l'appel de ce contructeur, vous devez appeler nextComponent() pour
|
||||
* initialiser la composante suivante
|
||||
*/
|
||||
public Display(String[] legacyText) {
|
||||
boolean f = true;
|
||||
for (String s : legacyText) {
|
||||
if (s == null) s = "";
|
||||
if (!f) first.addExtra("\n");
|
||||
f = false;
|
||||
convertAndAddLegacy(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit un message en mettant à la ligne après chaque chaine passé en
|
||||
* paramètre.<br/>
|
||||
* Après l'appel de ce contructeur, vous devez appeler nextComponent() pour
|
||||
* initialiser la composante suivante
|
||||
*/
|
||||
public Display(List<String> legacyText) {
|
||||
this(legacyText.toArray(new String[legacyText.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Après l'appel de ce contructeur, vous devez appeler nextComponent() pour
|
||||
* initialiser la composante suivante
|
||||
*/
|
||||
public Display(BaseComponent firstComponent) {
|
||||
if (firstComponent == null) throw new IllegalArgumentException("le paramètre ne doit pas être null");
|
||||
first.addExtra(firstComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Après l'appel de cette méthode, vous devez appeler nextComponent() pour
|
||||
* initialiser la composante suivante
|
||||
*/
|
||||
public Display convertAndAddLegacy(String legacyText) {
|
||||
finalizeCurrentComponent();
|
||||
|
||||
if (legacyText == null) return this;
|
||||
BaseComponent[] compo = TextComponent.fromLegacyText(legacyText);
|
||||
|
||||
for (BaseComponent c : compo)
|
||||
first.addExtra(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display nextComponent(String str) {
|
||||
finalizeCurrentComponent();
|
||||
if (str == null) str = "";
|
||||
current = new TextComponent(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display addComponent(BaseComponent cmp) {
|
||||
if (cmp == null) throw new IllegalArgumentException("le paramètre ne doit pas être null");
|
||||
finalizeCurrentComponent();
|
||||
first.addExtra(cmp);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Équivalent à <code>nextComponent("\n")</code>, sauf qu'un nouvel appel à
|
||||
* nextComponent() est nécessaire après.
|
||||
*/
|
||||
public Display nextLine() {
|
||||
finalizeCurrentComponent();
|
||||
first.addExtra(new TextComponent("\n"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setColor(ChatColor color) {
|
||||
current.setColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setBold(boolean b) {
|
||||
current.setBold(b);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setItalic(boolean i) {
|
||||
current.setItalic(i);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setUnderlined(boolean u) {
|
||||
current.setUnderlined(u);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setObfuscated(boolean o) {
|
||||
current.setObfuscated(o);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setStrikethrough(boolean s) {
|
||||
current.setStrikethrough(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setHoverText(Display content) {
|
||||
current.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new BaseComponent[] { content.get() }));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setClickURL(String url) {
|
||||
current.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setClickCommand(String cmd) {
|
||||
current.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, cmd));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Display setClickSuggest(String cmd) {
|
||||
current.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, cmd));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void finalizeCurrentComponent() {
|
||||
if (current != null) first.addExtra(current);
|
||||
current = null;
|
||||
}
|
||||
|
||||
public BaseComponent get() {
|
||||
finalizeCurrentComponent();
|
||||
return first;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
package fr.pandacube.java.util.chat_display;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
public class DisplayUtil {
|
||||
|
||||
private static Map<Integer, String> charList = new HashMap<Integer, String>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
put(-6, "§");
|
||||
put(2, "!.,:;i|¡");
|
||||
put(3, "'`lìí");
|
||||
put(4, " I[]tï×");
|
||||
put(5, "\"()*<>fk{}");
|
||||
put(7, "@~®");
|
||||
}
|
||||
};
|
||||
|
||||
private static final int defaultChatMaxWidth = 320;
|
||||
private static int chatMaxWidth = defaultChatMaxWidth;
|
||||
|
||||
private static final int defaultNbCharPerLineForConsole = 50;
|
||||
private static int nbCharPerLineForConsole = defaultNbCharPerLineForConsole;
|
||||
|
||||
public static final ChatColor COLOR_TITLE = ChatColor.GOLD;
|
||||
public static final ChatColor COLOR_LINK = ChatColor.GREEN;
|
||||
public static final ChatColor COLOR_COMMAND = ChatColor.GRAY;
|
||||
|
||||
public static BaseComponent createURLLink(String textLink, String url, String hoverText) {
|
||||
String dispURL = (url.length() > 50) ? (url.substring(0, 48) + "...") : url;
|
||||
|
||||
return new Display().nextComponent(textLink).setClickURL(url)
|
||||
.setHoverText(new Display(
|
||||
ChatColor.GRAY + ((hoverText == null) ? "Cliquez pour accéder au site :" : hoverText) + "\n"
|
||||
+ ChatColor.GRAY + dispURL))
|
||||
.setColor(COLOR_LINK).get();
|
||||
}
|
||||
|
||||
public static BaseComponent createCommandLink(String textLink, String commandWithSlash, String hoverText) {
|
||||
Display d = new Display().nextComponent(textLink).setClickCommand(commandWithSlash).setColor(COLOR_COMMAND);
|
||||
if (hoverText != null) d.setHoverText(new Display(hoverText));
|
||||
return d.get();
|
||||
}
|
||||
|
||||
public static BaseComponent createCommandSuggest(String textLink, String commandWithSlash, String hoverText) {
|
||||
Display d = new Display().nextComponent(textLink).setClickSuggest(commandWithSlash).setColor(COLOR_COMMAND);
|
||||
if (hoverText != null) d.setHoverText(new Display(hoverText));
|
||||
return d.get();
|
||||
}
|
||||
|
||||
public static BaseComponent centerText(BaseComponent text, char repeatedChar, ChatColor decorationColor,
|
||||
boolean console) {
|
||||
|
||||
int textWidth = strWidth(text.toPlainText(), console);
|
||||
if (textWidth > ((console) ? nbCharPerLineForConsole : chatMaxWidth)) return text;
|
||||
|
||||
String current = text.toPlainText();
|
||||
int count = 0;
|
||||
do {
|
||||
count++;
|
||||
current = repeatedChar + current + repeatedChar;
|
||||
} while (strWidth(current, console) <= ((console) ? nbCharPerLineForConsole : chatMaxWidth));
|
||||
count--;
|
||||
|
||||
String finalLeftOrRight = "";
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
finalLeftOrRight += repeatedChar;
|
||||
|
||||
Display d = new Display().nextComponent(finalLeftOrRight).setColor(decorationColor).addComponent(text);
|
||||
|
||||
if (repeatedChar != ' ') d.nextComponent(finalLeftOrRight).setColor(decorationColor);
|
||||
|
||||
return d.get();
|
||||
|
||||
}
|
||||
|
||||
public static BaseComponent leftText(BaseComponent text, char repeatedChar, ChatColor decorationColor, int nbLeft,
|
||||
boolean console) {
|
||||
|
||||
int textWidth = strWidth(text.toPlainText(), console);
|
||||
if (textWidth > ((console) ? nbCharPerLineForConsole : chatMaxWidth) || textWidth
|
||||
+ nbLeft * charW(repeatedChar, console) > ((console) ? nbCharPerLineForConsole : chatMaxWidth))
|
||||
return text;
|
||||
|
||||
Display d = new Display();
|
||||
|
||||
String finalLeft = "";
|
||||
if (nbLeft > 0) {
|
||||
for (int i = 0; i < nbLeft; i++)
|
||||
finalLeft += repeatedChar;
|
||||
d.nextComponent(finalLeft).setColor(decorationColor);
|
||||
}
|
||||
d.addComponent(text);
|
||||
|
||||
int count = 0;
|
||||
String current = finalLeft + text.toPlainText();
|
||||
do {
|
||||
count++;
|
||||
current += repeatedChar;
|
||||
} while (strWidth(current, console) <= ((console) ? nbCharPerLineForConsole : chatMaxWidth));
|
||||
count--;
|
||||
|
||||
if (repeatedChar != ' ') {
|
||||
String finalRight = "";
|
||||
for (int i = 0; i < count; i++)
|
||||
finalRight += repeatedChar;
|
||||
d.nextComponent(finalRight).setColor(decorationColor);
|
||||
}
|
||||
|
||||
return d.get();
|
||||
|
||||
}
|
||||
|
||||
public static BaseComponent rightText(BaseComponent text, char repeatedChar, ChatColor decorationColor, int nbRight,
|
||||
boolean console) {
|
||||
|
||||
int textWidth = strWidth(text.toPlainText(), console);
|
||||
if (textWidth > ((console) ? nbCharPerLineForConsole : chatMaxWidth) || textWidth
|
||||
+ nbRight * charW(repeatedChar, console) > ((console) ? nbCharPerLineForConsole : chatMaxWidth))
|
||||
return text;
|
||||
|
||||
String tempText = text.toPlainText();
|
||||
if (nbRight > 0) {
|
||||
tempText += decorationColor;
|
||||
for (int i = 0; i < nbRight; i++)
|
||||
tempText += repeatedChar;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
String current = tempText;
|
||||
do {
|
||||
count++;
|
||||
current = repeatedChar + current;
|
||||
} while (strWidth(current, console) <= ((console) ? nbCharPerLineForConsole : chatMaxWidth));
|
||||
count--;
|
||||
|
||||
String finalLeft = "";
|
||||
for (int i = 0; i < count; i++)
|
||||
finalLeft += repeatedChar;
|
||||
|
||||
Display d = new Display().nextComponent(finalLeft).setColor(decorationColor).addComponent(text);
|
||||
|
||||
if (repeatedChar != ' ') {
|
||||
String finalRight = "";
|
||||
for (int i = 0; i < nbRight; i++)
|
||||
finalRight += repeatedChar;
|
||||
d.nextComponent(finalRight).setColor(decorationColor);
|
||||
}
|
||||
|
||||
return d.get();
|
||||
|
||||
}
|
||||
|
||||
public static BaseComponent emptyLine(char repeatedChar, ChatColor decorationColor, boolean console) {
|
||||
int count = ((console) ? nbCharPerLineForConsole : chatMaxWidth) / charW(repeatedChar, console);
|
||||
String finalLine = "";
|
||||
for (int i = 0; i < count; i++)
|
||||
finalLine += repeatedChar;
|
||||
|
||||
return new Display().nextComponent(finalLine).setColor(decorationColor).get();
|
||||
}
|
||||
|
||||
public static int strWidth(String str, boolean console) {
|
||||
int count = 0;
|
||||
for (char c : str.toCharArray())
|
||||
count += charW(c, console);
|
||||
return (count < 0) ? 0 : count;
|
||||
}
|
||||
|
||||
private static int charW(char c, boolean console) {
|
||||
if (console) return (c == '§') ? -1 : 1;
|
||||
for (int px : charList.keySet())
|
||||
if (charList.get(px).indexOf(c) >= 0) return px;
|
||||
return 6;
|
||||
}
|
||||
|
||||
public static void setNbCharPerLineForConsole(int nb) {
|
||||
if (nb < 0) nb = 0;
|
||||
nbCharPerLineForConsole = nb;
|
||||
}
|
||||
|
||||
public static void resetNbCharPerLineForConsole() {
|
||||
nbCharPerLineForConsole = defaultNbCharPerLineForConsole;
|
||||
}
|
||||
|
||||
public static void setChatMaxWidth(int px) {
|
||||
if (px < 0) px = 0;
|
||||
chatMaxWidth = px;
|
||||
}
|
||||
|
||||
public static void resetChatMaxWidth() {
|
||||
chatMaxWidth = defaultChatMaxWidth;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package fr.pandacube.java.util.chat_display;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
public class TextProgressBar {
|
||||
private static String pattern_start = "[";
|
||||
private static String pattern_end = "]";
|
||||
private static ChatColor color_empty = ChatColor.DARK_GRAY;
|
||||
private static ChatColor color_decoration = ChatColor.GOLD;
|
||||
private static ChatColor color_default = ChatColor.RESET;
|
||||
private static String pattern_empty = ".";
|
||||
private static String pattern_full = "|";
|
||||
|
||||
public static String progressBar(double[] values, ChatColor[] colors, double total, int nbCar) {
|
||||
long[] sizes = new long[values.length];
|
||||
|
||||
int max_size = nbCar - pattern_start.length() - pattern_end.length();
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
double sum_values_before = 0;
|
||||
for (int j = i; j >= 0; j--)
|
||||
sum_values_before += values[j];
|
||||
|
||||
long car_position = Math.round(max_size * sum_values_before / total);
|
||||
|
||||
// évite les barre de progressions plus grandes que la taille
|
||||
// demandée
|
||||
if (car_position > max_size) car_position = max_size;
|
||||
|
||||
long sum_sizes_before = 0;
|
||||
for (int j = i - 1; j >= 0; j--)
|
||||
sum_sizes_before += sizes[j];
|
||||
|
||||
sizes[i] = car_position - sum_sizes_before;
|
||||
}
|
||||
int sum_sizes = 0;
|
||||
|
||||
String bar = color_decoration + pattern_start;
|
||||
for (int i = 0; i < sizes.length; i++) {
|
||||
sum_sizes += sizes[i];
|
||||
|
||||
ChatColor color = color_default;
|
||||
if (colors != null && i < colors.length && colors[i] != null) color = colors[i];
|
||||
|
||||
bar = bar + color;
|
||||
|
||||
for (int j = 0; j < sizes[i]; j++)
|
||||
bar = bar + pattern_full;
|
||||
}
|
||||
|
||||
bar = bar + color_empty;
|
||||
for (int j = 0; j < (max_size - sum_sizes); j++)
|
||||
bar = bar + pattern_empty;
|
||||
|
||||
bar = bar + color_decoration + pattern_end;
|
||||
return bar;
|
||||
}
|
||||
|
||||
public static String progressBar(double value, ChatColor color, double max, int nbCar) {
|
||||
return progressBar(new double[] { value }, new ChatColor[] { color }, max, nbCar);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
package fr.pandacube.java.util.commands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class AbstractCommand {
|
||||
|
||||
public final String commandName;
|
||||
|
||||
public AbstractCommand(String cmdName) {
|
||||
commandName = cmdName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Concatène les chaines de caractères passés dans <code>args</code> (avec
|
||||
* <code>" "</code> comme séparateur), en ommettant
|
||||
* celles qui se trouvent avant <code>index</code>.<br/>
|
||||
* Par exemple :
|
||||
* </p>
|
||||
* <code>
|
||||
* getLastParams(new String[] {"test", "bouya", "chaka", "bukkit"}, 1);
|
||||
* </code>
|
||||
* <p>
|
||||
* retournera la chaine "bouya chaka bukkit"
|
||||
*
|
||||
* @param args liste des arguments d'une commandes.<br/>
|
||||
* Le premier élément est l'argument qui suit le nom de la commande.
|
||||
* Usuellement, ce paramètre correspond au paramètre
|
||||
* <code>args</code> de la méthode onCommand
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public static String getLastParams(String[] args, int index) {
|
||||
if (index < 0 || index >= args.length) return null;
|
||||
return String.join(" ", Arrays.copyOfRange(args, index, args.length));
|
||||
}
|
||||
|
||||
/**
|
||||
* <i>Prends en charge les tokens avec des espaces, mais retourne les
|
||||
* propositions complètes</i>
|
||||
*
|
||||
* @param token
|
||||
* @param allProposal
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getTabProposalFromToken(String token, Collection<String> allProposal) {
|
||||
List<String> ret = new ArrayList<>();
|
||||
|
||||
for (String s : allProposal)
|
||||
if (s != null && s.toLowerCase().startsWith(token.toLowerCase())) ret.add(s);
|
||||
|
||||
if (ret.isEmpty()) ret.addAll(allProposal);
|
||||
|
||||
ret.removeIf(s -> s == null);
|
||||
ret.sort(null); // String implents Comparable
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static final TabProposal TAB_NO_PROPOSAL = () -> Collections.emptyList();
|
||||
|
||||
public static TabProposal TAB_PROPOSAL(Collection<String> proposals) {
|
||||
return () -> proposals;
|
||||
}
|
||||
|
||||
public static TabProposal TAB_INTEGERS(int startIncluded, int endIncluded) {
|
||||
List<String> proposals = new ArrayList<>(endIncluded - startIncluded + 1);
|
||||
for (int i = startIncluded; i <= endIncluded; i++) {
|
||||
proposals.add(Integer.toString(i));
|
||||
}
|
||||
return () -> proposals;
|
||||
}
|
||||
|
||||
public static TabProposal TAB_PROPOSAL_LAST_PARAMS(String[] args, int index, Collection<String> proposals) {
|
||||
String lastParamToken = getLastParams(args, index);
|
||||
String[] splittedToken = lastParamToken.split(" ", -1);
|
||||
int currentTokenPosition = splittedToken.length - 1;
|
||||
String[] previousTokens = Arrays.copyOf(splittedToken, currentTokenPosition);
|
||||
|
||||
List<String> currentTokenProposal = new ArrayList<>();
|
||||
for (String p : proposals) {
|
||||
String[] splittedProposal = p.split(" ", -1);
|
||||
if (splittedProposal.length <= currentTokenPosition)
|
||||
continue;
|
||||
if (!Arrays.equals(Arrays.copyOf(splittedToken, currentTokenPosition), previousTokens))
|
||||
continue;
|
||||
if (splittedProposal[currentTokenPosition].isEmpty())
|
||||
continue;
|
||||
|
||||
currentTokenProposal.add(splittedProposal[currentTokenPosition]);
|
||||
}
|
||||
|
||||
return () -> currentTokenProposal;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TabProposal {
|
||||
public abstract Collection<String> getProposal();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Throw an instance of this exception to indicate to the plugin command handler
|
||||
* that the user has missused the command. The message, if provided, must indicate
|
||||
* the reason of the mussusage of the command. It will be displayed on the screen
|
||||
* with eventually indication of how to use the command (help command for example).
|
||||
* If a {@link Throwable} cause is provided, it will be relayed to the plugin {@link Logger}.
|
||||
*
|
||||
*/
|
||||
public static class BadCommandUsage extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public BadCommandUsage() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BadCommandUsage(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public BadCommandUsage(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BadCommandUsage(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
145
src/main/java/fr/pandacube/java/util/config/AbstractConfig.java
Normal file
145
src/main/java/fr/pandacube/java/util/config/AbstractConfig.java
Normal file
@@ -0,0 +1,145 @@
|
||||
package fr.pandacube.java.util.config;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import fr.pandacube.java.util.Log;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
/**
|
||||
* Classe chargeant en mémoire un fichier de configuration ou un dossier donné
|
||||
* @author Marc Baloup
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractConfig {
|
||||
|
||||
/**
|
||||
* Correspond au dossier ou au fichier de configuration traité par la sous-classe
|
||||
* courante de {@link AbstractConfig}
|
||||
*/
|
||||
protected File configFile;
|
||||
|
||||
/**
|
||||
* @param fileOrDirName le nom du fichier ou du dossier correspondant à la sous-classe de {@link AbstractConfig}
|
||||
* @param isDir <code>true</code> si il s'agit d'un dossier, <code>false</code> sinon
|
||||
* @throws IOException si le fichier est impossible à créer
|
||||
*/
|
||||
public AbstractConfig(File configDir, String fileOrDirName, FileType type) throws IOException {
|
||||
configFile = new File(configDir, fileOrDirName);
|
||||
if (type == FileType.DIR)
|
||||
configFile.mkdir();
|
||||
else
|
||||
configFile.createNewFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne toutes les lignes d'un fichier donné
|
||||
* @param ignoreEmpty <code>true</code> si on doit ignorer les lignes vides
|
||||
* @param ignoreHashtagComment <code>true</code> si on doit ignorer les lignes commentés (commençant par un #)
|
||||
* @param trimOutput <code>true</code> si on doit appeller la méthode String.trim() sur chaque ligne retournée
|
||||
* @param f le fichier à lire
|
||||
* @return la liste des lignes utiles
|
||||
* @throws IOException
|
||||
*/
|
||||
protected List<String> getFileLines(boolean ignoreEmpty, boolean ignoreHashtagComment, boolean trimOutput, File f) throws IOException {
|
||||
if (!f.isFile())
|
||||
return null;
|
||||
|
||||
BufferedReader reader = new BufferedReader(new FileReader(f));
|
||||
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String trimmedLine = line.trim();
|
||||
|
||||
if (ignoreEmpty && trimmedLine.equals(""))
|
||||
continue;
|
||||
|
||||
if (ignoreHashtagComment && trimmedLine.startsWith("#"))
|
||||
continue;
|
||||
|
||||
if (trimOutput)
|
||||
lines.add(trimmedLine);
|
||||
else
|
||||
lines.add(line);
|
||||
}
|
||||
|
||||
|
||||
reader.close();
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retourne toutes les lignes du fichier de configuration
|
||||
* @param ignoreEmpty <code>true</code> si on doit ignorer les lignes vides
|
||||
* @param ignoreHashtagComment <code>true</code> si on doit ignorer les lignes commentés (commençant par un #)
|
||||
* @param trimOutput <code>true</code> si on doit appeller la méthode String.trim() sur chaque ligne retournée
|
||||
* @return la liste des lignes utiles
|
||||
* @throws IOException
|
||||
*/
|
||||
protected List<String> getFileLines(boolean ignoreEmpty, boolean ignoreHashtagComment, boolean trimOutput) throws IOException {
|
||||
return getFileLines(ignoreEmpty, ignoreHashtagComment, trimOutput, configFile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected List<File> getFileList() {
|
||||
if (!configFile.isDirectory())
|
||||
return null;
|
||||
|
||||
return Arrays.asList(configFile.listFiles());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Découpe une chaine de caractère contenant une série de noeuds
|
||||
* de permissions séparés par des point-virgules et la retourne sous forme d'une liste.
|
||||
* @param perms la chaine de permissions à traiter
|
||||
* @return <code>null</code> si le paramètre est nulle ou si <code>perms.equals("*")</code>, ou alors la chaine splittée.
|
||||
*/
|
||||
public static List<String> splitPermissionsString(String perms) {
|
||||
if (perms == null || perms.equals("*"))
|
||||
return null;
|
||||
return getSplittedString(perms, ";");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static List<String> getSplittedString(String value, String split) {
|
||||
return Collections.unmodifiableList(Arrays.asList(value.split(split)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String getTranslatedColorCode(String string) {
|
||||
return ChatColor.translateAlternateColorCodes('&', string);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected void warning(String message) {
|
||||
Log.warning("Erreur dans la configuration de '"+configFile.getName()+"' : "+message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected enum FileType {
|
||||
FILE, DIR
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package fr.pandacube.java.util.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class AbstractConfigManager {
|
||||
|
||||
protected final File configDir;
|
||||
|
||||
public AbstractConfigManager(File configD) throws IOException {
|
||||
configDir = configD;
|
||||
|
||||
configDir.mkdirs();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation must close all closeable configuration (saving for example)
|
||||
* @throws IOException
|
||||
*/
|
||||
public abstract void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Implementation must init all config data
|
||||
* @throws IOException
|
||||
*/
|
||||
public abstract void init() throws IOException;
|
||||
|
||||
|
||||
|
||||
|
||||
public synchronized void reloadConfig() throws IOException {
|
||||
close();
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
}
|
43
src/main/java/fr/pandacube/java/util/db/SQLContact.java
Normal file
43
src/main/java/fr/pandacube/java/util/db/SQLContact.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLContact extends SQLElement<SQLContact> {
|
||||
|
||||
public SQLContact() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLContact(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_contact";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLContact, Integer> time = new SQLField<>("time", SQLType.INT, false);
|
||||
public static final SQLFKField<SQLContact, String, SQLPlayer> playerId = new SQLFKField<>("playerId", SQLType.CHAR(36), true,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLContact, String> userName = new SQLField<>("userName", SQLType.VARCHAR(50), true);
|
||||
public static final SQLField<SQLContact, String> userMail = new SQLField<>("userMail", SQLType.VARCHAR(50), true);
|
||||
public static final SQLField<SQLContact, String> titre = new SQLField<>("titre", SQLType.VARCHAR(100), false);
|
||||
public static final SQLField<SQLContact, String> texte = new SQLField<>("texte", SQLType.TEXT, false);
|
||||
public static final SQLField<SQLContact, Boolean> hidden = new SQLField<>("hidden", SQLType.BOOLEAN, false, (Boolean) false);
|
||||
|
||||
public UUID getPlayerId() {
|
||||
String id = get(playerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setPlayerId(UUID pName) {
|
||||
set(playerId, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLForumCategorie extends SQLElement<SQLForumCategorie> {
|
||||
|
||||
public SQLForumCategorie() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLForumCategorie(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_forum_categorie";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLForumCategorie, String> nom = new SQLField<>("nom", SQLType.VARCHAR(100), false);
|
||||
public static final SQLField<SQLForumCategorie, Integer> ordre = new SQLField<>("ordre", SQLType.INT, false);
|
||||
|
||||
}
|
36
src/main/java/fr/pandacube/java/util/db/SQLForumForum.java
Normal file
36
src/main/java/fr/pandacube/java/util/db/SQLForumForum.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLForumForum extends SQLElement<SQLForumForum> {
|
||||
|
||||
public SQLForumForum() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLForumForum(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_forum_forum";
|
||||
}
|
||||
|
||||
public static final SQLFKField<SQLForumForum, Integer, SQLForumCategorie> catId = SQLFKField.idFK("catId", SQLType.INT, false,
|
||||
SQLForumCategorie.class);
|
||||
public static final SQLField<SQLForumForum, String> nom = new SQLField<>("nom", SQLType.VARCHAR(100), false);
|
||||
public static final SQLField<SQLForumForum, String> description = new SQLField<>("description", SQLType.TEXT, false);
|
||||
public static final SQLField<SQLForumForum, Integer> ordre = new SQLField<>("ordre", SQLType.INT, false);
|
||||
public static final SQLField<SQLForumForum, Integer> authView = new SQLField<>("authView", SQLType.INT, false);
|
||||
public static final SQLField<SQLForumForum, Integer> authPost = new SQLField<>("authPost", SQLType.INT, false);
|
||||
public static final SQLField<SQLForumForum, Integer> authThread = new SQLField<>("authThread", SQLType.INT, false);
|
||||
public static final SQLField<SQLForumForum, Integer> authAnchored = new SQLField<>("authAnchored", SQLType.INT, false);
|
||||
public static final SQLField<SQLForumForum, Integer> authModo = new SQLField<>("authModo", SQLType.INT, false);
|
||||
public static final SQLField<SQLForumForum, Integer> nbThreads = new SQLField<>("nbThreads", SQLType.INT, false);
|
||||
public static final SQLField<SQLForumForum, Integer> nbMessages = new SQLField<>("nbMessages", SQLType.INT, false);
|
||||
|
||||
}
|
41
src/main/java/fr/pandacube/java/util/db/SQLForumPost.java
Normal file
41
src/main/java/fr/pandacube/java/util/db/SQLForumPost.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLForumPost extends SQLElement<SQLForumPost> {
|
||||
|
||||
public SQLForumPost() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLForumPost(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_forum_post";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLForumPost, String> createur = new SQLField<>("createur", SQLType.CHAR(36), false);
|
||||
public static final SQLField<SQLForumPost, String> texte = new SQLField<>("texte", SQLType.TEXT, false);
|
||||
public static final SQLField<SQLForumPost, Integer> time = new SQLField<>("time", SQLType.INT, false);
|
||||
public static final SQLFKField<SQLForumPost, Integer, SQLForumThread> threadId = SQLFKField.idFK("threadId", SQLType.INT, false,
|
||||
SQLForumThread.class);
|
||||
public static final SQLField<SQLForumPost, Boolean> moderated = new SQLField<>("moderated", SQLType.BOOLEAN, false);
|
||||
|
||||
public UUID getCreateurId() {
|
||||
String id = get(createur);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setCreateurId(UUID pName) {
|
||||
set(createur, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
}
|
45
src/main/java/fr/pandacube/java/util/db/SQLForumThread.java
Normal file
45
src/main/java/fr/pandacube/java/util/db/SQLForumThread.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLForumThread extends SQLElement<SQLForumThread> {
|
||||
|
||||
public SQLForumThread() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLForumThread(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_forum_thread";
|
||||
}
|
||||
|
||||
public static final SQLFKField<SQLForumThread, Integer, SQLForumForum> forumId = SQLFKField.idFK("forumId", SQLType.INT, false,
|
||||
SQLForumForum.class);
|
||||
public static final SQLField<SQLForumThread, String> titre = new SQLField<>("titre", SQLType.VARCHAR(60), false);
|
||||
public static final SQLFKField<SQLForumThread, String, SQLPlayer> createur = new SQLFKField<>("createur", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLForumThread, Integer> vu = new SQLField<>("vu", SQLType.INT, false);
|
||||
public static final SQLField<SQLForumThread, Long> time = new SQLField<>("time", SQLType.BIGINT, false);
|
||||
public static final SQLField<SQLForumThread, Boolean> anchored = new SQLField<>("anchored", SQLType.BOOLEAN, false);
|
||||
public static final SQLField<SQLForumThread, Boolean> locked = new SQLField<>("locked", SQLType.BOOLEAN, false);
|
||||
public static final SQLField<SQLForumThread, Integer> nbMessages = new SQLField<>("nbMessages", SQLType.INT, false);
|
||||
|
||||
public UUID getCreateurId() {
|
||||
String id = get(createur);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setCreateurId(UUID pName) {
|
||||
set(createur, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
}
|
48
src/main/java/fr/pandacube/java/util/db/SQLLoginHistory.java
Normal file
48
src/main/java/fr/pandacube/java/util/db/SQLLoginHistory.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLLoginHistory extends SQLElement<SQLLoginHistory> {
|
||||
|
||||
public SQLLoginHistory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLLoginHistory(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_login_history";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLLoginHistory, Long> time = new SQLField<>("time", SQLType.BIGINT, false);
|
||||
public static final SQLFKField<SQLLoginHistory, String, SQLPlayer> playerId = new SQLFKField<>("playerId", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLLoginHistory, String> ip = new SQLField<>("ip", SQLType.VARCHAR(128), true);
|
||||
public static final SQLField<SQLLoginHistory, ActionType> actionType = new SQLField<>("actionType", SQLType.ENUM(ActionType.class),
|
||||
false);
|
||||
public static final SQLField<SQLLoginHistory, Integer> nbOnline = new SQLField<>("nbOnline", SQLType.INT, false);
|
||||
public static final SQLField<SQLLoginHistory, String> playerName = new SQLField<>("playerName", SQLType.VARCHAR(16), true);
|
||||
public static final SQLField<SQLLoginHistory, Integer> minecraftVersion = new SQLField<>("minecraftVersion", SQLType.INT, false, 0);
|
||||
public static final SQLField<SQLLoginHistory, String> hostName = new SQLField<>("hostName", SQLType.VARCHAR(128), true);
|
||||
|
||||
public UUID getPlayerId() {
|
||||
String id = get(playerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setPlayerId(UUID pName) {
|
||||
set(playerId, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
public enum ActionType {
|
||||
LOGIN, LOGOUT
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLLoginKickHistory extends SQLElement<SQLLoginKickHistory> {
|
||||
|
||||
public SQLLoginKickHistory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLLoginKickHistory(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_loginkick_history";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLLoginKickHistory, Long> time = new SQLField<>("time", SQLType.BIGINT, false);
|
||||
public static final SQLFKField<SQLLoginKickHistory, String, SQLPlayer> playerId = new SQLFKField<>("playerId", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLLoginKickHistory, String> ip = new SQLField<>("ip", SQLType.VARCHAR(128), true);
|
||||
public static final SQLField<SQLLoginKickHistory, String> playerName = new SQLField<>("playerName", SQLType.VARCHAR(16), true);
|
||||
public static final SQLField<SQLLoginKickHistory, Integer> minecraftVersion = new SQLField<>("minecraftVersion", SQLType.INT, false, 0);
|
||||
public static final SQLField<SQLLoginKickHistory, String> hostName = new SQLField<>("hostName", SQLType.VARCHAR(128), true);
|
||||
public static final SQLField<SQLLoginKickHistory, String> kickReason = new SQLField<>("kickReason", SQLType.VARCHAR(512), true);
|
||||
|
||||
public UUID getPlayerId() {
|
||||
String id = get(playerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setPlayerId(UUID pName) {
|
||||
set(playerId, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
}
|
41
src/main/java/fr/pandacube/java/util/db/SQLMPGroup.java
Normal file
41
src/main/java/fr/pandacube/java/util/db/SQLMPGroup.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.ORM;
|
||||
import fr.pandacube.java.util.db.sql_tools.ORMException;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElementList;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLOrderBy;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
|
||||
public class SQLMPGroup extends SQLElement<SQLMPGroup> {
|
||||
|
||||
public SQLMPGroup() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLMPGroup(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_mp_group";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLMPGroup, String> groupName = new SQLField<>("groupName", SQLType.VARCHAR(16), false);
|
||||
|
||||
public SQLElementList<SQLMPGroupUser> getGroupUsers() throws ORMException {
|
||||
return ORM.getAll(SQLMPGroupUser.class, new SQLWhereComp(SQLMPGroupUser.groupId, SQLComparator.EQ, getId()),
|
||||
new SQLOrderBy().addField(ORM.getSQLIdField(SQLMPGroupUser.class)), null, null);
|
||||
}
|
||||
|
||||
public static SQLMPGroup getByName(String name) throws ORMException {
|
||||
if (name == null) return null;
|
||||
|
||||
return ORM.getFirst(SQLMPGroup.class, new SQLWhereComp(groupName, SQLComparator.EQ, name), null);
|
||||
}
|
||||
|
||||
}
|
63
src/main/java/fr/pandacube/java/util/db/SQLMPGroupUser.java
Normal file
63
src/main/java/fr/pandacube/java/util/db/SQLMPGroupUser.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.ORM;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain.SQLBoolOp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
|
||||
public class SQLMPGroupUser extends SQLElement<SQLMPGroupUser> {
|
||||
|
||||
public SQLMPGroupUser() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLMPGroupUser(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_mp_group_user";
|
||||
}
|
||||
|
||||
public static final SQLFKField<SQLMPGroupUser, Integer, SQLMPGroup> groupId = SQLFKField.idFK("groupId", SQLType.INT, false,
|
||||
SQLMPGroup.class);
|
||||
public static final SQLFKField<SQLMPGroupUser, String, SQLPlayer> playerId = new SQLFKField<>("playerId", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
|
||||
// TODO ajouter un champ qui dit si le joueur est admin du groupe
|
||||
|
||||
public UUID getPlayerId() {
|
||||
String id = get(playerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setPlayerId(UUID id) {
|
||||
set(playerId, (id == null) ? null : id.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'instance de SQLMPGroupUser correcpondant à la présence d'un
|
||||
* joueur dans un groupe
|
||||
*
|
||||
* @param group le groupe concerné, sous forme d'instance de SQLMPGroup
|
||||
* @param player l'identifiant du joueur
|
||||
* @return null si la correspondance n'a pas été trouvée
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static SQLMPGroupUser getPlayerInGroup(SQLMPGroup group, UUID player) throws Exception {
|
||||
if (player == null || group == null) return null;
|
||||
return ORM.getFirst(SQLMPGroupUser.class,
|
||||
new SQLWhereChain(SQLBoolOp.AND).add(new SQLWhereComp(groupId, SQLComparator.EQ, group.getId()))
|
||||
.add(new SQLWhereComp(playerId, SQLComparator.EQ, player.toString())),
|
||||
null);
|
||||
}
|
||||
|
||||
}
|
137
src/main/java/fr/pandacube/java/util/db/SQLMPMessage.java
Normal file
137
src/main/java/fr/pandacube/java/util/db/SQLMPMessage.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.PlayerFinder;
|
||||
import fr.pandacube.java.util.db.sql_tools.ORM;
|
||||
import fr.pandacube.java.util.db.sql_tools.ORMException;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElementList;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLOrderBy;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereLike;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereNull;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLOrderBy.Direction;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain.SQLBoolOp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
|
||||
public class SQLMPMessage extends SQLElement<SQLMPMessage> {
|
||||
|
||||
public SQLMPMessage() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLMPMessage(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_mp_message";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLMPMessage, Long> time = new SQLField<>("time", SQLType.BIGINT, false);
|
||||
public static final SQLField<SQLMPMessage, Integer> securityKey = new SQLField<>("securityKey", SQLType.INT, false);
|
||||
public static final SQLFKField<SQLMPMessage, String, SQLPlayer> viewerId = new SQLFKField<>("viewerId", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLFKField<SQLMPMessage, String, SQLPlayer> sourceId = new SQLFKField<>("sourceId", SQLType.CHAR(36), true,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLFKField<SQLMPMessage, String, SQLPlayer> destId = new SQLFKField<>("destId", SQLType.CHAR(36), true,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLFKField<SQLMPMessage, Integer, SQLMPGroup> destGroup = SQLFKField.idFK("destGroup", SQLType.INT, true,
|
||||
SQLMPGroup.class);
|
||||
public static final SQLField<SQLMPMessage, String> message = new SQLField<>("message", SQLType.VARCHAR(512), false);
|
||||
public static final SQLField<SQLMPMessage, Boolean> wasRead = new SQLField<>("wasRead", SQLType.BOOLEAN, false);
|
||||
public static final SQLField<SQLMPMessage, Boolean> deleted = new SQLField<>("deleted", SQLType.BOOLEAN, false, (Boolean) false);
|
||||
public static final SQLField<SQLMPMessage, Boolean> serverSync = new SQLField<>("serverSync", SQLType.BOOLEAN, false);
|
||||
|
||||
public UUID getViewerId() {
|
||||
String id = get(viewerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setViewerId(UUID id) {
|
||||
set(viewerId, (id == null) ? null : id.toString());
|
||||
}
|
||||
|
||||
public UUID getSourceId() {
|
||||
String id = get(sourceId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setSourceId(UUID id) {
|
||||
set(sourceId, (id == null) ? null : id.toString());
|
||||
}
|
||||
|
||||
public UUID getDestId() {
|
||||
String id = get(destId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setDestId(UUID id) {
|
||||
set(destId, (id == null) ? null : id.toString());
|
||||
}
|
||||
|
||||
public static SQLElementList<SQLMPMessage> getAllUnsyncMessage() throws ORMException {
|
||||
return ORM.getAll(SQLMPMessage.class, new SQLWhereComp(SQLMPMessage.serverSync, SQLComparator.EQ, false),
|
||||
new SQLOrderBy().addField(SQLMPMessage.time), null, null);
|
||||
}
|
||||
|
||||
public static SQLElementList<SQLMPMessage> getAllUnreadForPlayer(UUID player) throws ORMException {
|
||||
return getForPlayer(player, true, null, false);
|
||||
}
|
||||
|
||||
public static SQLElementList<SQLMPMessage> getOneDiscussionForPlayer(UUID player, Object discussion,
|
||||
Integer numberLast, boolean revert) throws ORMException {
|
||||
if (player == null) return null;
|
||||
if (discussion != null && !(discussion instanceof String) && !(discussion instanceof UUID)) return null;
|
||||
if (discussion != null && discussion instanceof String
|
||||
&& !PlayerFinder.isValidPlayerName(discussion.toString()))
|
||||
return null;
|
||||
|
||||
SQLWhereChain where = new SQLWhereChain(SQLBoolOp.AND)
|
||||
.add(new SQLWhereComp(SQLMPMessage.viewerId, SQLComparator.EQ, player.toString()));
|
||||
if (discussion == null) // message de système
|
||||
where.add(new SQLWhereNull(SQLMPMessage.sourceId, true))
|
||||
.add(new SQLWhereNull(SQLMPMessage.destGroup, true));
|
||||
else if (discussion instanceof String) { // message de groupe
|
||||
SQLMPGroup groupEl = ORM.getFirst(SQLMPGroup.class,
|
||||
new SQLWhereComp(SQLMPGroup.groupName, SQLComparator.EQ, (String) discussion), null);
|
||||
if (groupEl == null) return null;
|
||||
where.add(new SQLWhereComp(SQLMPMessage.destGroup, SQLComparator.EQ, groupEl.getId()));
|
||||
}
|
||||
else if (discussion instanceof UUID && discussion.equals(player)) // message
|
||||
// à
|
||||
// lui
|
||||
// même
|
||||
where.add(new SQLWhereLike(SQLMPMessage.destId, discussion.toString()))
|
||||
.add(new SQLWhereLike(SQLMPMessage.sourceId, discussion.toString()))
|
||||
.add(new SQLWhereNull(SQLMPMessage.destGroup, true));
|
||||
else // discussion instanceof UUID
|
||||
where.add(new SQLWhereChain(SQLBoolOp.OR).add(new SQLWhereLike(SQLMPMessage.destId, discussion.toString()))
|
||||
.add(new SQLWhereLike(SQLMPMessage.sourceId, discussion.toString())))
|
||||
.add(new SQLWhereNull(SQLMPMessage.destGroup, true));
|
||||
|
||||
SQLOrderBy orderBy = new SQLOrderBy().addField(SQLMPMessage.time, revert ? Direction.DESC : Direction.ASC);
|
||||
|
||||
return ORM.getAll(SQLMPMessage.class, where, orderBy, numberLast, null);
|
||||
}
|
||||
|
||||
public static SQLElementList<SQLMPMessage> getForPlayer(UUID player, boolean onlyUnread, Integer numberLast,
|
||||
boolean revert) throws ORMException {
|
||||
if (player == null) return null;
|
||||
|
||||
SQLWhereChain where = new SQLWhereChain(SQLBoolOp.AND);
|
||||
where.add(new SQLWhereComp(SQLMPMessage.viewerId, SQLComparator.EQ, player.toString()));
|
||||
if (onlyUnread) where.add(new SQLWhereComp(SQLMPMessage.wasRead, SQLComparator.EQ, false));
|
||||
|
||||
SQLOrderBy orderBy = new SQLOrderBy().addField(SQLMPMessage.time, revert ? Direction.DESC : Direction.ASC);
|
||||
|
||||
return ORM.getAll(SQLMPMessage.class, where, orderBy, numberLast, null);
|
||||
}
|
||||
|
||||
}
|
57
src/main/java/fr/pandacube/java/util/db/SQLModoHistory.java
Normal file
57
src/main/java/fr/pandacube/java/util/db/SQLModoHistory.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLModoHistory extends SQLElement<SQLModoHistory> {
|
||||
|
||||
public SQLModoHistory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLModoHistory(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_modo_history";
|
||||
}
|
||||
|
||||
public static final SQLFKField<SQLModoHistory, String, SQLPlayer> modoId = new SQLFKField<>("modoId", SQLType.CHAR(36), true,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLModoHistory, ActionType> actionType = new SQLField<>("actionType", SQLType.ENUM(ActionType.class),
|
||||
false);
|
||||
public static final SQLField<SQLModoHistory, Long> time = new SQLField<>("time", SQLType.BIGINT, false);
|
||||
public static final SQLFKField<SQLModoHistory, String, SQLPlayer> playerId = new SQLFKField<>("playerId", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLModoHistory, Long> value = new SQLField<>("value", SQLType.BIGINT, true);
|
||||
public static final SQLField<SQLModoHistory, String> message = new SQLField<>("message", SQLType.VARCHAR(2048), false);
|
||||
|
||||
public UUID getModoId() {
|
||||
String id = get(modoId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setModoId(UUID pName) {
|
||||
set(modoId, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
public UUID getPlayerId() {
|
||||
String id = get(playerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setPlayerId(UUID pName) {
|
||||
set(playerId, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
public enum ActionType {
|
||||
BAN, UNBAN, MUTE, UNMUTE, REPORT, KICK
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLOnlineshopHistory extends SQLElement<SQLOnlineshopHistory> {
|
||||
|
||||
public SQLOnlineshopHistory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLOnlineshopHistory(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_onlineshop_history";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLOnlineshopHistory, Long> time = new SQLField<>("time", SQLType.BIGINT, false);
|
||||
public static final SQLField<SQLOnlineshopHistory, String> transactionId = new SQLField<>("transactionId", SQLType.VARCHAR(255), true);
|
||||
public static final SQLField<SQLOnlineshopHistory, SourceType> sourceType = new SQLField<>("sourceType", SQLType.ENUM(SourceType.class),
|
||||
false);
|
||||
public static final SQLFKField<SQLOnlineshopHistory, String, SQLPlayer> sourcePlayerId = new SQLFKField<>("sourcePlayerId",
|
||||
SQLType.CHAR(36), true, SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLOnlineshopHistory, Double> sourceQuantity = new SQLField<>("sourceQuantity", SQLType.DOUBLE, false);
|
||||
public static final SQLField<SQLOnlineshopHistory, String> sourceName = new SQLField<>("sourceName", SQLType.VARCHAR(64), false);
|
||||
public static final SQLField<SQLOnlineshopHistory, DestType> destType = new SQLField<>("destType", SQLType.ENUM(DestType.class), false);
|
||||
public static final SQLFKField<SQLOnlineshopHistory, String, SQLPlayer> destPlayerId = new SQLFKField<>("destPlayerId", SQLType.CHAR(36),
|
||||
false, SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLOnlineshopHistory, Double> destQuantity = new SQLField<>("destQuantity", SQLType.DOUBLE, false);
|
||||
public static final SQLField<SQLOnlineshopHistory, String> destName = new SQLField<>("destName", SQLType.VARCHAR(64), false);
|
||||
|
||||
public UUID getSourcePlayerId() {
|
||||
String id = get(sourcePlayerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setSourcePlayerId(UUID pName) {
|
||||
set(sourcePlayerId, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
public UUID getDestPlayerId() {
|
||||
String id = get(destPlayerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setDestPlayerId(UUID pName) {
|
||||
set(destPlayerId, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
public static enum SourceType {
|
||||
REAL_MONEY, BAMBOU
|
||||
}
|
||||
|
||||
public static enum DestType {
|
||||
BAMBOU, GRADE
|
||||
}
|
||||
|
||||
}
|
30
src/main/java/fr/pandacube/java/util/db/SQLPingHistory.java
Normal file
30
src/main/java/fr/pandacube/java/util/db/SQLPingHistory.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLPingHistory extends SQLElement<SQLPingHistory> {
|
||||
|
||||
public SQLPingHistory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLPingHistory(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_ping_history";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static final SQLField<SQLPingHistory, Long> time = new SQLField<>("time", SQLType.BIGINT, false);
|
||||
public static final SQLField<SQLPingHistory, String> ip = new SQLField<>("ip", SQLType.VARCHAR(128), true);
|
||||
public static final SQLField<SQLPingHistory, Integer> minecraftVersion = new SQLField<>("minecraftVersion", SQLType.INT, false, 0);
|
||||
public static final SQLField<SQLPingHistory, String> hostName = new SQLField<>("hostName", SQLType.VARCHAR(128), true);
|
||||
|
||||
|
||||
}
|
119
src/main/java/fr/pandacube/java/util/db/SQLPlayer.java
Normal file
119
src/main/java/fr/pandacube/java/util/db/SQLPlayer.java
Normal file
@@ -0,0 +1,119 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.ORM;
|
||||
import fr.pandacube.java.util.db.sql_tools.ORMException;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElementList;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain.SQLBoolOp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
|
||||
public class SQLPlayer extends SQLElement<SQLPlayer> {
|
||||
|
||||
public SQLPlayer() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLPlayer(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Nom de la table
|
||||
*/
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_player";
|
||||
}
|
||||
|
||||
/*
|
||||
* Champs de la table
|
||||
*/
|
||||
public static final SQLField<SQLPlayer, String> playerId = new SQLField<>("playerId", SQLType.CHAR(36), false);
|
||||
public static final SQLField<SQLPlayer, String> token = new SQLField<>("token", SQLType.CHAR(36), true);
|
||||
public static final SQLField<SQLPlayer, String> mailCheck = new SQLField<>("mailCheck", SQLType.VARCHAR(255), true);
|
||||
public static final SQLField<SQLPlayer, String> password = new SQLField<>("password", SQLType.VARCHAR(255), true);
|
||||
public static final SQLField<SQLPlayer, String> mail = new SQLField<>("mail", SQLType.VARCHAR(255), true);
|
||||
public static final SQLField<SQLPlayer, String> playerDisplayName = new SQLField<>("playerDisplayName", SQLType.VARCHAR(255),
|
||||
false);
|
||||
public static final SQLField<SQLPlayer, Long> firstTimeInGame = new SQLField<>("firstTimeInGame", SQLType.BIGINT, false, 0L);
|
||||
public static final SQLField<SQLPlayer, Long> timeWebRegister = new SQLField<>("timeWebRegister", SQLType.BIGINT, true);
|
||||
public static final SQLField<SQLPlayer, Long> lastTimeInGame = new SQLField<>("lastTimeInGame", SQLType.BIGINT, true);
|
||||
public static final SQLField<SQLPlayer, Long> lastWebActivity = new SQLField<>("lastWebActivity", SQLType.BIGINT, false, 0L);
|
||||
public static final SQLField<SQLPlayer, String> onlineInServer = new SQLField<>("onlineInServer", SQLType.VARCHAR(32), true);
|
||||
public static final SQLField<SQLPlayer, String> skinURL = new SQLField<>("skinURL", SQLType.VARCHAR(255), true);
|
||||
public static final SQLField<SQLPlayer, Boolean> isVanish = new SQLField<>("isVanish", SQLType.BOOLEAN, false,
|
||||
(Boolean) false);
|
||||
public static final SQLField<SQLPlayer, Date> birthday = new SQLField<>("birthday", SQLType.DATE, true);
|
||||
public static final SQLField<SQLPlayer, Integer> lastYearCelebBday = new SQLField<>("lastYearCelebratedBirthday", SQLType.INT,
|
||||
false, 0);
|
||||
public static final SQLField<SQLPlayer, Long> banTimeout = new SQLField<>("banTimeout", SQLType.BIGINT, true);
|
||||
public static final SQLField<SQLPlayer, Long> muteTimeout = new SQLField<>("muteTimeout", SQLType.BIGINT, true);
|
||||
public static final SQLField<SQLPlayer, Boolean> isWhitelisted = new SQLField<>("isWhitelisted", SQLType.BOOLEAN, false,
|
||||
(Boolean) false);
|
||||
public static final SQLField<SQLPlayer, Long> bambou = new SQLField<>("bambou", SQLType.BIGINT, false, 0L);
|
||||
public static final SQLField<SQLPlayer, String> grade = new SQLField<>("grade", SQLType.VARCHAR(36), false, "default");
|
||||
|
||||
/*
|
||||
* Getteurs spécifique (encapsulation)
|
||||
*/
|
||||
|
||||
public UUID getPlayerId() {
|
||||
String id = get(playerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public UUID getToken() {
|
||||
String id = get(token);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setteurs spécifique (encapsulation)
|
||||
*/
|
||||
|
||||
public void setPlayerId(UUID pName) {
|
||||
set(playerId, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
public void setToken(UUID t) {
|
||||
set(token, (t == null) ? (String) null : t.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static SQLPlayer getPlayerFromUUID(UUID playerId) throws ORMException {
|
||||
return ORM.getFirst(SQLPlayer.class,
|
||||
new SQLWhereComp(SQLPlayer.playerId, SQLComparator.EQ, playerId.toString()), null);
|
||||
}
|
||||
|
||||
|
||||
public static SQLElementList<SQLPlayer> getPlayersFromUUIDs(Set<UUID> playerIds) throws ORMException {
|
||||
Set<String> uuidsString = new HashSet<>();
|
||||
for (UUID id : playerIds)
|
||||
uuidsString.add(id.toString());
|
||||
return getPlayersFromUUIDStr(uuidsString);
|
||||
}
|
||||
|
||||
public static SQLElementList<SQLPlayer> getPlayersFromUUIDStr(Set<String> playerIds) throws ORMException {
|
||||
|
||||
SQLWhereChain where = new SQLWhereChain(SQLBoolOp.OR);
|
||||
for (String userId : playerIds) {
|
||||
where.add(new SQLWhereComp(SQLPlayer.playerId, SQLComparator.EQ, userId));
|
||||
}
|
||||
|
||||
return ORM.getAll(SQLPlayer.class, where, null, null, null);
|
||||
|
||||
}
|
||||
|
||||
}
|
91
src/main/java/fr/pandacube/java/util/db/SQLPlayerIgnore.java
Normal file
91
src/main/java/fr/pandacube/java/util/db/SQLPlayerIgnore.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.ORM;
|
||||
import fr.pandacube.java.util.db.sql_tools.ORMException;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElementList;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain.SQLBoolOp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
|
||||
public class SQLPlayerIgnore extends SQLElement<SQLPlayerIgnore> {
|
||||
|
||||
public SQLPlayerIgnore() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLPlayerIgnore(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_player_ignore";
|
||||
}
|
||||
|
||||
public static final SQLFKField<SQLPlayerIgnore, String, SQLPlayer> ignorer = new SQLFKField<>("ignorer", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLFKField<SQLPlayerIgnore, String, SQLPlayer> ignored = new SQLFKField<>("ignored", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
|
||||
public UUID getIgnorerId() {
|
||||
String id = get(ignorer);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setIgnorerId(UUID pName) {
|
||||
set(ignorer, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
public UUID getIgnoredId() {
|
||||
String id = get(ignored);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setIgnoredId(UUID pName) {
|
||||
set(ignored, (pName == null) ? (String) null : pName.toString());
|
||||
}
|
||||
|
||||
public static SQLPlayerIgnore getPlayerIgnoringPlayer(UUID ignorer, UUID ignored) throws ORMException {
|
||||
return ORM.getFirst(SQLPlayerIgnore.class,
|
||||
new SQLWhereChain(SQLBoolOp.AND)
|
||||
.add(new SQLWhereComp(SQLPlayerIgnore.ignorer, SQLComparator.EQ, ignorer.toString()))
|
||||
.add(new SQLWhereComp(SQLPlayerIgnore.ignored, SQLComparator.EQ, ignored.toString())),
|
||||
null);
|
||||
}
|
||||
|
||||
public static boolean isPlayerIgnoringPlayer(UUID ignorer, UUID ignored) throws ORMException {
|
||||
return getPlayerIgnoringPlayer(ignorer, ignored) != null;
|
||||
}
|
||||
|
||||
public static void setPlayerIgnorePlayer(UUID ignorer, UUID ignored, boolean newIgnoreState) throws ORMException {
|
||||
SQLPlayerIgnore el = getPlayerIgnoringPlayer(ignorer, ignored);
|
||||
if (el == null && newIgnoreState) {
|
||||
el = new SQLPlayerIgnore();
|
||||
el.setIgnorerId(ignorer);
|
||||
el.setIgnoredId(ignored);
|
||||
el.save();
|
||||
return;
|
||||
}
|
||||
if (el != null && !newIgnoreState) {
|
||||
el.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Map<String, SQLPlayer> getIgnoredPlayer(UUID ignorer) throws ORMException {
|
||||
SQLElementList<SQLPlayerIgnore> els = ORM.getAll(SQLPlayerIgnore.class,
|
||||
new SQLWhereComp(SQLPlayerIgnore.ignorer, SQLComparator.EQ, ignorer.toString()),
|
||||
null, null, null);
|
||||
|
||||
return els.getAllForeign(SQLPlayerIgnore.ignored);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLPlayerTexture extends SQLElement<SQLPlayerTexture> {
|
||||
|
||||
public SQLPlayerTexture() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLPlayerTexture(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_player_texture";
|
||||
}
|
||||
|
||||
public static final SQLFKField<SQLPlayerTexture, String, SQLPlayer> playerId = new SQLFKField<>("playerId", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLPlayerTexture, String> alias = new SQLField<>("alias", SQLType.VARCHAR(64), false);
|
||||
public static final SQLField<SQLPlayerTexture, String> textureData = new SQLField<>("textureData", SQLType.TEXT, false);
|
||||
public static final SQLField<SQLPlayerTexture, String> textureSignature = new SQLField<>("textureSignature", SQLType.VARCHAR(8192), false);
|
||||
|
||||
}
|
27
src/main/java/fr/pandacube/java/util/db/SQLShopStock.java
Normal file
27
src/main/java/fr/pandacube/java/util/db/SQLShopStock.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLShopStock extends SQLElement<SQLShopStock> {
|
||||
|
||||
public SQLShopStock() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLShopStock(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_shop_stock";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLShopStock, String> material = new SQLField<>("material", SQLType.VARCHAR(50), false);
|
||||
public static final SQLField<SQLShopStock, Integer> damage = new SQLField<>("damage", SQLType.INT, false, 0);
|
||||
public static final SQLField<SQLShopStock, Double> quantity = new SQLField<>("quantity", SQLType.DOUBLE, false);
|
||||
public static final SQLField<SQLShopStock, String> server = new SQLField<>("server", SQLType.VARCHAR(50), false);
|
||||
|
||||
}
|
50
src/main/java/fr/pandacube/java/util/db/SQLStaffTicket.java
Normal file
50
src/main/java/fr/pandacube/java/util/db/SQLStaffTicket.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLStaffTicket extends SQLElement<SQLStaffTicket> {
|
||||
|
||||
public SQLStaffTicket() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLStaffTicket(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_staff_ticket";
|
||||
}
|
||||
|
||||
public static final SQLFKField<SQLStaffTicket, String, SQLPlayer> playerId = new SQLFKField<>("playerId", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLStaffTicket, String> message = new SQLField<>("message", SQLType.VARCHAR(1024), false);
|
||||
public static final SQLField<SQLStaffTicket, Long> creationTime = new SQLField<>("creationTime", SQLType.BIGINT, false);
|
||||
public static final SQLFKField<SQLStaffTicket, String, SQLPlayer> staffPlayerId = new SQLFKField<>("staffPlayerId",
|
||||
SQLType.CHAR(36), true, SQLPlayer.class, SQLPlayer.playerId);
|
||||
|
||||
public UUID getPlayerId() {
|
||||
String id = get(playerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setPlayerId(UUID id) {
|
||||
set(playerId, (id == null) ? null : id.toString());
|
||||
}
|
||||
|
||||
public UUID getstaffPlayerId() {
|
||||
String id = get(staffPlayerId);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setstaffPlayerId(UUID id) {
|
||||
set(staffPlayerId, (id == null) ? null : id.toString());
|
||||
}
|
||||
|
||||
}
|
28
src/main/java/fr/pandacube/java/util/db/SQLStaticPages.java
Normal file
28
src/main/java/fr/pandacube/java/util/db/SQLStaticPages.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLStaticPages extends SQLElement<SQLStaticPages> {
|
||||
|
||||
public SQLStaticPages() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLStaticPages(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "pandacube_static_pages";
|
||||
}
|
||||
|
||||
public static final SQLField<SQLStaticPages, String> permalink = new SQLField<>("permalink", SQLType.VARCHAR(128), false);
|
||||
public static final SQLField<SQLStaticPages, String> titreHead = new SQLField<>("titreHead", SQLType.VARCHAR(128), false);
|
||||
public static final SQLField<SQLStaticPages, String> titreH2 = new SQLField<>("titreH2", SQLType.VARCHAR(255), false);
|
||||
public static final SQLField<SQLStaticPages, String> texte = new SQLField<>("texte", SQLType.TEXT, false);
|
||||
public static final SQLField<SQLStaticPages, String> permissions = new SQLField<>("permissions", SQLType.VARCHAR(255), true);
|
||||
|
||||
}
|
38
src/main/java/fr/pandacube/java/util/db/SQLUUIDPlayer.java
Normal file
38
src/main/java/fr/pandacube/java/util/db/SQLUUIDPlayer.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package fr.pandacube.java.util.db;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLElement;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLFKField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLField;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLType;
|
||||
|
||||
public class SQLUUIDPlayer extends SQLElement<SQLUUIDPlayer> {
|
||||
|
||||
public SQLUUIDPlayer() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SQLUUIDPlayer(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String tableName() {
|
||||
return "bungeeperms_uuidplayer";
|
||||
}
|
||||
|
||||
public static final SQLFKField<SQLUUIDPlayer, String, SQLPlayer> uuid = new SQLFKField<>("uuid", SQLType.CHAR(36), false,
|
||||
SQLPlayer.class, SQLPlayer.playerId);
|
||||
public static final SQLField<SQLUUIDPlayer, String> player = new SQLField<>("player", SQLType.VARCHAR(16), false);
|
||||
|
||||
public UUID getUUID() {
|
||||
String id = get(uuid);
|
||||
return (id == null) ? null : UUID.fromString(id);
|
||||
}
|
||||
|
||||
public void setUUID(UUID id) {
|
||||
set(uuid, (id == null) ? null : id.toString());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class DBConnection {
|
||||
Connection conn;
|
||||
String url;
|
||||
String login;
|
||||
String pass;
|
||||
|
||||
public DBConnection(String host, int port, String dbname, String l, String p)
|
||||
throws ClassNotFoundException, SQLException {
|
||||
Class.forName("com.mysql.jdbc.Driver");
|
||||
url = "jdbc:mysql://" + host + ":" + port + "/" + dbname
|
||||
+ "?autoReconnect=true"
|
||||
+ "&useUnicode=true"
|
||||
+ "&characterEncoding=utf8"
|
||||
+ "&characterSetResults=utf8"
|
||||
+ "&character_set_server=utf8mb4"
|
||||
+ "&character_set_connection=utf8mb4";
|
||||
login = l;
|
||||
pass = p;
|
||||
connect();
|
||||
}
|
||||
|
||||
public void reconnectIfNecessary() throws SQLException {
|
||||
try {
|
||||
Statement stmt = conn.createStatement();
|
||||
stmt.close();
|
||||
} catch (SQLException e) {
|
||||
try {
|
||||
close();
|
||||
} catch (Exception ex) {}
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
public Connection getNativeConnection() throws SQLException {
|
||||
if (!conn.isValid(1)) reconnectIfNecessary();
|
||||
return conn;
|
||||
}
|
||||
|
||||
private void connect() throws SQLException {
|
||||
conn = DriverManager.getConnection(url, login, pass);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (Exception e) {}
|
||||
|
||||
}
|
||||
|
||||
}
|
294
src/main/java/fr/pandacube/java/util/db/sql_tools/ORM.java
Normal file
294
src/main/java/fr/pandacube/java/util/db/sql_tools/ORM.java
Normal file
@@ -0,0 +1,294 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import fr.pandacube.java.util.EnumUtil;
|
||||
import fr.pandacube.java.util.Log;
|
||||
import fr.pandacube.java.util.db.SQLContact;
|
||||
import fr.pandacube.java.util.db.SQLForumCategorie;
|
||||
import fr.pandacube.java.util.db.SQLForumForum;
|
||||
import fr.pandacube.java.util.db.SQLForumPost;
|
||||
import fr.pandacube.java.util.db.SQLForumThread;
|
||||
import fr.pandacube.java.util.db.SQLLoginHistory;
|
||||
import fr.pandacube.java.util.db.SQLLoginKickHistory;
|
||||
import fr.pandacube.java.util.db.SQLMPGroup;
|
||||
import fr.pandacube.java.util.db.SQLMPGroupUser;
|
||||
import fr.pandacube.java.util.db.SQLMPMessage;
|
||||
import fr.pandacube.java.util.db.SQLModoHistory;
|
||||
import fr.pandacube.java.util.db.SQLOnlineshopHistory;
|
||||
import fr.pandacube.java.util.db.SQLPingHistory;
|
||||
import fr.pandacube.java.util.db.SQLPlayer;
|
||||
import fr.pandacube.java.util.db.SQLPlayerIgnore;
|
||||
import fr.pandacube.java.util.db.SQLShopStock;
|
||||
import fr.pandacube.java.util.db.SQLStaffTicket;
|
||||
import fr.pandacube.java.util.db.SQLStaticPages;
|
||||
import fr.pandacube.java.util.db.SQLUUIDPlayer;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain.SQLBoolOp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
import javafx.util.Pair;
|
||||
|
||||
/**
|
||||
* <b>ORM = Object-Relational Mapping</b>
|
||||
*
|
||||
* @author Marc Baloup
|
||||
*
|
||||
*/
|
||||
public final class ORM {
|
||||
|
||||
private static List<Class<? extends SQLElement<?>>> tables = new ArrayList<>();
|
||||
|
||||
private static DBConnection connection;
|
||||
|
||||
public static DBConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public synchronized static void init(DBConnection conn) {
|
||||
|
||||
connection = conn;
|
||||
|
||||
/*
|
||||
* Les tables à initialiser
|
||||
* utile des les initialiser ici, car on peut tout de suite déceler les
|
||||
* bugs ou erreurs dans la déclaration des SQLFields
|
||||
*/
|
||||
|
||||
try {
|
||||
initTable(SQLContact.class);
|
||||
initTable(SQLForumCategorie.class);
|
||||
initTable(SQLForumForum.class);
|
||||
initTable(SQLForumPost.class);
|
||||
initTable(SQLForumThread.class);
|
||||
initTable(SQLLoginHistory.class);
|
||||
initTable(SQLLoginKickHistory.class);
|
||||
initTable(SQLModoHistory.class);
|
||||
initTable(SQLMPGroup.class);
|
||||
initTable(SQLMPGroupUser.class);
|
||||
initTable(SQLMPMessage.class);
|
||||
initTable(SQLOnlineshopHistory.class);
|
||||
initTable(SQLPingHistory.class);
|
||||
initTable(SQLPlayer.class);
|
||||
initTable(SQLPlayerIgnore.class);
|
||||
initTable(SQLShopStock.class);
|
||||
initTable(SQLStaffTicket.class);
|
||||
initTable(SQLStaticPages.class);
|
||||
initTable(SQLUUIDPlayer.class);
|
||||
} catch (ORMInitTableException e) {
|
||||
Log.getLogger().log(Level.SEVERE, "Erreur d'initialisation d'une table dans l'ORM", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* package */ static synchronized <E extends SQLElement<E>> void initTable(Class<E> elemClass) throws ORMInitTableException {
|
||||
if (tables.contains(elemClass)) return;
|
||||
try {
|
||||
tables.add(elemClass);
|
||||
//Log.info("Start Init SQL table "+elemClass.getSimpleName());
|
||||
E instance = elemClass.newInstance();
|
||||
String tableName = instance.tableName();
|
||||
if (!tableExist(tableName)) createTable(instance);
|
||||
//Log.info("End init SQL table "+elemClass.getSimpleName());
|
||||
} catch (Exception|ExceptionInInitializerError e) {
|
||||
throw new ORMInitTableException(elemClass, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <E extends SQLElement<E>> void createTable(E elem) throws SQLException {
|
||||
|
||||
String sql = "CREATE TABLE IF NOT EXISTS " + elem.tableName() + " (";
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
Collection<SQLField<E, ?>> tableFields = elem.getFields().values();
|
||||
boolean first = true;
|
||||
for (SQLField<E, ?> f : tableFields) {
|
||||
Pair<String, List<Object>> statementPart = f.forSQLPreparedStatement();
|
||||
params.addAll(statementPart.getValue());
|
||||
|
||||
if (!first) sql += ", ";
|
||||
first = false;
|
||||
sql += statementPart.getKey();
|
||||
}
|
||||
|
||||
sql += ", PRIMARY KEY id(id))";
|
||||
PreparedStatement ps = connection.getNativeConnection().prepareStatement(sql);
|
||||
int i = 1;
|
||||
for (Object val : params)
|
||||
ps.setObject(i++, val);
|
||||
try {
|
||||
Log.info("Creating table " + elem.tableName() + ":\n" + ps.toString());
|
||||
ps.executeUpdate();
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean tableExist(String tableName) throws SQLException {
|
||||
ResultSet set = null;
|
||||
boolean exist = false;
|
||||
try {
|
||||
set = connection.getNativeConnection().getMetaData().getTables(null, null, tableName, null);
|
||||
exist = set.next();
|
||||
} finally {
|
||||
if (set != null) set.close();
|
||||
}
|
||||
return exist;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E extends SQLElement<E>> SQLField<E, Integer> getSQLIdField(Class<E> elemClass)
|
||||
throws ORMInitTableException {
|
||||
initTable(elemClass);
|
||||
return (SQLField<E, Integer>) SQLElement.fieldsCache.get(elemClass).get("id");
|
||||
}
|
||||
|
||||
public static <E extends SQLElement<E>> List<E> getByIds(Class<E> elemClass, Collection<Integer> ids)
|
||||
throws ORMException {
|
||||
return getByIds(elemClass, ids.toArray(new Integer[ids.size()]));
|
||||
}
|
||||
|
||||
public static <E extends SQLElement<E>> List<E> getByIds(Class<E> elemClass, Integer... ids) throws ORMException {
|
||||
SQLField<E, Integer> idField = getSQLIdField(elemClass);
|
||||
SQLWhereChain where = new SQLWhereChain(SQLBoolOp.OR);
|
||||
for (Integer id : ids)
|
||||
if (id != null) where.add(new SQLWhereComp(idField, SQLComparator.EQ, id));
|
||||
return getAll(elemClass, where, new SQLOrderBy().addField(idField), 1, null);
|
||||
}
|
||||
|
||||
public static <E extends SQLElement<E>> E getById(Class<E> elemClass, int id) throws ORMException {
|
||||
return getFirst(elemClass, new SQLWhereComp(getSQLIdField(elemClass), SQLComparator.EQ, id), null);
|
||||
}
|
||||
|
||||
public static <E extends SQLElement<E>> E getFirst(Class<E> elemClass, SQLWhere where, SQLOrderBy orderBy)
|
||||
throws ORMException {
|
||||
SQLElementList<E> elts = getAll(elemClass, where, orderBy, 1, null);
|
||||
return (elts.size() == 0) ? null : elts.get(0);
|
||||
}
|
||||
|
||||
public static <E extends SQLElement<E>> SQLElementList<E> getAll(Class<E> elemClass) throws ORMException {
|
||||
return getAll(elemClass, null, null, null, null);
|
||||
}
|
||||
|
||||
public static <E extends SQLElement<E>> SQLElementList<E> getAll(Class<E> elemClass, SQLWhere where,
|
||||
SQLOrderBy orderBy, Integer limit, Integer offset) throws ORMException {
|
||||
initTable(elemClass);
|
||||
|
||||
try {
|
||||
String sql = "SELECT * FROM " + elemClass.newInstance().tableName();
|
||||
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (where != null) {
|
||||
Pair<String, List<Object>> ret = where.toSQL();
|
||||
sql += " WHERE " + ret.getKey();
|
||||
params.addAll(ret.getValue());
|
||||
}
|
||||
if (orderBy != null) sql += " ORDER BY " + orderBy.toSQL();
|
||||
if (limit != null) sql += " LIMIT " + limit;
|
||||
if (offset != null) sql += " OFFSET " + offset;
|
||||
sql += ";";
|
||||
|
||||
SQLElementList<E> elmts = new SQLElementList<>();
|
||||
|
||||
PreparedStatement ps = connection.getNativeConnection().prepareStatement(sql);
|
||||
|
||||
try {
|
||||
|
||||
int i = 1;
|
||||
for (Object val : params) {
|
||||
if (val instanceof Enum<?>) val = ((Enum<?>) val).name();
|
||||
ps.setObject(i++, val);
|
||||
}
|
||||
Log.debug(ps.toString());
|
||||
ResultSet set = ps.executeQuery();
|
||||
|
||||
try {
|
||||
while (set.next())
|
||||
elmts.add(getElementInstance(set, elemClass));
|
||||
} finally {
|
||||
set.close();
|
||||
}
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
|
||||
return elmts;
|
||||
} catch (ReflectiveOperationException | SQLException e) {
|
||||
throw new ORMException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static <E extends SQLElement<E>> E getElementInstance(ResultSet set, Class<E> elemClass) throws ORMException {
|
||||
try {
|
||||
E instance = elemClass.getConstructor(int.class).newInstance(set.getInt("id"));
|
||||
|
||||
int fieldCount = set.getMetaData().getColumnCount();
|
||||
|
||||
for (int c = 1; c <= fieldCount; c++) {
|
||||
String fieldName = set.getMetaData().getColumnLabel(c);
|
||||
if (!instance.getFields().containsKey(fieldName)) continue;
|
||||
// ignore when field is present in database but not handled by SQLElement instance
|
||||
@SuppressWarnings("unchecked")
|
||||
SQLField<E, Object> sqlField = (SQLField<E, Object>) instance.getFields().get(fieldName);
|
||||
if (sqlField.type.getJavaType().isEnum()) {
|
||||
// JDBC ne supporte pas les enums
|
||||
String enumStrValue = set.getString(c);
|
||||
if (enumStrValue == null || set.wasNull()) instance.set(sqlField, null, false);
|
||||
else {
|
||||
Enum<?> enumValue = EnumUtil.searchUncheckedEnum(sqlField.type.getJavaType(), enumStrValue);
|
||||
if (enumValue == null) throw new ORMException("The enum constant '" + enumStrValue
|
||||
+ "' is not found in enum class " + sqlField.type.getJavaType().getName());
|
||||
instance.set(sqlField, enumValue, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object val = set.getObject(c, sqlField.type.getJavaType());
|
||||
if (val == null || set.wasNull()) instance.set(sqlField, null, false);
|
||||
else
|
||||
instance.set(sqlField, val, false);
|
||||
}
|
||||
|
||||
// la valeur venant de la BDD est marqué comme "non modifié"
|
||||
// dans l'instance
|
||||
// car le constructeur de l'instance met tout les champs comme
|
||||
// modifiés
|
||||
instance.modifiedSinceLastSave.remove(sqlField.name);
|
||||
}
|
||||
|
||||
if (!instance.isValidForSave()) throw new ORMException(
|
||||
"This SQLElement representing a database entry is not valid for save : " + instance.toString());
|
||||
|
||||
return instance;
|
||||
} catch (ReflectiveOperationException | IllegalArgumentException | SecurityException | SQLException e) {
|
||||
throw new ORMException("Can't instanciate " + elemClass.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private ORM() {} // rend la classe non instanciable
|
||||
|
||||
/*
|
||||
* public static void main(String[] args) throws Throwable {
|
||||
* ORM.init(new DBConnection("localhost", 3306, "pandacube", "pandacube",
|
||||
* "pandacube"));
|
||||
* List<SQLPlayer> players = ORM.getAll(SQLPlayer.class,
|
||||
* new SQLWhereChain(SQLBoolOp.AND)
|
||||
* .add(new SQLWhereNull(SQLPlayer.banTimeout, true))
|
||||
* .add(new SQLWhereChain(SQLBoolOp.OR)
|
||||
* .add(new SQLWhereComp(SQLPlayer.bambou, SQLComparator.EQ, 0L))
|
||||
* .add(new SQLWhereComp(SQLPlayer.grade, SQLComparator.EQ, "default"))
|
||||
* ),
|
||||
* new SQLOrderBy().addField(SQLPlayer.playerDisplayName), null, null);
|
||||
* for(SQLPlayer p : players) {
|
||||
* System.out.println(p.get(SQLPlayer.playerDisplayName));
|
||||
* }
|
||||
* // TODO mise à jour relative d'un champ (incrément / décrément)
|
||||
* }
|
||||
*/
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
public class ORMException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ORMException(Throwable initCause) {
|
||||
super(initCause);
|
||||
}
|
||||
|
||||
public ORMException(String message, Throwable initCause) {
|
||||
super(message, initCause);
|
||||
}
|
||||
|
||||
public ORMException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
public class ORMInitTableException extends ORMException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/* package */ <E extends SQLElement<E>> ORMInitTableException(Class<E> tableElem) {
|
||||
super("Error while initializing table " + ((tableElem != null) ? tableElem.getName() : "null"));
|
||||
}
|
||||
|
||||
/* package */ <E extends SQLElement<E>> ORMInitTableException(Class<E> tableElem, Throwable t) {
|
||||
super("Error while initializing table " + ((tableElem != null) ? tableElem.getName() : "null"), t);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,410 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import fr.pandacube.java.util.Log;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
|
||||
public abstract class SQLElement<E extends SQLElement<E>> {
|
||||
/** cache for fields for each subclass of SQLElement */
|
||||
/* package */ static final Map<Class<? extends SQLElement<?>>, SQLFieldMap<? extends SQLElement<?>>> fieldsCache = new HashMap<>();
|
||||
|
||||
DBConnection db = ORM.getConnection();
|
||||
|
||||
private boolean stored = false;
|
||||
private int id;
|
||||
|
||||
private final String tableName;
|
||||
private final SQLFieldMap<E> fields;
|
||||
|
||||
private final Map<SQLField<E, ?>, Object> values;
|
||||
/* package */ final Set<String> modifiedSinceLastSave;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public SQLElement() {
|
||||
tableName = tableName();
|
||||
|
||||
try {
|
||||
ORM.initTable((Class<E>)getClass());
|
||||
} catch (ORMInitTableException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (fieldsCache.get(getClass()) == null) {
|
||||
fields = new SQLFieldMap<>((Class<E>)getClass());
|
||||
|
||||
// le champ id commun à toutes les tables
|
||||
fields.addField(new SQLField<>("id", SQLType.INT, false, true, 0));
|
||||
|
||||
generateFields(fields);
|
||||
fieldsCache.put((Class<E>)getClass(), fields);
|
||||
}
|
||||
else
|
||||
fields = (SQLFieldMap<E>) fieldsCache.get(getClass());
|
||||
|
||||
values = new LinkedHashMap<>(fields.size());
|
||||
modifiedSinceLastSave = new HashSet<>(fields.size());
|
||||
|
||||
initDefaultValues();
|
||||
|
||||
}
|
||||
|
||||
protected SQLElement(int id) {
|
||||
this();
|
||||
@SuppressWarnings("unchecked")
|
||||
SQLField<E, Integer> idField = (SQLField<E, Integer>) fields.get("id");
|
||||
set(idField, id, false);
|
||||
this.id = id;
|
||||
stored = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the table in the database.
|
||||
*/
|
||||
protected abstract String tableName();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initDefaultValues() {
|
||||
// remplissage des données par défaut (si peut être null ou si valeur
|
||||
// par défaut existe)
|
||||
for (@SuppressWarnings("rawtypes")
|
||||
SQLField f : fields.values())
|
||||
if (f.defaultValue != null) set(f, f.defaultValue);
|
||||
else if (f.canBeNull || (f.autoIncrement && !stored)) set(f, null);
|
||||
}
|
||||
|
||||
protected void generateFields(SQLFieldMap<E> listToFill) {
|
||||
|
||||
java.lang.reflect.Field[] declaredFields = getClass().getDeclaredFields();
|
||||
for (java.lang.reflect.Field field : declaredFields) {
|
||||
if (!java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue;
|
||||
|
||||
try {
|
||||
Object val = field.get(null);
|
||||
if (val == null || !(val instanceof SQLField)) continue;
|
||||
|
||||
listToFill.addField((SQLField<?, ?>) val);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
Log.severe("Can't get value of static field " + field.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* package */ Map<String, SQLField<E, ?>> getFields() {
|
||||
return Collections.unmodifiableMap(fields);
|
||||
}
|
||||
|
||||
public Map<SQLField<E, ?>, Object> getValues() {
|
||||
return Collections.unmodifiableMap(values);
|
||||
}
|
||||
|
||||
public <T> void set(SQLField<E, T> field, T value) {
|
||||
set(field, value, true);
|
||||
}
|
||||
|
||||
/* package */ <T> void set(SQLField<E, T> sqlField, T value, boolean setModified) {
|
||||
if (sqlField == null) throw new IllegalArgumentException("sqlField can't be null");
|
||||
if (!fields.containsValue(sqlField))
|
||||
throw new IllegalArgumentException(sqlField.getSQLElementType().getName()+sqlField.name + " is not a SQLField of " + getClass().getName());
|
||||
|
||||
boolean modify = false;
|
||||
if (value == null) {
|
||||
if (sqlField.canBeNull || (sqlField.autoIncrement && !stored)) modify = true;
|
||||
else
|
||||
throw new IllegalArgumentException(
|
||||
"SQLField '" + sqlField.name + "' of " + getClass().getName() + " is a NOT NULL field");
|
||||
}
|
||||
else if (sqlField.type.isAssignableFrom(value)) modify = true;
|
||||
else
|
||||
throw new IllegalArgumentException("SQLField '" + sqlField.name + "' of " + getClass().getName()
|
||||
+ " type is '" + sqlField.type.toString() + "' and can't accept values of type "
|
||||
+ value.getClass().getName());
|
||||
|
||||
if (modify) if (!values.containsKey(sqlField)) {
|
||||
values.put(sqlField, value);
|
||||
if (setModified) modifiedSinceLastSave.add(sqlField.name);
|
||||
}
|
||||
else {
|
||||
Object oldVal = values.get(sqlField);
|
||||
if (!Objects.equals(oldVal, value)) {
|
||||
values.put(sqlField, value);
|
||||
if (setModified) modifiedSinceLastSave.add(sqlField.name);
|
||||
}
|
||||
// sinon, rien n'est modifié
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public <T> T get(SQLField<E, T> field) {
|
||||
if (field == null) throw new IllegalArgumentException("field can't be null");
|
||||
if (values.containsKey(field)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T val = (T) values.get(field);
|
||||
return val;
|
||||
}
|
||||
throw new IllegalArgumentException("The field '" + field.name + "' in this instance of " + getClass().getName()
|
||||
+ " does not exist or is not set");
|
||||
}
|
||||
|
||||
public <T, F extends SQLElement<F>> F getForeign(SQLFKField<E, T, F> field) throws ORMException {
|
||||
T fkValue = get(field);
|
||||
if (fkValue == null) return null;
|
||||
return ORM.getFirst(field.getForeignElementClass(),
|
||||
new SQLWhereComp(field.getForeignField(), SQLComparator.EQ, fkValue), null);
|
||||
}
|
||||
|
||||
public boolean isValidForSave() {
|
||||
return values.keySet().containsAll(fields.values());
|
||||
}
|
||||
|
||||
private Map<SQLField<E, ?>, Object> getOnlyModifiedValues() {
|
||||
Map<SQLField<E, ?>, Object> modifiedValues = new LinkedHashMap<>();
|
||||
values.forEach((k, v) -> {
|
||||
if (modifiedSinceLastSave.contains(k.name)) modifiedValues.put(k, v);
|
||||
});
|
||||
return modifiedValues;
|
||||
}
|
||||
|
||||
public boolean isModified(SQLField<E, ?> field) {
|
||||
return modifiedSinceLastSave.contains(field.name);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "resource" })
|
||||
public void save() throws ORMException {
|
||||
if (!isValidForSave())
|
||||
throw new IllegalStateException(toString() + " has at least one undefined value and can't be saved.");
|
||||
|
||||
ORM.initTable((Class<E>)getClass());
|
||||
String toStringStatement = "";
|
||||
try {
|
||||
|
||||
Connection conn = db.getNativeConnection();
|
||||
|
||||
if (stored) { // mettre à jour les valeurs dans la base
|
||||
|
||||
// restaurer l'ID au cas il aurait été changé à la main dans
|
||||
// values
|
||||
SQLField<E, Integer> idField = (SQLField<E, Integer>) fields.get("id");
|
||||
values.put(idField, id);
|
||||
modifiedSinceLastSave.remove("id");
|
||||
Map<SQLField<E, ?>, Object> modifiedValues = getOnlyModifiedValues();
|
||||
|
||||
if (modifiedValues.isEmpty()) return;
|
||||
|
||||
String sql = "";
|
||||
List<Object> psValues = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<SQLField<E, ?>, Object> entry : modifiedValues.entrySet()) {
|
||||
sql += entry.getKey().name + " = ? ,";
|
||||
if (entry.getKey().type.getJavaType().isEnum()) // prise en
|
||||
// charge
|
||||
// enum (non
|
||||
// prise en
|
||||
// charge
|
||||
// par JDBC)
|
||||
psValues.add(((Enum<?>) entry.getValue()).name());
|
||||
else
|
||||
psValues.add(entry.getValue());
|
||||
}
|
||||
|
||||
if (sql.length() > 0) sql = sql.substring(0, sql.length() - 1);
|
||||
|
||||
PreparedStatement ps = conn.prepareStatement("UPDATE " + tableName + " SET " + sql + " WHERE id=" + id);
|
||||
|
||||
try {
|
||||
|
||||
int i = 1;
|
||||
for (Object val : psValues)
|
||||
ps.setObject(i++, val);
|
||||
|
||||
toStringStatement = ps.toString();
|
||||
ps.executeUpdate();
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
else { // ajouter dans la base
|
||||
|
||||
// restaurer l'ID au cas il aurait été changé à la main dans
|
||||
// values
|
||||
values.put(fields.get("id"), null);
|
||||
|
||||
String concat_vals = "";
|
||||
String concat_fields = "";
|
||||
List<Object> psValues = new ArrayList<>();
|
||||
|
||||
boolean first = true;
|
||||
for (Map.Entry<SQLField<E, ?>, Object> entry : values.entrySet()) {
|
||||
if (!first) {
|
||||
concat_vals += ",";
|
||||
concat_fields += ",";
|
||||
}
|
||||
first = false;
|
||||
concat_vals += " ? ";
|
||||
concat_fields += entry.getKey().name;
|
||||
if (entry.getKey().type.getJavaType().isEnum()) // prise en
|
||||
// charge
|
||||
// enum (non
|
||||
// prise en
|
||||
// charge
|
||||
// par JDBC)
|
||||
psValues.add(((Enum<?>) entry.getValue()).name());
|
||||
else
|
||||
psValues.add(entry.getValue());
|
||||
}
|
||||
|
||||
PreparedStatement ps = conn.prepareStatement(
|
||||
"INSERT INTO " + tableName + " (" + concat_fields + ") VALUES (" + concat_vals + ")",
|
||||
Statement.RETURN_GENERATED_KEYS);
|
||||
try {
|
||||
|
||||
int i = 1;
|
||||
for (Object val : psValues)
|
||||
ps.setObject(i++, val);
|
||||
|
||||
toStringStatement = ps.toString();
|
||||
ps.executeUpdate();
|
||||
|
||||
ResultSet rs = ps.getGeneratedKeys();
|
||||
try {
|
||||
if (rs.next()) id = rs.getInt(1);
|
||||
|
||||
stored = true;
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
modifiedSinceLastSave.clear();
|
||||
} catch (SQLException e) {
|
||||
throw new ORMException("Error while executing SQL statement " + toStringStatement, e);
|
||||
}
|
||||
Log.debug(toStringStatement);
|
||||
}
|
||||
|
||||
public boolean isStored() {
|
||||
return stored;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return (stored) ? id : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public SQLField<E, Integer> getFieldId() {
|
||||
return (SQLField<E, Integer>) fields.get("id");
|
||||
}
|
||||
|
||||
public void delete() throws ORMException {
|
||||
|
||||
try {
|
||||
if (stored) { // supprimer la ligne de la base
|
||||
PreparedStatement st = db.getNativeConnection()
|
||||
.prepareStatement("DELETE FROM " + tableName + " WHERE id=" + id);
|
||||
try {
|
||||
Log.debug(st.toString());
|
||||
st.executeUpdate();
|
||||
markAsNotStored();
|
||||
} finally {
|
||||
st.close();
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new ORMException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode appelée quand l'élément courant est retirée de la base de données
|
||||
* via une requête externe
|
||||
*/
|
||||
/* package */ void markAsNotStored() {
|
||||
stored = false;
|
||||
id = 0;
|
||||
modifiedSinceLastSave.clear();
|
||||
values.forEach((k, v) -> modifiedSinceLastSave.add(k.name));
|
||||
}
|
||||
|
||||
protected static class SQLFieldMap<E extends SQLElement<E>> extends LinkedHashMap<String, SQLField<E, ?>> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Class<E> sqlElemClass;
|
||||
|
||||
private SQLFieldMap(Class<E> elemClass) {
|
||||
sqlElemClass = elemClass;
|
||||
}
|
||||
|
||||
private void addField(SQLField<?, ?> f) {
|
||||
if (f == null) return;
|
||||
if (containsKey(f.name)) throw new IllegalArgumentException(
|
||||
"SQLField " + f.name + " already exist in " + sqlElemClass.getName());
|
||||
@SuppressWarnings("unchecked")
|
||||
SQLField<E, ?> checkedF = (SQLField<E, ?>) f;
|
||||
checkedF.setSQLElementType(sqlElemClass);
|
||||
put(checkedF.name, checkedF);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder b = new ToStringBuilder(this);
|
||||
|
||||
for (SQLField<E, ?> f : fields.values())
|
||||
try {
|
||||
b.append(f.name, get(f));
|
||||
} catch (IllegalArgumentException e) {
|
||||
b.append(f.name, "(Undefined)");
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(getClass().isInstance(o))) return false;
|
||||
SQLElement<?> oEl = (SQLElement<?>) o;
|
||||
if (oEl.getId() == null) return false;
|
||||
return oEl.getId().equals(getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public JsonObject asJsonObject() {
|
||||
JsonObject json = new JsonObject();
|
||||
for (SQLField<E, ?> f : getFields().values()) {
|
||||
json.add(f.name, new Gson().toJsonTree(get(f)));
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,210 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
|
||||
import fr.pandacube.java.util.Log;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereChain.SQLBoolOp;
|
||||
import fr.pandacube.java.util.db.sql_tools.SQLWhereComp.SQLComparator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public class SQLElementList<E extends SQLElement<E>> extends ArrayList<E> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Map<SQLField<E, ?>, Object> modifiedValues = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public synchronized boolean add(E e) {
|
||||
if (e == null || !e.isStored()) return false;
|
||||
return super.add(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Défini une valeur à un champ qui sera appliquée dans la base de données à
|
||||
* tous les
|
||||
* entrées présente dans cette liste lors de l'appel à {@link #saveCommon()}
|
||||
* .
|
||||
* Les valeurs stockés dans chaque élément de cette liste ne seront affectés
|
||||
* que lors de
|
||||
* l'appel à {@link #saveCommon()}
|
||||
*
|
||||
* @param <T>
|
||||
* @param field le champs à modifier
|
||||
* @param value la valeur à lui appliquer
|
||||
*/
|
||||
public synchronized <T> void setCommon(SQLField<E, T> field, T value) {
|
||||
if (field != null && field.name == "id")
|
||||
throw new IllegalArgumentException("Can't modify id field in a SQLElementList");
|
||||
if (field == null)
|
||||
throw new IllegalArgumentException("field can't be null");
|
||||
|
||||
Class<E> elemClass = field.getSQLElementType();
|
||||
try {
|
||||
E emptyElement = elemClass.newInstance();
|
||||
emptyElement.set(field, value, false);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Illegal field or value or can't instanciante an empty instance of "
|
||||
+ elemClass.getName() + ". (the instance is only created to test validity of field and value)", e);
|
||||
}
|
||||
|
||||
// ici, la valeur est bonne
|
||||
modifiedValues.put(field, value);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Applique toutes les valeurs défini avec
|
||||
* {@link #setCommon(SQLField, Object)} à toutes
|
||||
* les entrées dans la base de données correspondants aux entrées de cette
|
||||
* liste. Les nouvelles
|
||||
* valeurs sont aussi mises à jour dans les objets contenus dans cette
|
||||
* liste, si la valeur n'a pas été modifiée individuellement avec
|
||||
* {@link SQLElement#set(SQLField, Object)}.<br/>
|
||||
* Les objets de cette liste qui n'ont pas leur données en base de données
|
||||
* sont ignorées.
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
public synchronized void saveCommon() throws SQLException {
|
||||
List<E> storedEl = getStoredEl();
|
||||
if (storedEl.isEmpty()) return;
|
||||
|
||||
String sqlSet = "";
|
||||
List<Object> psValues = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<SQLField<E, ?>, Object> entry : modifiedValues.entrySet()) {
|
||||
sqlSet += entry.getKey().name + " = ? ,";
|
||||
if (entry.getKey().type.getJavaType().isEnum()) // prise en charge
|
||||
// enum (non prise
|
||||
// en charge par
|
||||
// JDBC)
|
||||
psValues.add(((Enum<?>) entry.getValue()).name());
|
||||
else
|
||||
psValues.add(entry.getValue());
|
||||
}
|
||||
|
||||
if (sqlSet.length() > 0) sqlSet = sqlSet.substring(0, sqlSet.length() - 1);
|
||||
|
||||
String sqlWhere = "";
|
||||
boolean first = true;
|
||||
for (E el : storedEl) {
|
||||
if (!first) sqlWhere += " OR ";
|
||||
first = false;
|
||||
sqlWhere += "id = " + el.getId();
|
||||
}
|
||||
|
||||
PreparedStatement ps = ORM.getConnection().getNativeConnection()
|
||||
.prepareStatement("UPDATE " + storedEl.get(0).tableName() + " SET " + sqlSet + " WHERE " + sqlWhere);
|
||||
try {
|
||||
|
||||
int i = 1;
|
||||
for (Object val : psValues)
|
||||
ps.setObject(i++, val);
|
||||
|
||||
Log.debug(ps.toString());
|
||||
ps.executeUpdate();
|
||||
|
||||
applyNewValuesToElements(storedEl);
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void applyNewValuesToElements(List<E> storedEl) {
|
||||
// applique les valeurs dans chaques objets de la liste
|
||||
for (E el : storedEl)
|
||||
for (@SuppressWarnings("rawtypes")
|
||||
SQLField entry : modifiedValues.keySet())
|
||||
if (!el.isModified(entry)) el.set(entry, modifiedValues.get(entry), false);
|
||||
}
|
||||
|
||||
private List<E> getStoredEl() {
|
||||
List<E> listStored = new ArrayList<>();
|
||||
forEach(el -> {
|
||||
if (el.isStored()) listStored.add(el);
|
||||
});
|
||||
return listStored;
|
||||
}
|
||||
|
||||
public synchronized void removeFromDB() {
|
||||
List<E> storedEl = getStoredEl();
|
||||
if (storedEl.isEmpty()) return;
|
||||
|
||||
try {
|
||||
|
||||
String sqlWhere = "";
|
||||
boolean first = true;
|
||||
for (E el : storedEl) {
|
||||
if (!first) sqlWhere += " OR ";
|
||||
first = false;
|
||||
sqlWhere += "id = " + el.getId();
|
||||
}
|
||||
|
||||
PreparedStatement st = ORM.getConnection().getNativeConnection()
|
||||
.prepareStatement("DELETE FROM " + storedEl.get(0).tableName() + " WHERE " + sqlWhere);
|
||||
try {
|
||||
Log.debug(st.toString());
|
||||
st.executeUpdate();
|
||||
|
||||
for (E el : storedEl)
|
||||
el.markAsNotStored();
|
||||
|
||||
} finally {
|
||||
st.close();
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
Log.severe(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public <T, F extends SQLElement<F>> Map<T, F> getAllForeign(SQLFKField<E, T, F> foreignKey) throws ORMException {
|
||||
Set<T> values = new HashSet<>();
|
||||
forEach(v -> {
|
||||
T val = v.get(foreignKey);
|
||||
if (val != null)
|
||||
values.add(val);
|
||||
});
|
||||
|
||||
if (values.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
SQLWhereChain where = new SQLWhereChain(SQLBoolOp.OR);
|
||||
values.forEach(v -> where.add(new SQLWhereComp(foreignKey.getForeignField(), SQLComparator.EQ, v)));
|
||||
|
||||
|
||||
SQLElementList<F> foreignElemts = ORM.getAll(foreignKey.getForeignElementClass(), where, null, null, null);
|
||||
|
||||
Map<T, F> ret = new HashMap<>();
|
||||
foreignElemts.forEach(foreignVal -> ret.put(foreignVal.get(foreignKey.getForeignField()), foreignVal));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public JsonArray asJsonArray() {
|
||||
JsonArray json = new JsonArray();
|
||||
forEach(el -> json.add(el.asJsonObject()));
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import fr.pandacube.java.util.Log;
|
||||
|
||||
public class SQLFKField<E extends SQLElement<E>, T, F extends SQLElement<F>> extends SQLField<E, T> {
|
||||
|
||||
private SQLField<F, T> sqlForeignKeyField;
|
||||
private Class<F> sqlForeignKeyElemClass;
|
||||
|
||||
public SQLFKField(String n, SQLType<T> t, boolean nul, Class<F> fkEl, SQLField<F, T> fkF) {
|
||||
super(n, t, nul);
|
||||
construct(fkEl, fkF);
|
||||
}
|
||||
|
||||
public SQLFKField(String n, SQLType<T> t, boolean nul, T deflt, Class<F> fkEl, SQLField<F, T> fkF) {
|
||||
super(n, t, nul, deflt);
|
||||
construct(fkEl, fkF);
|
||||
}
|
||||
|
||||
public static <E extends SQLElement<E>, F extends SQLElement<F>> SQLFKField<E, Integer, F> idFK(String n, SQLType<Integer> t, boolean nul,
|
||||
Class<F> fkEl) {
|
||||
if (fkEl == null) throw new IllegalArgumentException("foreignKeyElement can't be null");
|
||||
try {
|
||||
return new SQLFKField<>(n, t, nul, fkEl, ORM.getSQLIdField(fkEl));
|
||||
} catch (ORMInitTableException e) {
|
||||
Log.severe("Can't create Foreign key Field called '" + n + "'", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <E extends SQLElement<E>, F extends SQLElement<F>> SQLFKField<E, Integer, F> idFKField(String n, SQLType<Integer> t, boolean nul,
|
||||
Integer deflt, Class<F> fkEl) {
|
||||
if (fkEl == null) throw new IllegalArgumentException("foreignKeyElement can't be null");
|
||||
try {
|
||||
return new SQLFKField<>(n, t, nul, deflt, fkEl, ORM.getSQLIdField(fkEl));
|
||||
} catch (ORMInitTableException e) {
|
||||
Log.severe("Can't create Foreign key Field called '" + n + "'", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void construct(Class<F> fkEl, SQLField<F, T> fkF) {
|
||||
if (fkF == null) throw new IllegalArgumentException("foreignKeyField can't be null");
|
||||
try {
|
||||
ORM.initTable(fkEl);
|
||||
} catch (ORMInitTableException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (!fkEl.equals(fkF.getSQLElementType()))
|
||||
throw new IllegalArgumentException("foreignKeyField must be from supplied foreignKeyElement");
|
||||
if (!type.equals(fkF.type))
|
||||
throw new IllegalArgumentException("foreignKeyField and current Field must have the same SQLType");
|
||||
sqlForeignKeyField = fkF;
|
||||
sqlForeignKeyElemClass = fkEl;
|
||||
}
|
||||
|
||||
public SQLField<F, T> getForeignField() {
|
||||
return sqlForeignKeyField;
|
||||
}
|
||||
|
||||
public Class<F> getForeignElementClass() {
|
||||
return sqlForeignKeyElemClass;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class SQLField<E extends SQLElement<E>, T> {
|
||||
|
||||
private Class<E> sqlElemClass;
|
||||
public final String name;
|
||||
public final SQLType<T> type;
|
||||
public final boolean canBeNull;
|
||||
public final boolean autoIncrement;
|
||||
/* package */ final T defaultValue;
|
||||
|
||||
public SQLField(String n, SQLType<T> t, boolean nul, boolean autoIncr, T deflt) {
|
||||
name = n;
|
||||
type = t;
|
||||
canBeNull = nul;
|
||||
autoIncrement = autoIncr;
|
||||
defaultValue = deflt;
|
||||
}
|
||||
|
||||
public SQLField(String n, SQLType<T> t, boolean nul) {
|
||||
this(n, t, nul, false, null);
|
||||
}
|
||||
|
||||
public SQLField(String n, SQLType<T> t, boolean nul, boolean autoIncr) {
|
||||
this(n, t, nul, autoIncr, null);
|
||||
}
|
||||
|
||||
public SQLField(String n, SQLType<T> t, boolean nul, T deflt) {
|
||||
this(n, t, nul, false, deflt);
|
||||
}
|
||||
|
||||
/* package */ Pair<String, List<Object>> forSQLPreparedStatement() {
|
||||
List<Object> params = new ArrayList<>(1);
|
||||
if (defaultValue != null && !autoIncrement) params.add(defaultValue);
|
||||
return new Pair<>(name + " " + type.toString() + (canBeNull ? " NULL" : " NOT NULL")
|
||||
+ (autoIncrement ? " AUTO_INCREMENT" : "")
|
||||
+ ((defaultValue == null || autoIncrement) ? "" : " DEFAULT ?"), params);
|
||||
}
|
||||
|
||||
/* package */ void setSQLElementType(Class<E> elemClass) {
|
||||
sqlElemClass = elemClass;
|
||||
}
|
||||
|
||||
public Class<E> getSQLElementType() {
|
||||
return sqlElemClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Don't use this {@link #toString()} method in a SQL query, because
|
||||
* the default value is not escaped correctly</b>
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return forSQLPreparedStatement().getKey().replaceFirst("\\?",
|
||||
(defaultValue != null && !autoIncrement) ? defaultValue.toString() : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof SQLField)) return false;
|
||||
SQLField<?, ?> f = (SQLField<?, ?>) obj;
|
||||
if (!f.name.equals(name)) return false;
|
||||
if (!f.sqlElemClass.equals(sqlElemClass)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode() + sqlElemClass.hashCode();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SQLOrderBy {
|
||||
|
||||
private List<OBField> orderByFields = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Construit une nouvelle clause ORDER BY
|
||||
*/
|
||||
public SQLOrderBy() {}
|
||||
|
||||
/**
|
||||
* Ajoute un champ dans la clause ORDER BY en construction
|
||||
*
|
||||
* @param field le champ SQL à ordonner
|
||||
* @param d le sens de tri (croissant ASC ou décroissant DESC)
|
||||
* @return l'objet courant (permet de chainer les ajouts de champs)
|
||||
*/
|
||||
public SQLOrderBy addField(SQLField<?, ?> field, Direction d) {
|
||||
orderByFields.add(new OBField(field, d));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute un champ dans la clause ORDER BY en construction,
|
||||
* avec comme ordre de tri croissant ASC par défaut
|
||||
*
|
||||
* @param field le champ SQL à ordonner dans l'ordre croissant ASC
|
||||
* @return l'objet courant (permet de chainer les ajouts de champs)
|
||||
*/
|
||||
public SQLOrderBy addField(SQLField<?, ?> field) {
|
||||
return addField(field, Direction.ASC);
|
||||
}
|
||||
|
||||
/* package */ String toSQL() {
|
||||
String ret = "";
|
||||
boolean first = true;
|
||||
for (OBField f : orderByFields) {
|
||||
if (!first) ret += ", ";
|
||||
first = false;
|
||||
ret += f.field.name + " " + f.direction.name();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toSQL();
|
||||
}
|
||||
|
||||
private class OBField {
|
||||
public final SQLField<?, ?> field;
|
||||
public final Direction direction;
|
||||
|
||||
public OBField(SQLField<?, ?> f, Direction d) {
|
||||
field = f;
|
||||
direction = d;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
ASC, DESC;
|
||||
}
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.sql.Date;
|
||||
|
||||
public class SQLType<T> {
|
||||
|
||||
private final String sqlType;
|
||||
private final String sqlTypeParam;
|
||||
private final Class<T> javaTypes;
|
||||
|
||||
public SQLType(String sqlT, String sqlP, Class<T> javaT) {
|
||||
sqlType = sqlT;
|
||||
sqlTypeParam = sqlP;
|
||||
javaTypes = javaT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return sqlType + sqlTypeParam;
|
||||
}
|
||||
|
||||
public boolean isAssignableFrom(Object val) {
|
||||
if (javaTypes.isInstance(val)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || !(obj instanceof SQLType)) return false;
|
||||
return toString().equals(((SQLType<?>) obj).toString());
|
||||
}
|
||||
|
||||
public Class<T> getJavaType() {
|
||||
return javaTypes;
|
||||
}
|
||||
|
||||
public static final SQLType<Boolean> BOOLEAN = new SQLType<>("BOOLEAN", "", Boolean.class);
|
||||
|
||||
public static final SQLType<Byte> TINYINT = new SQLType<>("TINYINT", "", Byte.class);
|
||||
public static final SQLType<Byte> BYTE = TINYINT;
|
||||
|
||||
public static final SQLType<Short> SMALLINT = new SQLType<>("SMALLINT", "", Short.class);
|
||||
public static final SQLType<Short> SHORT = SMALLINT;
|
||||
|
||||
public static final SQLType<Integer> INT = new SQLType<>("INT", "", Integer.class);
|
||||
public static final SQLType<Integer> INTEGER = INT;
|
||||
|
||||
public static final SQLType<Long> BIGINT = new SQLType<>("BIGINT", "", Long.class);
|
||||
public static final SQLType<Long> LONG = BIGINT;
|
||||
|
||||
public static final SQLType<Date> DATE = new SQLType<>("DATE", "", Date.class);
|
||||
|
||||
public static final SQLType<Float> FLOAT = new SQLType<>("FLOAT", "", Float.class);
|
||||
|
||||
public static final SQLType<Double> DOUBLE = new SQLType<>("DOUBLE", "", Double.class);
|
||||
|
||||
public static final SQLType<String> CHAR(int charCount) {
|
||||
if (charCount <= 0) throw new IllegalArgumentException("charCount must be positive.");
|
||||
return new SQLType<>("CHAR", "(" + charCount + ")", String.class);
|
||||
}
|
||||
|
||||
public static final SQLType<String> VARCHAR(int charCount) {
|
||||
if (charCount <= 0) throw new IllegalArgumentException("charCount must be positive.");
|
||||
return new SQLType<>("VARCHAR", "(" + charCount + ")", String.class);
|
||||
}
|
||||
|
||||
public static final SQLType<String> TEXT = new SQLType<>("TEXT", "", String.class);
|
||||
public static final SQLType<String> STRING = TEXT;
|
||||
|
||||
public static final <T extends Enum<T>> SQLType<T> ENUM(Class<T> enumType) {
|
||||
if (enumType == null) throw new IllegalArgumentException("enumType can't be null.");
|
||||
String enumStr = "'";
|
||||
boolean first = true;
|
||||
for (T el : enumType.getEnumConstants()) {
|
||||
if (!first) enumStr += "', '";
|
||||
first = false;
|
||||
enumStr += el.name();
|
||||
|
||||
}
|
||||
enumStr += "'";
|
||||
|
||||
return new SQLType<>("VARCHAR", "(" + enumStr + ")", enumType);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javafx.util.Pair;
|
||||
|
||||
public abstract class SQLWhere {
|
||||
|
||||
public abstract Pair<String, List<Object>> toSQL();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toSQL().getKey();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class SQLWhereChain extends SQLWhere {
|
||||
|
||||
private SQLBoolOp operator;
|
||||
private List<SQLWhere> conditions = new ArrayList<>();
|
||||
|
||||
public SQLWhereChain(SQLBoolOp op) {
|
||||
if (op == null) throw new IllegalArgumentException("op can't be null");
|
||||
operator = op;
|
||||
}
|
||||
|
||||
public SQLWhereChain add(SQLWhere sqlWhere) {
|
||||
if (sqlWhere == null) throw new IllegalArgumentException("sqlWhere can't be null");
|
||||
conditions.add(sqlWhere);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, List<Object>> toSQL() {
|
||||
String sql = "";
|
||||
List<Object> params = new ArrayList<>();
|
||||
boolean first = true;
|
||||
|
||||
for (SQLWhere w : conditions) {
|
||||
if (!first) sql += " " + operator.sql + " ";
|
||||
first = false;
|
||||
|
||||
Pair<String, List<Object>> ret = w.toSQL();
|
||||
sql += "(" + ret.getKey() + ")";
|
||||
params.addAll(ret.getValue());
|
||||
}
|
||||
|
||||
return new Pair<>(sql, params);
|
||||
}
|
||||
|
||||
public enum SQLBoolOp {
|
||||
/** Equivalent to SQL "<code>AND</code>" */
|
||||
AND("AND"), /** Equivalent to SQL "<code>OR</code>" */
|
||||
OR("OR");
|
||||
public final String sql;
|
||||
|
||||
private SQLBoolOp(String s) {
|
||||
sql = s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class SQLWhereComp extends SQLWhere {
|
||||
|
||||
private SQLField<?, ?> left;
|
||||
private SQLComparator comp;
|
||||
private Object right;
|
||||
|
||||
/**
|
||||
* Compare a field with a value
|
||||
*
|
||||
* @param l the field at left of the comparison operator. Can't be null
|
||||
* @param c the comparison operator, can't be null
|
||||
* @param r the value at right of the comparison operator. Can't be null
|
||||
*/
|
||||
public <T> SQLWhereComp(SQLField<?, T> l, SQLComparator c, T r) {
|
||||
if (l == null || r == null || c == null)
|
||||
throw new IllegalArgumentException("All arguments for SQLWhereComp constructor can't be null");
|
||||
left = l;
|
||||
comp = c;
|
||||
right = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, List<Object>> toSQL() {
|
||||
List<Object> params = new ArrayList<>();
|
||||
params.add(right);
|
||||
return new Pair<>(left.name + " " + comp.sql + " ? ", params);
|
||||
}
|
||||
|
||||
public enum SQLComparator {
|
||||
/** Equivalent to SQL "<code>=</code>" */
|
||||
EQ("="), /** Equivalent to SQL "<code>></code>" */
|
||||
GT(">"), /** Equivalent to SQL "<code>>=</code>" */
|
||||
GEQ(">="), /** Equivalent to SQL "<code><</code>" */
|
||||
LT("<"), /** Equivalent to SQL "<code><=</code>" */
|
||||
LEQ("<="), /** Equivalent to SQL "<code>!=</code>" */
|
||||
NEQ("!=");
|
||||
|
||||
public final String sql;
|
||||
|
||||
private SQLComparator(String s) {
|
||||
sql = s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class SQLWhereLike extends SQLWhere {
|
||||
|
||||
private SQLField<?, String> field;
|
||||
private String likeExpr;
|
||||
|
||||
/**
|
||||
* Compare a field with a value
|
||||
*
|
||||
* @param f the field at left of the LIKE keyword. Can't be null
|
||||
* @param like the like expression.
|
||||
*/
|
||||
public SQLWhereLike(SQLField<?, String> f, String like) {
|
||||
if (f == null || like == null)
|
||||
throw new IllegalArgumentException("All arguments for SQLWhereLike constructor can't be null");
|
||||
field = f;
|
||||
likeExpr = like;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, List<Object>> toSQL() {
|
||||
ArrayList<Object> params = new ArrayList<>();
|
||||
params.add(likeExpr);
|
||||
return new Pair<>(field.name + " LIKE ? ", params);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package fr.pandacube.java.util.db.sql_tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import fr.pandacube.java.util.Log;
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class SQLWhereNull extends SQLWhere {
|
||||
|
||||
private SQLField<?, ?> fild;
|
||||
private boolean nulll;
|
||||
|
||||
/**
|
||||
* Init a IS NULL / IS NOT NULL expression for a SQL WHERE condition.
|
||||
*
|
||||
* @param field the field to check null / not null state
|
||||
* @param isNull true if we want to ckeck if "IS NULL", or false to check if
|
||||
* "IS NOT NULL"
|
||||
*/
|
||||
public SQLWhereNull(SQLField<?, ?> field, boolean isNull) {
|
||||
if (field == null) throw new IllegalArgumentException("field can't be null");
|
||||
if (!field.canBeNull) Log.getLogger().log(Level.WARNING,
|
||||
"Useless : Trying to check IS [NOT] NULL on the field " + field.getSQLElementType().getName() + "#"
|
||||
+ field.name + " which is declared in the ORM as 'can't be null'");
|
||||
fild = field;
|
||||
nulll = isNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, List<Object>> toSQL() {
|
||||
return new Pair<>(fild.name + " IS" + ((nulll) ? " NULL" : " NOT NULL"), new ArrayList<>());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package fr.pandacube.java.util.measurement;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class DistanceUtil {
|
||||
|
||||
public static String distanceToString(double meterDist, int precision, DistanceUnit... desiredUnits) {
|
||||
|
||||
Arrays.sort(desiredUnits);
|
||||
|
||||
DistanceUnit choosenUnit = desiredUnits[0]; // la plus petite unitée
|
||||
for (DistanceUnit unit : desiredUnits) {
|
||||
if (meterDist / unit.multiplicator < 1) continue;
|
||||
choosenUnit = unit;
|
||||
}
|
||||
|
||||
if (choosenUnit != desiredUnits[0] && precision <= 2) precision = 2;
|
||||
|
||||
String precisionFormat = "##0";
|
||||
if (precision > 0) precisionFormat += ".";
|
||||
for (int i = 0; i < precision; i++)
|
||||
precisionFormat += "0";
|
||||
DecimalFormat df = new DecimalFormat(precisionFormat);
|
||||
|
||||
double dist = meterDist / choosenUnit.multiplicator;
|
||||
|
||||
return df.format(dist) + choosenUnit.unitStr;
|
||||
}
|
||||
|
||||
public static String distanceToString(double meterDist, int precision) {
|
||||
return distanceToString(meterDist, precision, DistanceUnit.M, DistanceUnit.KM);
|
||||
}
|
||||
|
||||
public enum DistanceUnit {
|
||||
NM(0.000000001, "nm"), µM(0.000001, "µm"), MM(0.001, "mm"), CM(0.01, "cm"), M(1, "m"), KM(1000, "km");
|
||||
|
||||
private final double multiplicator;
|
||||
private final String unitStr;
|
||||
|
||||
private DistanceUnit(double mult, String s) {
|
||||
multiplicator = mult;
|
||||
unitStr = s;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package fr.pandacube.java.util.measurement;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class MemoryUtil {
|
||||
|
||||
private static final DecimalFormat format = new DecimalFormat("#####0.00");
|
||||
|
||||
public static String humanReadableSize(long octet, boolean si) {
|
||||
|
||||
double size = octet;
|
||||
|
||||
int diveBy = si ? 1000 : 1024;
|
||||
|
||||
if (size < diveBy) return size + "o";
|
||||
size /= diveBy;
|
||||
if (size < diveBy) return format.format(size) + (si ? "ko" : "kio");
|
||||
size /= diveBy;
|
||||
if (size < diveBy) return format.format(size) + (si ? "Mo" : "Mio");
|
||||
size /= diveBy;
|
||||
if (size < diveBy) return format.format(size) + (si ? "Go" : "Gio");
|
||||
size /= diveBy;
|
||||
|
||||
return format.format(size) + (si ? "To" : "Tio");
|
||||
}
|
||||
|
||||
public static String humanReadableSize(long octet) {
|
||||
return humanReadableSize(octet, false);
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package fr.pandacube.java.util.measurement;
|
||||
|
||||
public class TimeUtil {
|
||||
public static String durationToString(long msec_time, boolean dec_seconde) {
|
||||
int j = 0, h = 0, m = 0, s = 0;
|
||||
long msec = msec_time;
|
||||
|
||||
j = (int) (msec / (1000 * 60 * 60 * 24));
|
||||
msec -= (long) (1000 * 60 * 60 * 24) * j;
|
||||
h = (int) (msec / (1000 * 60 * 60));
|
||||
msec -= (long) (1000 * 60 * 60) * h;
|
||||
m = (int) (msec / (1000 * 60));
|
||||
msec -= (long) (1000 * 60) * m;
|
||||
s = (int) (msec / 1000);
|
||||
msec -= (long) 1000 * s;
|
||||
|
||||
String result = "";
|
||||
if (j > 0) result = result.concat(j + "j ");
|
||||
if (h > 0) result = result.concat(h + "h ");
|
||||
if (m > 0) result = result.concat(m + "m ");
|
||||
if (s > 0 && !dec_seconde) result = result.concat(s + "s");
|
||||
else if (dec_seconde && (s > 0 || msec > 0)) {
|
||||
msec += s * 1000;
|
||||
result = result.concat((msec / 1000D) + "s");
|
||||
}
|
||||
|
||||
if (result.equals("")) result = "0";
|
||||
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
public static String durationToString(long msec_time) {
|
||||
return durationToString(msec_time, false);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,224 @@
|
||||
package fr.pandacube.java.util.network.client;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
import fr.pandacube.java.Pandacube;
|
||||
import fr.pandacube.java.util.Log;
|
||||
import fr.pandacube.java.util.network.packet.Packet;
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.packet.PacketException;
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.ResponseCallback;
|
||||
import fr.pandacube.java.util.network.packet.packets.global.PacketServerException;
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class TCPClient extends Thread implements Closeable {
|
||||
|
||||
private Socket socket;
|
||||
private SocketAddress addr;
|
||||
private TCPClientListener listener;
|
||||
private InputStream in;
|
||||
private OutputStream out;
|
||||
private Object outSynchronizer = new Object();
|
||||
private List<Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>>> callbacks = Collections.synchronizedList(new ArrayList<>());
|
||||
private List<Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>>> callbacksAvoidListener = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private AtomicBoolean isClosed = new AtomicBoolean(false);
|
||||
|
||||
public TCPClient(InetSocketAddress a, String connName, TCPClientListener l) throws IOException {
|
||||
super("TCPCl " + connName);
|
||||
setDaemon(true);
|
||||
if (a == null || l == null) throw new IllegalArgumentException("les arguments ne peuvent pas être null");
|
||||
socket = new Socket();
|
||||
socket.setReceiveBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socket.setSendBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socket.setSoTimeout(Pandacube.NETWORK_TIMEOUT);
|
||||
socket.connect(a);
|
||||
addr = a;
|
||||
listener = l;
|
||||
try {
|
||||
listener.onConnect(this);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling TCPClientListener.onConnect()", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
byte[] code = new byte[1];
|
||||
while (!socket.isClosed() && in.read(code) != -1) {
|
||||
byte[] sizeB = new byte[4];
|
||||
if (in.read(sizeB) != 4) throw new IOException("Socket " + addr + " fermé");
|
||||
|
||||
int size = ByteBuffer.wrap(sizeB).getInt();
|
||||
|
||||
byte[] content = new byte[size];
|
||||
|
||||
forceReadBytes(content);
|
||||
|
||||
byte[] packetData = ByteBuffer.allocate(1 + 4 + size).put(code).put(sizeB).put(content).array();
|
||||
|
||||
try {
|
||||
|
||||
Packet p = Packet.constructPacket(packetData);
|
||||
|
||||
if (!(p instanceof PacketServer))
|
||||
throw new PacketException(p.getClass().getCanonicalName() + " is not a subclass of PacketServer");
|
||||
|
||||
if (p instanceof PacketServerException) {
|
||||
|
||||
try {
|
||||
listener.onServerException(this, ((PacketServerException)p).getExceptionString());
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling TCPClientListener.onServerException()", e);
|
||||
}
|
||||
}
|
||||
|
||||
PacketServer ps = (PacketServer) p;
|
||||
|
||||
boolean callbackExecuted = executeCallbacks(ps, callbacksAvoidListener);
|
||||
|
||||
try {
|
||||
if (!callbackExecuted)
|
||||
listener.onPacketReceive(this, ps);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling TCPClientListener.onPacketReceive()", e);
|
||||
}
|
||||
|
||||
executeCallbacks(ps, callbacks);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while handling packet from server", e);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe(e);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
private boolean executeCallbacks(PacketServer ps, List<Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>>> callbacks) {
|
||||
boolean executedOne = false;
|
||||
synchronized (callbacks) {
|
||||
for(Iterator<Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>>> it = callbacks.iterator(); it.hasNext();) {
|
||||
Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>> c = it.next();
|
||||
try {
|
||||
if (c.getKey().test(ps)) {
|
||||
it.remove();
|
||||
c.getValue().call(ps);
|
||||
executedOne = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while executing callback", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return executedOne;
|
||||
}
|
||||
|
||||
|
||||
private void forceReadBytes(byte[] buff) throws IOException {
|
||||
int pos = 0;
|
||||
do {
|
||||
int nbR = in.read(buff, pos, buff.length - pos);
|
||||
if (nbR == -1) throw new IOException("Can't read required amount of byte");
|
||||
pos += nbR;
|
||||
} while (pos < buff.length);
|
||||
}
|
||||
|
||||
public void send(PacketClient packet) throws IOException {
|
||||
synchronized (outSynchronizer) {
|
||||
out.write(packet.getFullSerializedPacket());
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void sendAndGetResponse(PacketClient packet, Predicate<PacketServer> responseCondition, ResponseCallback<PacketServer> callback, boolean avoidListener) throws IOException {
|
||||
Pair<Predicate<PacketServer>, ResponseCallback<PacketServer>> p = new Pair<>(responseCondition, callback);
|
||||
if (avoidListener)
|
||||
callbacksAvoidListener.add(p);
|
||||
else
|
||||
callbacks.add(p);
|
||||
send(packet);
|
||||
}
|
||||
|
||||
|
||||
public PacketServer sendAndWaitForResponse(PacketClient packet, Predicate<PacketServer> responseCondition) throws IOException, InterruptedException {
|
||||
AtomicReference<PacketServer> psStorage = new AtomicReference<>(null);
|
||||
synchronized (psStorage) {
|
||||
sendAndGetResponse(packet, responseCondition, packetServer -> {
|
||||
synchronized (psStorage) {
|
||||
psStorage.set(packetServer);
|
||||
psStorage.notifyAll();
|
||||
}
|
||||
}, true);
|
||||
|
||||
psStorage.wait();
|
||||
return psStorage.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
synchronized (outSynchronizer) {
|
||||
if (isClosed.get()) return;
|
||||
socket.close();
|
||||
isClosed.set(true);
|
||||
|
||||
try {
|
||||
listener.onDisconnect(this);
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while calling TCPClientListener.onDisconnect()", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.warning(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendSilently(PacketClient packet) {
|
||||
try {
|
||||
send(packet);
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
public SocketAddress getServerAddress() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return isClosed.get() || socket.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("thread", getName())
|
||||
.append("socket", socket.getRemoteSocketAddress()).toString();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package fr.pandacube.java.util.network.client;
|
||||
|
||||
import fr.pandacube.java.util.Log;
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
|
||||
public interface TCPClientListener {
|
||||
|
||||
/**
|
||||
* Called when a connection is opened
|
||||
* @param connection the connection which is opening
|
||||
*/
|
||||
public void onConnect(TCPClient connection);
|
||||
|
||||
/**
|
||||
* Called when the server send us a PacketServerException when an exception is thrown
|
||||
* server side when handling our packets or eventually when the client is concerned
|
||||
* with this error.<br/>
|
||||
* The default implementation of this method just call the internal Logger of PandacubeUtil
|
||||
* to print the stacktrace of the Exception.
|
||||
* @param connection the connection which just received the error
|
||||
* @param exceptionString a string representation of the exception. If the server
|
||||
* use this Java library, it may be a full representation of
|
||||
* {@link Throwable#printStackTrace()}.
|
||||
*/
|
||||
public default void onServerException(TCPClient connection, String exceptionString) {
|
||||
Log.severe("Exception thrown by server through " + connection + " : \n"+exceptionString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the server send us a packet
|
||||
* @param connection the connection where the packet come from
|
||||
* @param packet the packet
|
||||
*/
|
||||
public void onPacketReceive(TCPClient connection, PacketServer packet);
|
||||
|
||||
/**
|
||||
* Called before the connection closed
|
||||
* @param connection the connection which is closing
|
||||
*/
|
||||
public void onDisconnect(TCPClient connection);
|
||||
|
||||
}
|
118
src/main/java/fr/pandacube/java/util/network/packet/Packet.java
Normal file
118
src/main/java/fr/pandacube/java/util/network/packet/Packet.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package fr.pandacube.java.util.network.packet;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import fr.pandacube.java.Pandacube;
|
||||
import fr.pandacube.java.util.Log;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteSerializable;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketClientClose;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketClientDeclareProcess;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketClientProcessQueryResponse;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketServerClose;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketServerConnectSuccess;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketServerProcessDeclarationConfirm;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketServerProcessInput;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketServerProcessQuery;
|
||||
import fr.pandacube.java.util.network.packet.packets.global.PacketClientAuthenticate;
|
||||
import fr.pandacube.java.util.network.packet.packets.global.PacketClientLogRecord;
|
||||
import fr.pandacube.java.util.network.packet.packets.global.PacketServerCantAuthenticate;
|
||||
import fr.pandacube.java.util.network.packet.packets.global.PacketServerException;
|
||||
import fr.pandacube.java.util.network.packet.packets.web.PacketClientWebRequest;
|
||||
import fr.pandacube.java.util.network.packet.packets.web.PacketServerWebResponse;
|
||||
|
||||
/** <pre>
|
||||
* Identification des packets réseaux
|
||||
* byte (xxxxxxxx)
|
||||
* client : server :
|
||||
* clt / sv (x-------) (0-------) (1-------)
|
||||
* 0x00 - 0x7F 0x80 - 0xFF
|
||||
* use case (-xxx----)
|
||||
* - web (-000----) 0x00 - 0x0F 0x80 - 0x8F (client is Apache, server is PandacubeCore master (PandacubeWeb))
|
||||
* - spigot (-001----) 0x10 - 0x1F 0x90 - 0x9F (client is PandacubeSpigot, server is PandacubeCore master)
|
||||
* - bungee (-010----) 0x20 - 0x2F 0xA0 - 0xAF (client is PandacubeBungee, server is PandacubeCore master)
|
||||
* -coreslave(-011----) 0x30 - 0x3F 0xB0 - 0xBF (client is PandacubeCore slave, sv is PandacubeCore master)
|
||||
* - global (-101----) 0x50 - 0x5F 0xD0 - 0xDF
|
||||
*
|
||||
* - reserved if not enough packet id in certain use case
|
||||
* (-11x----) 0x60 - 0x7F 0xE0 - 0xFF
|
||||
*
|
||||
* packet id (----xxxx)
|
||||
* </pre>
|
||||
*/
|
||||
public abstract class Packet implements ByteSerializable {
|
||||
|
||||
private final byte code;
|
||||
|
||||
public Packet(byte c) {
|
||||
code = c;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public byte[] getFullSerializedPacket() {
|
||||
ByteBuffer internal = new ByteBuffer(CHARSET).putObject(this);
|
||||
byte[] data = Arrays.copyOfRange(internal.array(), 0, internal.getPosition());
|
||||
|
||||
return new ByteBuffer(5 + data.length, CHARSET).putByte(code).putInt(data.length).putByteArray(data).array();
|
||||
}
|
||||
|
||||
public static final Charset CHARSET = Pandacube.NETWORK_CHARSET;
|
||||
|
||||
private static Map<Byte, Class<? extends Packet>> packetTypes = new HashMap<>();
|
||||
|
||||
public static Packet constructPacket(byte[] data) {
|
||||
if (!packetTypes.containsKey(data[0]))
|
||||
throw new PacketException("Packet identifier not recognized: 0x" + String.format("%02X", data[0])
|
||||
+ ". Maybe this packet is not registered with Packet.addPacket()");
|
||||
|
||||
try {
|
||||
Packet p = packetTypes.get(data[0]).newInstance();
|
||||
ByteBuffer dataBuffer = new ByteBuffer(Arrays.copyOfRange(data, 5, data.length), CHARSET);
|
||||
p.deserializeFromByteBuffer(dataBuffer);
|
||||
return p;
|
||||
} catch (Exception e) {
|
||||
throw new PacketException("Error while constructing packet", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Packet> void addPacket(Class<T> packetClass) {
|
||||
try {
|
||||
Packet p = packetClass.newInstance();
|
||||
packetTypes.put(p.code, packetClass);
|
||||
} catch (Exception e) {
|
||||
Log.severe(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static {
|
||||
|
||||
/*
|
||||
* Ajout des types de packets (client + serveur)
|
||||
*/
|
||||
addPacket(PacketClientClose.class);
|
||||
addPacket(PacketClientDeclareProcess.class);
|
||||
addPacket(PacketClientProcessQueryResponse.class);
|
||||
addPacket(PacketServerClose.class);
|
||||
addPacket(PacketServerConnectSuccess.class);
|
||||
addPacket(PacketServerProcessDeclarationConfirm.class);
|
||||
addPacket(PacketServerProcessInput.class);
|
||||
addPacket(PacketServerProcessQuery.class);
|
||||
|
||||
addPacket(PacketClientAuthenticate.class);
|
||||
addPacket(PacketClientLogRecord.class);
|
||||
addPacket(PacketServerCantAuthenticate.class);
|
||||
addPacket(PacketServerException.class);
|
||||
|
||||
addPacket(PacketClientWebRequest.class);
|
||||
addPacket(PacketServerWebResponse.class);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package fr.pandacube.java.util.network.packet;
|
||||
|
||||
/**
|
||||
* On attend d'un instance de {@link PacketClient} qu'il soit envoyé depuis
|
||||
* une connexion Client vers une connexion Serveur (d'un point de vue TCP)
|
||||
*/
|
||||
public abstract class PacketClient extends Packet {
|
||||
|
||||
public PacketClient(byte c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package fr.pandacube.java.util.network.packet;
|
||||
|
||||
/**
|
||||
*
|
||||
* Thrown when there is a problem when constructing, sending or handling a packet.
|
||||
*
|
||||
* Only the server may send a string representation of an exception to the client, not the reverse
|
||||
*
|
||||
*/
|
||||
public class PacketException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PacketException(String m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
public PacketException(String m, Throwable t) {
|
||||
super(m, t);
|
||||
}
|
||||
|
||||
public PacketException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package fr.pandacube.java.util.network.packet;
|
||||
|
||||
/**
|
||||
* On attend d'un instance de {@link PacketServer} qu'il soit envoyé depuis
|
||||
* une connexion Serveur vers une connexion Client (d'un point de vue TCP)
|
||||
*/
|
||||
public abstract class PacketServer extends Packet {
|
||||
|
||||
public PacketServer(byte c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package fr.pandacube.java.util.network.packet;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ResponseCallback<T extends Packet> {
|
||||
|
||||
public void call(T packet);
|
||||
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
package fr.pandacube.java.util.network.packet.bytebuffer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Array8Bit {
|
||||
|
||||
public static final int BIT_COUNT = 8;
|
||||
|
||||
private boolean[] values = new boolean[BIT_COUNT];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param b unsigned integer value. Lowest significant bit will be used.
|
||||
*/
|
||||
public Array8Bit(int b) {
|
||||
for (int i = 0; i<BIT_COUNT; i++) {
|
||||
values[i] = (b % 2 == 1);
|
||||
b >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Array8Bit(boolean[] bits) {
|
||||
if (bits == null || bits.length != BIT_COUNT)
|
||||
throw new IllegalArgumentException("bits is null or bits.length != "+BIT_COUNT);
|
||||
values = Arrays.copyOf(bits, BIT_COUNT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* i = 0 is the lowest significant bit
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
public boolean getValue(int i) {
|
||||
return values[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* i = 0 is the lowest significant bit
|
||||
* @param i
|
||||
* @param b
|
||||
*/
|
||||
public void setValue(int i, boolean b) {
|
||||
values[i] = b;
|
||||
}
|
||||
|
||||
|
||||
public byte getValuesAsByte() {
|
||||
byte b = 0;
|
||||
for (int i=BIT_COUNT-1; i>=0; i--) {
|
||||
b <<= 1;
|
||||
if (values[i]) b |= 1;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,324 @@
|
||||
package fr.pandacube.java.util.network.packet.bytebuffer;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ByteBuffer implements Cloneable {
|
||||
|
||||
private java.nio.ByteBuffer buff;
|
||||
private Charset charset;
|
||||
|
||||
public ByteBuffer(Charset c) {
|
||||
this(16, c);
|
||||
}
|
||||
|
||||
public ByteBuffer(int initSize, Charset c) {
|
||||
buff = java.nio.ByteBuffer.allocate(initSize);
|
||||
charset = c;
|
||||
}
|
||||
|
||||
public ByteBuffer(byte[] data, Charset c) {
|
||||
buff = java.nio.ByteBuffer.wrap(Arrays.copyOf(data, data.length));
|
||||
charset = c;
|
||||
}
|
||||
|
||||
private void askForBufferExtension(int needed) {
|
||||
while (buff.remaining() < needed) {
|
||||
java.nio.ByteBuffer newBuff = java.nio.ByteBuffer.wrap(Arrays.copyOf(buff.array(), buff.array().length * 2));
|
||||
newBuff.position(buff.position());
|
||||
buff = newBuff;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer clone() {
|
||||
return new ByteBuffer(Arrays.copyOf(buff.array(), buff.array().length), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#get()
|
||||
*/
|
||||
public byte getByte() {
|
||||
return buff.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#get(byte[])
|
||||
*/
|
||||
public byte[] getByteArray(byte[] b) {
|
||||
buff.get(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next byte array wich is preceded with his size as integer,
|
||||
* or null if the founded size is negative.
|
||||
* @return
|
||||
*/
|
||||
public byte[] getSizedByteArray() {
|
||||
int size = getInt();
|
||||
if (size < 0) return null;
|
||||
return getByteArray(new byte[size]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#getChar()
|
||||
*/
|
||||
public char getChar() {
|
||||
return buff.getChar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#getShort()
|
||||
*/
|
||||
public short getShort() {
|
||||
return buff.getShort();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#getInt()
|
||||
*/
|
||||
public int getInt() {
|
||||
return buff.getInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#getLong()
|
||||
*/
|
||||
public long getLong() {
|
||||
return buff.getLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#getFloat()
|
||||
*/
|
||||
public float getFloat() {
|
||||
return buff.getFloat();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#getDouble()
|
||||
*/
|
||||
public double getDouble() {
|
||||
return buff.getDouble();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#put(byte)
|
||||
*/
|
||||
public ByteBuffer putByte(byte b) {
|
||||
askForBufferExtension(Byte.BYTES);
|
||||
buff.put(b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#put(byte[])
|
||||
*/
|
||||
public ByteBuffer putByteArray(byte[] b) {
|
||||
askForBufferExtension(b.length * Byte.BYTES);
|
||||
buff.put(b);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ByteBuffer putSizedByteArray(byte[] b) {
|
||||
if (b == null) {
|
||||
return putInt(-1);
|
||||
}
|
||||
putInt(b.length);
|
||||
return putByteArray(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#putChar(char)
|
||||
*/
|
||||
public ByteBuffer putChar(char value) {
|
||||
askForBufferExtension(Character.BYTES);
|
||||
buff.putChar(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#putShort(short)
|
||||
*/
|
||||
public ByteBuffer putShort(short value) {
|
||||
askForBufferExtension(Short.BYTES);
|
||||
buff.putShort(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#putInt(int)
|
||||
*/
|
||||
public ByteBuffer putInt(int value) {
|
||||
askForBufferExtension(Integer.BYTES);
|
||||
buff.putInt(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#putLong(long)
|
||||
*/
|
||||
public ByteBuffer putLong(long value) {
|
||||
askForBufferExtension(Long.BYTES);
|
||||
buff.putLong(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#putFloat(float)
|
||||
*/
|
||||
public ByteBuffer putFloat(float value) {
|
||||
askForBufferExtension(Float.BYTES);
|
||||
buff.putFloat(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#putDouble(double)
|
||||
*/
|
||||
public ByteBuffer putDouble(double value) {
|
||||
askForBufferExtension(Double.BYTES);
|
||||
buff.putDouble(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#position()
|
||||
*/
|
||||
public int getPosition() {
|
||||
return buff.position();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#position(int)
|
||||
*/
|
||||
public void setPosition(int p) {
|
||||
buff.position(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#capacity()
|
||||
*/
|
||||
public int capacity() {
|
||||
return buff.capacity();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param s null String are supported
|
||||
* @return
|
||||
*/
|
||||
public ByteBuffer putString(String s) {
|
||||
if (s == null) {
|
||||
return putInt(-1);
|
||||
}
|
||||
return putSizedByteArray(s.getBytes(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* returned string can be null
|
||||
* @return
|
||||
*/
|
||||
public String getString() {
|
||||
byte[] binaryString = getSizedByteArray();
|
||||
return (binaryString == null) ? null : new String(binaryString, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* The objet will be serialized and the data put in the current buffer
|
||||
*
|
||||
* @param obj the object to serialize. Can't be null.
|
||||
* @return the current buffer
|
||||
*/
|
||||
public ByteBuffer putObject(ByteSerializable obj) {
|
||||
obj.serializeToByteBuffer(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask to object passed as argument to deserialize data in buffer and fill
|
||||
* the object content. ByteSerializable object are never null.
|
||||
*
|
||||
* @param <T>
|
||||
* @param clazz the class wich will be instanciated with his no-argument Constructor
|
||||
* before filled by using {@link ByteSerializable#deserializeFromByteBuffer(ByteBuffer)}
|
||||
* @return obj a reference to the filled object
|
||||
*/
|
||||
public <T extends ByteSerializable> T getObject(Class<T> clazz) {
|
||||
try {
|
||||
T obj = clazz.newInstance();
|
||||
obj.deserializeFromByteBuffer(this);
|
||||
return obj;
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException("A ByteSerializable must have a no-argument Constructor", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param list The list itself can be null, but not the values.
|
||||
* @return
|
||||
*/
|
||||
public ByteBuffer putListObject(List<ByteSerializable> list) {
|
||||
if (list.stream().anyMatch(e -> e == null))
|
||||
throw new IllegalArgumentException("List of object can't contains any null value");
|
||||
putInt(list.size());
|
||||
for (ByteSerializable obj : list)
|
||||
putObject(obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param list The list can be null, and any String can be null too.
|
||||
* @return
|
||||
*/
|
||||
public ByteBuffer putListOfString(List<String> list) {
|
||||
if (list == null) {
|
||||
return putInt(-1);
|
||||
}
|
||||
putInt(list.size());
|
||||
for (String str : list)
|
||||
putString(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param clazz
|
||||
* @return Can be null. If not, there is no null element inside.
|
||||
*/
|
||||
public <T extends ByteSerializable> List<T> getListObject(Class<T> clazz) {
|
||||
int size = getInt();
|
||||
if (size < 0)
|
||||
return null;
|
||||
List<T> list = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++)
|
||||
list.add(getObject(clazz));
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a List of String. The list can be null, and any element can be null too.
|
||||
*/
|
||||
public <T extends ByteSerializable> List<String> getListOfString() {
|
||||
int size = getInt();
|
||||
if (size < 0)
|
||||
return null;
|
||||
List<String> list = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++)
|
||||
list.add(getString());
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.nio.ByteBuffer#array()
|
||||
*/
|
||||
public byte[] array() {
|
||||
return buff.array();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package fr.pandacube.java.util.network.packet.bytebuffer;
|
||||
|
||||
/**
|
||||
* Cette interface permet à un {@link ByteBuffer} de sérialiser sous forme de
|
||||
* données binaire
|
||||
* les attributs de la classe courante.<br/>
|
||||
* <br/>
|
||||
* Les classes concrètes implémentant cette interface doivent avoir un
|
||||
* constructeur vide, utilisé
|
||||
* lors de la désérialisation
|
||||
*
|
||||
*/
|
||||
public interface ByteSerializable {
|
||||
|
||||
public void serializeToByteBuffer(ByteBuffer buffer);
|
||||
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer);
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketClientClose extends PacketClient {
|
||||
|
||||
public PacketClientClose() {
|
||||
super((byte)0x31);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketClientDeclareProcess extends PacketClient {
|
||||
|
||||
private String processName;
|
||||
private String type;
|
||||
|
||||
public PacketClientDeclareProcess() {
|
||||
super((byte)0x30);
|
||||
}
|
||||
|
||||
public void setProcessName(String pN) {
|
||||
processName = pN;
|
||||
}
|
||||
|
||||
public void setType(String t) {
|
||||
type = t;
|
||||
}
|
||||
|
||||
public String getProcessName() { return processName; }
|
||||
public String getType() { return type; }
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(processName);
|
||||
buffer.putString(type);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
processName = buffer.getString();
|
||||
type = buffer.getString();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
import fr.pandacube.java.util.network.packet.packets.core_slave.PacketServerProcessQuery.QueryType;
|
||||
|
||||
public class PacketClientProcessQueryResponse extends PacketClient {
|
||||
|
||||
private QueryType type;
|
||||
private int queryId;
|
||||
private byte[] responseData = null;
|
||||
|
||||
|
||||
public PacketClientProcessQueryResponse() {
|
||||
super((byte)0x32);
|
||||
}
|
||||
|
||||
public QueryType getType() { return type; }
|
||||
public int getQueryId() { return queryId; }
|
||||
public byte[] getResponseData() { return responseData; }
|
||||
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putInt(type.ordinal());
|
||||
buffer.putInt(queryId);
|
||||
buffer.putSizedByteArray(responseData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
type = QueryType.values()[buffer.getInt()];
|
||||
queryId = buffer.getInt();
|
||||
responseData = buffer.getSizedByteArray();
|
||||
}
|
||||
|
||||
|
||||
public static PacketClientProcessQueryResponse destroyResponse(int queryId) {
|
||||
PacketClientProcessQueryResponse q = new PacketClientProcessQueryResponse();
|
||||
q.type = QueryType.DESTROY;
|
||||
q.queryId = queryId;
|
||||
return q;
|
||||
}
|
||||
|
||||
public static PacketClientProcessQueryResponse isAliveResponse(int queryId, boolean resp) {
|
||||
PacketClientProcessQueryResponse q = new PacketClientProcessQueryResponse();
|
||||
q.type = QueryType.IS_ALIVE;
|
||||
q.queryId = queryId;
|
||||
q.responseData = new byte[] {(byte)(resp ? 1 : 0)};
|
||||
return q;
|
||||
}
|
||||
|
||||
public static PacketClientProcessQueryResponse exitStatusResponse(int queryId, int resp) {
|
||||
PacketClientProcessQueryResponse q = new PacketClientProcessQueryResponse();
|
||||
q.type = QueryType.EXIT_STATUS;
|
||||
q.queryId = queryId;
|
||||
q.responseData = new ByteBuffer(4, CHARSET).putInt(resp).array();
|
||||
return q;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerClose extends PacketServer {
|
||||
|
||||
public PacketServerClose() {
|
||||
super((byte)0xB0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerConnectSuccess extends PacketServer {
|
||||
|
||||
public PacketServerConnectSuccess() {
|
||||
super((byte)0xB2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
// no data
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerProcessDeclarationConfirm extends PacketServer {
|
||||
|
||||
private String serverName;
|
||||
|
||||
public PacketServerProcessDeclarationConfirm() {
|
||||
super((byte)0xB1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(serverName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
serverName = buffer.getString();
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public void setProcessName(String name) {
|
||||
serverName = name;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerProcessInput extends PacketServer {
|
||||
|
||||
private String serverName;
|
||||
private byte[] dataToSend;
|
||||
|
||||
public PacketServerProcessInput() {
|
||||
super((byte)0xB3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(serverName);
|
||||
buffer.putSizedByteArray(dataToSend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
serverName = buffer.getString();
|
||||
dataToSend = buffer.getSizedByteArray();
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public byte[] getDataToSend() {
|
||||
return dataToSend;
|
||||
}
|
||||
|
||||
public void setDataToSend(byte[] dataToSend) {
|
||||
this.dataToSend = dataToSend;
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.core_slave;
|
||||
|
||||
import fr.pandacube.java.util.RandomUtil;
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerProcessQuery extends PacketServer {
|
||||
|
||||
private String processName;
|
||||
private QueryType type;
|
||||
private int queryId = RandomUtil.rand.nextInt();
|
||||
private byte[] queryData = null;
|
||||
|
||||
|
||||
public PacketServerProcessQuery() {
|
||||
super((byte)0xB4);
|
||||
}
|
||||
|
||||
public String getProcessName() { return processName; }
|
||||
public QueryType getType() { return type; }
|
||||
public int getQueryId() { return queryId; }
|
||||
public byte[] getQueryData() { return queryData; }
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putInt(type.ordinal());
|
||||
buffer.putInt(queryId);
|
||||
buffer.putSizedByteArray(queryData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
type = QueryType.values()[buffer.getInt()];
|
||||
queryId = buffer.getInt();
|
||||
queryData = buffer.getSizedByteArray();
|
||||
}
|
||||
|
||||
|
||||
public static PacketServerProcessQuery startQuery(String processName) {
|
||||
PacketServerProcessQuery q = new PacketServerProcessQuery();
|
||||
q.processName = processName;
|
||||
q.type = QueryType.START;
|
||||
return q;
|
||||
}
|
||||
|
||||
public static PacketServerProcessQuery destroyQuery(String processName, boolean wait) {
|
||||
PacketServerProcessQuery q = new PacketServerProcessQuery();
|
||||
q.processName = processName;
|
||||
q.type = QueryType.DESTROY;
|
||||
q.queryData = new byte[] {(byte)(wait ? 1 : 0)};
|
||||
return q;
|
||||
}
|
||||
|
||||
public static PacketServerProcessQuery isAliveQuery(String processName) {
|
||||
PacketServerProcessQuery q = new PacketServerProcessQuery();
|
||||
q.processName = processName;
|
||||
q.type = QueryType.IS_ALIVE;
|
||||
return q;
|
||||
}
|
||||
|
||||
public static PacketServerProcessQuery exitStatusQuery(String processName) {
|
||||
PacketServerProcessQuery q = new PacketServerProcessQuery();
|
||||
q.processName = processName;
|
||||
q.type = QueryType.EXIT_STATUS;
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
public enum QueryType {
|
||||
START, DESTROY, IS_ALIVE, EXIT_STATUS;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.global;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketClientAuthenticate extends PacketClient {
|
||||
|
||||
private String password;
|
||||
private String additionalData = "";
|
||||
|
||||
public PacketClientAuthenticate() {
|
||||
super((byte)0x50);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(password);
|
||||
buffer.putString(additionalData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
password = buffer.getString();
|
||||
additionalData = buffer.getString();
|
||||
}
|
||||
|
||||
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String p) { password = p; }
|
||||
|
||||
public String getAdditionalData() { return additionalData; }
|
||||
public void setAdditionalData(String data) { additionalData = data; }
|
||||
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.global;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketClientLogRecord extends PacketClient {
|
||||
|
||||
private long time;
|
||||
private String level;
|
||||
private String threadName;
|
||||
private String message;
|
||||
private String throwable;
|
||||
|
||||
public PacketClientLogRecord() {
|
||||
super((byte)0x51);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
time = buffer.getLong();
|
||||
level = buffer.getString();
|
||||
threadName = buffer.getString();
|
||||
message = buffer.getString();
|
||||
throwable = buffer.getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putLong(time);
|
||||
buffer.putString(level);
|
||||
buffer.putString(threadName);
|
||||
buffer.putString(message);
|
||||
buffer.putString(throwable);
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public String getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(String level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public String getThreadName() {
|
||||
return threadName;
|
||||
}
|
||||
|
||||
public void setThreadName(String threadName) {
|
||||
this.threadName = threadName;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getThrowable() {
|
||||
return throwable;
|
||||
}
|
||||
|
||||
public void setThrowable(String throwable) {
|
||||
this.throwable = throwable;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.global;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerCantAuthenticate extends PacketServer {
|
||||
|
||||
public PacketServerCantAuthenticate() {
|
||||
super((byte)0xD1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.global;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerCommand extends PacketServer {
|
||||
|
||||
private String command;
|
||||
private boolean async;
|
||||
private boolean returnResult;
|
||||
|
||||
public PacketServerCommand() {
|
||||
super((byte)0xD2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(command);
|
||||
buffer.putByte((byte) (async ? 1 : 0));
|
||||
buffer.putByte((byte) (returnResult ? 1 : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
command = buffer.getString();
|
||||
async = buffer.getByte() != 0;
|
||||
returnResult = buffer.getByte() != 0;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.global;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerException extends PacketServer {
|
||||
|
||||
private String exception;
|
||||
|
||||
public PacketServerException() {
|
||||
super((byte)0xD0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
exception = buffer.getString();
|
||||
}
|
||||
|
||||
|
||||
public void setException(Exception e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
exception = sw.toString();
|
||||
}
|
||||
|
||||
public String getExceptionString() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.web;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketClientWebRequest extends PacketClient {
|
||||
|
||||
private String password;
|
||||
private String jsonData;
|
||||
|
||||
public PacketClientWebRequest() {
|
||||
super((byte)0x00);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(password);
|
||||
buffer.putString(jsonData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
password = buffer.getString();
|
||||
jsonData = buffer.getString();
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getJsonData() {
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
public void setJsonData(String jsonData) {
|
||||
this.jsonData = jsonData;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package fr.pandacube.java.util.network.packet.packets.web;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
|
||||
public class PacketServerWebResponse extends PacketServer {
|
||||
|
||||
private String jsonData;
|
||||
|
||||
public PacketServerWebResponse() {
|
||||
super((byte)0x80);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeToByteBuffer(ByteBuffer buffer) {
|
||||
buffer.putString(jsonData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeFromByteBuffer(ByteBuffer buffer) {
|
||||
jsonData = buffer.getString();
|
||||
}
|
||||
|
||||
public String getJsonData() {
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
public void setJsonData(String jsonData) {
|
||||
this.jsonData = jsonData;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
package fr.pandacube.java.util.network.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import fr.pandacube.java.util.network.server.TCPServer.TCPServerClientConnection;
|
||||
|
||||
public class BandwidthCalculation {
|
||||
|
||||
private List<PacketStat> packetHistory = new LinkedList<>();
|
||||
|
||||
public synchronized void addPacket(TCPServerClientConnection co, boolean in, long size) {
|
||||
packetHistory.add(new PacketStat(co, in, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instant bandwith in byte/s
|
||||
*
|
||||
* @param input true if getting input bw, false if getting output, null if
|
||||
* getting input + output
|
||||
* @param co
|
||||
* @return
|
||||
*/
|
||||
public synchronized long getBandWidth(Boolean input, TCPServerClientConnection co) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
Iterator<PacketStat> it = packetHistory.iterator();
|
||||
long sum = 0;
|
||||
while (it.hasNext()) {
|
||||
PacketStat el = it.next();
|
||||
if (el.time < currentTime - 1000) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
if (input != null && el.input != input.booleanValue()) continue;
|
||||
if (co != null && !co.equals(el.connection)) continue;
|
||||
sum += el.packetSize;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private class PacketStat {
|
||||
public final long time;
|
||||
public final long packetSize;
|
||||
public final boolean input;
|
||||
public final TCPServerClientConnection connection;
|
||||
|
||||
public PacketStat(TCPServerClientConnection co, boolean input, long size) {
|
||||
time = System.currentTimeMillis();
|
||||
packetSize = size;
|
||||
this.input = input;
|
||||
connection = co;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,354 @@
|
||||
package fr.pandacube.java.util.network.server;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
import fr.pandacube.java.Pandacube;
|
||||
import fr.pandacube.java.util.Log;
|
||||
import fr.pandacube.java.util.network.packet.Packet;
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.packet.PacketException;
|
||||
import fr.pandacube.java.util.network.packet.PacketServer;
|
||||
import fr.pandacube.java.util.network.packet.ResponseCallback;
|
||||
import fr.pandacube.java.util.network.packet.bytebuffer.ByteBuffer;
|
||||
import fr.pandacube.java.util.network.packet.packets.global.PacketServerException;
|
||||
import javafx.util.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Marc Baloup
|
||||
*
|
||||
*/
|
||||
public class TCPServer extends Thread implements Closeable {
|
||||
private static AtomicInteger connectionCounterId = new AtomicInteger(0);
|
||||
|
||||
private ServerSocket socket;
|
||||
private TCPServerListener listener;
|
||||
private String socketName;
|
||||
|
||||
private List<TCPServerClientConnection> clients = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private AtomicBoolean isClosed = new AtomicBoolean(false);
|
||||
|
||||
public final BandwidthCalculation bandwidthCalculation = new BandwidthCalculation();
|
||||
|
||||
public TCPServer(int port, String sckName, TCPServerListener l) throws IOException {
|
||||
super("TCPSv " + sckName);
|
||||
setDaemon(true);
|
||||
if (port <= 0 || port > 65535) throw new IllegalArgumentException("le numéro de port est invalide");
|
||||
socket = new ServerSocket();
|
||||
socket.setReceiveBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socket.setPerformancePreferences(0, 1, 0);
|
||||
socket.bind(new InetSocketAddress(port));
|
||||
listener = l;
|
||||
try {
|
||||
listener.onSocketOpen(this);
|
||||
} catch(Exception e) {
|
||||
Log.severe("Exception while calling TCPServerListener.onSocketOpen()", e);
|
||||
}
|
||||
socketName = sckName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
@SuppressWarnings("resource")
|
||||
Socket socketClient = socket.accept();
|
||||
socketClient.setSendBufferSize(Pandacube.NETWORK_TCP_BUFFER_SIZE);
|
||||
socketClient.setSoTimeout(Pandacube.NETWORK_TIMEOUT);
|
||||
|
||||
try {
|
||||
TCPServerClientConnection co = new TCPServerClientConnection(socketClient,
|
||||
connectionCounterId.getAndIncrement());
|
||||
clients.add(co);
|
||||
co.start();
|
||||
} catch (IOException e) {
|
||||
Log.getLogger().log(Level.SEVERE, "Connexion impossible avec " + socketClient.getInetAddress());
|
||||
}
|
||||
}
|
||||
} catch(SocketException e) {
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Plus aucune connexion ne peux être acceptée", e);
|
||||
}
|
||||
}
|
||||
|
||||
public class TCPServerClientConnection extends Thread {
|
||||
private Socket cSocket;
|
||||
private InputStream in;
|
||||
private OutputStream out;
|
||||
private SocketAddress address;
|
||||
private TCPServerConnectionOutputThread outThread;
|
||||
private List<Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>>> callbacks = Collections.synchronizedList(new ArrayList<>());
|
||||
private List<Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>>> callbacksAvoidListener = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
|
||||
public TCPServerClientConnection(Socket s, int coId) throws IOException {
|
||||
super("TCPSv " + socketName + " Conn#" + coId + " In");
|
||||
setDaemon(true);
|
||||
cSocket = s;
|
||||
in = cSocket.getInputStream();
|
||||
out = cSocket.getOutputStream();
|
||||
address = new InetSocketAddress(cSocket.getInetAddress(), cSocket.getPort());
|
||||
try {
|
||||
listener.onClientConnect(TCPServer.this, this);
|
||||
} catch(Exception e) {
|
||||
Log.severe("Exception while calling TCPServerListener.onClientConnect()", e);
|
||||
}
|
||||
outThread = new TCPServerConnectionOutputThread(coId);
|
||||
outThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
byte[] code = new byte[1];
|
||||
while (!cSocket.isClosed() && in.read(code) != -1) {
|
||||
byte[] sizeB = new byte[4];
|
||||
if (in.read(sizeB) != 4) throw new IOException("Socket " + address + " closed");
|
||||
|
||||
int size = new ByteBuffer(sizeB, Packet.CHARSET).getInt();
|
||||
|
||||
byte[] content = new byte[size];
|
||||
|
||||
forceReadBytes(content);
|
||||
|
||||
byte[] packetData = new ByteBuffer(1 + 4 + size, Packet.CHARSET).putByteArray(code).putByteArray(sizeB)
|
||||
.putByteArray(content).array();
|
||||
|
||||
bandwidthCalculation.addPacket(this, true, packetData.length);
|
||||
|
||||
try {
|
||||
Packet p = Packet.constructPacket(packetData);
|
||||
|
||||
if (!(p instanceof PacketClient))
|
||||
throw new PacketException(p.getClass().getCanonicalName() + " is not an instanceof PacketClient");
|
||||
|
||||
PacketClient pc = (PacketClient) p;
|
||||
|
||||
boolean oneCallbackExecuted = executeCallbacks(pc, callbacksAvoidListener);
|
||||
|
||||
if (!oneCallbackExecuted)
|
||||
listener.onPacketReceive(TCPServer.this, this, pc);
|
||||
|
||||
executeCallbacks(pc, callbacks);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while handling packet. This exception will be sent to the client with PacketServerException packet.", e);
|
||||
PacketServerException packet = new PacketServerException();
|
||||
packet.setException(e);
|
||||
send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
} catch(SocketException e) {
|
||||
} catch (Exception e) {
|
||||
Log.severe("Closing connection " + address, e);
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
private boolean executeCallbacks(PacketClient pc, List<Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>>> callbacks) {
|
||||
boolean executedOne = false;
|
||||
synchronized (callbacks) {
|
||||
for(Iterator<Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>>> it = callbacks.iterator(); it.hasNext();) {
|
||||
Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>> c = it.next();
|
||||
try {
|
||||
if (c.getKey().test(pc)) {
|
||||
it.remove();
|
||||
c.getValue().call(pc);
|
||||
executedOne = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.severe("Exception while executing callback", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return executedOne;
|
||||
}
|
||||
|
||||
public void send(PacketServer p) {
|
||||
outThread.addPacket(p);
|
||||
}
|
||||
|
||||
public void sendAndGetResponse(PacketServer packet, Predicate<PacketClient> responseCondition, ResponseCallback<PacketClient> callback, boolean avoidListener) {
|
||||
Pair<Predicate<PacketClient>, ResponseCallback<PacketClient>> p = new Pair<>(responseCondition, callback);
|
||||
if (avoidListener)
|
||||
callbacksAvoidListener.add(p);
|
||||
else
|
||||
callbacks.add(p);
|
||||
send(packet);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param packet the packet to send
|
||||
* @param responseCondition {@link Predicate} that check each received packet to know which
|
||||
* is the expected one as a response of the sended packet.
|
||||
* @param avoidListener
|
||||
* @param timeout
|
||||
* @return
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public PacketClient sendAndWaitForResponse(PacketServer packet, Predicate<PacketClient> responseCondition, boolean avoidListener, long timeout) throws InterruptedException {
|
||||
AtomicReference<PacketClient> pcStorage = new AtomicReference<>(null);
|
||||
synchronized (pcStorage) {
|
||||
sendAndGetResponse(packet, responseCondition, packetClient -> {
|
||||
synchronized (pcStorage) {
|
||||
pcStorage.set(packetClient);
|
||||
pcStorage.notifyAll();
|
||||
}
|
||||
}, true);
|
||||
|
||||
pcStorage.wait(timeout);
|
||||
return pcStorage.get();
|
||||
}
|
||||
}
|
||||
|
||||
private void forceReadBytes(byte[] buff) throws IOException {
|
||||
int pos = 0;
|
||||
do {
|
||||
int nbR = in.read(buff, pos, buff.length - pos);
|
||||
if (nbR == -1) throw new IOException("Can't read required amount of byte");
|
||||
pos += nbR;
|
||||
} while (pos < buff.length);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (cSocket.isClosed()) return;
|
||||
|
||||
try {
|
||||
listener.onClientDisconnect(TCPServer.this, this);
|
||||
} catch(Exception e) {
|
||||
Log.severe("Exception while calling TCPServerListener.onClientDisconnect()", e);
|
||||
}
|
||||
clients.remove(this);
|
||||
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
cSocket.close();
|
||||
if (!Thread.currentThread().equals(outThread)) send(new PacketServer((byte) 0) {
|
||||
@Override public void serializeToByteBuffer(ByteBuffer buffer) {}
|
||||
@Override public void deserializeFromByteBuffer(ByteBuffer buffer) {}
|
||||
});
|
||||
// provoque une exception dans le thread de sortie, et la
|
||||
// termine
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
|
||||
private class TCPServerConnectionOutputThread extends Thread {
|
||||
private BlockingQueue<PacketServer> packetQueue = new LinkedBlockingDeque<>();
|
||||
|
||||
public TCPServerConnectionOutputThread(int coId) {
|
||||
super("TCPSv " + socketName + " Conn#" + coId + " Out");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
private void addPacket(PacketServer packet) {
|
||||
packetQueue.add(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (!cSocket.isClosed()) {
|
||||
PacketServer packet = packetQueue.poll(1, TimeUnit.SECONDS);
|
||||
byte[] data;
|
||||
if (packet != null) {
|
||||
try {
|
||||
data = packet.getFullSerializedPacket();
|
||||
bandwidthCalculation.addPacket(TCPServerClientConnection.this, false, data.length);
|
||||
out.write(data);
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
Log.severe("Can't send packet "+packet.getClass(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (InterruptedException|IOException e) {}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("thread", getName())
|
||||
.append("socket", cSocket).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
if (isClosed.get()) return;
|
||||
isClosed.set(true);
|
||||
|
||||
clients.forEach(el -> el.close());
|
||||
|
||||
try {
|
||||
listener.onSocketClose(this);
|
||||
} catch(Exception e) {
|
||||
Log.severe("Exception while calling TCPServerListener.onSocketClose()", e);
|
||||
}
|
||||
|
||||
socket.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return isClosed.get() || socket.isClosed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<TCPServerClientConnection> getClients() {
|
||||
synchronized (clients) {
|
||||
return new ArrayList<>(clients);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("thread", getName())
|
||||
.append("socket", socket).toString();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package fr.pandacube.java.util.network.server;
|
||||
|
||||
import fr.pandacube.java.util.network.packet.PacketClient;
|
||||
import fr.pandacube.java.util.network.server.TCPServer.TCPServerClientConnection;
|
||||
|
||||
public interface TCPServerListener {
|
||||
|
||||
public void onSocketOpen(TCPServer svConnection);
|
||||
|
||||
public void onClientConnect(TCPServer svConnection, TCPServerClientConnection clientConnection);
|
||||
|
||||
public void onPacketReceive(TCPServer svConnection, TCPServerClientConnection clientConnection,
|
||||
PacketClient packet);
|
||||
|
||||
public void onClientDisconnect(TCPServer svConnection, TCPServerClientConnection clientConnection);
|
||||
|
||||
public void onSocketClose(TCPServer svConnection);
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package fr.pandacube.java.util.network_api.client;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public abstract class AbstractRequest {
|
||||
|
||||
private final String pass;
|
||||
private String command;
|
||||
private String data;
|
||||
|
||||
protected AbstractRequest(String cmd, String p) {
|
||||
if (cmd == null || cmd.isEmpty()) throw new IllegalArgumentException("Un message doit-être défini");
|
||||
command = cmd;
|
||||
pass = p;
|
||||
}
|
||||
|
||||
protected void setData(String d) {
|
||||
if (d == null) d = "";
|
||||
data = d;
|
||||
}
|
||||
|
||||
public void sendPacket(PrintStream out) {
|
||||
out.print(pass + "\n");
|
||||
out.print(command + "\n");
|
||||
out.print(data.getBytes().length + "\n");
|
||||
out.print(data);
|
||||
out.flush();
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package fr.pandacube.java.util.network_api.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
public class NetworkAPISender {
|
||||
|
||||
public static ResponseAnalyser sendRequest(InetSocketAddress cible, AbstractRequest request) throws IOException {
|
||||
Socket s = new Socket(cible.getAddress(), cible.getPort());
|
||||
|
||||
PrintStream out = new PrintStream(s.getOutputStream());
|
||||
|
||||
request.sendPacket(out);
|
||||
s.shutdownOutput();
|
||||
|
||||
ResponseAnalyser response = new ResponseAnalyser(s);
|
||||
|
||||
s.close();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
package fr.pandacube.java.util.network_api.client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
|
||||
public class ResponseAnalyser {
|
||||
/**
|
||||
* Indique si la requête s'est bien exécutée (l'entête de la réponse est
|
||||
* 'ok')
|
||||
*/
|
||||
public final boolean good;
|
||||
|
||||
public final String data;
|
||||
|
||||
public ResponseAnalyser(Socket socket) throws IOException {
|
||||
if (socket == null || socket.isClosed() || socket.isInputShutdown())
|
||||
throw new IllegalArgumentException("le socket doit être non null et doit être ouvert sur le flux d'entrée");
|
||||
|
||||
// on lis la réponse
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
String line;
|
||||
|
||||
// lecture de la première ligne
|
||||
line = in.readLine();
|
||||
good = line.equalsIgnoreCase("OK");
|
||||
|
||||
// lecture de la deuxième ligne
|
||||
line = in.readLine();
|
||||
|
||||
int data_size = 0;
|
||||
try {
|
||||
data_size = Integer.parseInt(line);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new RuntimeException("Réponse mal formée : la deuxième ligne doit-être un nombre entier");
|
||||
}
|
||||
|
||||
// lecture du reste
|
||||
StringBuilder sB_data = new StringBuilder();
|
||||
char[] c = new char[100];
|
||||
int nbC = 0;
|
||||
while ((nbC = in.read(c)) != -1)
|
||||
sB_data.append(c, 0, nbC);
|
||||
data = sB_data.toString();
|
||||
|
||||
if (data.getBytes().length != data_size) throw new RuntimeException("Réponse mal formée : " + data_size
|
||||
+ " caractères annoncée dans la requête, mais " + data.getBytes().length + " s'y trouvent.");
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package fr.pandacube.java.util.network_api.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import fr.pandacube.java.util.Log;
|
||||
|
||||
public abstract class AbstractRequestExecutor {
|
||||
|
||||
public final String command;
|
||||
|
||||
public AbstractRequestExecutor(String cmd, NetworkAPIListener napiListener) {
|
||||
command = cmd.toLowerCase();
|
||||
napiListener.registerRequestExecutor(command, this);
|
||||
}
|
||||
|
||||
public void execute(String data, Socket socket) throws IOException {
|
||||
if (socket == null || socket.isClosed() || socket.isOutputShutdown())
|
||||
throw new IllegalArgumentException("le socket doit être non null et doit être ouvert sur le flux d'entrée");
|
||||
|
||||
try {
|
||||
|
||||
Response rep = run(socket.getInetAddress(), data);
|
||||
rep.sendPacket(new PrintStream(socket.getOutputStream()));
|
||||
|
||||
} catch (Exception e) {
|
||||
new Response(false, e.toString()).sendPacket(new PrintStream(socket.getOutputStream()));
|
||||
Log.severe(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data La représentation sous forme de String des données envoyés
|
||||
* dans la requête
|
||||
* @return La réponse à retourner au client
|
||||
*/
|
||||
protected abstract Response run(InetAddress source, String data);
|
||||
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package fr.pandacube.java.util.network_api.server;
|
||||
|
||||
/**
|
||||
* Interface permettant de gérer l'exécution asynchrone d'un PacketExecutor.
|
||||
*
|
||||
* @author Marc Baloup
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface NAPIExecutionHandler {
|
||||
|
||||
public void handleRun(Runnable executor);
|
||||
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
package fr.pandacube.java.util.network_api.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class NetworkAPIListener implements Runnable {
|
||||
|
||||
private int port = 0;
|
||||
String pass;
|
||||
private ServerSocket serverSocket;
|
||||
private HashMap<String, AbstractRequestExecutor> requestExecutors = new HashMap<>();
|
||||
private String name;
|
||||
private NAPIExecutionHandler nAPIExecutionHandler;
|
||||
|
||||
/**
|
||||
* Instencie le côté serveur du NetworkAPI
|
||||
*
|
||||
* @param n nom du networkAPI (permet l'identification dans les logs)
|
||||
* @param p le port d'écoute
|
||||
* @param pa le mot de passe réseau
|
||||
* @param peh PacketExecutionHandler permettant de prendre en charge
|
||||
* l'exécution asynchrone d'une requête reçu pas un client
|
||||
*/
|
||||
public NetworkAPIListener(String n, int p, String pa, NAPIExecutionHandler peh) {
|
||||
port = p;
|
||||
pass = pa;
|
||||
name = n;
|
||||
nAPIExecutionHandler = peh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (this) {
|
||||
try {
|
||||
serverSocket = new ServerSocket(port);
|
||||
} catch (IOException e) {
|
||||
System.err.println(e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("NetworkAPI '" + name + "' à l'écoute sur le port " + port);
|
||||
|
||||
try {
|
||||
// réception des connexion client
|
||||
while (!serverSocket.isClosed()) {
|
||||
@SuppressWarnings("resource")
|
||||
Socket socketClient = serverSocket.accept();
|
||||
nAPIExecutionHandler.handleRun(new PacketExecutor(socketClient, this));
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
|
||||
synchronized (this) {
|
||||
try {
|
||||
if (!serverSocket.isClosed()) serverSocket.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
System.out.println("NetworkAPI '" + name + "' ferme le port " + port);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Ferme le ServerSocket. Ceci provoque l'arrêt du thread associé à
|
||||
* l'instance de la classe
|
||||
*/
|
||||
public synchronized void closeServerSocket() {
|
||||
if (serverSocket != null) try {
|
||||
serverSocket.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void registerRequestExecutor(String command, AbstractRequestExecutor executor) {
|
||||
requestExecutors.put(command, executor);
|
||||
}
|
||||
|
||||
public AbstractRequestExecutor getRequestExecutor(String command) {
|
||||
return requestExecutors.get(command);
|
||||
}
|
||||
|
||||
public String getCommandList() {
|
||||
return Arrays.toString(requestExecutors.keySet().toArray());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package fr.pandacube.java.util.network_api.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import fr.pandacube.java.util.Log;
|
||||
|
||||
/**
|
||||
* Prends en charge un socket client et le transmet au gestionnaire de paquet
|
||||
* correspondant.<br/>
|
||||
* La connexion est fermée après chaque requête du client (règle pouvant
|
||||
* évoluer)
|
||||
*
|
||||
* @author Marc Baloup
|
||||
*
|
||||
*/
|
||||
public class PacketExecutor implements Runnable {
|
||||
private Socket socket;
|
||||
private NetworkAPIListener networkAPIListener;
|
||||
|
||||
public PacketExecutor(Socket s, NetworkAPIListener napiListener) {
|
||||
socket = s;
|
||||
networkAPIListener = napiListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
// analyse de la requête
|
||||
RequestAnalyser analyse = new RequestAnalyser(socket, networkAPIListener);
|
||||
|
||||
AbstractRequestExecutor executor = networkAPIListener.getRequestExecutor(analyse.command);
|
||||
|
||||
executor.execute(analyse.data, socket);
|
||||
|
||||
} catch (Throwable e) {
|
||||
Response rep = new Response();
|
||||
rep.good = false;
|
||||
rep.data = e.toString();
|
||||
try {
|
||||
rep.sendPacket(new PrintStream(socket.getOutputStream()));
|
||||
} catch (IOException e1) {}
|
||||
if (e instanceof IOException)
|
||||
Log.warning("Impossible de lire le packet reçu sur le socket " + socket + " : " + e.toString());
|
||||
else
|
||||
Log.severe(e);
|
||||
}
|
||||
|
||||
try {
|
||||
socket.close();
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
package fr.pandacube.java.util.network_api.server;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
|
||||
public class RequestAnalyser {
|
||||
|
||||
private NetworkAPIListener networkAPIListener;
|
||||
public final String command;
|
||||
public final String data;
|
||||
|
||||
public RequestAnalyser(Socket socket, NetworkAPIListener napiListener) throws IOException, BadRequestException {
|
||||
if (socket == null || socket.isClosed() || socket.isInputShutdown() || napiListener == null)
|
||||
throw new IllegalArgumentException(
|
||||
"le socket doit être non null et doit être ouvert sur le flux d'entrée et napiListener ne doit pas être null");
|
||||
|
||||
networkAPIListener = napiListener;
|
||||
|
||||
// on lis la réponse
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
String line;
|
||||
|
||||
// lecture de la première ligne
|
||||
line = in.readLine();
|
||||
if (line == null || !line.equals(networkAPIListener.pass)) throw new BadRequestException("wrong_password");
|
||||
|
||||
// lecture de la deuxième ligne
|
||||
line = in.readLine();
|
||||
if (line == null || networkAPIListener.getRequestExecutor(line) == null)
|
||||
throw new BadRequestException("command_not_exists");
|
||||
command = line;
|
||||
|
||||
// lecture de la troisième ligne
|
||||
line = in.readLine();
|
||||
|
||||
int data_size = 0;
|
||||
try {
|
||||
data_size = Integer.parseInt(line);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new BadRequestException("wrong_data_size_format");
|
||||
}
|
||||
|
||||
// lecture du reste
|
||||
StringBuilder sB_data = new StringBuilder();
|
||||
char[] c = new char[100];
|
||||
int nbC = 0;
|
||||
while ((nbC = in.read(c)) != -1)
|
||||
sB_data.append(c, 0, nbC);
|
||||
|
||||
data = sB_data.toString();
|
||||
|
||||
if (data.getBytes().length != data_size) throw new BadRequestException("wrong_data_size");
|
||||
|
||||
socket.shutdownInput();
|
||||
}
|
||||
|
||||
public class BadRequestException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public BadRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package fr.pandacube.java.util.network_api.server;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class Response {
|
||||
public boolean good = true;
|
||||
public String data = "";
|
||||
|
||||
public Response(boolean good, String data) {
|
||||
this.good = good;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit une réponse positive avec aucune donnée. Équivaut à
|
||||
* <code>new Response(true, "")</code>
|
||||
*/
|
||||
public Response() {}
|
||||
|
||||
public void sendPacket(PrintStream out) {
|
||||
|
||||
if (data == null) data = "";
|
||||
|
||||
out.print((good ? "OK" : "ERROR") + "\n");
|
||||
out.print(data.getBytes().length + "\n");
|
||||
out.print(data);
|
||||
out.flush();
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package fr.pandacube.java.util.network_api.server;
|
||||
|
||||
public class ThreadNAPIExecutionHandler implements NAPIExecutionHandler {
|
||||
|
||||
@Override
|
||||
public void handleRun(Runnable executor) {
|
||||
Thread t = new Thread(executor);
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user