Cleaned PropertyManager, refactored GameLoop to external package.
This commit is contained in:
@@ -46,7 +46,7 @@ public abstract class AppModule extends RootBusNode implements AppAccess {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown()
|
public final void shutdown()
|
||||||
{
|
{
|
||||||
app.shutdown();
|
app.shutdown();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public abstract class AppSubModule extends BusNode implements AppAccess {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown()
|
public final void shutdown()
|
||||||
{
|
{
|
||||||
app.shutdown();
|
app.shutdown();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ public interface BusAccess {
|
|||||||
/**
|
/**
|
||||||
* @return event bus
|
* @return event bus
|
||||||
*/
|
*/
|
||||||
public abstract EventBus bus();
|
EventBus bus();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import mightypork.gamecore.control.bus.clients.ToggleableClient;
|
|||||||
*/
|
*/
|
||||||
public abstract class BusNode implements BusAccess, DelegatingClient, ToggleableClient {
|
public abstract class BusNode implements BusAccess, DelegatingClient, ToggleableClient {
|
||||||
|
|
||||||
private BusAccess busAccess;
|
private final BusAccess busAccess;
|
||||||
|
|
||||||
private final Set<Object> clients = new LinkedHashSet<Object>();
|
private final Set<Object> clients = new LinkedHashSet<Object>();
|
||||||
private boolean listening = true;
|
private boolean listening = true;
|
||||||
@@ -45,7 +45,7 @@ public abstract class BusNode implements BusAccess, DelegatingClient, Toggleable
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isListening()
|
public final boolean isListening()
|
||||||
{
|
{
|
||||||
return listening;
|
return listening;
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ public abstract class BusNode implements BusAccess, DelegatingClient, Toggleable
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EventBus bus()
|
public final EventBus bus()
|
||||||
{
|
{
|
||||||
return busAccess.bus();
|
return busAccess.bus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,38 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
|||||||
|
|
||||||
import mightypork.gamecore.control.bus.events.MainLoopTaskRequest;
|
import mightypork.gamecore.control.bus.events.MainLoopTaskRequest;
|
||||||
import mightypork.gamecore.control.bus.events.UpdateEvent;
|
import mightypork.gamecore.control.bus.events.UpdateEvent;
|
||||||
|
import mightypork.gamecore.control.interf.NoImpl;
|
||||||
import mightypork.gamecore.control.timing.TimerDelta;
|
import mightypork.gamecore.control.timing.TimerDelta;
|
||||||
|
import mightypork.gamecore.gui.renderers.Renderable;
|
||||||
|
import mightypork.gamecore.gui.screens.ScreenRegistry;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delta-timed game loop with task queue etc.
|
||||||
|
*
|
||||||
|
* @author MightyPork
|
||||||
|
*/
|
||||||
public abstract class GameLoop extends AppModule implements MainLoopTaskRequest.Listener {
|
public abstract class GameLoop extends AppModule implements MainLoopTaskRequest.Listener {
|
||||||
|
|
||||||
private final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>();
|
private final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>();
|
||||||
/** timer */
|
|
||||||
private TimerDelta timer;
|
private TimerDelta timer;
|
||||||
|
private Renderable mainRenderable;
|
||||||
private boolean running = true;
|
private boolean running = true;
|
||||||
|
|
||||||
|
|
||||||
public GameLoop(AppAccess app) {
|
/**
|
||||||
|
* @param app {@link AppAccess} instance
|
||||||
|
* @param rootRenderable main {@link Renderable}, typically a
|
||||||
|
* {@link ScreenRegistry}
|
||||||
|
*/
|
||||||
|
public GameLoop(AppAccess app, Renderable rootRenderable) {
|
||||||
super(app);
|
super(app);
|
||||||
|
|
||||||
|
if (rootRenderable == null) {
|
||||||
|
throw new NullPointerException("Master renderable must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mainRenderable = rootRenderable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -36,17 +55,29 @@ public abstract class GameLoop extends AppModule implements MainLoopTaskRequest.
|
|||||||
r.run();
|
r.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
tick();
|
beforeRender();
|
||||||
|
|
||||||
|
mainRenderable.render();
|
||||||
|
|
||||||
|
afterRender();
|
||||||
|
|
||||||
disp().endFrame();
|
disp().endFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
@NoImpl
|
||||||
* Called each frame, in rendering context.
|
protected void beforeRender()
|
||||||
*/
|
{
|
||||||
protected abstract void tick();
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NoImpl
|
||||||
|
protected void afterRender()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import mightypork.gamecore.control.interf.Updateable;
|
|||||||
* @author MightyPork
|
* @author MightyPork
|
||||||
*/
|
*/
|
||||||
// sending via queue would hog the bus
|
// sending via queue would hog the bus
|
||||||
@ImmediateEvent
|
|
||||||
@UnloggedEvent
|
@UnloggedEvent
|
||||||
|
@ImmediateEvent
|
||||||
public class UpdateEvent implements Event<Updateable> {
|
public class UpdateEvent implements Event<Updateable> {
|
||||||
|
|
||||||
private final double deltaTime;
|
private final double deltaTime;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package mightypork.gamecore.control.interf;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface NoImpl {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,10 +10,8 @@ package mightypork.gamecore.control.timing;
|
|||||||
public class FpsMeter {
|
public class FpsMeter {
|
||||||
|
|
||||||
private long frames = 0;
|
private long frames = 0;
|
||||||
private long drops = 0;
|
|
||||||
private long lastTimeMillis = System.currentTimeMillis();
|
private long lastTimeMillis = System.currentTimeMillis();
|
||||||
private long lastSecFPS = 0;
|
private long lastSecFPS = 0;
|
||||||
private long lastSecDrop = 0;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,31 +30,10 @@ public class FpsMeter {
|
|||||||
{
|
{
|
||||||
if (System.currentTimeMillis() - lastTimeMillis > 1000) {
|
if (System.currentTimeMillis() - lastTimeMillis > 1000) {
|
||||||
lastSecFPS = frames;
|
lastSecFPS = frames;
|
||||||
lastSecDrop = drops;
|
|
||||||
frames = 0;
|
frames = 0;
|
||||||
drops = 0;
|
long over = System.currentTimeMillis() - lastTimeMillis - 1000;
|
||||||
lastTimeMillis = System.currentTimeMillis();
|
lastTimeMillis = System.currentTimeMillis() - over;
|
||||||
}
|
}
|
||||||
frames++;
|
frames++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notification that some frames have been dropped
|
|
||||||
*
|
|
||||||
* @param dropped dropped frames
|
|
||||||
*/
|
|
||||||
public void drop(int dropped)
|
|
||||||
{
|
|
||||||
drops += dropped;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return current second's dropped frames
|
|
||||||
*/
|
|
||||||
public long getDropped()
|
|
||||||
{
|
|
||||||
return lastSecDrop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ public class TextRenderer extends PluggableRenderer {
|
|||||||
private Align align;
|
private Align align;
|
||||||
|
|
||||||
|
|
||||||
|
public TextRenderer(GLFont font, RGB color, Align align) {
|
||||||
|
this(font, "MISSINGNO", color, align);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public TextRenderer(GLFont font, String text, RGB color, Align align) {
|
public TextRenderer(GLFont font, String text, RGB color, Align align) {
|
||||||
this.font = new FontRenderer(font);
|
this.font = new FontRenderer(font);
|
||||||
this.text = text;
|
this.text = text;
|
||||||
@@ -52,8 +57,19 @@ public class TextRenderer extends PluggableRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getText()
|
||||||
|
{
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render()
|
public void render()
|
||||||
|
{
|
||||||
|
render(getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void render(String text)
|
||||||
{
|
{
|
||||||
final double h = getRect().getHeight();
|
final double h = getRect().getHeight();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package mightypork.gamecore.gui.screens;
|
package mightypork.gamecore.gui.screens;
|
||||||
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.*;
|
|
||||||
import mightypork.gamecore.control.AppAccess;
|
import mightypork.gamecore.control.AppAccess;
|
||||||
import mightypork.gamecore.control.AppSubModule;
|
import mightypork.gamecore.control.AppSubModule;
|
||||||
import mightypork.gamecore.control.bus.events.ScreenChangeEvent;
|
import mightypork.gamecore.control.bus.events.ScreenChangeEvent;
|
||||||
@@ -9,7 +8,7 @@ import mightypork.gamecore.gui.renderers.Renderable;
|
|||||||
import mightypork.gamecore.input.KeyBinder;
|
import mightypork.gamecore.input.KeyBinder;
|
||||||
import mightypork.gamecore.input.KeyBindingPool;
|
import mightypork.gamecore.input.KeyBindingPool;
|
||||||
import mightypork.gamecore.input.KeyStroke;
|
import mightypork.gamecore.input.KeyStroke;
|
||||||
import mightypork.gamecore.render.DisplaySystem;
|
import mightypork.gamecore.render.Render;
|
||||||
import mightypork.utils.math.constraints.RectConstraint;
|
import mightypork.utils.math.constraints.RectConstraint;
|
||||||
import mightypork.utils.math.coord.Coord;
|
import mightypork.utils.math.coord.Coord;
|
||||||
import mightypork.utils.math.coord.Rect;
|
import mightypork.utils.math.coord.Rect;
|
||||||
@@ -148,7 +147,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind
|
|||||||
if (!isActive()) return;
|
if (!isActive()) return;
|
||||||
|
|
||||||
if (needSetupViewport) {
|
if (needSetupViewport) {
|
||||||
DisplaySystem.setupOrtho();
|
Render.setupOrtho();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderScreen();
|
renderScreen();
|
||||||
|
|||||||
@@ -3,16 +3,12 @@ package mightypork.gamecore.render;
|
|||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.*;
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import mightypork.gamecore.control.AppAccess;
|
import mightypork.gamecore.control.AppAccess;
|
||||||
import mightypork.gamecore.control.RootBusNode;
|
import mightypork.gamecore.control.RootBusNode;
|
||||||
import mightypork.gamecore.control.bus.events.ScreenChangeEvent;
|
import mightypork.gamecore.control.bus.events.ScreenChangeEvent;
|
||||||
|
import mightypork.gamecore.control.timing.FpsMeter;
|
||||||
import mightypork.utils.logging.Log;
|
import mightypork.utils.logging.Log;
|
||||||
import mightypork.utils.math.constraints.RectConstraint;
|
import mightypork.utils.math.constraints.RectConstraint;
|
||||||
import mightypork.utils.math.coord.Coord;
|
import mightypork.utils.math.coord.Coord;
|
||||||
@@ -29,6 +25,7 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
|
|||||||
private DisplayMode windowDisplayMode;
|
private DisplayMode windowDisplayMode;
|
||||||
private int targetFps;
|
private int targetFps;
|
||||||
public static boolean yAxisDown = true;
|
public static boolean yAxisDown = true;
|
||||||
|
private FpsMeter fpsMeter;
|
||||||
|
|
||||||
|
|
||||||
public DisplaySystem(AppAccess app) {
|
public DisplaySystem(AppAccess app) {
|
||||||
@@ -58,13 +55,13 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
|
|||||||
Display.setTitle(title);
|
Display.setTitle(title);
|
||||||
Display.create();
|
Display.create();
|
||||||
|
|
||||||
|
fpsMeter = new FpsMeter();
|
||||||
|
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
switchFullscreen();
|
switchFullscreen();
|
||||||
Display.update();
|
Display.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
Render.init();
|
|
||||||
|
|
||||||
} catch (final LWJGLException e) {
|
} catch (final LWJGLException e) {
|
||||||
throw new RuntimeException("Could not initialize screen", e);
|
throw new RuntimeException("Could not initialize screen", e);
|
||||||
}
|
}
|
||||||
@@ -106,13 +103,18 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take screenshot (expensive processing is done on-demand when screenshot
|
||||||
|
* is processed).
|
||||||
|
*
|
||||||
|
* @return screenshot object
|
||||||
|
*/
|
||||||
public static Screenshot takeScreenshot()
|
public static Screenshot takeScreenshot()
|
||||||
{
|
{
|
||||||
glReadBuffer(GL_FRONT);
|
glReadBuffer(GL_FRONT);
|
||||||
final int width = Display.getDisplayMode().getWidth();
|
final int width = Display.getDisplayMode().getWidth();
|
||||||
final int height = Display.getDisplayMode().getHeight();
|
final int height = Display.getDisplayMode().getHeight();
|
||||||
final int bpp = 4; // Assuming a 32-bit display with a byte each for red,
|
final int bpp = 4;
|
||||||
// green, blue, and alpha.
|
|
||||||
final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
|
final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
|
||||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||||
|
|
||||||
@@ -177,6 +179,7 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
|
|||||||
|
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
fpsMeter.frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -196,62 +199,11 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
|
|||||||
return new Rect(getSize());
|
return new Rect(getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Screenshot {
|
/**
|
||||||
|
* @return current FPS
|
||||||
private int width;
|
*/
|
||||||
private int height;
|
public final long getFps()
|
||||||
private int bpp;
|
|
||||||
private ByteBuffer bytes;
|
|
||||||
private BufferedImage image;
|
|
||||||
|
|
||||||
|
|
||||||
public Screenshot(int width, int height, int bpp, ByteBuffer buffer) {
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
this.bpp = bpp;
|
|
||||||
this.bytes = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public BufferedImage getImage()
|
|
||||||
{
|
{
|
||||||
if (image != null) return image;
|
return fpsMeter.getFPS();
|
||||||
|
|
||||||
image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB);
|
|
||||||
|
|
||||||
// convert to a buffered image
|
|
||||||
for (int x = 0; x < this.width; x++) {
|
|
||||||
for (int y = 0; y < this.height; y++) {
|
|
||||||
final int i = (x + (this.width * y)) * this.bpp;
|
|
||||||
final int r = this.bytes.get(i) & 0xFF;
|
|
||||||
final int g = this.bytes.get(i + 1) & 0xFF;
|
|
||||||
final int b = this.bytes.get(i + 2) & 0xFF;
|
|
||||||
image.setRGB(x, this.height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void save(File file) throws IOException
|
|
||||||
{
|
|
||||||
ImageIO.write(getImage(), "PNG", file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void setupOrtho()
|
|
||||||
{
|
|
||||||
// fix projection for changed size
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
|
||||||
glLoadIdentity();
|
|
||||||
final Coord s = getSize();
|
|
||||||
glViewport(0, 0, s.xi(), s.yi());
|
|
||||||
glOrtho(0, s.x, (yAxisDown ? 1 : -1) * s.y, 0, -1000, 1000);
|
|
||||||
|
|
||||||
// back to modelview
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,32 +30,6 @@ public class Render {
|
|||||||
private static final Coord AXIS_Y = new Coord(0, 1, 0);
|
private static final Coord AXIS_Y = new Coord(0, 1, 0);
|
||||||
private static final Coord AXIS_Z = new Coord(0, 0, 1);
|
private static final Coord AXIS_Z = new Coord(0, 0, 1);
|
||||||
|
|
||||||
|
|
||||||
public static void init()
|
|
||||||
{
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
glLoadIdentity();
|
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
|
||||||
|
|
||||||
glClearDepth(1f);
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
glDepthFunc(GL_LEQUAL);
|
|
||||||
|
|
||||||
glEnable(GL_NORMALIZE);
|
|
||||||
|
|
||||||
glShadeModel(GL_SMOOTH);
|
|
||||||
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind GL color
|
* Bind GL color
|
||||||
*
|
*
|
||||||
@@ -491,4 +465,37 @@ public class Render {
|
|||||||
quadTextured(quad, txquad.uvs, txquad.tx, tint);
|
quadTextured(quad, txquad.uvs, txquad.tx, tint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup Ortho projection for 2D graphics
|
||||||
|
*/
|
||||||
|
public static void setupOrtho()
|
||||||
|
{
|
||||||
|
// fix projection for changed size
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
final Coord s = DisplaySystem.getSize();
|
||||||
|
glViewport(0, 0, s.xi(), s.yi());
|
||||||
|
glOrtho(0, s.x, (DisplaySystem.yAxisDown ? 1 : -1) * s.y, 0, -1000, 1000);
|
||||||
|
|
||||||
|
// back to modelview
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
|
||||||
|
glLoadIdentity();
|
||||||
|
|
||||||
|
glDisable(GL_LIGHTING);
|
||||||
|
|
||||||
|
glClearDepth(1f);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
|
||||||
|
glEnable(GL_NORMALIZE);
|
||||||
|
|
||||||
|
glShadeModel(GL_SMOOTH);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package mightypork.gamecore.render;
|
||||||
|
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screenshot object, can be used to extract image or write to file.<br>
|
||||||
|
* Screenshot, once taken, can be safely processed in separate thread.
|
||||||
|
*
|
||||||
|
* @author MightyPork
|
||||||
|
*/
|
||||||
|
public class Screenshot {
|
||||||
|
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
private int bpp;
|
||||||
|
private ByteBuffer bytes;
|
||||||
|
private BufferedImage image;
|
||||||
|
|
||||||
|
|
||||||
|
public Screenshot(int width, int height, int bpp, ByteBuffer buffer) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.bpp = bpp;
|
||||||
|
this.bytes = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BufferedImage getImage()
|
||||||
|
{
|
||||||
|
if (image != null) return image;
|
||||||
|
|
||||||
|
image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB);
|
||||||
|
|
||||||
|
// convert to a buffered image
|
||||||
|
for (int x = 0; x < this.width; x++) {
|
||||||
|
for (int y = 0; y < this.height; y++) {
|
||||||
|
final int i = (x + (this.width * y)) * this.bpp;
|
||||||
|
final int r = this.bytes.get(i) & 0xFF;
|
||||||
|
final int g = this.bytes.get(i + 1) & 0xFF;
|
||||||
|
final int b = this.bytes.get(i + 2) & 0xFF;
|
||||||
|
image.setRGB(x, this.height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void save(File file) throws IOException
|
||||||
|
{
|
||||||
|
ImageIO.write(getImage(), "PNG", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,6 +56,7 @@ public class App implements AppAccess {
|
|||||||
public static void main(String[] args)
|
public static void main(String[] args)
|
||||||
{
|
{
|
||||||
Config.init();
|
Config.init();
|
||||||
|
Config.save();
|
||||||
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());
|
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package mightypork.rogue;
|
package mightypork.rogue;
|
||||||
|
|
||||||
|
|
||||||
import mightypork.utils.files.PropertyManager;
|
import mightypork.utils.config.PropertyManager;
|
||||||
import mightypork.utils.logging.Log;
|
import mightypork.utils.logging.Log;
|
||||||
|
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ public class Config {
|
|||||||
mgr.cfgNewlineBeforeComments(true);
|
mgr.cfgNewlineBeforeComments(true);
|
||||||
mgr.cfgSeparateSections(true);
|
mgr.cfgSeparateSections(true);
|
||||||
|
|
||||||
mgr.putInteger(PK_LAST_RUN_VERSION, def_LAST_RUN_VERSION);
|
mgr.putInteger(PK_LAST_RUN_VERSION, def_LAST_RUN_VERSION, null);
|
||||||
mgr.putBoolean(PK_START_IN_FS, def_START_IN_FS, "Go to fullscreen on startup.");
|
mgr.putBoolean(PK_START_IN_FS, def_START_IN_FS, "Go to fullscreen on startup.");
|
||||||
|
|
||||||
load(); // load what has been "put"
|
load(); // load what has been "put"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class Const {
|
|||||||
public static final String TITLEBAR = APP_NAME + " v." + VERSION;
|
public static final String TITLEBAR = APP_NAME + " v." + VERSION;
|
||||||
|
|
||||||
// AUDIO
|
// AUDIO
|
||||||
public static final int FPS_RENDER = 80; // max
|
public static final int FPS_RENDER = 100; // max
|
||||||
|
|
||||||
// INITIAL WINDOW SIZE
|
// INITIAL WINDOW SIZE
|
||||||
public static final int WINDOW_W = 1024;
|
public static final int WINDOW_W = 1024;
|
||||||
|
|||||||
@@ -11,24 +11,8 @@ import mightypork.rogue.util.Utils;
|
|||||||
|
|
||||||
public class MainLoop extends GameLoop implements ActionRequest.Listener {
|
public class MainLoop extends GameLoop implements ActionRequest.Listener {
|
||||||
|
|
||||||
final Renderable renderable;
|
|
||||||
|
|
||||||
|
|
||||||
public MainLoop(App app, Renderable masterRenderable) {
|
public MainLoop(App app, Renderable masterRenderable) {
|
||||||
super(app);
|
super(app, masterRenderable);
|
||||||
|
|
||||||
if (masterRenderable == null) {
|
|
||||||
throw new NullPointerException("Master renderable must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.renderable = masterRenderable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void tick()
|
|
||||||
{
|
|
||||||
renderable.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import mightypork.gamecore.render.DisplaySystem;
|
import mightypork.gamecore.render.DisplaySystem;
|
||||||
import mightypork.gamecore.render.DisplaySystem.Screenshot;
|
import mightypork.gamecore.render.Screenshot;
|
||||||
import mightypork.utils.logging.Log;
|
import mightypork.utils.logging.Log;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,13 @@ public class LayerBouncyBoxes extends ScreenLayer {
|
|||||||
boxes.add(bbr);
|
boxes.add(bbr);
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.add(new TextRenderer(Res.getFont("default"), "This is a text, yo!", RGB.WHITE, Align.LEFT));
|
layout.add(new TextRenderer(Res.getFont("default"), RGB.WHITE, Align.LEFT) {
|
||||||
|
@Override
|
||||||
|
public String getText()
|
||||||
|
{
|
||||||
|
return "Running at " + disp().getFps() + " fps!";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
|
|||||||
);
|
);
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
text = new TextRenderer(Res.getFont("default"), "YO", RGB.YELLOW, Align.CENTER);
|
text = new TextRenderer(Res.getFont("default"), RGB.YELLOW, Align.CENTER);
|
||||||
text.setContext(flyingFontBox);
|
text.setContext(flyingFontBox);
|
||||||
|
|
||||||
bindKeyStroke(new KeyStroke(Keys.KEY_RETURN), new Runnable() {
|
bindKeyStroke(new KeyStroke(Keys.KEY_RETURN), new Runnable() {
|
||||||
@@ -87,6 +87,7 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
|
|||||||
final double t = 2;
|
final double t = 2;
|
||||||
|
|
||||||
size.fadeTo(100 + rand.nextInt(700), t / 2D);
|
size.fadeTo(100 + rand.nextInt(700), t / 2D);
|
||||||
|
|
||||||
xPos.fadeTo(pos.x, t);
|
xPos.fadeTo(pos.x, t);
|
||||||
yPos.fadeTo(pos.y, t);
|
yPos.fadeTo(pos.y, t);
|
||||||
}
|
}
|
||||||
@@ -96,7 +97,7 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
|
|||||||
public void render()
|
public void render()
|
||||||
{
|
{
|
||||||
cat.render();
|
cat.render();
|
||||||
text.render();
|
text.render(disp().getFps()+" fps");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,450 @@
|
|||||||
|
package mightypork.utils.config;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import mightypork.utils.math.Range;
|
||||||
|
import mightypork.utils.math.coord.Coord;
|
||||||
|
import mightypork.utils.objects.Convert;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property manager with advanced formatting and value checking.<br>
|
||||||
|
* Methods starting with put are for filling. Most of the others are shortcuts
|
||||||
|
* to getters.
|
||||||
|
*
|
||||||
|
* @author MightyPork
|
||||||
|
*/
|
||||||
|
public class PropertyManager {
|
||||||
|
|
||||||
|
private abstract class Property<T> {
|
||||||
|
|
||||||
|
public String comment;
|
||||||
|
public String key;
|
||||||
|
|
||||||
|
public T value;
|
||||||
|
public T defaultValue;
|
||||||
|
|
||||||
|
|
||||||
|
public Property(String key, T defaultValue, String comment) {
|
||||||
|
super();
|
||||||
|
this.comment = comment;
|
||||||
|
this.key = key;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
this.value = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void parse(String string);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return Convert.toString(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BooleanProperty extends Property<Boolean> {
|
||||||
|
|
||||||
|
public BooleanProperty(String key, Boolean defaultValue, String comment) {
|
||||||
|
super(key, defaultValue, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(String string)
|
||||||
|
{
|
||||||
|
value = Convert.toBoolean(string, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class IntegerProperty extends Property<Integer> {
|
||||||
|
|
||||||
|
public IntegerProperty(String key, Integer defaultValue, String comment) {
|
||||||
|
super(key, defaultValue, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(String string)
|
||||||
|
{
|
||||||
|
value = Convert.toInteger(string, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DoubleProperty extends Property<Double> {
|
||||||
|
|
||||||
|
public DoubleProperty(String key, Double defaultValue, String comment) {
|
||||||
|
super(key, defaultValue, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(String string)
|
||||||
|
{
|
||||||
|
value = Convert.toDouble(string, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StringProperty extends Property<String> {
|
||||||
|
|
||||||
|
public StringProperty(String key, String defaultValue, String comment) {
|
||||||
|
super(key, defaultValue, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(String string)
|
||||||
|
{
|
||||||
|
value = Convert.toString(string, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RangeProperty extends Property<Range> {
|
||||||
|
|
||||||
|
public RangeProperty(String key, Range defaultValue, String comment) {
|
||||||
|
super(key, defaultValue, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(String string)
|
||||||
|
{
|
||||||
|
value = Convert.toRange(string, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CoordProperty extends Property<Coord> {
|
||||||
|
|
||||||
|
public CoordProperty(String key, Coord defaultValue, String comment) {
|
||||||
|
super(key, defaultValue, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(String string)
|
||||||
|
{
|
||||||
|
value = Convert.toCoord(string, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** put newline before entry comments */
|
||||||
|
private boolean cfgNewlineBeforeComments = true;
|
||||||
|
/** Put newline between sections. */
|
||||||
|
private boolean cfgSeparateSections = true;
|
||||||
|
/** Force save, even if nothing changed (used to save changed comments) */
|
||||||
|
private boolean cfgForceSave;
|
||||||
|
|
||||||
|
private final File file;
|
||||||
|
private String fileComment = "";
|
||||||
|
|
||||||
|
private final TreeMap<String, Property<?>> entries;
|
||||||
|
private final TreeMap<String, String> renameTable;
|
||||||
|
private final TreeMap<String, String> overrideValues;
|
||||||
|
private SortedProperties props = new SortedProperties();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create property manager from file path and an initial comment.
|
||||||
|
*
|
||||||
|
* @param file file with the props
|
||||||
|
* @param comment the initial comment. Use \n in it if you want.
|
||||||
|
*/
|
||||||
|
public PropertyManager(File file, String comment) {
|
||||||
|
this.file = file;
|
||||||
|
this.entries = new TreeMap<String, Property<?>>();
|
||||||
|
this.overrideValues = new TreeMap<String, String>();
|
||||||
|
this.renameTable = new TreeMap<String, String>();
|
||||||
|
this.fileComment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load, fix and write to file.
|
||||||
|
*/
|
||||||
|
public void apply()
|
||||||
|
{
|
||||||
|
boolean needsSave = false;
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
new File(file.getParent()).mkdirs();
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
props.load(fis);
|
||||||
|
|
||||||
|
} catch (final IOException e) {
|
||||||
|
needsSave = true;
|
||||||
|
props = new SortedProperties();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (fis != null) fis.close();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
props.cfgBlankRowBetweenSections = cfgSeparateSections;
|
||||||
|
props.cfgBlankRowBeforeComment = cfgNewlineBeforeComments;
|
||||||
|
|
||||||
|
final ArrayList<String> keyList = new ArrayList<String>();
|
||||||
|
|
||||||
|
// rename keys
|
||||||
|
for (final Entry<String, String> entry : renameTable.entrySet()) {
|
||||||
|
if (props.getProperty(entry.getKey()) == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
props.setProperty(entry.getValue(), props.getProperty(entry.getKey()));
|
||||||
|
props.remove(entry.getKey());
|
||||||
|
needsSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the override values into the freshly loaded properties file
|
||||||
|
for (final Entry<String, String> entry : overrideValues.entrySet()) {
|
||||||
|
props.setProperty(entry.getKey(), entry.getValue());
|
||||||
|
needsSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate entries one by one, replace with default when needed
|
||||||
|
for (final Property<?> entry : entries.values()) {
|
||||||
|
keyList.add(entry.key);
|
||||||
|
|
||||||
|
final String propOrig = props.getProperty(entry.key);
|
||||||
|
|
||||||
|
entry.parse(propOrig);
|
||||||
|
|
||||||
|
if (!entry.toString().equals(propOrig)) {
|
||||||
|
needsSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.comment != null) {
|
||||||
|
props.setKeyComment(entry.key, entry.comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propOrig == null || !entry.toString().equals(propOrig)) {
|
||||||
|
props.setProperty(entry.key, entry.toString());
|
||||||
|
|
||||||
|
needsSave = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removed unused props
|
||||||
|
for (final String propname : props.keySet().toArray(new String[props.size()])) {
|
||||||
|
if (!keyList.contains(propname)) {
|
||||||
|
props.remove(propname);
|
||||||
|
needsSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// save if needed
|
||||||
|
if (needsSave || cfgForceSave) {
|
||||||
|
try {
|
||||||
|
props.store(new FileOutputStream(file), fileComment);
|
||||||
|
} catch (final IOException ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideValues.clear();
|
||||||
|
renameTable.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param newlineBeforeComments put newline before comments
|
||||||
|
*/
|
||||||
|
public void cfgNewlineBeforeComments(boolean newlineBeforeComments)
|
||||||
|
{
|
||||||
|
this.cfgNewlineBeforeComments = newlineBeforeComments;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param separateSections do separate sections by newline
|
||||||
|
*/
|
||||||
|
public void cfgSeparateSections(boolean separateSections)
|
||||||
|
{
|
||||||
|
this.cfgSeparateSections = separateSections;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param forceSave save even if unchanged.
|
||||||
|
*/
|
||||||
|
public void cfgForceSave(boolean forceSave)
|
||||||
|
{
|
||||||
|
this.cfgForceSave = forceSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a property entry (rarely used)
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @return the entry
|
||||||
|
*/
|
||||||
|
private Property<?> get(String n)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return entries.get(n);
|
||||||
|
} catch (final Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get boolean property
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @return the boolean found, or false
|
||||||
|
*/
|
||||||
|
public Boolean getBoolean(String n)
|
||||||
|
{
|
||||||
|
return Convert.toBoolean(get(n).value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get numeric property
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @return the int found, or null
|
||||||
|
*/
|
||||||
|
public Integer getInteger(String n)
|
||||||
|
{
|
||||||
|
return Convert.toInteger(get(n).value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get numeric property as double
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @return the double found, or null
|
||||||
|
*/
|
||||||
|
public Double getDouble(String n)
|
||||||
|
{
|
||||||
|
return Convert.toDouble(get(n).value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string property
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @return the string found, or null
|
||||||
|
*/
|
||||||
|
public String getString(String n)
|
||||||
|
{
|
||||||
|
return Convert.toString(get(n).value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a boolean property
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @param d default value
|
||||||
|
* @param comment the in-file comment
|
||||||
|
*/
|
||||||
|
public void putBoolean(String n, boolean d, String comment)
|
||||||
|
{
|
||||||
|
entries.put(n, new BooleanProperty(n, d, comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a numeric property (double)
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @param d default value
|
||||||
|
* @param comment the in-file comment
|
||||||
|
*/
|
||||||
|
public void putDouble(String n, double d, String comment)
|
||||||
|
{
|
||||||
|
entries.put(n, new DoubleProperty(n, d, comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a numeric property
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @param d default value
|
||||||
|
* @param comment the in-file comment
|
||||||
|
*/
|
||||||
|
public void putInteger(String n, int d, String comment)
|
||||||
|
{
|
||||||
|
entries.put(n, new IntegerProperty(n, d, comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a string property
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @param d default value
|
||||||
|
* @param comment the in-file comment
|
||||||
|
*/
|
||||||
|
public void putString(String n, String d, String comment)
|
||||||
|
{
|
||||||
|
entries.put(n, new StringProperty(n, d, comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a coord property
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @param d default value
|
||||||
|
* @param comment the in-file comment
|
||||||
|
*/
|
||||||
|
public void putCoord(String n, Coord d, String comment)
|
||||||
|
{
|
||||||
|
entries.put(n, new CoordProperty(n, d, comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a range property
|
||||||
|
*
|
||||||
|
* @param n key
|
||||||
|
* @param d default value
|
||||||
|
* @param comment the in-file comment
|
||||||
|
*/
|
||||||
|
public void putRange(String n, Range d, String comment)
|
||||||
|
{
|
||||||
|
entries.put(n, new RangeProperty(n, d, comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename key before doing "apply"; value is preserved
|
||||||
|
*
|
||||||
|
* @param oldKey old key
|
||||||
|
* @param newKey new key
|
||||||
|
*/
|
||||||
|
public void renameKey(String oldKey, String newKey)
|
||||||
|
{
|
||||||
|
renameTable.put(oldKey, newKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set value saved to certain key; use to save runtime-changed configuration
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* @param key key
|
||||||
|
* @param value the saved value
|
||||||
|
*/
|
||||||
|
public void setValue(String key, Object value)
|
||||||
|
{
|
||||||
|
overrideValues.put(key, value.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-1
@@ -1,4 +1,4 @@
|
|||||||
package mightypork.utils.files;
|
package mightypork.utils.config;
|
||||||
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -9,6 +9,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import mightypork.utils.files.FileUtils;
|
||||||
import mightypork.utils.logging.Log;
|
import mightypork.utils.logging.Log;
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
package mightypork.utils.config;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties stored in file, alphabetically sorted.<br>
|
||||||
|
* Uses UTF-8 encoding and each property can have it's own comment.
|
||||||
|
*
|
||||||
|
* @author MightyPork
|
||||||
|
*/
|
||||||
|
public class SortedProperties extends java.util.Properties {
|
||||||
|
|
||||||
|
/** Option: put empty line before each comment. */
|
||||||
|
public boolean cfgBlankRowBeforeComment = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option: Separate sections by newline<br>
|
||||||
|
* Section = string before first dot in key.
|
||||||
|
*/
|
||||||
|
public boolean cfgBlankRowBetweenSections = true;
|
||||||
|
|
||||||
|
/** Comments for individual keys */
|
||||||
|
private final Hashtable<String, String> keyComments = new Hashtable<String, String>();
|
||||||
|
|
||||||
|
|
||||||
|
private static void writeComments(BufferedWriter bw, String comm) throws IOException
|
||||||
|
{
|
||||||
|
final String comments = comm.replace("\n\n", "\n \n");
|
||||||
|
|
||||||
|
final int len = comments.length();
|
||||||
|
int current = 0;
|
||||||
|
int last = 0;
|
||||||
|
final char[] uu = new char[6];
|
||||||
|
uu[0] = '\\';
|
||||||
|
uu[1] = 'u';
|
||||||
|
while (current < len) {
|
||||||
|
final char c = comments.charAt(current);
|
||||||
|
if (c > '\u00ff' || c == '\n' || c == '\r') {
|
||||||
|
if (last != current) {
|
||||||
|
bw.write("# " + comments.substring(last, current));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c > '\u00ff') {
|
||||||
|
uu[2] = hexDigit(c, 12);
|
||||||
|
uu[3] = hexDigit(c, 8);
|
||||||
|
uu[4] = hexDigit(c, 4);
|
||||||
|
uu[5] = hexDigit(c, 0);
|
||||||
|
bw.write(new String(uu));
|
||||||
|
} else {
|
||||||
|
bw.newLine();
|
||||||
|
if (c == '\r' && current != len - 1 && comments.charAt(current + 1) == '\n') {
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last = current + 1;
|
||||||
|
}
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
if (last != current) {
|
||||||
|
bw.write("# " + comments.substring(last, current));
|
||||||
|
}
|
||||||
|
|
||||||
|
bw.newLine();
|
||||||
|
bw.newLine();
|
||||||
|
bw.newLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
@Override
|
||||||
|
public synchronized Enumeration keys()
|
||||||
|
{
|
||||||
|
final Enumeration keysEnum = super.keys();
|
||||||
|
final Vector keyList = new Vector();
|
||||||
|
while (keysEnum.hasMoreElements()) {
|
||||||
|
keyList.add(keysEnum.nextElement());
|
||||||
|
}
|
||||||
|
Collections.sort(keyList); //sort!
|
||||||
|
return keyList.elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode)
|
||||||
|
{
|
||||||
|
final int len = theString.length();
|
||||||
|
int bufLen = len * 2;
|
||||||
|
if (bufLen < 0) {
|
||||||
|
bufLen = Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
final StringBuffer result = new StringBuffer(bufLen);
|
||||||
|
|
||||||
|
for (int x = 0; x < len; x++) {
|
||||||
|
final char ch = theString.charAt(x);
|
||||||
|
|
||||||
|
// Handle common case first, selecting largest block that
|
||||||
|
// avoids the specials below
|
||||||
|
if ((ch > 61) && (ch < 127)) {
|
||||||
|
if (ch == '\\') {
|
||||||
|
result.append('\\');
|
||||||
|
result.append('\\');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.append(ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case ' ':
|
||||||
|
if (x == 0 || escapeSpace) {
|
||||||
|
result.append('\\');
|
||||||
|
}
|
||||||
|
result.append(' ');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\t':
|
||||||
|
result.append('\\');
|
||||||
|
result.append('t');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
result.append('\\');
|
||||||
|
result.append('n');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\r':
|
||||||
|
result.append('\\');
|
||||||
|
result.append('r');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\f':
|
||||||
|
result.append('\\');
|
||||||
|
result.append('f');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '=': // Fall through
|
||||||
|
case ':': // Fall through
|
||||||
|
case '#': // Fall through
|
||||||
|
case '!':
|
||||||
|
result.append('\\');
|
||||||
|
result.append(ch);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (((ch < 0x0020) || (ch > 0x007e)) & escapeUnicode) {
|
||||||
|
result.append('\\');
|
||||||
|
result.append('u');
|
||||||
|
result.append(hexDigit(ch, 12));
|
||||||
|
result.append(hexDigit(ch, 8));
|
||||||
|
result.append(hexDigit(ch, 4));
|
||||||
|
result.append(hexDigit(ch, 0));
|
||||||
|
} else {
|
||||||
|
result.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set additional comment to a key
|
||||||
|
*
|
||||||
|
* @param key key for comment
|
||||||
|
* @param comment the comment
|
||||||
|
*/
|
||||||
|
public void setKeyComment(String key, String comment)
|
||||||
|
{
|
||||||
|
keyComments.put(key, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Override
|
||||||
|
public void store(OutputStream out, String comments) throws IOException
|
||||||
|
{
|
||||||
|
final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
|
||||||
|
|
||||||
|
final boolean escUnicode = false;
|
||||||
|
boolean firstEntry = true;
|
||||||
|
String lastSectionBeginning = "";
|
||||||
|
|
||||||
|
if (comments != null) {
|
||||||
|
writeComments(bw, comments);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
for (final Enumeration e = keys(); e.hasMoreElements();) {
|
||||||
|
boolean wasNewLine = false;
|
||||||
|
|
||||||
|
String key = (String) e.nextElement();
|
||||||
|
String val = (String) get(key);
|
||||||
|
key = saveConvert(key, true, escUnicode);
|
||||||
|
val = saveConvert(val, false, escUnicode);
|
||||||
|
|
||||||
|
if (cfgBlankRowBetweenSections && !lastSectionBeginning.equals(key.split("[.]")[0])) {
|
||||||
|
if (!firstEntry) {
|
||||||
|
bw.newLine();
|
||||||
|
bw.newLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
wasNewLine = true;
|
||||||
|
lastSectionBeginning = key.split("[.]")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyComments.containsKey(key)) {
|
||||||
|
String cm = keyComments.get(key);
|
||||||
|
cm = cm.replace("\r", "\n");
|
||||||
|
cm = cm.replace("\r\n", "\n");
|
||||||
|
cm = cm.replace("\n\n", "\n \n");
|
||||||
|
|
||||||
|
final String[] cmlines = cm.split("\n");
|
||||||
|
|
||||||
|
if (!wasNewLine && !firstEntry && cfgBlankRowBeforeComment) {
|
||||||
|
bw.newLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final String cmline : cmlines) {
|
||||||
|
bw.write("# " + cmline);
|
||||||
|
bw.newLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bw.write(key + " = " + val);
|
||||||
|
bw.newLine();
|
||||||
|
|
||||||
|
firstEntry = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bw.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String escapifyStr(String str)
|
||||||
|
{
|
||||||
|
final StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
final int len = str.length();
|
||||||
|
for (int x = 0; x < len; x++) {
|
||||||
|
final char ch = str.charAt(x);
|
||||||
|
if (ch <= 0x007e) {
|
||||||
|
result.append(ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append('\\');
|
||||||
|
result.append('u');
|
||||||
|
result.append(hexDigit(ch, 12));
|
||||||
|
result.append(hexDigit(ch, 8));
|
||||||
|
result.append(hexDigit(ch, 4));
|
||||||
|
result.append(hexDigit(ch, 0));
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static char hexDigit(char ch, int offset)
|
||||||
|
{
|
||||||
|
final int val = (ch >> offset) & 0xF;
|
||||||
|
if (val <= 9) {
|
||||||
|
return (char) ('0' + val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char) ('A' + val - 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(InputStream is) throws IOException
|
||||||
|
{
|
||||||
|
load(is, "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void load(InputStream is, String encoding) throws IOException
|
||||||
|
{
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
final InputStreamReader isr = new InputStreamReader(is, encoding);
|
||||||
|
while (true) {
|
||||||
|
final int temp = isr.read();
|
||||||
|
if (temp < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final char c = (char) temp;
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String read = sb.toString();
|
||||||
|
|
||||||
|
final String inputString = escapifyStr(read);
|
||||||
|
final byte[] bs = inputString.getBytes("ISO-8859-1");
|
||||||
|
final ByteArrayInputStream bais = new ByteArrayInputStream(bs);
|
||||||
|
|
||||||
|
super.load(bais);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,963 +0,0 @@
|
|||||||
package mightypork.utils.files;
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import mightypork.utils.math.Calc;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property manager with advanced formatting and value checking.<br>
|
|
||||||
* Methods starting with put are for filling. Most of the others are shortcuts
|
|
||||||
* to getters.
|
|
||||||
*
|
|
||||||
* @author MightyPork
|
|
||||||
*/
|
|
||||||
public class PropertyManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Properties stored in file, alphabetically sorted.<br>
|
|
||||||
* Property file is much cleaner than the normal java.util.Properties,
|
|
||||||
* newlines can be inserted to separate categories, and individual keys can
|
|
||||||
* have their own inline comments.
|
|
||||||
*
|
|
||||||
* @author MightyPork
|
|
||||||
*/
|
|
||||||
private static class SortedProperties extends Properties {
|
|
||||||
|
|
||||||
/** A table of hex digits */
|
|
||||||
private static final char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this is here because the original method is private.
|
|
||||||
*
|
|
||||||
* @param nibble
|
|
||||||
* @return hex char.
|
|
||||||
*/
|
|
||||||
private static char toHex(int nibble)
|
|
||||||
{
|
|
||||||
return hexChars[(nibble & 0xF)];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void writeComments(BufferedWriter bw, String comm) throws IOException
|
|
||||||
{
|
|
||||||
final String comments = comm.replace("\n\n", "\n \n");
|
|
||||||
|
|
||||||
final int len = comments.length();
|
|
||||||
int current = 0;
|
|
||||||
int last = 0;
|
|
||||||
final char[] uu = new char[6];
|
|
||||||
uu[0] = '\\';
|
|
||||||
uu[1] = 'u';
|
|
||||||
while (current < len) {
|
|
||||||
final char c = comments.charAt(current);
|
|
||||||
if (c > '\u00ff' || c == '\n' || c == '\r') {
|
|
||||||
if (last != current) {
|
|
||||||
bw.write("# " + comments.substring(last, current));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c > '\u00ff') {
|
|
||||||
uu[2] = toHex((c >> 12) & 0xf);
|
|
||||||
uu[3] = toHex((c >> 8) & 0xf);
|
|
||||||
uu[4] = toHex((c >> 4) & 0xf);
|
|
||||||
uu[5] = toHex(c & 0xf);
|
|
||||||
bw.write(new String(uu));
|
|
||||||
} else {
|
|
||||||
bw.newLine();
|
|
||||||
if (c == '\r' && current != len - 1 && comments.charAt(current + 1) == '\n') {
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last = current + 1;
|
|
||||||
}
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
if (last != current) {
|
|
||||||
bw.write("# " + comments.substring(last, current));
|
|
||||||
}
|
|
||||||
bw.newLine();
|
|
||||||
bw.newLine();
|
|
||||||
bw.newLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Option: put empty line before each comment. */
|
|
||||||
public boolean cfgEmptyLineBeforeComment = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Option: Separate sections by newline<br>
|
|
||||||
* Section = string before first dot in key.
|
|
||||||
*/
|
|
||||||
public boolean cfgSeparateSectionsByEmptyLine = true;
|
|
||||||
|
|
||||||
private boolean firstEntry = true;
|
|
||||||
|
|
||||||
/** Comments for individual keys */
|
|
||||||
private final Hashtable<String, String> keyComments = new Hashtable<String, String>();
|
|
||||||
|
|
||||||
private String lastSectionBeginning = "";
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
||||||
@Override
|
|
||||||
public synchronized Enumeration keys()
|
|
||||||
{
|
|
||||||
final Enumeration keysEnum = super.keys();
|
|
||||||
final Vector keyList = new Vector();
|
|
||||||
while (keysEnum.hasMoreElements()) {
|
|
||||||
keyList.add(keysEnum.nextElement());
|
|
||||||
}
|
|
||||||
Collections.sort(keyList);
|
|
||||||
return keyList.elements();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode)
|
|
||||||
{
|
|
||||||
final int len = theString.length();
|
|
||||||
int bufLen = len * 2;
|
|
||||||
if (bufLen < 0) {
|
|
||||||
bufLen = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
final StringBuffer outBuffer = new StringBuffer(bufLen);
|
|
||||||
|
|
||||||
for (int x = 0; x < len; x++) {
|
|
||||||
final char aChar = theString.charAt(x);
|
|
||||||
|
|
||||||
// Handle common case first, selecting largest block that
|
|
||||||
// avoids the specials below
|
|
||||||
if ((aChar > 61) && (aChar < 127)) {
|
|
||||||
if (aChar == '\\') {
|
|
||||||
outBuffer.append('\\');
|
|
||||||
outBuffer.append('\\');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
outBuffer.append(aChar);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (aChar) {
|
|
||||||
case ' ':
|
|
||||||
if (x == 0 || escapeSpace) {
|
|
||||||
outBuffer.append('\\');
|
|
||||||
}
|
|
||||||
outBuffer.append(' ');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\t':
|
|
||||||
outBuffer.append('\\');
|
|
||||||
outBuffer.append('t');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\n':
|
|
||||||
outBuffer.append('\\');
|
|
||||||
outBuffer.append('n');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\r':
|
|
||||||
outBuffer.append('\\');
|
|
||||||
outBuffer.append('r');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\f':
|
|
||||||
outBuffer.append('\\');
|
|
||||||
outBuffer.append('f');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '=': // Fall through
|
|
||||||
case ':': // Fall through
|
|
||||||
case '#': // Fall through
|
|
||||||
case '!':
|
|
||||||
outBuffer.append('\\');
|
|
||||||
outBuffer.append(aChar);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) {
|
|
||||||
outBuffer.append('\\');
|
|
||||||
outBuffer.append('u');
|
|
||||||
outBuffer.append(toHex((aChar >> 12) & 0xF));
|
|
||||||
outBuffer.append(toHex((aChar >> 8) & 0xF));
|
|
||||||
outBuffer.append(toHex((aChar >> 4) & 0xF));
|
|
||||||
outBuffer.append(toHex(aChar & 0xF));
|
|
||||||
} else {
|
|
||||||
outBuffer.append(aChar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return outBuffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set additional comment to a key
|
|
||||||
*
|
|
||||||
* @param key key for comment
|
|
||||||
* @param comment the comment
|
|
||||||
*/
|
|
||||||
public void setKeyComment(String key, String comment)
|
|
||||||
{
|
|
||||||
keyComments.put(key, comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
@Override
|
|
||||||
public void store(OutputStream out, String comments) throws IOException
|
|
||||||
{
|
|
||||||
final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
|
|
||||||
|
|
||||||
final boolean escUnicode = false;
|
|
||||||
|
|
||||||
if (comments != null) {
|
|
||||||
writeComments(bw, comments);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (this) {
|
|
||||||
for (final Enumeration e = keys(); e.hasMoreElements();) {
|
|
||||||
boolean wasNewLine = false;
|
|
||||||
|
|
||||||
String key = (String) e.nextElement();
|
|
||||||
String val = (String) get(key);
|
|
||||||
key = saveConvert(key, true, escUnicode);
|
|
||||||
val = saveConvert(val, false, escUnicode);
|
|
||||||
|
|
||||||
if (cfgSeparateSectionsByEmptyLine && !lastSectionBeginning.equals(key.split("[.]")[0])) {
|
|
||||||
if (!firstEntry) {
|
|
||||||
bw.newLine();
|
|
||||||
bw.newLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
wasNewLine = true;
|
|
||||||
lastSectionBeginning = key.split("[.]")[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyComments.containsKey(key)) {
|
|
||||||
String cm = keyComments.get(key);
|
|
||||||
cm = cm.replace("\r", "\n");
|
|
||||||
cm = cm.replace("\r\n", "\n");
|
|
||||||
cm = cm.replace("\n\n", "\n \n");
|
|
||||||
|
|
||||||
final String[] cmlines = cm.split("\n");
|
|
||||||
|
|
||||||
if (!wasNewLine && !firstEntry && cfgEmptyLineBeforeComment) {
|
|
||||||
bw.newLine();
|
|
||||||
}
|
|
||||||
for (final String cmline : cmlines) {
|
|
||||||
bw.write("# " + cmline);
|
|
||||||
bw.newLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bw.write(key + " = " + val);
|
|
||||||
bw.newLine();
|
|
||||||
|
|
||||||
firstEntry = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bw.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class which loads Properties from UTF-8 file (Properties use
|
|
||||||
* "ISO-8859-1" by default)
|
|
||||||
*
|
|
||||||
* @author Itay Maman
|
|
||||||
*/
|
|
||||||
private static class PropertiesLoader {
|
|
||||||
|
|
||||||
private static String escapifyStr(String str)
|
|
||||||
{
|
|
||||||
final StringBuilder result = new StringBuilder();
|
|
||||||
|
|
||||||
final int len = str.length();
|
|
||||||
for (int x = 0; x < len; x++) {
|
|
||||||
final char ch = str.charAt(x);
|
|
||||||
if (ch <= 0x007e) {
|
|
||||||
result.append(ch);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.append('\\');
|
|
||||||
result.append('u');
|
|
||||||
result.append(hexDigit(ch, 12));
|
|
||||||
result.append(hexDigit(ch, 8));
|
|
||||||
result.append(hexDigit(ch, 4));
|
|
||||||
result.append(hexDigit(ch, 0));
|
|
||||||
}
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static char hexDigit(char ch, int offset)
|
|
||||||
{
|
|
||||||
final int val = (ch >> offset) & 0xF;
|
|
||||||
if (val <= 9) {
|
|
||||||
return (char) ('0' + val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (char) ('A' + val - 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static SortedProperties loadProperties(SortedProperties props, InputStream is) throws IOException
|
|
||||||
{
|
|
||||||
return loadProperties(props, is, "utf-8");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static SortedProperties loadProperties(SortedProperties props, InputStream is, String encoding) throws IOException
|
|
||||||
{
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
final InputStreamReader isr = new InputStreamReader(is, encoding);
|
|
||||||
while (true) {
|
|
||||||
final int temp = isr.read();
|
|
||||||
if (temp < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
final char c = (char) temp;
|
|
||||||
sb.append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String read = sb.toString();
|
|
||||||
|
|
||||||
final String inputString = escapifyStr(read);
|
|
||||||
final byte[] bs = inputString.getBytes("ISO-8859-1");
|
|
||||||
final ByteArrayInputStream bais = new ByteArrayInputStream(bs);
|
|
||||||
|
|
||||||
final SortedProperties ps = props;
|
|
||||||
ps.load(bais);
|
|
||||||
return ps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property entry in Property manager.
|
|
||||||
*
|
|
||||||
* @author MightyPork
|
|
||||||
*/
|
|
||||||
private class Property {
|
|
||||||
|
|
||||||
public String entryComment;
|
|
||||||
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
public boolean bool = false;
|
|
||||||
public boolean defbool = false;
|
|
||||||
|
|
||||||
public double num = -1;
|
|
||||||
public double defnum = -1;
|
|
||||||
|
|
||||||
public String defstr = "";
|
|
||||||
public String str = "";
|
|
||||||
|
|
||||||
public PropertyType type;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property
|
|
||||||
*
|
|
||||||
* @param key key
|
|
||||||
* @param default_value default value
|
|
||||||
* @param entry_type type
|
|
||||||
* @param entry_comment entry comment
|
|
||||||
*/
|
|
||||||
public Property(String key, boolean default_value, PropertyType entry_type, String entry_comment) {
|
|
||||||
name = key;
|
|
||||||
defbool = default_value;
|
|
||||||
type = entry_type;
|
|
||||||
entryComment = entry_comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property entry
|
|
||||||
*
|
|
||||||
* @param key property key
|
|
||||||
* @param default_value default value
|
|
||||||
* @param entry_type property type from enum
|
|
||||||
* @param entry_comment property comment or null
|
|
||||||
*/
|
|
||||||
public Property(String key, double default_value, PropertyType entry_type, String entry_comment) {
|
|
||||||
name = key;
|
|
||||||
defnum = default_value;
|
|
||||||
type = entry_type;
|
|
||||||
entryComment = entry_comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property
|
|
||||||
*
|
|
||||||
* @param key key
|
|
||||||
* @param default_value default value
|
|
||||||
* @param entry_type type
|
|
||||||
* @param entry_comment entry comment
|
|
||||||
*/
|
|
||||||
public Property(String key, String default_value, PropertyType entry_type, String entry_comment) {
|
|
||||||
name = key;
|
|
||||||
defstr = default_value;
|
|
||||||
type = entry_type;
|
|
||||||
entryComment = entry_comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get boolean
|
|
||||||
*
|
|
||||||
* @return the boolean
|
|
||||||
*/
|
|
||||||
public boolean getBoolean()
|
|
||||||
{
|
|
||||||
return bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get number
|
|
||||||
*
|
|
||||||
* @return the number
|
|
||||||
*/
|
|
||||||
public int getInteger()
|
|
||||||
{
|
|
||||||
return (int) Math.round(num);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get number as double
|
|
||||||
*
|
|
||||||
* @return the number
|
|
||||||
*/
|
|
||||||
public double getDouble()
|
|
||||||
{
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get string
|
|
||||||
*
|
|
||||||
* @return the string
|
|
||||||
*/
|
|
||||||
public String getString()
|
|
||||||
{
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is this entry valid?
|
|
||||||
*
|
|
||||||
* @return is valid
|
|
||||||
*/
|
|
||||||
public boolean isValid()
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case STRING:
|
|
||||||
return str != null;
|
|
||||||
|
|
||||||
case BOOLEAN:
|
|
||||||
case INT:
|
|
||||||
case DOUBLE:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load property value from a file
|
|
||||||
*
|
|
||||||
* @param string the string loaded
|
|
||||||
* @return was OK
|
|
||||||
*/
|
|
||||||
public boolean parse(String string)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case INT:
|
|
||||||
|
|
||||||
if (string == null) {
|
|
||||||
num = defnum;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
num = Integer.parseInt(string.trim());
|
|
||||||
} catch (final NumberFormatException e) {
|
|
||||||
num = defnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DOUBLE:
|
|
||||||
|
|
||||||
if (string == null) {
|
|
||||||
num = defnum;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
num = Double.parseDouble(string.trim());
|
|
||||||
} catch (final NumberFormatException e) {
|
|
||||||
num = defnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STRING:
|
|
||||||
|
|
||||||
if (string == null) {
|
|
||||||
str = defstr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
str = string;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BOOLEAN:
|
|
||||||
|
|
||||||
if (string == null) {
|
|
||||||
bool = defbool;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String string2 = string.toLowerCase();
|
|
||||||
bool = string2.equals("yes") || string2.equals("true") || string2.equals("on") || string2.equals("enabled") || string2.equals("enable");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* prepare the contents for insertion into Properties
|
|
||||||
*
|
|
||||||
* @return the string prepared, or null if type is invalid
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
if (!isValid()) {
|
|
||||||
if (type == PropertyType.INT || type == PropertyType.DOUBLE) {
|
|
||||||
num = defnum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case INT:
|
|
||||||
return Integer.toString((int) num);
|
|
||||||
|
|
||||||
case DOUBLE:
|
|
||||||
return Calc.floatToString((float) num);
|
|
||||||
|
|
||||||
case STRING:
|
|
||||||
return str;
|
|
||||||
|
|
||||||
case BOOLEAN:
|
|
||||||
return bool ? "True" : "False";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If this entry is not valid, change it to the dafault value.
|
|
||||||
*/
|
|
||||||
public void validate()
|
|
||||||
{
|
|
||||||
if (!isValid()) {
|
|
||||||
if (type == PropertyType.STRING) {
|
|
||||||
str = defstr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property types
|
|
||||||
*/
|
|
||||||
private enum PropertyType
|
|
||||||
{
|
|
||||||
BOOLEAN, INT, STRING, DOUBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** put newline before entry comments */
|
|
||||||
private boolean cfgNewlineBeforeComments = true;
|
|
||||||
/** Disable entry validation */
|
|
||||||
private boolean cfgNoValidate = true;
|
|
||||||
/** Put newline between sections. */
|
|
||||||
private boolean cfgSeparateSections = true;
|
|
||||||
/** Force save, even if nothing changed (used to save changed comments) */
|
|
||||||
private boolean cfgForceSave;
|
|
||||||
|
|
||||||
private final File file;
|
|
||||||
private String fileComment = "";
|
|
||||||
|
|
||||||
private final TreeMap<String, Property> entries;
|
|
||||||
private final TreeMap<String, String> keyRename;
|
|
||||||
private final TreeMap<String, String> setValues;
|
|
||||||
private SortedProperties pr = new SortedProperties();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create property manager from file path and an initial comment.
|
|
||||||
*
|
|
||||||
* @param file file with the props
|
|
||||||
* @param comment the initial comment. Use \n in it if you want.
|
|
||||||
*/
|
|
||||||
public PropertyManager(File file, String comment) {
|
|
||||||
this.file = file;
|
|
||||||
this.entries = new TreeMap<String, Property>();
|
|
||||||
this.setValues = new TreeMap<String, String>();
|
|
||||||
this.keyRename = new TreeMap<String, String>();
|
|
||||||
this.fileComment = comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load, fix and write to file.
|
|
||||||
*/
|
|
||||||
public void apply()
|
|
||||||
{
|
|
||||||
boolean needsSave = false;
|
|
||||||
FileInputStream fis = null;
|
|
||||||
try {
|
|
||||||
new File(file.getParent()).mkdirs();
|
|
||||||
fis = new FileInputStream(file);
|
|
||||||
pr = PropertiesLoader.loadProperties(pr, fis);
|
|
||||||
|
|
||||||
} catch (final IOException e) {
|
|
||||||
needsSave = true;
|
|
||||||
pr = new SortedProperties();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (fis != null) fis.close();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.cfgSeparateSectionsByEmptyLine = cfgSeparateSections;
|
|
||||||
pr.cfgEmptyLineBeforeComment = cfgNewlineBeforeComments;
|
|
||||||
|
|
||||||
final ArrayList<String> keyList = new ArrayList<String>();
|
|
||||||
|
|
||||||
// rename keys
|
|
||||||
for (final Entry<String, String> entry : keyRename.entrySet()) {
|
|
||||||
if (pr.getProperty(entry.getKey()) == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pr.setProperty(entry.getValue(), pr.getProperty(entry.getKey()));
|
|
||||||
pr.remove(entry.getKey());
|
|
||||||
needsSave = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the override values into the freshly loaded properties file
|
|
||||||
for (final Entry<String, String> entry : setValues.entrySet()) {
|
|
||||||
pr.setProperty(entry.getKey(), entry.getValue());
|
|
||||||
needsSave = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate entries one by one, replace with default when needed
|
|
||||||
for (final Property entry : entries.values()) {
|
|
||||||
keyList.add(entry.name);
|
|
||||||
|
|
||||||
final String propOrig = pr.getProperty(entry.name);
|
|
||||||
if (!entry.parse(propOrig)) needsSave = true;
|
|
||||||
if (!cfgNoValidate) {
|
|
||||||
entry.validate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.entryComment != null) {
|
|
||||||
pr.setKeyComment(entry.name, entry.entryComment);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propOrig == null || !entry.toString().equals(propOrig)) {
|
|
||||||
pr.setProperty(entry.name, entry.toString());
|
|
||||||
|
|
||||||
needsSave = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// removed unused props
|
|
||||||
for (final String propname : pr.keySet().toArray(new String[pr.size()])) {
|
|
||||||
if (!keyList.contains(propname)) {
|
|
||||||
pr.remove(propname);
|
|
||||||
needsSave = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// save if needed
|
|
||||||
if (needsSave || cfgForceSave) {
|
|
||||||
try {
|
|
||||||
pr.store(new FileOutputStream(file), fileComment);
|
|
||||||
} catch (final IOException ioe) {
|
|
||||||
ioe.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setValues.clear();
|
|
||||||
keyRename.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param newlineBeforeComments put newline before comments
|
|
||||||
*/
|
|
||||||
public void cfgNewlineBeforeComments(boolean newlineBeforeComments)
|
|
||||||
{
|
|
||||||
this.cfgNewlineBeforeComments = newlineBeforeComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param separateSections do separate sections by newline
|
|
||||||
*/
|
|
||||||
public void cfgSeparateSections(boolean separateSections)
|
|
||||||
{
|
|
||||||
this.cfgSeparateSections = separateSections;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param forceSave save even if unchanged.
|
|
||||||
*/
|
|
||||||
public void cfgForceSave(boolean forceSave)
|
|
||||||
{
|
|
||||||
this.cfgForceSave = forceSave;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param validate enable validation
|
|
||||||
*/
|
|
||||||
public void enableValidation(boolean validate)
|
|
||||||
{
|
|
||||||
this.cfgNoValidate = !validate;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a property entry (rarely used)
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @return the entry
|
|
||||||
*/
|
|
||||||
private Property get(String n)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return entries.get(n);
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get boolean property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @return the boolean found, or false
|
|
||||||
*/
|
|
||||||
public Boolean getBoolean(String n)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return entries.get(n).getBoolean();
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get numeric property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @return the int found, or null
|
|
||||||
*/
|
|
||||||
public Integer getInteger(String n)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return get(n).getInteger();
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get numeric property as double
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @return the double found, or null
|
|
||||||
*/
|
|
||||||
public Double getDouble(String n)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return get(n).getDouble();
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
return -1D;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get string property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @return the string found, or null
|
|
||||||
*/
|
|
||||||
public String getString(String n)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return get(n).getString();
|
|
||||||
} catch (final Throwable t) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a boolean property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @param d default value
|
|
||||||
*/
|
|
||||||
public void putBoolean(String n, boolean d)
|
|
||||||
{
|
|
||||||
entries.put(n, new Property(n, d, PropertyType.BOOLEAN, null));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a boolean property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @param d default value
|
|
||||||
* @param comment the in-file comment
|
|
||||||
*/
|
|
||||||
public void putBoolean(String n, boolean d, String comment)
|
|
||||||
{
|
|
||||||
entries.put(n, new Property(n, d, PropertyType.BOOLEAN, comment));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a numeric property (double)
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @param d default value
|
|
||||||
*/
|
|
||||||
public void putDouble(String n, int d)
|
|
||||||
{
|
|
||||||
entries.put(n, new Property(n, d, PropertyType.DOUBLE, null));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a numeric property (double)
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @param d default value
|
|
||||||
* @param comment the in-file comment
|
|
||||||
*/
|
|
||||||
public void putDouble(String n, int d, String comment)
|
|
||||||
{
|
|
||||||
entries.put(n, new Property(n, d, PropertyType.DOUBLE, comment));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a numeric property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @param d default value
|
|
||||||
*/
|
|
||||||
public void putInteger(String n, int d)
|
|
||||||
{
|
|
||||||
entries.put(n, new Property(n, d, PropertyType.INT, null));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a numeric property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @param d default value
|
|
||||||
* @param comment the in-file comment
|
|
||||||
*/
|
|
||||||
public void putInteger(String n, int d, String comment)
|
|
||||||
{
|
|
||||||
entries.put(n, new Property(n, d, PropertyType.INT, comment));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a string property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @param d default value
|
|
||||||
*/
|
|
||||||
public void putString(String n, String d)
|
|
||||||
{
|
|
||||||
entries.put(n, new Property(n, d, PropertyType.STRING, null));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a string property
|
|
||||||
*
|
|
||||||
* @param n key
|
|
||||||
* @param d default value
|
|
||||||
* @param comment the in-file comment
|
|
||||||
*/
|
|
||||||
public void putString(String n, String d, String comment)
|
|
||||||
{
|
|
||||||
entries.put(n, new Property(n, d, PropertyType.STRING, comment));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rename key before doing "apply"; value is preserved
|
|
||||||
*
|
|
||||||
* @param oldKey old key
|
|
||||||
* @param newKey new key
|
|
||||||
*/
|
|
||||||
public void renameKey(String oldKey, String newKey)
|
|
||||||
{
|
|
||||||
keyRename.put(oldKey, newKey);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set value saved to certain key; use to save runtime-changed configuration
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* @param key key
|
|
||||||
* @param value the saved value
|
|
||||||
*/
|
|
||||||
public void setValue(String key, Object value)
|
|
||||||
{
|
|
||||||
setValues.put(key, value.toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+46
-35
@@ -12,7 +12,7 @@ import mightypork.utils.math.coord.Coord;
|
|||||||
*
|
*
|
||||||
* @author MightyPork
|
* @author MightyPork
|
||||||
*/
|
*/
|
||||||
public class Convertor {
|
public class Convert {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get INTEGER
|
* Get INTEGER
|
||||||
@@ -21,7 +21,7 @@ public class Convertor {
|
|||||||
* @param def default value
|
* @param def default value
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
public static int getInteger(Object o, Integer def)
|
public static int toInteger(Object o, Integer def)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (o == null) return def;
|
if (o == null) return def;
|
||||||
@@ -30,7 +30,6 @@ public class Convertor {
|
|||||||
if (o instanceof Range) return ((Range) o).randInt();
|
if (o instanceof Range) return ((Range) o).randInt();
|
||||||
if (o instanceof Boolean) return ((Boolean) o) ? 1 : 0;
|
if (o instanceof Boolean) return ((Boolean) o) ? 1 : 0;
|
||||||
} catch (final NumberFormatException e) {}
|
} catch (final NumberFormatException e) {}
|
||||||
Log.w("Cannot convert " + o + " to Integer.");
|
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +41,7 @@ public class Convertor {
|
|||||||
* @param def default value
|
* @param def default value
|
||||||
* @return double
|
* @return double
|
||||||
*/
|
*/
|
||||||
public static double getDouble(Object o, Double def)
|
public static double toDouble(Object o, Double def)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (o == null) return def;
|
if (o == null) return def;
|
||||||
@@ -51,7 +50,6 @@ public class Convertor {
|
|||||||
if (o instanceof Range) return ((Range) o).randDouble();
|
if (o instanceof Range) return ((Range) o).randDouble();
|
||||||
if (o instanceof Boolean) return ((Boolean) o) ? 1 : 0;
|
if (o instanceof Boolean) return ((Boolean) o) ? 1 : 0;
|
||||||
} catch (final NumberFormatException e) {}
|
} catch (final NumberFormatException e) {}
|
||||||
Log.w("Cannot convert " + o + " to Double.");
|
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,13 +61,12 @@ public class Convertor {
|
|||||||
* @param def default value
|
* @param def default value
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public static double getFloat(Object o, Float def)
|
public static double toFloat(Object o, Float def)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (o == null) return def;
|
if (o == null) return def;
|
||||||
if (o instanceof Number) return ((Number) o).floatValue();
|
if (o instanceof Number) return ((Number) o).floatValue();
|
||||||
} catch (final NumberFormatException e) {}
|
} catch (final NumberFormatException e) {}
|
||||||
Log.w("Cannot convert " + o + " to Float.");
|
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +78,7 @@ public class Convertor {
|
|||||||
* @param def default value
|
* @param def default value
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static boolean getBoolean(Object o, Boolean def)
|
public static boolean toBoolean(Object o, Boolean def)
|
||||||
{
|
{
|
||||||
if (o == null) return def;
|
if (o == null) return def;
|
||||||
|
|
||||||
@@ -107,7 +104,6 @@ public class Convertor {
|
|||||||
|
|
||||||
if (o instanceof Boolean) return ((Boolean) o).booleanValue();
|
if (o instanceof Boolean) return ((Boolean) o).booleanValue();
|
||||||
if (o instanceof Number) return ((Number) o).intValue() != 0;
|
if (o instanceof Number) return ((Number) o).intValue() != 0;
|
||||||
Log.w("Cannot convert " + o + " to Boolean.");
|
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,11 +115,29 @@ public class Convertor {
|
|||||||
* @param def default value
|
* @param def default value
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
public static String getString(Object o, String def)
|
public static String toString(Object o, String def)
|
||||||
{
|
{
|
||||||
if (o == null) return def;
|
if (o == null) return def;
|
||||||
if (o instanceof String) return ((String) o);
|
if (o instanceof String) return ((String) o);
|
||||||
Log.w("Cannot convert " + o + " to String.");
|
|
||||||
|
if(o instanceof Boolean) {
|
||||||
|
return (Boolean) o ? "True" : "False";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(o instanceof Coord) {
|
||||||
|
Coord c = (Coord) o;
|
||||||
|
return String.format("[%f:%f:%f]", c.x, c.y, c.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(o instanceof Range) {
|
||||||
|
Range c = (Range) o;
|
||||||
|
return String.format("%f:%f", c.getMin(), c.getMax());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(o instanceof Class<?>) {
|
||||||
|
return Log.str(o);
|
||||||
|
}
|
||||||
|
|
||||||
return o.toString();
|
return o.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +150,7 @@ public class Convertor {
|
|||||||
* @param def default value
|
* @param def default value
|
||||||
* @return AiCoord
|
* @return AiCoord
|
||||||
*/
|
*/
|
||||||
public static Coord getCoord(Object o, Coord def)
|
public static Coord toCoord(Object o, Coord def)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (o == null) return def;
|
if (o == null) return def;
|
||||||
@@ -145,8 +159,6 @@ public class Convertor {
|
|||||||
|
|
||||||
// colon to semicolon
|
// colon to semicolon
|
||||||
s = s.replace(':', ';');
|
s = s.replace(':', ';');
|
||||||
// comma to semicolon
|
|
||||||
s = s.replace(',', ';');
|
|
||||||
// remove brackets if any
|
// remove brackets if any
|
||||||
s = s.replaceAll("[\\(\\[\\{\\)\\]\\}]", "");
|
s = s.replaceAll("[\\(\\[\\{\\)\\]\\}]", "");
|
||||||
final String[] parts = s.split("[;]");
|
final String[] parts = s.split("[;]");
|
||||||
@@ -156,7 +168,6 @@ public class Convertor {
|
|||||||
} catch (final NumberFormatException e) {
|
} catch (final NumberFormatException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
Log.w("Cannot convert " + o + " to Coord.");
|
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +179,7 @@ public class Convertor {
|
|||||||
* @param def default value
|
* @param def default value
|
||||||
* @return AiCoord
|
* @return AiCoord
|
||||||
*/
|
*/
|
||||||
public static Range getRange(Object o, Range def)
|
public static Range toRange(Object o, Range def)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (o == null) return def;
|
if (o == null) return def;
|
||||||
@@ -190,8 +201,9 @@ public class Convertor {
|
|||||||
|
|
||||||
}
|
}
|
||||||
if (o instanceof Range) return (Range) o;
|
if (o instanceof Range) return (Range) o;
|
||||||
} catch (final NumberFormatException e) {}
|
} catch (final NumberFormatException e) {
|
||||||
Log.w("Cannot convert " + o + " to Range.");
|
// ignore
|
||||||
|
}
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,9 +214,9 @@ public class Convertor {
|
|||||||
* @param o object
|
* @param o object
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
public static int getInteger(Object o)
|
public static int toInteger(Object o)
|
||||||
{
|
{
|
||||||
return getInteger(o, 0);
|
return toInteger(o, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -214,9 +226,9 @@ public class Convertor {
|
|||||||
* @param o object
|
* @param o object
|
||||||
* @return double
|
* @return double
|
||||||
*/
|
*/
|
||||||
public static double getDouble(Object o)
|
public static double toDouble(Object o)
|
||||||
{
|
{
|
||||||
return getDouble(o, 0d);
|
return toDouble(o, 0d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -226,9 +238,9 @@ public class Convertor {
|
|||||||
* @param o object
|
* @param o object
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public static double getFloat(Object o)
|
public static double toFloat(Object o)
|
||||||
{
|
{
|
||||||
return getFloat(o, 0f);
|
return toFloat(o, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -238,9 +250,9 @@ public class Convertor {
|
|||||||
* @param o object
|
* @param o object
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static boolean getBoolean(Object o)
|
public static boolean toBoolean(Object o)
|
||||||
{
|
{
|
||||||
return getBoolean(o, false);
|
return toBoolean(o, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -250,22 +262,21 @@ public class Convertor {
|
|||||||
* @param o object
|
* @param o object
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
public static String getString(Object o)
|
public static String toString(Object o)
|
||||||
{
|
{
|
||||||
return getString(o, "");
|
return toString(o, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get AI_COORD (if special string constant is present instead, build coord
|
* Get Coord
|
||||||
* of it)
|
|
||||||
*
|
*
|
||||||
* @param o object
|
* @param o object
|
||||||
* @return AiCoord
|
* @return Coord
|
||||||
*/
|
*/
|
||||||
public static Coord getCoord(Object o)
|
public static Coord toCoord(Object o)
|
||||||
{
|
{
|
||||||
return getCoord(o, Coord.zero());
|
return toCoord(o, Coord.zero());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -275,9 +286,9 @@ public class Convertor {
|
|||||||
* @param o object
|
* @param o object
|
||||||
* @return AiCoord
|
* @return AiCoord
|
||||||
*/
|
*/
|
||||||
public static Range getRange(Object o)
|
public static Range toRange(Object o)
|
||||||
{
|
{
|
||||||
return getRange(o, new Range());
|
return toRange(o, new Range());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user