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
|
||||
public void shutdown()
|
||||
public final void shutdown()
|
||||
{
|
||||
app.shutdown();
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public abstract class AppSubModule extends BusNode implements AppAccess {
|
||||
|
||||
|
||||
@Override
|
||||
public void shutdown()
|
||||
public final void shutdown()
|
||||
{
|
||||
app.shutdown();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ public interface BusAccess {
|
||||
/**
|
||||
* @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 {
|
||||
|
||||
private BusAccess busAccess;
|
||||
private final BusAccess busAccess;
|
||||
|
||||
private final Set<Object> clients = new LinkedHashSet<Object>();
|
||||
private boolean listening = true;
|
||||
@@ -45,7 +45,7 @@ public abstract class BusNode implements BusAccess, DelegatingClient, Toggleable
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isListening()
|
||||
public final boolean isListening()
|
||||
{
|
||||
return listening;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public abstract class BusNode implements BusAccess, DelegatingClient, Toggleable
|
||||
|
||||
|
||||
@Override
|
||||
public EventBus bus()
|
||||
public final EventBus 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.UpdateEvent;
|
||||
import mightypork.gamecore.control.interf.NoImpl;
|
||||
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 {
|
||||
|
||||
private final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>();
|
||||
/** timer */
|
||||
private TimerDelta timer;
|
||||
private Renderable mainRenderable;
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
tick();
|
||||
beforeRender();
|
||||
|
||||
mainRenderable.render();
|
||||
|
||||
afterRender();
|
||||
|
||||
disp().endFrame();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called each frame, in rendering context.
|
||||
*/
|
||||
protected abstract void tick();
|
||||
@NoImpl
|
||||
protected void beforeRender()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
@NoImpl
|
||||
protected void afterRender()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
@@ -12,8 +12,9 @@ import mightypork.gamecore.control.interf.Updateable;
|
||||
* @author MightyPork
|
||||
*/
|
||||
// sending via queue would hog the bus
|
||||
@ImmediateEvent
|
||||
|
||||
@UnloggedEvent
|
||||
@ImmediateEvent
|
||||
public class UpdateEvent implements Event<Updateable> {
|
||||
|
||||
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 {
|
||||
|
||||
private long frames = 0;
|
||||
private long drops = 0;
|
||||
private long lastTimeMillis = System.currentTimeMillis();
|
||||
private long lastSecFPS = 0;
|
||||
private long lastSecDrop = 0;
|
||||
|
||||
|
||||
/**
|
||||
@@ -32,31 +30,10 @@ public class FpsMeter {
|
||||
{
|
||||
if (System.currentTimeMillis() - lastTimeMillis > 1000) {
|
||||
lastSecFPS = frames;
|
||||
lastSecDrop = drops;
|
||||
frames = 0;
|
||||
drops = 0;
|
||||
lastTimeMillis = System.currentTimeMillis();
|
||||
long over = System.currentTimeMillis() - lastTimeMillis - 1000;
|
||||
lastTimeMillis = System.currentTimeMillis() - over;
|
||||
}
|
||||
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;
|
||||
|
||||
|
||||
public TextRenderer(GLFont font, RGB color, Align align) {
|
||||
this(font, "MISSINGNO", color, align);
|
||||
}
|
||||
|
||||
|
||||
public TextRenderer(GLFont font, String text, RGB color, Align align) {
|
||||
this.font = new FontRenderer(font);
|
||||
this.text = text;
|
||||
@@ -52,8 +57,19 @@ public class TextRenderer extends PluggableRenderer {
|
||||
}
|
||||
|
||||
|
||||
public String getText()
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render()
|
||||
{
|
||||
render(getText());
|
||||
}
|
||||
|
||||
|
||||
public void render(String text)
|
||||
{
|
||||
final double h = getRect().getHeight();
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mightypork.gamecore.gui.screens;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import mightypork.gamecore.control.AppAccess;
|
||||
import mightypork.gamecore.control.AppSubModule;
|
||||
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.KeyBindingPool;
|
||||
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.coord.Coord;
|
||||
import mightypork.utils.math.coord.Rect;
|
||||
@@ -148,7 +147,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind
|
||||
if (!isActive()) return;
|
||||
|
||||
if (needSetupViewport) {
|
||||
DisplaySystem.setupOrtho();
|
||||
Render.setupOrtho();
|
||||
}
|
||||
|
||||
renderScreen();
|
||||
|
||||
@@ -3,16 +3,12 @@ package mightypork.gamecore.render;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import mightypork.gamecore.control.AppAccess;
|
||||
import mightypork.gamecore.control.RootBusNode;
|
||||
import mightypork.gamecore.control.bus.events.ScreenChangeEvent;
|
||||
import mightypork.gamecore.control.timing.FpsMeter;
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.math.constraints.RectConstraint;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
@@ -29,6 +25,7 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
|
||||
private DisplayMode windowDisplayMode;
|
||||
private int targetFps;
|
||||
public static boolean yAxisDown = true;
|
||||
private FpsMeter fpsMeter;
|
||||
|
||||
|
||||
public DisplaySystem(AppAccess app) {
|
||||
@@ -58,13 +55,13 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
|
||||
Display.setTitle(title);
|
||||
Display.create();
|
||||
|
||||
fpsMeter = new FpsMeter();
|
||||
|
||||
if (fullscreen) {
|
||||
switchFullscreen();
|
||||
Display.update();
|
||||
}
|
||||
|
||||
Render.init();
|
||||
|
||||
} catch (final LWJGLException 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()
|
||||
{
|
||||
glReadBuffer(GL_FRONT);
|
||||
final int width = Display.getDisplayMode().getWidth();
|
||||
final int height = Display.getDisplayMode().getHeight();
|
||||
final int bpp = 4; // Assuming a 32-bit display with a byte each for red,
|
||||
// green, blue, and alpha.
|
||||
final int bpp = 4;
|
||||
final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
@@ -177,6 +179,7 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
|
||||
|
||||
glLoadIdentity();
|
||||
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());
|
||||
}
|
||||
|
||||
public static 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()
|
||||
/**
|
||||
* @return current FPS
|
||||
*/
|
||||
public final long getFps()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
return fpsMeter.getFPS();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,32 +30,6 @@ public class Render {
|
||||
private static final Coord AXIS_Y = new Coord(0, 1, 0);
|
||||
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
|
||||
*
|
||||
@@ -491,4 +465,37 @@ public class Render {
|
||||
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)
|
||||
{
|
||||
Config.init();
|
||||
Config.save();
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import mightypork.utils.files.PropertyManager;
|
||||
import mightypork.utils.config.PropertyManager;
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public class Config {
|
||||
mgr.cfgNewlineBeforeComments(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.");
|
||||
|
||||
load(); // load what has been "put"
|
||||
|
||||
@@ -15,7 +15,7 @@ public class Const {
|
||||
public static final String TITLEBAR = APP_NAME + " v." + VERSION;
|
||||
|
||||
// AUDIO
|
||||
public static final int FPS_RENDER = 80; // max
|
||||
public static final int FPS_RENDER = 100; // max
|
||||
|
||||
// INITIAL WINDOW SIZE
|
||||
public static final int WINDOW_W = 1024;
|
||||
|
||||
@@ -11,24 +11,8 @@ import mightypork.rogue.util.Utils;
|
||||
|
||||
public class MainLoop extends GameLoop implements ActionRequest.Listener {
|
||||
|
||||
final Renderable renderable;
|
||||
|
||||
|
||||
public MainLoop(App app, Renderable masterRenderable) {
|
||||
super(app);
|
||||
|
||||
if (masterRenderable == null) {
|
||||
throw new NullPointerException("Master renderable must not be null.");
|
||||
}
|
||||
|
||||
this.renderable = masterRenderable;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void tick()
|
||||
{
|
||||
renderable.render();
|
||||
super(app, masterRenderable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import mightypork.gamecore.render.DisplaySystem;
|
||||
import mightypork.gamecore.render.DisplaySystem.Screenshot;
|
||||
import mightypork.gamecore.render.Screenshot;
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,13 @@ public class LayerBouncyBoxes extends ScreenLayer {
|
||||
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
|
||||
|
||||
text = new TextRenderer(Res.getFont("default"), "YO", RGB.YELLOW, Align.CENTER);
|
||||
text = new TextRenderer(Res.getFont("default"), RGB.YELLOW, Align.CENTER);
|
||||
text.setContext(flyingFontBox);
|
||||
|
||||
bindKeyStroke(new KeyStroke(Keys.KEY_RETURN), new Runnable() {
|
||||
@@ -87,6 +87,7 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
|
||||
final double t = 2;
|
||||
|
||||
size.fadeTo(100 + rand.nextInt(700), t / 2D);
|
||||
|
||||
xPos.fadeTo(pos.x, t);
|
||||
yPos.fadeTo(pos.y, t);
|
||||
}
|
||||
@@ -96,7 +97,7 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
|
||||
public void 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;
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import mightypork.utils.files.FileUtils;
|
||||
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
|
||||
*/
|
||||
public class Convertor {
|
||||
public class Convert {
|
||||
|
||||
/**
|
||||
* Get INTEGER
|
||||
@@ -21,7 +21,7 @@ public class Convertor {
|
||||
* @param def default value
|
||||
* @return integer
|
||||
*/
|
||||
public static int getInteger(Object o, Integer def)
|
||||
public static int toInteger(Object o, Integer def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
@@ -30,7 +30,6 @@ public class Convertor {
|
||||
if (o instanceof Range) return ((Range) o).randInt();
|
||||
if (o instanceof Boolean) return ((Boolean) o) ? 1 : 0;
|
||||
} catch (final NumberFormatException e) {}
|
||||
Log.w("Cannot convert " + o + " to Integer.");
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -42,7 +41,7 @@ public class Convertor {
|
||||
* @param def default value
|
||||
* @return double
|
||||
*/
|
||||
public static double getDouble(Object o, Double def)
|
||||
public static double toDouble(Object o, Double def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
@@ -51,7 +50,6 @@ public class Convertor {
|
||||
if (o instanceof Range) return ((Range) o).randDouble();
|
||||
if (o instanceof Boolean) return ((Boolean) o) ? 1 : 0;
|
||||
} catch (final NumberFormatException e) {}
|
||||
Log.w("Cannot convert " + o + " to Double.");
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -63,13 +61,12 @@ public class Convertor {
|
||||
* @param def default value
|
||||
* @return float
|
||||
*/
|
||||
public static double getFloat(Object o, Float def)
|
||||
public static double toFloat(Object o, Float def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
if (o instanceof Number) return ((Number) o).floatValue();
|
||||
} catch (final NumberFormatException e) {}
|
||||
Log.w("Cannot convert " + o + " to Float.");
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -81,7 +78,7 @@ public class Convertor {
|
||||
* @param def default value
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean getBoolean(Object o, Boolean def)
|
||||
public static boolean toBoolean(Object o, Boolean def)
|
||||
{
|
||||
if (o == null) return def;
|
||||
|
||||
@@ -107,7 +104,6 @@ public class Convertor {
|
||||
|
||||
if (o instanceof Boolean) return ((Boolean) o).booleanValue();
|
||||
if (o instanceof Number) return ((Number) o).intValue() != 0;
|
||||
Log.w("Cannot convert " + o + " to Boolean.");
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -119,11 +115,29 @@ public class Convertor {
|
||||
* @param def default value
|
||||
* @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 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();
|
||||
}
|
||||
|
||||
@@ -136,7 +150,7 @@ public class Convertor {
|
||||
* @param def default value
|
||||
* @return AiCoord
|
||||
*/
|
||||
public static Coord getCoord(Object o, Coord def)
|
||||
public static Coord toCoord(Object o, Coord def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
@@ -145,8 +159,6 @@ public class Convertor {
|
||||
|
||||
// colon to semicolon
|
||||
s = s.replace(':', ';');
|
||||
// comma to semicolon
|
||||
s = s.replace(',', ';');
|
||||
// remove brackets if any
|
||||
s = s.replaceAll("[\\(\\[\\{\\)\\]\\}]", "");
|
||||
final String[] parts = s.split("[;]");
|
||||
@@ -156,7 +168,6 @@ public class Convertor {
|
||||
} catch (final NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
Log.w("Cannot convert " + o + " to Coord.");
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -168,7 +179,7 @@ public class Convertor {
|
||||
* @param def default value
|
||||
* @return AiCoord
|
||||
*/
|
||||
public static Range getRange(Object o, Range def)
|
||||
public static Range toRange(Object o, Range def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
@@ -190,8 +201,9 @@ public class Convertor {
|
||||
|
||||
}
|
||||
if (o instanceof Range) return (Range) o;
|
||||
} catch (final NumberFormatException e) {}
|
||||
Log.w("Cannot convert " + o + " to Range.");
|
||||
} catch (final NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -202,9 +214,9 @@ public class Convertor {
|
||||
* @param o object
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* @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
|
||||
* of it)
|
||||
* Get Coord
|
||||
*
|
||||
* @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
|
||||
* @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