From cc85d8a9d339002a6d81400c16dd2cf5fa277692 Mon Sep 17 00:00:00 2001 From: ondra Date: Tue, 8 Apr 2014 17:53:23 +0200 Subject: [PATCH] Cleaned PropertyManager, refactored GameLoop to external package. --- .../gamecore/control/AppModule.java | 2 +- .../gamecore/control/AppSubModule.java | 2 +- .../gamecore/control/BusAccess.java | 2 +- src/mightypork/gamecore/control/BusNode.java | 6 +- src/mightypork/gamecore/control/GameLoop.java | 45 +- .../control/bus/events/UpdateEvent.java | 3 +- .../gamecore/control/interf/NoImpl.java | 11 + .../gamecore/control/timing/FpsMeter.java | 27 +- .../gamecore/gui/renderers/TextRenderer.java | 16 + .../gamecore/gui/screens/Screen.java | 5 +- .../gamecore/render/DisplaySystem.java | 82 +- src/mightypork/gamecore/render/Render.java | 59 +- .../gamecore/render/Screenshot.java | 60 ++ src/mightypork/rogue/App.java | 1 + src/mightypork/rogue/Config.java | 4 +- src/mightypork/rogue/Const.java | 2 +- src/mightypork/rogue/MainLoop.java | 18 +- src/mightypork/rogue/TaskTakeScreenshot.java | 2 +- .../test_bouncyboxes/LayerBouncyBoxes.java | 8 +- .../test_cat_sound/LayerFlyingCat.java | 5 +- .../utils/config/PropertyManager.java | 450 ++++++++ .../utils/{files => config}/SimpleConfig.java | 3 +- .../utils/config/SortedProperties.java | 303 ++++++ .../utils/files/PropertyManager.java | 963 ------------------ .../objects/{Convertor.java => Convert.java} | 81 +- 25 files changed, 1004 insertions(+), 1156 deletions(-) create mode 100644 src/mightypork/gamecore/control/interf/NoImpl.java create mode 100644 src/mightypork/gamecore/render/Screenshot.java create mode 100644 src/mightypork/utils/config/PropertyManager.java rename src/mightypork/utils/{files => config}/SimpleConfig.java (98%) create mode 100644 src/mightypork/utils/config/SortedProperties.java delete mode 100644 src/mightypork/utils/files/PropertyManager.java rename src/mightypork/utils/objects/{Convertor.java => Convert.java} (76%) diff --git a/src/mightypork/gamecore/control/AppModule.java b/src/mightypork/gamecore/control/AppModule.java index 556b322..03f4622 100644 --- a/src/mightypork/gamecore/control/AppModule.java +++ b/src/mightypork/gamecore/control/AppModule.java @@ -46,7 +46,7 @@ public abstract class AppModule extends RootBusNode implements AppAccess { @Override - public void shutdown() + public final void shutdown() { app.shutdown(); } diff --git a/src/mightypork/gamecore/control/AppSubModule.java b/src/mightypork/gamecore/control/AppSubModule.java index 021f3c4..0804ea9 100644 --- a/src/mightypork/gamecore/control/AppSubModule.java +++ b/src/mightypork/gamecore/control/AppSubModule.java @@ -46,7 +46,7 @@ public abstract class AppSubModule extends BusNode implements AppAccess { @Override - public void shutdown() + public final void shutdown() { app.shutdown(); } diff --git a/src/mightypork/gamecore/control/BusAccess.java b/src/mightypork/gamecore/control/BusAccess.java index 1202b08..9467397 100644 --- a/src/mightypork/gamecore/control/BusAccess.java +++ b/src/mightypork/gamecore/control/BusAccess.java @@ -9,6 +9,6 @@ public interface BusAccess { /** * @return event bus */ - public abstract EventBus bus(); + EventBus bus(); } diff --git a/src/mightypork/gamecore/control/BusNode.java b/src/mightypork/gamecore/control/BusNode.java index f8815da..8bd2048 100644 --- a/src/mightypork/gamecore/control/BusNode.java +++ b/src/mightypork/gamecore/control/BusNode.java @@ -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 clients = new LinkedHashSet(); 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(); } diff --git a/src/mightypork/gamecore/control/GameLoop.java b/src/mightypork/gamecore/control/GameLoop.java index c6183e3..fb47ad4 100644 --- a/src/mightypork/gamecore/control/GameLoop.java +++ b/src/mightypork/gamecore/control/GameLoop.java @@ -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 taskQueue = new ConcurrentLinkedQueue(); - /** 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 diff --git a/src/mightypork/gamecore/control/bus/events/UpdateEvent.java b/src/mightypork/gamecore/control/bus/events/UpdateEvent.java index c18fc85..2cb1b91 100644 --- a/src/mightypork/gamecore/control/bus/events/UpdateEvent.java +++ b/src/mightypork/gamecore/control/bus/events/UpdateEvent.java @@ -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 { private final double deltaTime; diff --git a/src/mightypork/gamecore/control/interf/NoImpl.java b/src/mightypork/gamecore/control/interf/NoImpl.java new file mode 100644 index 0000000..7280d1d --- /dev/null +++ b/src/mightypork/gamecore/control/interf/NoImpl.java @@ -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 { + +} diff --git a/src/mightypork/gamecore/control/timing/FpsMeter.java b/src/mightypork/gamecore/control/timing/FpsMeter.java index de0b66b..5376b3c 100644 --- a/src/mightypork/gamecore/control/timing/FpsMeter.java +++ b/src/mightypork/gamecore/control/timing/FpsMeter.java @@ -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; - } } diff --git a/src/mightypork/gamecore/gui/renderers/TextRenderer.java b/src/mightypork/gamecore/gui/renderers/TextRenderer.java index bb0151f..8aa55ad 100644 --- a/src/mightypork/gamecore/gui/renderers/TextRenderer.java +++ b/src/mightypork/gamecore/gui/renderers/TextRenderer.java @@ -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(); diff --git a/src/mightypork/gamecore/gui/screens/Screen.java b/src/mightypork/gamecore/gui/screens/Screen.java index 3078fdc..73c5833 100644 --- a/src/mightypork/gamecore/gui/screens/Screen.java +++ b/src/mightypork/gamecore/gui/screens/Screen.java @@ -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(); diff --git a/src/mightypork/gamecore/render/DisplaySystem.java b/src/mightypork/gamecore/render/DisplaySystem.java index c3f8cd0..d94b880 100644 --- a/src/mightypork/gamecore/render/DisplaySystem.java +++ b/src/mightypork/gamecore/render/DisplaySystem.java @@ -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() - { - 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() + /** + * @return current FPS + */ + public final long getFps() { - // 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(); } } diff --git a/src/mightypork/gamecore/render/Render.java b/src/mightypork/gamecore/render/Render.java index 7124ed6..527a335 100644 --- a/src/mightypork/gamecore/render/Render.java +++ b/src/mightypork/gamecore/render/Render.java @@ -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 * @@ -490,5 +464,38 @@ 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); + + } } diff --git a/src/mightypork/gamecore/render/Screenshot.java b/src/mightypork/gamecore/render/Screenshot.java new file mode 100644 index 0000000..4ecf4a9 --- /dev/null +++ b/src/mightypork/gamecore/render/Screenshot.java @@ -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.
+ * 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); + } +} diff --git a/src/mightypork/rogue/App.java b/src/mightypork/rogue/App.java index 1a1b064..3950461 100644 --- a/src/mightypork/rogue/App.java +++ b/src/mightypork/rogue/App.java @@ -56,6 +56,7 @@ public class App implements AppAccess { public static void main(String[] args) { Config.init(); + Config.save(); Thread.setDefaultUncaughtExceptionHandler(new CrashHandler()); diff --git a/src/mightypork/rogue/Config.java b/src/mightypork/rogue/Config.java index 2384a31..4428437 100644 --- a/src/mightypork/rogue/Config.java +++ b/src/mightypork/rogue/Config.java @@ -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" diff --git a/src/mightypork/rogue/Const.java b/src/mightypork/rogue/Const.java index ebf1eed..ac8af01 100644 --- a/src/mightypork/rogue/Const.java +++ b/src/mightypork/rogue/Const.java @@ -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; diff --git a/src/mightypork/rogue/MainLoop.java b/src/mightypork/rogue/MainLoop.java index 70361a0..2c676fc 100644 --- a/src/mightypork/rogue/MainLoop.java +++ b/src/mightypork/rogue/MainLoop.java @@ -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); } diff --git a/src/mightypork/rogue/TaskTakeScreenshot.java b/src/mightypork/rogue/TaskTakeScreenshot.java index a22b92a..24756a0 100644 --- a/src/mightypork/rogue/TaskTakeScreenshot.java +++ b/src/mightypork/rogue/TaskTakeScreenshot.java @@ -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; diff --git a/src/mightypork/rogue/screens/test_bouncyboxes/LayerBouncyBoxes.java b/src/mightypork/rogue/screens/test_bouncyboxes/LayerBouncyBoxes.java index 2496ce0..cc6da6d 100644 --- a/src/mightypork/rogue/screens/test_bouncyboxes/LayerBouncyBoxes.java +++ b/src/mightypork/rogue/screens/test_bouncyboxes/LayerBouncyBoxes.java @@ -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!"; + } + }); } diff --git a/src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java b/src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java index 04005d5..539c3cb 100644 --- a/src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java +++ b/src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java @@ -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"); } } diff --git a/src/mightypork/utils/config/PropertyManager.java b/src/mightypork/utils/config/PropertyManager.java new file mode 100644 index 0000000..2bfdf0c --- /dev/null +++ b/src/mightypork/utils/config/PropertyManager.java @@ -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.
+ * Methods starting with put are for filling. Most of the others are shortcuts + * to getters. + * + * @author MightyPork + */ +public class PropertyManager { + + private abstract class Property { + + 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 { + + 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 { + + 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 { + + 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 { + + 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 { + + 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 { + + 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> entries; + private final TreeMap renameTable; + private final TreeMap 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>(); + this.overrideValues = new TreeMap(); + this.renameTable = new TreeMap(); + 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 keyList = new ArrayList(); + + // rename keys + for (final Entry 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 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; + } + +} diff --git a/src/mightypork/utils/files/SimpleConfig.java b/src/mightypork/utils/config/SimpleConfig.java similarity index 98% rename from src/mightypork/utils/files/SimpleConfig.java rename to src/mightypork/utils/config/SimpleConfig.java index 66784e4..3ace1e7 100644 --- a/src/mightypork/utils/files/SimpleConfig.java +++ b/src/mightypork/utils/config/SimpleConfig.java @@ -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; diff --git a/src/mightypork/utils/config/SortedProperties.java b/src/mightypork/utils/config/SortedProperties.java new file mode 100644 index 0000000..2dd453e --- /dev/null +++ b/src/mightypork/utils/config/SortedProperties.java @@ -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.
+ * 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
+ * Section = string before first dot in key. + */ + public boolean cfgBlankRowBetweenSections = true; + + /** Comments for individual keys */ + private final Hashtable keyComments = new Hashtable(); + + + 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); + } +} diff --git a/src/mightypork/utils/files/PropertyManager.java b/src/mightypork/utils/files/PropertyManager.java deleted file mode 100644 index c79dab9..0000000 --- a/src/mightypork/utils/files/PropertyManager.java +++ /dev/null @@ -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.
- * 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.
- * 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
- * Section = string before first dot in key. - */ - public boolean cfgSeparateSectionsByEmptyLine = true; - - private boolean firstEntry = true; - - /** Comments for individual keys */ - private final Hashtable keyComments = new Hashtable(); - - 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 entries; - private final TreeMap keyRename; - private final TreeMap 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(); - this.setValues = new TreeMap(); - this.keyRename = new TreeMap(); - 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 keyList = new ArrayList(); - - // rename keys - for (final Entry 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 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; - } - -} diff --git a/src/mightypork/utils/objects/Convertor.java b/src/mightypork/utils/objects/Convert.java similarity index 76% rename from src/mightypork/utils/objects/Convertor.java rename to src/mightypork/utils/objects/Convert.java index 9b194e9..6d57bb3 100644 --- a/src/mightypork/utils/objects/Convertor.java +++ b/src/mightypork/utils/objects/Convert.java @@ -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()); } }