Cleaned PropertyManager, refactored GameLoop to external package.

v5stable
ondra 10 years ago
parent 5e35bfe958
commit cc85d8a9d3
  1. 2
      src/mightypork/gamecore/control/AppModule.java
  2. 2
      src/mightypork/gamecore/control/AppSubModule.java
  3. 2
      src/mightypork/gamecore/control/BusAccess.java
  4. 6
      src/mightypork/gamecore/control/BusNode.java
  5. 45
      src/mightypork/gamecore/control/GameLoop.java
  6. 3
      src/mightypork/gamecore/control/bus/events/UpdateEvent.java
  7. 11
      src/mightypork/gamecore/control/interf/NoImpl.java
  8. 27
      src/mightypork/gamecore/control/timing/FpsMeter.java
  9. 16
      src/mightypork/gamecore/gui/renderers/TextRenderer.java
  10. 5
      src/mightypork/gamecore/gui/screens/Screen.java
  11. 82
      src/mightypork/gamecore/render/DisplaySystem.java
  12. 59
      src/mightypork/gamecore/render/Render.java
  13. 60
      src/mightypork/gamecore/render/Screenshot.java
  14. 1
      src/mightypork/rogue/App.java
  15. 4
      src/mightypork/rogue/Config.java
  16. 2
      src/mightypork/rogue/Const.java
  17. 18
      src/mightypork/rogue/MainLoop.java
  18. 2
      src/mightypork/rogue/TaskTakeScreenshot.java
  19. 8
      src/mightypork/rogue/screens/test_bouncyboxes/LayerBouncyBoxes.java
  20. 5
      src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java
  21. 450
      src/mightypork/utils/config/PropertyManager.java
  22. 3
      src/mightypork/utils/config/SimpleConfig.java
  23. 303
      src/mightypork/utils/config/SortedProperties.java
  24. 963
      src/mightypork/utils/files/PropertyManager.java
  25. 81
      src/mightypork/utils/objects/Convert.java

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

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

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

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

@ -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());
}
}
Loading…
Cancel
Save