Conversion en projet Maven

This commit is contained in:
2016-11-26 03:12:18 +01:00
parent 552a797501
commit 40942fb25f
114 changed files with 88 additions and 8 deletions

View 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)
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -0,0 +1,6 @@
package fr.pandacube.java.util;
@FunctionalInterface
public interface Callback<T> {
public void done(T arg);
}

View 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()));
}
}

View 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)];
}
}

View 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());
}
}

View 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);
} // */
}

View 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);
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View File

@@ -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")));
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View 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
}
}

View File

@@ -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();
}
}

View 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());
}
}

View File

@@ -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);
}

View 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);
}

View 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());
}
}

View 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());
}
}

View 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
}
}

View File

@@ -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());
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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
}
}

View File

@@ -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
}
}

View 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);
}

View 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);
}
}

View 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);
}
}

View File

@@ -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);
}

View 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);
}

View 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());
}
}

View 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);
}

View 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());
}
}

View File

@@ -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) {}
}
}

View 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)
* }
*/
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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>&lt;</code>" */
LT("<"), /** Equivalent to SQL "<code>&lt;=</code>" */
LEQ("<="), /** Equivalent to SQL "<code>!=</code>" */
NEQ("!=");
public final String sql;
private SQLComparator(String s) {
sql = s;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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<>());
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View 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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,8 @@
package fr.pandacube.java.util.network.packet;
@FunctionalInterface
public interface ResponseCallback<T extends Packet> {
public void call(T packet);
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -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) {
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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.");
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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());
}
}

View File

@@ -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) {}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}