From c94cd05da58fc33bc249cff7648f824f5ef90f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 22 Apr 2014 02:14:45 +0200 Subject: [PATCH] Entirely rewritten ION, world saving, added map movement etc --- amap.ion | Bin 0 -> 1564 bytes .../gamecore/input/InputSystem.java | 6 + src/mightypork/rogue/App.java | 14 +- .../rogue/screens/ingame/WorldLayer.java | 70 +- .../rogue/screens/main_menu/MenuButton.java | 18 +- src/mightypork/rogue/world/MapGenerator.java | 16 +- src/mightypork/rogue/world/MapObserver.java | 8 +- src/mightypork/rogue/world/Player.java | 127 ++ src/mightypork/rogue/world/PlayerInfo.java | 83 -- src/mightypork/rogue/world/World.java | 77 +- src/mightypork/rogue/world/WorldEntity.java | 13 + src/mightypork/rogue/world/WorldPos.java | 78 +- src/mightypork/rogue/world/item/Item.java | 21 +- .../rogue/world/item/ItemModel.java | 4 - .../rogue/world/map/EntityRenderContext.java | 14 + src/mightypork/rogue/world/map/Level.java | 53 +- .../rogue/world/map/MapRenderContext.java | 33 + .../rogue/world/map/TileRenderContext.java | 37 +- src/mightypork/rogue/world/tile/Tile.java | 50 +- .../rogue/world/tile/TileModel.java | 11 +- .../world/tile/models/AbstractNullTile.java | 12 +- .../rogue/world/tile/models/Floor.java | 7 + .../rogue/world/tile/models/SimpleTile.java | 2 +- .../rogue/world/tile/models/Wall.java | 7 + src/mightypork/test/TestIonArray.java | 25 - src/mightypork/test/TestIonArray2.java | 43 - src/mightypork/test/TestRandomSeed.java | 35 - src/mightypork/test/testworldtofile.java | 6 - .../vect/mutable/VectAnimated.java | 46 +- src/mightypork/util/files/ion/Ion.java | 1316 ----------------- src/mightypork/util/files/ion/IonBundle.java | 58 - .../util/files/ion/IonConstructor.java | 31 - src/mightypork/util/files/ion/Ionizable.java | 24 - src/mightypork/util/files/ion/Streamable.java | 36 - .../ion/templates/StreamableArrayList.java | 28 - .../ion/templates/StreamableHashMap.java | 28 - .../ion/templates/StreamableHashSet.java | 28 - .../templates/StreamableLinkedHashMap.java | 28 - .../ion/templates/StreamableLinkedList.java | 28 - .../files/ion/templates/StreamableStack.java | 28 - .../ion/templates/StreamableTreeSet.java | 28 - src/mightypork/util/ion/Ion.java | 296 ++++ src/mightypork/util/ion/IonBinary.java | 39 + src/mightypork/util/ion/IonBundle.java | 284 ++++ src/mightypork/util/ion/IonBundled.java | 18 + src/mightypork/util/ion/IonInput.java | 439 ++++++ src/mightypork/util/ion/IonMapWrapper.java | 55 + src/mightypork/util/ion/IonOutput.java | 383 +++++ .../util/ion/IonSequenceWrapper.java | 55 + 49 files changed, 2062 insertions(+), 2084 deletions(-) create mode 100644 amap.ion create mode 100644 src/mightypork/rogue/world/Player.java delete mode 100644 src/mightypork/rogue/world/PlayerInfo.java create mode 100644 src/mightypork/rogue/world/WorldEntity.java create mode 100644 src/mightypork/rogue/world/map/EntityRenderContext.java create mode 100644 src/mightypork/rogue/world/map/MapRenderContext.java delete mode 100644 src/mightypork/test/TestIonArray.java delete mode 100644 src/mightypork/test/TestIonArray2.java delete mode 100644 src/mightypork/test/TestRandomSeed.java delete mode 100644 src/mightypork/test/testworldtofile.java delete mode 100644 src/mightypork/util/files/ion/Ion.java delete mode 100644 src/mightypork/util/files/ion/IonBundle.java delete mode 100644 src/mightypork/util/files/ion/IonConstructor.java delete mode 100644 src/mightypork/util/files/ion/Ionizable.java delete mode 100644 src/mightypork/util/files/ion/Streamable.java delete mode 100644 src/mightypork/util/files/ion/templates/StreamableArrayList.java delete mode 100644 src/mightypork/util/files/ion/templates/StreamableHashMap.java delete mode 100644 src/mightypork/util/files/ion/templates/StreamableHashSet.java delete mode 100644 src/mightypork/util/files/ion/templates/StreamableLinkedHashMap.java delete mode 100644 src/mightypork/util/files/ion/templates/StreamableLinkedList.java delete mode 100644 src/mightypork/util/files/ion/templates/StreamableStack.java delete mode 100644 src/mightypork/util/files/ion/templates/StreamableTreeSet.java create mode 100644 src/mightypork/util/ion/Ion.java create mode 100644 src/mightypork/util/ion/IonBinary.java create mode 100644 src/mightypork/util/ion/IonBundle.java create mode 100644 src/mightypork/util/ion/IonBundled.java create mode 100644 src/mightypork/util/ion/IonInput.java create mode 100644 src/mightypork/util/ion/IonMapWrapper.java create mode 100644 src/mightypork/util/ion/IonOutput.java create mode 100644 src/mightypork/util/ion/IonSequenceWrapper.java diff --git a/amap.ion b/amap.ion new file mode 100644 index 0000000000000000000000000000000000000000..6ef2c2da3b8c9e4a373b7b18f7ef3c668f97244c GIT binary patch literal 1564 zcma)6J5Iwu5Cs8IoHU3y9DgUkOnh z4xwXFw-4Egv>EBCi#625kqOzdW+Njok{ANlC|F5QU%QC+nj#^x#lkHiD@on!a9?E0 righi9)fVFluD}sZDiRO~^I1#|O4S7wbBwUk{wFt|$8Sf^-+T2Bn*nO} literal 0 HcmV?d00001 diff --git a/src/mightypork/gamecore/input/InputSystem.java b/src/mightypork/gamecore/input/InputSystem.java index 8791e2a..52d9bfb 100644 --- a/src/mightypork/gamecore/input/InputSystem.java +++ b/src/mightypork/gamecore/input/InputSystem.java @@ -204,4 +204,10 @@ public class InputSystem extends RootBusNode implements Updateable, KeyBinder { { Mouse.setGrabbed(grab); } + + + public static boolean isKeyDown(int key) + { + return Keyboard.isKeyDown(key); + } } diff --git a/src/mightypork/rogue/App.java b/src/mightypork/rogue/App.java index 14e5ba2..f8a832c 100644 --- a/src/mightypork/rogue/App.java +++ b/src/mightypork/rogue/App.java @@ -22,15 +22,12 @@ import mightypork.rogue.screens.main_menu.ScreenMainMenu; import mightypork.rogue.screens.test_bouncyboxes.ScreenTestBouncy; import mightypork.rogue.screens.test_cat_sound.ScreenTestCat; import mightypork.rogue.screens.test_render.ScreenTestRender; -import mightypork.rogue.world.PlayerInfo; -import mightypork.rogue.world.World; -import mightypork.rogue.world.WorldPos; import mightypork.rogue.world.item.Item; import mightypork.rogue.world.map.Level; import mightypork.rogue.world.tile.Tile; import mightypork.util.control.eventbus.EventBus; import mightypork.util.control.eventbus.events.Event; -import mightypork.util.files.ion.Ion; +import mightypork.util.ion.Ion; import mightypork.util.logging.Log; import mightypork.util.logging.writers.LogWriter; @@ -109,12 +106,9 @@ public final class App extends BaseApp { @Override protected void preInit() { - Ion.registerIonizable(Item.ION_MARK, Item.class); - Ion.registerIonizable(Level.ION_MARK, Level.class); - Ion.registerIonizable(PlayerInfo.ION_MARK, PlayerInfo.class); - Ion.registerIonizable(Tile.ION_MARK, Tile.class); - Ion.registerIonizable(World.ION_MARK, World.class); - Ion.registerIonizable(WorldPos.ION_MARK, WorldPos.class); + Ion.registerBinary(Item.ION_MARK, Item.class); + Ion.registerBinary(Level.ION_MARK, Level.class); + Ion.registerBinary(Tile.ION_MARK, Tile.class); } diff --git a/src/mightypork/rogue/screens/ingame/WorldLayer.java b/src/mightypork/rogue/screens/ingame/WorldLayer.java index 577bb15..4dbe0fb 100644 --- a/src/mightypork/rogue/screens/ingame/WorldLayer.java +++ b/src/mightypork/rogue/screens/ingame/WorldLayer.java @@ -6,9 +6,12 @@ import java.util.Random; import mightypork.gamecore.gui.screens.Screen; import mightypork.gamecore.gui.screens.ScreenLayer; +import mightypork.gamecore.input.InputSystem; +import mightypork.gamecore.input.KeyStroke; +import mightypork.gamecore.input.Keys; import mightypork.rogue.world.MapGenerator; import mightypork.rogue.world.World; -import mightypork.util.files.ion.Ion; +import mightypork.util.ion.Ion; public class WorldLayer extends ScreenLayer { @@ -17,17 +20,82 @@ public class WorldLayer extends ScreenLayer { { super(screen); + // FIXME just temporary test here + final Random rand = new Random(); final World w = MapGenerator.createWorld(rand.nextLong()); + try { Ion.toFile("amap.ion", w); } catch (final IOException e) { e.printStackTrace(); + System.exit(1); + return; } +// final World w; +// +// try { +// w = Ion.fromFile("amap.ion", World.class); +// } catch (IOException e) { +// e.printStackTrace(); +// System.exit(1); +// return; +// } + final WorldRenderer wr = new WorldRenderer(w); wr.setRect(root); root.add(wr); + + bindKey(new KeyStroke(true, Keys.LEFT), new Runnable() { + + @Override + public void run() + { + w.getPlayer().walk(-1, 0); + } + }); + bindKey(new KeyStroke(true, Keys.RIGHT), new Runnable() { + + @Override + public void run() + { + w.getPlayer().walk(1, 0); + } + }); + bindKey(new KeyStroke(true, Keys.UP), new Runnable() { + + @Override + public void run() + { + w.getPlayer().walk(0, -1); + } + }); + bindKey(new KeyStroke(true, Keys.DOWN), new Runnable() { + + @Override + public void run() + { + w.getPlayer().walk(0, 1); + } + }); + + w.getPlayer().setTargetListener(new Runnable() { + + @Override + public void run() + { + if (InputSystem.isKeyDown(Keys.LEFT)) { + w.getPlayer().walk(-1, 0); + } else if (InputSystem.isKeyDown(Keys.RIGHT)) { + w.getPlayer().walk(1, 0); + } else if (InputSystem.isKeyDown(Keys.UP)) { + w.getPlayer().walk(0, -1); + } else if (InputSystem.isKeyDown(Keys.DOWN)) { + w.getPlayer().walk(0, 1); + } + } + }); } diff --git a/src/mightypork/rogue/screens/main_menu/MenuButton.java b/src/mightypork/rogue/screens/main_menu/MenuButton.java index a823582..fcd965a 100644 --- a/src/mightypork/rogue/screens/main_menu/MenuButton.java +++ b/src/mightypork/rogue/screens/main_menu/MenuButton.java @@ -7,7 +7,6 @@ import mightypork.gamecore.gui.components.painters.TextPainter; import mightypork.gamecore.render.fonts.GLFont; import mightypork.rogue.Res; import mightypork.util.constraints.num.Num; -import mightypork.util.constraints.num.mutable.NumVar; import mightypork.util.constraints.vect.Vect; import mightypork.util.constraints.vect.mutable.VectVar; import mightypork.util.math.color.Color; @@ -21,23 +20,14 @@ class MenuButton extends ClickableComponent { private final Vect offsetActive = Vect.make(this.height().perc(-5), Num.ZERO); private final Color color; - private final double ALPHA_OFF = 0.6; - private final double ALPHA_ON = 1; - - private final double SH_ALPHA_OFF = 0.1; - private final double SH_ALPHA_ON = 0.5; - - private final NumVar alphaMul = Num.makeVar(ALPHA_OFF); - private final NumVar alphaMulSh = Num.makeVar(SH_ALPHA_OFF); - public MenuButton(String text, Color color) { - this.color = color.withAlpha(alphaMul); + this.color = color; this.painter = new TextPainter(font, AlignX.CENTER, this.color, text); this.painter.setRect(this.shrink(this.height().perc(8)).move(offset)); - this.painter.setShadow(Color.BLACK.withAlpha(alphaMulSh), height().div(24).toVectXY()); + this.painter.setShadow(Color.BLACK.withAlpha(0.3), height().div(24).toVectXY()); } @@ -46,12 +36,8 @@ class MenuButton extends ClickableComponent { { if (isMouseOver()) { offset.setTo(offsetActive); - alphaMul.setTo(ALPHA_ON); - alphaMulSh.setTo(SH_ALPHA_ON); } else { offset.setTo(Vect.ZERO); - alphaMul.setTo(ALPHA_OFF); - alphaMulSh.setTo(SH_ALPHA_OFF); } painter.render(); diff --git a/src/mightypork/rogue/world/MapGenerator.java b/src/mightypork/rogue/world/MapGenerator.java index 6f4a267..29ab4b9 100644 --- a/src/mightypork/rogue/world/MapGenerator.java +++ b/src/mightypork/rogue/world/MapGenerator.java @@ -4,6 +4,7 @@ package mightypork.rogue.world; import java.util.Random; import mightypork.rogue.world.map.Level; +import mightypork.rogue.world.tile.TileModel; import mightypork.rogue.world.tile.Tiles; @@ -20,33 +21,30 @@ public class MapGenerator { final World w = new World(); w.setSeed(seed); - final int levels = 4 + rand.nextInt(6); - - for (int i = 0; i < levels; i++) { - w.addLevel(createLevel(rand.nextLong())); - } + w.addLevel(createLevel(rand.nextLong(), Tiles.CRYSTAL_FLOOR, Tiles.CRYSTAL_WALL)); + w.addLevel(createLevel(rand.nextLong(), Tiles.BRCOBBLE_FLOOR, Tiles.BRCOBBLE_WALL)); // TODO place on start position - w.setPlayer(new PlayerInfo(10, 10, 0)); + w.getPlayer().teleport(new WorldPos(10, 10, 0)); return w; } } - private static Level createLevel(long seed) + private static Level createLevel(long seed, TileModel floor, TileModel wall) { // TODO final Level lm = new Level(20, 20); lm.setSeed(seed); - lm.fill(Tiles.CRYSTAL_FLOOR); + lm.fill(floor); final Random rand = new Random(); rand.setSeed(seed); for (int i = 0; i < 150; i++) { - lm.setTile(Tiles.CRYSTAL_WALL, rand.nextInt(20), rand.nextInt(20)); + lm.setTile(wall, rand.nextInt(20), rand.nextInt(20)); } return lm; diff --git a/src/mightypork/rogue/world/MapObserver.java b/src/mightypork/rogue/world/MapObserver.java index c9b2cf4..a6b13a7 100644 --- a/src/mightypork/rogue/world/MapObserver.java +++ b/src/mightypork/rogue/world/MapObserver.java @@ -6,13 +6,7 @@ package mightypork.rogue.world; * * @author MightyPork */ -public interface MapObserver { - - /** - * @return observer's position - */ - public WorldPos getPosition(); - +public interface MapObserver extends WorldEntity { /** * @return observed range (in tiles) diff --git a/src/mightypork/rogue/world/Player.java b/src/mightypork/rogue/world/Player.java new file mode 100644 index 0000000..6f336b6 --- /dev/null +++ b/src/mightypork/rogue/world/Player.java @@ -0,0 +1,127 @@ +package mightypork.rogue.world; + + +import java.io.IOException; + +import mightypork.util.constraints.vect.Vect; +import mightypork.util.constraints.vect.mutable.VectAnimated; +import mightypork.util.control.timing.Updateable; +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonBundled; +import mightypork.util.math.Easing; + + +/** + * Player info + * + * @author MightyPork + */ +public class Player implements IonBundled, MapObserver, Updateable { + + private final WorldPos position = new WorldPos(); + private final VectAnimated walkOffset = new VectAnimated(Vect.ZERO, Easing.LINEAR); + private final WorldPos target = new WorldPos(); + private Runnable targetListener; + + + public Player() + { + walkOffset.setDefaultDuration(0.25); + } + + + public Player(int x, int y, int floor) + { + this.position.setTo(x, y, floor); + this.target.setTo(position); + } + + + public Player(WorldPos pos) + { + this(pos.x, pos.y, pos.floor); + } + + + @Override + public void load(IonBundle in) throws IOException + { + in.loadBundled("pos", position); + in.loadBundled("target", target); + } + + + @Override + public void save(IonBundle out) throws IOException + { + out.putBundled("pos", position); + out.putBundled("target", target); + } + + + @Override + public WorldPos getLogicalPosition() + { + return position; + } + + + @Override + public Vect getVisualOffset() + { + return walkOffset; + } + + + @Override + public int getViewRange() + { + return 15; + } + + + public void teleport(WorldPos pos) + { + position.setTo(pos); + target.setTo(pos); + walkOffset.reset(); + } + + + public void walk(int offsetX, int offsetY) + { + this.target.setTo(position.x + offsetX, position.y + offsetY, this.position.floor); + } + + + @Override + public void update(double delta) + { + if (!walkOffset.isFinished()) { + walkOffset.update(delta); + if (walkOffset.isFinished()) { + position.add(walkOffset.xi(), walkOffset.yi(), position.floor); + walkOffset.reset(); + targetListener.run(); + } + } + + if (walkOffset.isFinished() && !target.equals(position)) { + + int x = (target.x - position.x); + if (x > 0) x = 1; + if (x < 0) x = -1; + int y = (target.y - position.y); + if (y > 0) y = 1; + if (y < 0) y = -1; + + walkOffset.animate(x, y, 0); + } + } + + + public void setTargetListener(Runnable r) + { + this.targetListener = r; + } +} diff --git a/src/mightypork/rogue/world/PlayerInfo.java b/src/mightypork/rogue/world/PlayerInfo.java deleted file mode 100644 index d4cb16f..0000000 --- a/src/mightypork/rogue/world/PlayerInfo.java +++ /dev/null @@ -1,83 +0,0 @@ -package mightypork.rogue.world; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.IonBundle; -import mightypork.util.files.ion.IonConstructor; -import mightypork.util.files.ion.Ionizable; - - -/** - * Player info - * - * @author MightyPork - */ -public class PlayerInfo implements Ionizable, MapObserver { - - public static final short ION_MARK = 708; - - public WorldPos position = new WorldPos(); - - - @IonConstructor - public PlayerInfo() - { - } - - - public PlayerInfo(int x, int y, int floor) - { - this.position.setTo(x, y, floor); - } - - - public PlayerInfo(WorldPos pos) - { - this.position = pos; - } - - - @Override - public void load(InputStream in) throws IOException - { - final IonBundle ib = (IonBundle) Ion.readObject(in); - - position = ib.get("pos", position); - } - - - @Override - public void save(OutputStream out) throws IOException - { - final IonBundle ib = new IonBundle(); - - ib.put("pos", position); - - Ion.writeObject(out, ib); - } - - - @Override - public short getIonMark() - { - return ION_MARK; - } - - - @Override - public WorldPos getPosition() - { - return position; - } - - - @Override - public int getViewRange() - { - return 15; - } -} diff --git a/src/mightypork/rogue/world/World.java b/src/mightypork/rogue/world/World.java index 5890968..ffa2e2c 100644 --- a/src/mightypork/rogue/world/World.java +++ b/src/mightypork/rogue/world/World.java @@ -2,8 +2,7 @@ package mightypork.rogue.world; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -14,19 +13,15 @@ import mightypork.util.constraints.rect.RectConst; import mightypork.util.constraints.rect.proxy.RectBound; import mightypork.util.constraints.vect.VectConst; import mightypork.util.control.timing.Updateable; -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.IonBundle; -import mightypork.util.files.ion.Ionizable; -import mightypork.util.files.ion.templates.StreamableArrayList; +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonBundled; -public class World implements Ionizable, Updateable { +public class World implements IonBundled, Updateable { - public static final short ION_MARK = 706; + private final ArrayList levels = new ArrayList<>(); - private StreamableArrayList levels = new StreamableArrayList<>(); - - private PlayerInfo player = new PlayerInfo(); + private final Player player = new Player(); private transient final Set observers = new HashSet<>(); @@ -35,36 +30,20 @@ public class World implements Ionizable, Updateable { @Override - public void load(InputStream in) throws IOException + public void load(IonBundle in) throws IOException { - // world data - final IonBundle ib = (IonBundle) Ion.readObject(in); - player = ib.get("player", player); - seed = ib.get("seed", seed); - - Ion.readSequence(in, levels); + in.loadBundled("player", player); + seed = in.get("seed", 0L); + in.loadSequence("levels", levels); } @Override - public void save(OutputStream out) throws IOException - { - final IonBundle ib = new IonBundle(); - ib.put("player", player); - ib.put("seed", seed); - Ion.writeObject(out, ib); - - Ion.writeSequence(out, levels); - } - - - public void setPlayer(PlayerInfo player) + public void save(IonBundle out) throws IOException { - removeObserver(this.player); - - this.player = player; - - addObserver(player); + out.putBundled("player", player); + out.put("seed", seed); + out.putSequence("levels", levels); } @@ -86,19 +65,14 @@ public class World implements Ionizable, Updateable { } - @Override - public short getIonMark() - { - return ION_MARK; - } - - @Override public void update(double delta) { + player.update(delta); + for (int level = 0; level < levels.size(); level++) { for (final MapObserver observer : observers) { - if (observer.getPosition().floor == level) { + if (observer.getLogicalPosition().floor == level) { levels.get(level).update(observer, delta); } } @@ -108,7 +82,7 @@ public class World implements Ionizable, Updateable { public Level getLevelForObserver(MapObserver observer) { - return levels.get(observer.getPosition().floor); + return levels.get(observer.getLogicalPosition().floor); } @@ -138,19 +112,22 @@ public class World implements Ionizable, Updateable { final VectConst vpCenter = r.center().sub(tileSize * 0.5, tileSize).freeze(); // 0.5 to center, 1 to move up (down is teh navbar) - final int playerX = player.getPosition().x; - final int playerY = player.getPosition().y; + final double playerX = player.getLogicalPosition().x + player.getVisualOffset().x(); + final double playerY = player.getLogicalPosition().y + player.getVisualOffset().y(); // total map area //@formatter:off final RectConst mapRect = vpCenter.startRect().grow( playerX*tileSize, - playerY*tileSize,// (floor.getWidth() - playerX) * tileSize, + playerY*tileSize, (floor.getHeight() - playerY) * tileSize ).freeze(); //@formatter:on + System.out.println(playerX + "," + playerY + " : " + mapRect); + System.out.println(floor.getWidth() + "," + floor.getHeight()); + // tiles to render final int x1 = (int) Math.floor(playerX - (vpW / tileSize)); final int y1 = (int) Math.floor(playerY - (vpH / tileSize)); @@ -177,4 +154,10 @@ public class World implements Ionizable, Updateable { return seed; } + + public Player getPlayer() + { + return player; + } + } diff --git a/src/mightypork/rogue/world/WorldEntity.java b/src/mightypork/rogue/world/WorldEntity.java new file mode 100644 index 0000000..9e2c8f7 --- /dev/null +++ b/src/mightypork/rogue/world/WorldEntity.java @@ -0,0 +1,13 @@ +package mightypork.rogue.world; + + +import mightypork.util.constraints.vect.Vect; + + +public interface WorldEntity { + + WorldPos getLogicalPosition(); + + + Vect getVisualOffset(); +} diff --git a/src/mightypork/rogue/world/WorldPos.java b/src/mightypork/rogue/world/WorldPos.java index 6cc6314..20870f2 100644 --- a/src/mightypork/rogue/world/WorldPos.java +++ b/src/mightypork/rogue/world/WorldPos.java @@ -2,12 +2,9 @@ package mightypork.rogue.world; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.IonConstructor; -import mightypork.util.files.ion.Ionizable; +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonBundled; /** @@ -15,57 +12,88 @@ import mightypork.util.files.ion.Ionizable; * * @author MightyPork */ -public class WorldPos implements Ionizable { - - public static final short ION_MARK = 707; +public class WorldPos implements IonBundled { public int x, y, floor; - public WorldPos(int x, int y, int z) + public WorldPos(int x, int y, int floor) { super(); this.x = x; this.y = y; - this.floor = z; + this.floor = floor; } - @IonConstructor public WorldPos() { } @Override - public void load(InputStream in) throws IOException + public void load(IonBundle in) throws IOException { - x = Ion.readInt(in); - y = Ion.readInt(in); - floor = Ion.readInt(in); + x = in.get("x", 0); + y = in.get("y", 0); + floor = in.get("z", 0); } @Override - public void save(OutputStream out) throws IOException + public void save(IonBundle out) throws IOException + { + out.put("x", x); + out.put("y", y); + out.put("z", floor); + } + + + public void setTo(int x, int y, int floor) { - Ion.writeInt(out, x); - Ion.writeInt(out, y); - Ion.writeInt(out, floor); + this.x = x; + this.y = y; + this.floor = floor; + } + + + public void setTo(WorldPos other) + { + this.x = other.x; + this.y = other.y; + this.floor = other.floor; } @Override - public short getIonMark() + public int hashCode() { - return ION_MARK; + final int prime = 31; + int result = 1; + result = prime * result + floor; + result = prime * result + x; + result = prime * result + y; + return result; } - public void setTo(int x, int y, int z) + @Override + public boolean equals(Object obj) { - this.x = x; - this.y = y; - this.floor = z; + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof WorldPos)) return false; + final WorldPos other = (WorldPos) obj; + if (floor != other.floor) return false; + if (x != other.x) return false; + if (y != other.y) return false; + return true; } + + + public void add(int x, int y, int floor) + { + setTo(this.x + x, this.y + y, this.floor + floor); + } + } diff --git a/src/mightypork/rogue/world/item/Item.java b/src/mightypork/rogue/world/item/Item.java index 28c084d..0712e7c 100644 --- a/src/mightypork/rogue/world/item/Item.java +++ b/src/mightypork/rogue/world/item/Item.java @@ -2,18 +2,16 @@ package mightypork.rogue.world.item; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import mightypork.util.constraints.rect.proxy.RectBound; -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.IonConstructor; -import mightypork.util.files.ion.Ionizable; +import mightypork.util.ion.IonBinary; +import mightypork.util.ion.IonInput; +import mightypork.util.ion.IonOutput; -public class Item implements Ionizable { +public class Item implements IonBinary { - public static final short ION_MARK = 701; + public static final short ION_MARK = 51; private transient ItemModel model; @@ -26,7 +24,6 @@ public class Item implements Ionizable { } - @IonConstructor public Item() { } @@ -46,16 +43,16 @@ public class Item implements Ionizable { @Override - public void save(OutputStream out) throws IOException + public void save(IonOutput out) throws IOException { - Ion.writeShort(out, (short) id); + out.writeIntByte(id); } @Override - public void load(InputStream in) throws IOException + public void load(IonInput in) throws IOException { - id = Ion.readShort(in); + id = in.readIntByte(); // if id changed, get new model if (model == null || id != model.id) model = Items.get(id); diff --git a/src/mightypork/rogue/world/item/ItemModel.java b/src/mightypork/rogue/world/item/ItemModel.java index 8aa90a3..c9f445a 100644 --- a/src/mightypork/rogue/world/item/ItemModel.java +++ b/src/mightypork/rogue/world/item/ItemModel.java @@ -1,12 +1,8 @@ package mightypork.rogue.world.item; -import mightypork.rogue.world.map.TileRenderContext; import mightypork.util.annotations.DefaultImpl; -import mightypork.util.constraints.num.proxy.NumBoundAdapter; -import mightypork.util.constraints.rect.Rect; import mightypork.util.constraints.rect.proxy.RectBound; -import mightypork.util.constraints.rect.proxy.RectBoundAdapter; public abstract class ItemModel { diff --git a/src/mightypork/rogue/world/map/EntityRenderContext.java b/src/mightypork/rogue/world/map/EntityRenderContext.java new file mode 100644 index 0000000..b388181 --- /dev/null +++ b/src/mightypork/rogue/world/map/EntityRenderContext.java @@ -0,0 +1,14 @@ +package mightypork.rogue.world.map; + + +import mightypork.util.constraints.rect.Rect; + + +public class EntityRenderContext extends MapRenderContext { + + public EntityRenderContext(MapAccess map, Rect drawArea) + { + super(map, drawArea); + } + +} diff --git a/src/mightypork/rogue/world/map/Level.java b/src/mightypork/rogue/world/map/Level.java index 777b90a..34f3284 100644 --- a/src/mightypork/rogue/world/map/Level.java +++ b/src/mightypork/rogue/world/map/Level.java @@ -2,17 +2,16 @@ package mightypork.rogue.world.map; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import mightypork.rogue.world.MapObserver; import mightypork.rogue.world.WorldPos; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.TileModel; import mightypork.rogue.world.tile.Tiles; -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.IonConstructor; -import mightypork.util.files.ion.Ionizable; +import mightypork.util.ion.IonBinary; +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonInput; +import mightypork.util.ion.IonOutput; import mightypork.util.math.noise.NoiseGen; @@ -21,9 +20,9 @@ import mightypork.util.math.noise.NoiseGen; * * @author MightyPork */ -public class Level implements MapAccess, Ionizable { +public class Level implements MapAccess, IonBinary { - public static final int ION_MARK = 702; + public static final int ION_MARK = 52; private int width, height; @@ -33,10 +32,9 @@ public class Level implements MapAccess, Ionizable { /** Level seed (used for generation and tile variation) */ public long seed; - private NoiseGen noiseGen; + private transient NoiseGen noiseGen; - @IonConstructor public Level() { } @@ -53,8 +51,6 @@ public class Level implements MapAccess, Ionizable { private void buildArray() { this.tiles = new Tile[height][width]; - - fill(Tiles.NULL_EMPTY); } @@ -131,32 +127,43 @@ public class Level implements MapAccess, Ionizable { @Override - public void load(InputStream in) throws IOException + public void load(IonInput in) throws IOException { - seed = Ion.readLong(in); - width = Ion.readInt(in); - height = Ion.readInt(in); + // metadata + final IonBundle ib = in.readBundle(); + seed = ib.get("seed", 0L); + width = ib.get("w", 0); + height = ib.get("h", 0); + // init array of size buildArray(); + // load tiles for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - tiles[y][x] = new Tile(); - tiles[y][x].load(in); + // no mark + final Tile t = new Tile(); + t.load(in); + tiles[y][x] = t; } } } @Override - public void save(OutputStream out) throws IOException - { - Ion.writeLong(out, seed); - Ion.writeInt(out, width); - Ion.writeInt(out, height); + public void save(IonOutput out) throws IOException + { + // metadata + final IonBundle ib = new IonBundle(); + ib.put("seed", seed); + ib.put("w", width); + ib.put("h", height); + out.writeBundle(ib); + // tiles (writing this way to save space) for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { + // no mark to save space tiles[y][x].save(out); } } @@ -173,7 +180,7 @@ public class Level implements MapAccess, Ionizable { public void update(MapObserver observer, double delta) { final int viewRange = observer.getViewRange(); - final WorldPos position = observer.getPosition(); + final WorldPos position = observer.getLogicalPosition(); int x1 = position.x - viewRange; int y1 = position.y - viewRange; diff --git a/src/mightypork/rogue/world/map/MapRenderContext.java b/src/mightypork/rogue/world/map/MapRenderContext.java new file mode 100644 index 0000000..5cbdf49 --- /dev/null +++ b/src/mightypork/rogue/world/map/MapRenderContext.java @@ -0,0 +1,33 @@ +package mightypork.rogue.world.map; + + +import mightypork.util.constraints.rect.Rect; +import mightypork.util.constraints.rect.builders.TiledRect; + + +public abstract class MapRenderContext { + + protected final MapAccess map; + protected final TiledRect tiler; + private final Rect mapRect; + + + public MapRenderContext(MapAccess map, Rect drawArea) + { + this.map = map; + this.tiler = drawArea.tiles(map.getWidth(), map.getHeight()); + this.mapRect = drawArea; + } + + + public Rect getRectForTile(int x, int y) + { + return tiler.tile(x, y); + } + + + public Rect getMapRect() + { + return mapRect; + } +} diff --git a/src/mightypork/rogue/world/map/TileRenderContext.java b/src/mightypork/rogue/world/map/TileRenderContext.java index c2be8b0..d929b77 100644 --- a/src/mightypork/rogue/world/map/TileRenderContext.java +++ b/src/mightypork/rogue/world/map/TileRenderContext.java @@ -3,32 +3,27 @@ package mightypork.rogue.world.map; import mightypork.rogue.world.tile.Tile; import mightypork.util.constraints.rect.Rect; -import mightypork.util.constraints.rect.builders.TiledRect; import mightypork.util.constraints.rect.proxy.RectBound; import mightypork.util.math.noise.NoiseGen; /** - * Context for tile rendering. Provides tile rect, surrounding tiles, rendered - * tile and random noise values for position-static tile variation. + * Context for tile rendering. * * @author MightyPork */ -public final class TileRenderContext implements RectBound { +public final class TileRenderContext extends MapRenderContext implements RectBound { - private final MapAccess map; - private final TiledRect tiler; + public int x; + public int y; private final NoiseGen noise; - public int x, y; - public TileRenderContext(MapAccess map, Rect drawArea) { - this.map = map; - this.tiler = drawArea.tiles(map.getWidth(), map.getHeight()); + super(map, drawArea); - this.tiler.setOverlap(0.001); // avoid gaps (rounding error?) + this.tiler.setOverlap(0.01); // avoid gaps (rounding error?) this.noise = map.getNoiseGen(); } @@ -56,16 +51,6 @@ public final class TileRenderContext implements RectBound { } - /** - * Rect of the current tile to draw - */ - @Override - public Rect getRect() - { - return tiler.tile(x, y); - } - - /** * @return per-coord noise value 0..1 */ @@ -79,4 +64,14 @@ public final class TileRenderContext implements RectBound { { map.getTile(x, y).render(this); } + + + /** + * Rect of the current tile to draw + */ + @Override + public Rect getRect() + { + return getRectForTile(x, y); + } } diff --git a/src/mightypork/rogue/world/tile/Tile.java b/src/mightypork/rogue/world/tile/Tile.java index b4f6855..00e9435 100644 --- a/src/mightypork/rogue/world/tile/Tile.java +++ b/src/mightypork/rogue/world/tile/Tile.java @@ -2,24 +2,21 @@ package mightypork.rogue.world.tile; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.Stack; import mightypork.rogue.world.item.Item; import mightypork.rogue.world.map.TileRenderContext; import mightypork.util.control.timing.Animator; import mightypork.util.control.timing.Updateable; -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.IonBundle; -import mightypork.util.files.ion.IonConstructor; -import mightypork.util.files.ion.Ionizable; -import mightypork.util.files.ion.Streamable; +import mightypork.util.ion.IonBinary; +import mightypork.util.ion.IonBundle; +import mightypork.util.ion.IonInput; +import mightypork.util.ion.IonOutput; -public final class Tile implements Ionizable, Updateable { +public final class Tile implements IonBinary, Updateable { - public static final short ION_MARK = 700; + public static final short ION_MARK = 50; private transient TileModel model; @@ -50,7 +47,6 @@ public final class Tile implements Ionizable, Updateable { } - @IonConstructor public Tile() { } @@ -60,45 +56,45 @@ public final class Tile implements Ionizable, Updateable { { model.render(context); - if (!items.isEmpty()) { + if (hasItems()) { getItemRenderer().render(items.peek(), context); } } @Override - public void save(OutputStream out) throws IOException + public void save(IonOutput out) throws IOException { - if (model.isNullTile()) throw new RuntimeException("Cannot save null tile."); + out.writeIntByte(id); - Ion.writeShort(out, (short) id); // tile ID - Ion.writeSequence(out, items); // if empty, writes single END mark + if (model.hasDroppedItems()) { + out.writeSequence(items); + } - // models with metadata can save their stuff if (model.hasMetadata()) { - IonBundle ib = new IonBundle(); + final IonBundle ib = new IonBundle(); model.saveMetadata(this, ib); - Ion.writeObject(out, ib); + out.writeBundle(ib); } } @Override - public void load(InputStream in) throws IOException + public void load(IonInput in) throws IOException { - id = Ion.readShort(in); + id = in.readIntByte(); - // check if model is changed (can happen) + // check model if (model == null || id != model.id) { model = Tiles.get(id); } - Ion.readSequence(in, items); // if END is found, nothing is read. + if (model.hasDroppedItems()) { + in.readSequence(items); + } - // load model's stuff if (model.hasMetadata()) { - IonBundle ib = (IonBundle) Ion.readObject(in); - model.loadMetadata(this, ib); + model.loadMetadata(this, in.readBundle()); } } @@ -107,7 +103,7 @@ public final class Tile implements Ionizable, Updateable { public void update(double delta) { model.update(this, delta); - if (!items.isEmpty()) { + if (hasItems()) { getItemRenderer().update(delta); } } @@ -131,7 +127,7 @@ public final class Tile implements Ionizable, Updateable { public boolean hasItems() { - return !items.isEmpty(); + return model.hasDroppedItems() && !items.isEmpty(); } diff --git a/src/mightypork/rogue/world/tile/TileModel.java b/src/mightypork/rogue/world/tile/TileModel.java index fe4889d..12aee6b 100644 --- a/src/mightypork/rogue/world/tile/TileModel.java +++ b/src/mightypork/rogue/world/tile/TileModel.java @@ -3,7 +3,7 @@ package mightypork.rogue.world.tile; import mightypork.rogue.world.map.TileRenderContext; import mightypork.util.annotations.DefaultImpl; -import mightypork.util.files.ion.IonBundle; +import mightypork.util.ion.IonBundle; /** @@ -104,4 +104,13 @@ public abstract class TileModel { * @return has data */ public abstract boolean hasMetadata(); + + + /** + * Check if the tile can hold dropped items. Walls and such can return false + * to save disk space (no need to write empty list). + * + * @return true + */ + public abstract boolean hasDroppedItems(); } diff --git a/src/mightypork/rogue/world/tile/models/AbstractNullTile.java b/src/mightypork/rogue/world/tile/models/AbstractNullTile.java index aee829f..4250e84 100644 --- a/src/mightypork/rogue/world/tile/models/AbstractNullTile.java +++ b/src/mightypork/rogue/world/tile/models/AbstractNullTile.java @@ -4,7 +4,7 @@ package mightypork.rogue.world.tile.models; import mightypork.rogue.world.map.TileRenderContext; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.TileModel; -import mightypork.util.files.ion.IonBundle; +import mightypork.util.ion.IonBundle; /** @@ -59,20 +59,30 @@ public abstract class AbstractNullTile extends TileModel { return inst; } + @Override public boolean hasMetadata() { return false; } + @Override public void loadMetadata(Tile tile, IonBundle ib) { } + @Override public void saveMetadata(Tile tile, IonBundle ib) { } + + @Override + public boolean hasDroppedItems() + { + return false; + } + } diff --git a/src/mightypork/rogue/world/tile/models/Floor.java b/src/mightypork/rogue/world/tile/models/Floor.java index ea16196..6f1f7aa 100644 --- a/src/mightypork/rogue/world/tile/models/Floor.java +++ b/src/mightypork/rogue/world/tile/models/Floor.java @@ -20,4 +20,11 @@ public class Floor extends SimpleTile { return true; } + + @Override + public boolean hasDroppedItems() + { + return true; + } + } diff --git a/src/mightypork/rogue/world/tile/models/SimpleTile.java b/src/mightypork/rogue/world/tile/models/SimpleTile.java index ff82a93..febd5c7 100644 --- a/src/mightypork/rogue/world/tile/models/SimpleTile.java +++ b/src/mightypork/rogue/world/tile/models/SimpleTile.java @@ -8,7 +8,7 @@ import mightypork.rogue.world.map.TileRenderContext; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.TileModel; import mightypork.util.annotations.DefaultImpl; -import mightypork.util.files.ion.IonBundle; +import mightypork.util.ion.IonBundle; /** diff --git a/src/mightypork/rogue/world/tile/models/Wall.java b/src/mightypork/rogue/world/tile/models/Wall.java index d9348a9..aca076b 100644 --- a/src/mightypork/rogue/world/tile/models/Wall.java +++ b/src/mightypork/rogue/world/tile/models/Wall.java @@ -20,4 +20,11 @@ public class Wall extends SimpleTile { return false; } + + @Override + public boolean hasDroppedItems() + { + return false; + } + } diff --git a/src/mightypork/test/TestIonArray.java b/src/mightypork/test/TestIonArray.java deleted file mode 100644 index 629dd92..0000000 --- a/src/mightypork/test/TestIonArray.java +++ /dev/null @@ -1,25 +0,0 @@ -package mightypork.test; - - -import java.io.File; -import java.io.IOException; -import java.util.Random; - -import mightypork.util.files.ion.Ion; - - -public class TestIonArray { - - public static void main(String[] args) throws IOException - { - final byte[] array = new byte[1024 * 8]; - - final Random rand = new Random(); - - for (int i = 0; i < array.length; i++) { - array[i] = (byte) rand.nextInt(); - } - - Ion.toFile(new File("hello.ion"), array); - } -} diff --git a/src/mightypork/test/TestIonArray2.java b/src/mightypork/test/TestIonArray2.java deleted file mode 100644 index 65ff2e2..0000000 --- a/src/mightypork/test/TestIonArray2.java +++ /dev/null @@ -1,43 +0,0 @@ -package mightypork.test; - - -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import mightypork.util.files.ion.Ion; - - -public class TestIonArray2 { - - public static void main(String[] args) throws IOException - { - final int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 99999, 8888888 }; - - final OutputStream out = new FileOutputStream("fuck.ion"); - - Ion.writeIntArray(out, array); - Ion.writeString(out, "HELLO DUDE WHATSUP"); - Ion.writeCharArray(out, "HERE'S ONE COOL ARRAY!!!".toCharArray()); - - // --- - - final InputStream in = new FileInputStream("fuck.ion"); - - final int[] a = Ion.readIntArray(in); - - for (final int i : a) - System.out.println(i); - - final String s = Ion.readString(in); - System.out.println(s); - - final char[] v = Ion.readCharArray(in); - - for (final int i : v) - System.out.print((char) i); - - } -} diff --git a/src/mightypork/test/TestRandomSeed.java b/src/mightypork/test/TestRandomSeed.java deleted file mode 100644 index ff19311..0000000 --- a/src/mightypork/test/TestRandomSeed.java +++ /dev/null @@ -1,35 +0,0 @@ -package mightypork.test; - - -import java.util.Random; - - -public class TestRandomSeed { - - public static void main(String[] args) - { - - { - final Random rand = new Random(); - final long begin = System.currentTimeMillis(); - - for (int i = 0; i < 1000000; i++) { - rand.setSeed(1000); - } - - System.out.println((System.currentTimeMillis() - begin) / 1000D); - } - - { - final long begin = System.currentTimeMillis(); - - for (int i = 0; i < 1000000; i++) { - final Random rand = new Random(); - rand.setSeed(1000); - } - - System.out.println((System.currentTimeMillis() - begin) / 1000D); - } - - } -} diff --git a/src/mightypork/test/testworldtofile.java b/src/mightypork/test/testworldtofile.java deleted file mode 100644 index d6188cf..0000000 --- a/src/mightypork/test/testworldtofile.java +++ /dev/null @@ -1,6 +0,0 @@ -package mightypork.test; - - -public class testworldtofile { - -} diff --git a/src/mightypork/util/constraints/vect/mutable/VectAnimated.java b/src/mightypork/util/constraints/vect/mutable/VectAnimated.java index 0fa6166..1c40be7 100644 --- a/src/mightypork/util/constraints/vect/mutable/VectAnimated.java +++ b/src/mightypork/util/constraints/vect/mutable/VectAnimated.java @@ -17,7 +17,7 @@ import mightypork.util.math.Easing; public class VectAnimated extends VectMutable implements Pauseable, Updateable { private final NumAnimated x, y, z; - private double defaultDuration = 0; + private double defaultDuration = 0.5; /** @@ -83,45 +83,30 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable { @Override public void setX(double x) { - this.x.animate(x, defaultDuration); + this.x.setTo(x); } @Override public void setY(double y) { - this.y.animate(y, defaultDuration); + this.y.setTo(y); } @Override public void setZ(double z) { - this.z.animate(z, defaultDuration); + this.z.setTo(z); } - /** - * Add offset with animation - * - * @param offset added offset - * @param duration animation time (seconds) - */ public void add(Vect offset, double duration) { animate(this.add(offset), duration); } - /** - * Animate to given coordinates in given amount of time - * - * @param x - * @param y - * @param z - * @param duration animation time (seconds) - * @return this - */ public VectAnimated animate(double x, double y, double z, double duration) { this.x.animate(x, duration); @@ -131,13 +116,6 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable { } - /** - * Animate to given vec in given amount of time. - * - * @param target target (only it's current value will be used) - * @param duration animation time (seconds) - * @return this - */ public VectAnimated animate(Vect target, double duration) { animate(target.x(), target.y(), target.z(), duration); @@ -145,6 +123,22 @@ public class VectAnimated extends VectMutable implements Pauseable, Updateable { } + public VectAnimated animate(double x, double y, double z) + { + this.x.animate(x, defaultDuration); + this.y.animate(y, defaultDuration); + this.z.animate(z, defaultDuration); + return this; + } + + + public VectAnimated animate(Vect target) + { + animate(target.x(), target.y(), target.z()); + return this; + } + + /** * @return the default duration (seconds) */ diff --git a/src/mightypork/util/files/ion/Ion.java b/src/mightypork/util/files/ion/Ion.java deleted file mode 100644 index 474a9d0..0000000 --- a/src/mightypork/util/files/ion/Ion.java +++ /dev/null @@ -1,1316 +0,0 @@ -package mightypork.util.files.ion; - - -import java.io.*; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import mightypork.rogue.world.tile.Tile; -import mightypork.util.error.CorruptedDataException; -import mightypork.util.logging.Log; - - -/** - * Universal data storage system - * - * @author MightyPork - */ -public class Ion { - - /* - * 0-49 ... primitive and built-in types - * 60-79 ... technical marks - * 80-99 ... internal ION data types - */ - - // primitives 0..19 - /** Null mark */ - private static final short NULL = 0; - /** Boolean mark */ - private static final short BOOLEAN = 1; - /** Byte mark */ - private static final short BYTE = 2; - /** Character mark */ - private static final short CHAR = 3; - /** Short mark */ - private static final short SHORT = 4; - /** Integer mark */ - private static final short INT = 5; - /** Long mark */ - private static final short LONG = 6; - /** Float mark */ - private static final short FLOAT = 7; - /** Double mark */ - private static final short DOUBLE = 8; - /** String mark */ - private static final short STRING = 9; - /** Boolean array mark */ - private static final short BOOLEAN_ARRAY = 10; - /** Byte array mark */ - private static final short BYTE_ARRAY = 11; - /** Character array mark */ - private static final short CHAR_ARRAY = 12; - /** Short array mark */ - private static final short SHORT_ARRAY = 13; - /** Integer array mark */ - private static final short INT_ARRAY = 14; - /** Long array mark */ - private static final short LONG_ARRAY = 15; - /** Float array mark */ - private static final short FLOAT_ARRAY = 16; - /** Double array mark */ - private static final short DOUBLE_ARRAY = 17; - /** String array mark */ - private static final short STRING_ARRAY = 18; - - // technical 20..39 - - /** - * Entry mark - general purpose, marks an entry in sequence of objects. Used - * to indicate that the sequence continues wityh another element. - */ - public static final short ENTRY = 60; - - /** - * End mark - general purpose, marks end of sequence of stored objects. - */ - public static final short END = 61; - - // built in 80..99 - /** Map mark (built-in data structure) */ - static final short DATA_BUNDLE = 80; - - /** List mark (built-in data structure) */ - static final short DATA_LIST = 81; - - /** Ionizables */ - private static Map> customIonizables = new HashMap<>(); - - // buffers and helper arrays for storing to streams. - private static ByteBuffer bi = ByteBuffer.allocate(Integer.SIZE / 8); - private static ByteBuffer bd = ByteBuffer.allocate(Double.SIZE / 8); - private static ByteBuffer bf = ByteBuffer.allocate(Float.SIZE / 8); - private static ByteBuffer bc = ByteBuffer.allocate(Character.SIZE / 8); - private static ByteBuffer bl = ByteBuffer.allocate(Long.SIZE / 8); - private static ByteBuffer bs = ByteBuffer.allocate(Short.SIZE / 8); - private static byte[] ai = new byte[Integer.SIZE / 8]; - private static byte[] ad = new byte[Double.SIZE / 8]; - private static byte[] af = new byte[Float.SIZE / 8]; - private static byte[] ac = new byte[Character.SIZE / 8]; - private static byte[] al = new byte[Long.SIZE / 8]; - private static byte[] as = new byte[Short.SIZE / 8]; - - /** - * Indicates whether range checking should be performed when registering - * marks. - */ - private static boolean markRangeChecking; - - static { - markRangeChecking = false; - - registerIonizable(DATA_BUNDLE, IonBundle.class); - - markRangeChecking = true; - } - - - /** - * Register new {@link Ionizable} for direct reconstructing. - * - * @param mark mark to be used. Numbers 0..99 are reserved. Mark is of type - * Short, using values out of the short range will raise an - * except - * @param objClass class of the registered Ionizable - */ - public static void registerIonizable(int mark, Class objClass) - { - // negative marks are allowed. - if (mark > Short.MAX_VALUE) throw new IllegalArgumentException("Mark too high (max " + Short.MAX_VALUE + ")."); - if (mark < Short.MIN_VALUE) throw new IllegalArgumentException("Mark too low (min " + Short.MIN_VALUE + ")."); - - final short m = (short) mark; - - if (markRangeChecking && m >= 0 && m < 100) { - throw new IllegalArgumentException("Marks 0..99 are reserved."); - } - - if (customIonizables.containsKey(m)) { - throw new IllegalArgumentException("Mark " + m + " is already in use."); - } - - customIonizables.put(m, objClass); - } - - - /** - * Load an object from file. - * - * @param path file path - * @return the loaded object - * @throws CorruptedDataException - */ - public static Object fromFile(String path) throws CorruptedDataException - { - return fromFile(new File(path)); - } - - - /** - * Load an object from file. - * - * @param file file - * @return the loaded object - * @throws CorruptedDataException - */ - public static Object fromFile(File file) throws CorruptedDataException - { - try(InputStream in = new FileInputStream(file)) { - - final Object obj = fromStream(in); - return obj; - - } catch (final IOException e) { - throw new CorruptedDataException("Error loading ION file.", e); - } - } - - - /** - * Store an object to file. - * - * @param path file path - * @param obj object to store - * @throws IOException - */ - public static void toFile(String path, Object obj) throws IOException - { - toFile(new File(path), obj); - } - - - /** - * Store an object to file. - * - * @param file file - * @param obj object to store - * @throws IOException - */ - public static void toFile(File file, Object obj) throws IOException - { - try(OutputStream out = new FileOutputStream(file)) { - - writeObject(out, obj); - - out.flush(); - out.close(); - } catch (final Exception e) { - throw new IOException("Error writing to ION file.", e); - } - } - - - /** - * Load an object from stream. - * - * @param in input stream - * @return the loaded object - * @throws IOException - */ - public static Object fromStream(InputStream in) throws IOException - { - return readObject(in); - } - - - /** - * Write an object to a stream. - * - * @param out output stream - * @param obj written object - * @throws IOException - */ - public static void toStream(OutputStream out, Object obj) throws IOException - { - writeObject(out, obj); - } - - - /** - * Read single object from input stream, preceded by a mark. If a mark is - * not present, the behavior is undefined - in case the read bytes happen to - * match one of the registered marks, some garbage will be read. Otherwise - * an exception will be be thrown. - * - * @param in input stream - * @return the loaded object - * @throws IOException - */ - public static Object readObject(InputStream in) throws IOException - { - final short mark = readMark(in); - if (customIonizables.containsKey(mark)) { - Ionizable loaded; - - try { - - final Class clz = customIonizables.get(mark); - loaded = ((Ionizable) clz.newInstance()); - - } catch (InstantiationException | IllegalAccessException e) { - throw new RuntimeException("Cound not instantiate: " + Log.str(customIonizables.get(mark)), e); - } - - loaded.load(in); - return loaded; - } - - int length; - - switch (mark) { - case NULL: - return null; - - case BOOLEAN: - return readBoolean(in); - - case BYTE: - return readByte(in); - - case CHAR: - return readChar(in); - - case SHORT: - return readShort(in); - - case INT: - return readInt(in); - - case LONG: - return readLong(in); - - case FLOAT: - return readFloat(in); - - case DOUBLE: - return readDouble(in); - - case STRING: - return readString(in); - - case BOOLEAN_ARRAY: - length = readInt(in); - final boolean[] bools = new boolean[length]; - for (int i = 0; i < length; i++) { - bools[i] = readBoolean(in); - } - return bools; - - case BYTE_ARRAY: - length = readInt(in); - final byte[] bytes = new byte[length]; - for (int i = 0; i < length; i++) { - bytes[i] = readByte(in); - } - return bytes; - - case CHAR_ARRAY: - length = readInt(in); - final char[] chars = new char[length]; - for (int i = 0; i < length; i++) { - chars[i] = readChar(in); - } - return chars; - - case SHORT_ARRAY: - length = readInt(in); - final short[] shorts = new short[length]; - for (int i = 0; i < length; i++) { - shorts[i] = readShort(in); - } - return shorts; - - case INT_ARRAY: - length = readInt(in); - final int[] ints = new int[length]; - for (int i = 0; i < length; i++) { - ints[i] = readInt(in); - } - return ints; - - case LONG_ARRAY: - length = readInt(in); - final long[] longs = new long[length]; - for (int i = 0; i < length; i++) { - longs[i] = readLong(in); - } - return longs; - - case FLOAT_ARRAY: - length = readInt(in); - final float[] floats = new float[length]; - for (int i = 0; i < length; i++) { - floats[i] = readFloat(in); - } - return floats; - - case DOUBLE_ARRAY: - length = readInt(in); - final double[] doubles = new double[length]; - for (int i = 0; i < length; i++) { - doubles[i] = readDouble(in); - } - return doubles; - - case STRING_ARRAY: - length = readInt(in); - final String[] Strings = new String[length]; - for (int i = 0; i < length; i++) { - Strings[i] = readString(in); - } - return Strings; - - default: - throw new CorruptedDataException("Invalid Ion mark: " + mark); - } - } - - - public static void expect(InputStream in, short mark) throws IOException - { - if (readMark(in) != mark) throw new CorruptedDataException("Unexpected mark in ION stream."); - } - - - public static short readMark(InputStream in) throws IOException - { - return readShort(in); - } - - - public static void writeMark(OutputStream out, short mark) throws IOException - { - writeShort(out, mark); - } - - - /** - * Write a single object to output stream, with a mark. - * - * @param out output stream - * @param obj stored object - * @throws IOException - */ - public static void writeObject(OutputStream out, Object obj) throws IOException - { - if (obj instanceof Tile) throw new IllegalAccessError(); - if (obj == null) { - writeMark(out, NULL); - return; - } - - if (obj instanceof Ionizable) { - - final short mark = ((Ionizable) obj).getIonMark(); - - final Class clzRegistered = customIonizables.get(mark); - - if (clzRegistered == null) { - throw new IOException("Ionizable object not registered: " + Log.str(obj.getClass())); - } - - if (clzRegistered != obj.getClass()) { - throw new IOException("Registered class does not match actual one for " + Log.str(obj.getClass())); - } - - writeMark(out, mark); - ((Ionizable) obj).save(out); - return; - } - - if (obj instanceof Boolean) { - writeMark(out, BOOLEAN); - writeBoolean(out, (Boolean) obj); - return; - } - - if (obj instanceof Byte) { - writeMark(out, BYTE); - writeByte(out, (Byte) obj); - return; - } - - if (obj instanceof Character) { - writeMark(out, CHAR); - writeChar(out, (Character) obj); - return; - } - - if (obj instanceof Short) { - writeMark(out, SHORT); - writeShort(out, (Short) obj); - return; - } - - if (obj instanceof Integer) { - writeMark(out, INT); - writeInt(out, (Integer) obj); - return; - } - - if (obj instanceof Long) { - writeMark(out, LONG); - writeLong(out, (Long) obj); - return; - } - - if (obj instanceof Float) { - writeMark(out, FLOAT); - writeFloat(out, (Float) obj); - return; - } - - if (obj instanceof Double) { - writeMark(out, DOUBLE); - writeDouble(out, (Double) obj); - return; - } - - if (obj instanceof String) { - writeMark(out, STRING); - writeString(out, (String) obj); - return; - } - - if (obj instanceof boolean[]) { - writeMark(out, BOOLEAN_ARRAY); - writeBooleanArray(out, (boolean[]) obj); - return; - } - - if (obj instanceof byte[]) { - writeMark(out, BYTE_ARRAY); - writeByteArray(out, (byte[]) obj); - return; - } - - if (obj instanceof char[]) { - writeMark(out, CHAR_ARRAY); - writeCharArray(out, (char[]) obj); - return; - } - - if (obj instanceof short[]) { - writeMark(out, SHORT_ARRAY); - writeShortArray(out, (short[]) obj); - return; - } - - if (obj instanceof int[]) { - writeMark(out, INT_ARRAY); - writeIntArray(out, (int[]) obj); - return; - } - - if (obj instanceof long[]) { - writeMark(out, LONG_ARRAY); - writeLongArray(out, (long[]) obj); - return; - } - - if (obj instanceof float[]) { - writeMark(out, FLOAT_ARRAY); - writeFloatArray(out, (float[]) obj); - return; - } - - if (obj instanceof double[]) { - writeMark(out, DOUBLE_ARRAY); - writeDoubleArray(out, (double[]) obj); - return; - } - - if (obj instanceof String[]) { - writeMark(out, STRING_ARRAY); - writeStringArray(out, (String[]) obj); - return; - } - - throw new IOException("Object " + Log.str(obj) + " could not be be ionized."); - } - - - private static byte[] getBytesBool(boolean bool) - { - return new byte[] { (byte) (bool ? 1 : 0) }; - } - - - private static byte[] getBytesByte(byte num) - { - return new byte[] { num }; - } - - - private static byte[] getBytesChar(char num) - { - synchronized (bc) { - bc.clear(); - bc.putChar(num); - return bc.array(); - } - } - - - private static byte[] getBytesShort(short num) - { - synchronized (bs) { - bs.clear(); - bs.putShort(num); - return bs.array(); - } - } - - - private static byte[] getBytesInt(int num) - { - synchronized (bi) { - bi.clear(); - bi.putInt(num); - return bi.array(); - } - } - - - private static byte[] getBytesLong(long num) - { - synchronized (bl) { - bl.clear(); - bl.putLong(num); - return bl.array(); - } - } - - - private static byte[] getBytesFloat(float num) - { - synchronized (bf) { - bf.clear(); - bf.putFloat(num); - return bf.array(); - } - } - - - private static byte[] getBytesDouble(double num) - { - synchronized (bd) { - bd.clear(); - bd.putDouble(num); - return bd.array(); - } - } - - - /** - * Write a boolean (without a mark) - * - * @param out output stream - * @param b boolean to write - * @throws IOException - */ - public static void writeBoolean(OutputStream out, boolean b) throws IOException - { - out.write(getBytesBool(b)); - } - - - /** - * Write a byte (without a mark) - * - * @param out output stream - * @param b byte to write - * @throws IOException - */ - public static void writeByte(OutputStream out, byte b) throws IOException - { - out.write(getBytesByte(b)); - } - - - /** - * Write a char (without a mark) - * - * @param out output stream - * @param c char to write - * @throws IOException - */ - public static void writeChar(OutputStream out, char c) throws IOException - { - out.write(getBytesChar(c)); - } - - - /** - * Write a short (without a mark) - * - * @param out output stream - * @param s short to write - * @throws IOException - */ - public static void writeShort(OutputStream out, short s) throws IOException - { - out.write(getBytesShort(s)); - } - - - /** - * Write an integer (without a mark) - * - * @param out output stream - * @param i integer to write - * @throws IOException - */ - public static void writeInt(OutputStream out, int i) throws IOException - { - out.write(getBytesInt(i)); - } - - - /** - * Write a long (without a mark) - * - * @param out output stream - * @param l long to write - * @throws IOException - */ - public static void writeLong(OutputStream out, long l) throws IOException - { - out.write(getBytesLong(l)); - } - - - /** - * Write a float (without a mark) - * - * @param out output stream - * @param f float to write - * @throws IOException - */ - public static void writeFloat(OutputStream out, float f) throws IOException - { - out.write(getBytesFloat(f)); - } - - - /** - * Write a double (without a mark) - * - * @param out output stream - * @param d double to write - * @throws IOException - */ - public static void writeDouble(OutputStream out, double d) throws IOException - { - out.write(getBytesDouble(d)); - } - - - /** - * Write a String (without a mark) - * - * @param out output stream - * @param str String to write - * @throws IOException - */ - public static void writeString(OutputStream out, String str) throws IOException - { - writeCharArray(out, str.toCharArray()); - } - - - /** - * Write boolean array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeBooleanArray(OutputStream out, boolean[] arr) throws IOException - { - writeInt(out, arr.length); - for (final boolean a : arr) { - writeBoolean(out, a); - } - } - - - /** - * Write byte array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeByteArray(OutputStream out, byte[] arr) throws IOException - { - writeInt(out, arr.length); - for (final byte a : arr) { - writeByte(out, a); - } - } - - - /** - * Write char array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeCharArray(OutputStream out, char[] arr) throws IOException - { - writeInt(out, arr.length); - for (final char a : arr) { - writeChar(out, a); - } - } - - - /** - * Write short array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeShortArray(OutputStream out, short[] arr) throws IOException - { - writeInt(out, arr.length); - for (final short a : arr) { - writeShort(out, a); - } - } - - - /** - * Write int array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeIntArray(OutputStream out, int[] arr) throws IOException - { - writeInt(out, arr.length); - for (final int a : arr) { - writeInt(out, a); - } - } - - - /** - * Write long array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeLongArray(OutputStream out, long[] arr) throws IOException - { - writeInt(out, arr.length); - for (final long a : arr) { - writeLong(out, a); - } - } - - - /** - * Write float array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeFloatArray(OutputStream out, float[] arr) throws IOException - { - writeInt(out, arr.length); - for (final float a : arr) { - writeFloat(out, a); - } - } - - - /** - * Write double array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeDoubleArray(OutputStream out, double[] arr) throws IOException - { - writeInt(out, arr.length); - for (final double a : arr) { - writeDouble(out, a); - } - } - - - /** - * Write String array - * - * @param out output stream - * @param arr array to write - * @throws IOException - */ - public static void writeStringArray(OutputStream out, String[] arr) throws IOException - { - writeInt(out, arr.length); - for (final String a : arr) { - writeString(out, a); - } - } - - - /** - * Read a boolean (without a mark) - * - * @param in input stream - * @return boolean read - * @throws IOException - */ - public static boolean readBoolean(InputStream in) throws IOException - { - return readByte(in) > 0; - } - - - /** - * Read a byte (without a mark) - * - * @param in input stream - * @return byte read - * @throws IOException - */ - public static byte readByte(InputStream in) throws IOException - { - final int b = in.read(); - if (-1 == b) throw new IOException("End of stream."); - return (byte) b; - } - - - /** - * Read a char (without a mark) - * - * @param in input stream - * @return char read - * @throws IOException - */ - public static char readChar(InputStream in) throws IOException - { - synchronized (ac) { - if (-1 == in.read(ac, 0, ac.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(ac); - return buf.getChar(); - } - } - - - /** - * Read a short (without a mark) - * - * @param in input stream - * @return short read - * @throws IOException - */ - public static short readShort(InputStream in) throws IOException - { - synchronized (as) { - if (-1 == in.read(as, 0, as.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(as); - return buf.getShort(); - } - } - - - /** - * Read a long (without a mark) - * - * @param in input stream - * @return long read - * @throws IOException - */ - public static long readLong(InputStream in) throws IOException - { - synchronized (al) { - if (-1 == in.read(al, 0, al.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(al); - return buf.getLong(); - } - } - - - /** - * Read an integer (without a mark) - * - * @param in input stream - * @return integer read - * @throws IOException - */ - public static int readInt(InputStream in) throws IOException - { - synchronized (ai) { - if (-1 == in.read(ai, 0, ai.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(ai); - return buf.getInt(); - } - } - - - /** - * Read a float (without a mark) - * - * @param in input stream - * @return float read - * @throws IOException - */ - public static float readFloat(InputStream in) throws IOException - { - synchronized (af) { - if (-1 == in.read(af, 0, af.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(af); - return buf.getFloat(); - } - } - - - /** - * Read a double (without a mark) - * - * @param in input stream - * @return double read - * @throws IOException - */ - public static double readDouble(InputStream in) throws IOException - { - synchronized (ad) { - if (-1 == in.read(ad, 0, ad.length)) throw new IOException("End of stream."); - final ByteBuffer buf = ByteBuffer.wrap(ad); - return buf.getDouble(); - } - } - - - /** - * Read a string (without a mark) - * - * @param in input stream - * @return string read - * @throws IOException - */ - public static String readString(InputStream in) throws IOException - { - return String.valueOf(readCharArray(in)); - } - - - /** - * Read a boolean array (without a mark) - * - * @param in input stream - * @return boolean read - * @throws IOException - */ - public static boolean[] readBooleanArray(InputStream in) throws IOException - { - final int length = readInt(in); - final boolean[] booleans = new boolean[length]; - for (int i = 0; i < length; i++) { - booleans[i] = readBoolean(in); - } - return booleans; - } - - - /** - * Read a byte array (without a mark) - * - * @param in input stream - * @return byte read - * @throws IOException - */ - public static byte[] readByteArray(InputStream in) throws IOException - { - final int length = readInt(in); - final byte[] bytes = new byte[length]; - for (int i = 0; i < length; i++) { - bytes[i] = readByte(in); - } - return bytes; - } - - - /** - * Read a char array (without a mark) - * - * @param in input stream - * @return char read - * @throws IOException - */ - public static char[] readCharArray(InputStream in) throws IOException - { - final int length = readInt(in); - final char[] chars = new char[length]; - for (int i = 0; i < length; i++) { - chars[i] = readChar(in); - } - return chars; - } - - - /** - * Read a short array (without a mark) - * - * @param in input stream - * @return short read - * @throws IOException - */ - public static short[] readShortArray(InputStream in) throws IOException - { - final int length = readInt(in); - final short[] shorts = new short[length]; - for (int i = 0; i < length; i++) { - shorts[i] = readShort(in); - } - return shorts; - } - - - /** - * Read a int array (without a mark) - * - * @param in input stream - * @return int read - * @throws IOException - */ - public static int[] readIntArray(InputStream in) throws IOException - { - final int length = readInt(in); - final int[] ints = new int[length]; - for (int i = 0; i < length; i++) { - ints[i] = readInt(in); - } - return ints; - } - - - /** - * Read a long array (without a mark) - * - * @param in input stream - * @return long read - * @throws IOException - */ - public static long[] readLongArray(InputStream in) throws IOException - { - final int length = readInt(in); - final long[] longs = new long[length]; - for (int i = 0; i < length; i++) { - longs[i] = readLong(in); - } - return longs; - } - - - /** - * Read a float array (without a mark) - * - * @param in input stream - * @return float read - * @throws IOException - */ - public static float[] readFloatArray(InputStream in) throws IOException - { - final int length = readInt(in); - final float[] floats = new float[length]; - for (int i = 0; i < length; i++) { - floats[i] = readFloat(in); - } - return floats; - } - - - /** - * Read a double array (without a mark) - * - * @param in input stream - * @return double read - * @throws IOException - */ - public static double[] readDoubleArray(InputStream in) throws IOException - { - final int length = readInt(in); - final double[] doubles = new double[length]; - for (int i = 0; i < length; i++) { - doubles[i] = readDouble(in); - } - return doubles; - } - - - /** - * Read a String array (without a mark) - * - * @param in input stream - * @return String read - * @throws IOException - */ - public static String[] readStringArray(InputStream in) throws IOException - { - final int length = readInt(in); - final String[] Strings = new String[length]; - for (int i = 0; i < length; i++) { - Strings[i] = readString(in); - } - return Strings; - } - - - /** - * Reads mark and returns true if the mark is ENTRY, false if the mark is - * END. Throws an exception otherwise. - * - * @param in input stream - * @return mark was ENTRY - * @throws IOException when the mark is neither ENTRY or END. - */ - public static boolean hasNextEntry(InputStream in) throws IOException - { - final short mark = readMark(in); - if (mark == ENTRY) return true; - if (mark == END) return false; - - throw new CorruptedDataException("Unexpected mark " + mark + " encountered in sequence, expected " + ENTRY + " or " + END); - } - - - /** - * Read a sequence of elements into an ArrayList - * - * @param in input stream - * @return the collection - * @throws IOException - */ - public static Collection readSequence(InputStream in) throws IOException - { - return readSequence(in, new ArrayList()); - } - - - /** - * Load entries into a collection. The collection is cleaned first. - * - * @param in input stream - * @param filled collection to populate - * @return the collection - * @throws IOException - */ - @SuppressWarnings("unchecked") - public static Collection readSequence(InputStream in, Collection filled) throws IOException - { - try { - filled.clear(); - while (hasNextEntry(in)) { - filled.add((T) readObject(in)); - } - return filled; - } catch (final ClassCastException e) { - throw new CorruptedDataException("Unexpected element type.", e); - } - } - - - /** - * Write collection entries to a stream - * - * @param out output stream - * @param sequence written collection - * @throws IOException - */ - public static void writeSequence(OutputStream out, Collection sequence) throws IOException - { - for (final T element : sequence) { - writeMark(out, ENTRY); - writeObject(out, element); - } - writeMark(out, END); - } - - - /** - * Read element pairs into a HashMap - * - * @param in input stream - * @return the map - * @throws IOException - */ - public static Map readMap(InputStream in) throws IOException - { - return readMap(in, new HashMap()); - } - - - /** - * Load data into a map. The map is cleaned first. - * - * @param in input stream - * @param filled filled map - * @return the map - * @throws IOException - */ - @SuppressWarnings("unchecked") - public static Map readMap(InputStream in, Map filled) throws IOException - { - try { - filled.clear(); - while (hasNextEntry(in)) { - final K key = (K) readObject(in); - final V value = (V) readObject(in); - - filled.put(key, value); - } - return filled; - } catch (final ClassCastException e) { - throw new CorruptedDataException("Unexpected element type.", e); - } - } - - - /** - * Write a map - * - * @param out output stream - * @param map map to write - * @throws IOException - */ - public static void writeMap(OutputStream out, Map map) throws IOException - { - for (final Entry e : map.entrySet()) { - if (e.getValue() == null) { - continue; - } - - writeMark(out, ENTRY); - writeObject(out, e.getKey()); - writeObject(out, e.getValue()); - } - writeMark(out, END); - } - -} diff --git a/src/mightypork/util/files/ion/IonBundle.java b/src/mightypork/util/files/ion/IonBundle.java deleted file mode 100644 index be7525f..0000000 --- a/src/mightypork/util/files/ion/IonBundle.java +++ /dev/null @@ -1,58 +0,0 @@ -package mightypork.util.files.ion; - - -import mightypork.util.files.ion.templates.StreamableHashMap; - - -/** - *

- * Data bundle. - *

- *

- * Storing data in a bundle guarantees that future versions will be compatible - * with the older format. Reading using default values ensures that you will get - * some value even if it was not saved in the file. - *

- * - * @author MightyPork - */ -public class IonBundle extends StreamableHashMap implements Ionizable { - - /** - * Get an object. If not found, fallback is returned. - * - * @param key key - * @param fallback fallback - * @return element - */ - public T get(String key, T fallback) - { - try { - final T itm = (T) super.get(key); - if (itm == null) return fallback; - return itm; - } catch (final ClassCastException e) { - return fallback; - } - } - - - /** - * Store an element. It's allowed to store any object, but only primitive - * types, String, their arrays, and Ionizable objects can be successfully - * stored to stream.. - */ - @Override - public Object put(String key, Object value) - { - return super.put(key, value); - } - - - @Override - public short getIonMark() - { - return Ion.DATA_BUNDLE; - } - -} diff --git a/src/mightypork/util/files/ion/IonConstructor.java b/src/mightypork/util/files/ion/IonConstructor.java deleted file mode 100644 index 820cdfb..0000000 --- a/src/mightypork/util/files/ion/IonConstructor.java +++ /dev/null @@ -1,31 +0,0 @@ -package mightypork.util.files.ion; - - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - - -/** - *

- * Implicit constructor marked like this is intended to be solely used for ION - * de-serialization. - *

- *

- * Constructors marked like this should create a functional instance with - * default values. - *

- *

- * This is a descriptive annotation and has no other function. - *

- * - * @author MightyPork - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.CONSTRUCTOR) -@Documented -public @interface IonConstructor { - -} diff --git a/src/mightypork/util/files/ion/Ionizable.java b/src/mightypork/util/files/ion/Ionizable.java deleted file mode 100644 index bc423cc..0000000 --- a/src/mightypork/util/files/ion/Ionizable.java +++ /dev/null @@ -1,24 +0,0 @@ -package mightypork.util.files.ion; - - -/** - *

- * Data object that can be reconstructed by Ion based on it's mark. Such object - * MUST provide an implicit constructor. - *

- *

- * All {@link Ionizable}s must be registered to {@link Ion}, otherwise they - * can't be written/loaded using the mark. - *

- * - * @author MightyPork - */ -public interface Ionizable extends Streamable { - - /** - * Get Ion mark byte. - * - * @return Ion mark byte. - */ - public short getIonMark(); -} diff --git a/src/mightypork/util/files/ion/Streamable.java b/src/mightypork/util/files/ion/Streamable.java deleted file mode 100644 index c4d473d..0000000 --- a/src/mightypork/util/files/ion/Streamable.java +++ /dev/null @@ -1,36 +0,0 @@ -package mightypork.util.files.ion; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - - -/** - * - * Saveable to a stream. - * - * - * @author MightyPork - */ -public interface Streamable { - - /** - * Load data from the input stream. Must be compatible with the - * save method. - * - * @param in input stream - * @throws IOException - */ - void load(InputStream in) throws IOException; - - - /** - * Store data to output stream. - * - * @param out Output stream - * @throws IOException - */ - void save(OutputStream out) throws IOException; - -} diff --git a/src/mightypork/util/files/ion/templates/StreamableArrayList.java b/src/mightypork/util/files/ion/templates/StreamableArrayList.java deleted file mode 100644 index c202667..0000000 --- a/src/mightypork/util/files/ion/templates/StreamableArrayList.java +++ /dev/null @@ -1,28 +0,0 @@ -package mightypork.util.files.ion.templates; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; - -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.Streamable; - - -public class StreamableArrayList extends ArrayList implements Streamable { - - @Override - public void load(InputStream in) throws IOException - { - Ion.readSequence(in, this); - } - - - @Override - public void save(OutputStream out) throws IOException - { - Ion.writeSequence(out, this); - } - -} diff --git a/src/mightypork/util/files/ion/templates/StreamableHashMap.java b/src/mightypork/util/files/ion/templates/StreamableHashMap.java deleted file mode 100644 index 4e8d394..0000000 --- a/src/mightypork/util/files/ion/templates/StreamableHashMap.java +++ /dev/null @@ -1,28 +0,0 @@ -package mightypork.util.files.ion.templates; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashMap; - -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.Streamable; - - -public class StreamableHashMap extends HashMap implements Streamable { - - @Override - public void load(InputStream in) throws IOException - { - Ion.readMap(in, this); - } - - - @Override - public void save(OutputStream out) throws IOException - { - Ion.writeMap(out, this); - } - -} diff --git a/src/mightypork/util/files/ion/templates/StreamableHashSet.java b/src/mightypork/util/files/ion/templates/StreamableHashSet.java deleted file mode 100644 index c0cacd0..0000000 --- a/src/mightypork/util/files/ion/templates/StreamableHashSet.java +++ /dev/null @@ -1,28 +0,0 @@ -package mightypork.util.files.ion.templates; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashSet; - -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.Streamable; - - -public abstract class StreamableHashSet extends HashSet implements Streamable { - - @Override - public void load(InputStream in) throws IOException - { - Ion.readSequence(in, this); - } - - - @Override - public void save(OutputStream out) throws IOException - { - Ion.writeSequence(out, this); - } - -} diff --git a/src/mightypork/util/files/ion/templates/StreamableLinkedHashMap.java b/src/mightypork/util/files/ion/templates/StreamableLinkedHashMap.java deleted file mode 100644 index 8983131..0000000 --- a/src/mightypork/util/files/ion/templates/StreamableLinkedHashMap.java +++ /dev/null @@ -1,28 +0,0 @@ -package mightypork.util.files.ion.templates; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.LinkedHashMap; - -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.Streamable; - - -public class StreamableLinkedHashMap extends LinkedHashMap implements Streamable { - - @Override - public void load(InputStream in) throws IOException - { - Ion.readMap(in, this); - } - - - @Override - public void save(OutputStream out) throws IOException - { - Ion.writeMap(out, this); - } - -} diff --git a/src/mightypork/util/files/ion/templates/StreamableLinkedList.java b/src/mightypork/util/files/ion/templates/StreamableLinkedList.java deleted file mode 100644 index 5f92bbd..0000000 --- a/src/mightypork/util/files/ion/templates/StreamableLinkedList.java +++ /dev/null @@ -1,28 +0,0 @@ -package mightypork.util.files.ion.templates; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.LinkedList; - -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.Streamable; - - -public class StreamableLinkedList extends LinkedList implements Streamable { - - @Override - public void load(InputStream in) throws IOException - { - Ion.readSequence(in, this); - } - - - @Override - public void save(OutputStream out) throws IOException - { - Ion.writeSequence(out, this); - } - -} diff --git a/src/mightypork/util/files/ion/templates/StreamableStack.java b/src/mightypork/util/files/ion/templates/StreamableStack.java deleted file mode 100644 index ccdac67..0000000 --- a/src/mightypork/util/files/ion/templates/StreamableStack.java +++ /dev/null @@ -1,28 +0,0 @@ -package mightypork.util.files.ion.templates; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Stack; - -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.Streamable; - - -public class StreamableStack extends Stack implements Streamable { - - @Override - public void load(InputStream in) throws IOException - { - Ion.readSequence(in, this); - } - - - @Override - public void save(OutputStream out) throws IOException - { - Ion.writeSequence(out, this); - } - -} diff --git a/src/mightypork/util/files/ion/templates/StreamableTreeSet.java b/src/mightypork/util/files/ion/templates/StreamableTreeSet.java deleted file mode 100644 index b7e1cf6..0000000 --- a/src/mightypork/util/files/ion/templates/StreamableTreeSet.java +++ /dev/null @@ -1,28 +0,0 @@ -package mightypork.util.files.ion.templates; - - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.TreeSet; - -import mightypork.util.files.ion.Ion; -import mightypork.util.files.ion.Streamable; - - -public class StreamableTreeSet extends TreeSet implements Streamable { - - @Override - public void load(InputStream in) throws IOException - { - Ion.readSequence(in, this); - } - - - @Override - public void save(OutputStream out) throws IOException - { - Ion.writeSequence(out, this); - } - -} diff --git a/src/mightypork/util/ion/Ion.java b/src/mightypork/util/ion/Ion.java new file mode 100644 index 0000000..ab77f80 --- /dev/null +++ b/src/mightypork/util/ion/Ion.java @@ -0,0 +1,296 @@ +package mightypork.util.ion; + + +import java.io.*; + +import mightypork.util.logging.Log; + + +/** + * Universal data storage system (main API class) + * + * @author MightyPork + */ +public class Ion { + + // marks for object saving + /** Null mark */ + static final int NULL = 0; + /** Boolean mark */ + static final int BOOLEAN = 1; + /** Byte mark */ + static final int BYTE = 2; + /** Character mark */ + static final int CHAR = 3; + /** Short mark */ + static final int SHORT = 4; + /** Integer mark */ + static final int INT = 5; + /** Long mark */ + static final int LONG = 6; + /** Float mark */ + static final int FLOAT = 7; + /** Double mark */ + static final int DOUBLE = 8; + /** String mark */ + static final int STRING = 9; + /** Boolean array mark */ + static final int BOOLEAN_ARRAY = 10; + /** Byte array mark */ + static final int BYTE_ARRAY = 11; + /** Character array mark */ + static final int CHAR_ARRAY = 12; + /** Short array mark */ + static final int SHORT_ARRAY = 13; + /** Integer array mark */ + static final int INT_ARRAY = 14; + /** Long array mark */ + static final int LONG_ARRAY = 15; + /** Float array mark */ + static final int FLOAT_ARRAY = 16; + /** Double array mark */ + static final int DOUBLE_ARRAY = 17; + /** String array mark */ + static final int STRING_ARRAY = 18; + /** Entry mark - start of map or sequence entry */ + static final int ENTRY = 19; + /** End mark - end of sequence or map */ + static final int END = 20; + /** Map mark (built-in data structure) */ + static final int DATA_BUNDLE = 21; + /** Sequence wrapper for bundle */ + static final int SEQUENCE_WRAPPER = 22; + /** Map wrapper for bundle */ + static final int MAP_WRAPPER = 23; + + // technical 20..39 + + /** Ionizables */ + @SuppressWarnings("rawtypes") + private static Class[] registered = new Class[256]; + + private static boolean reservedMarkChecking; + + static { + reservedMarkChecking = false; + + // register built-ins + registerBinary(DATA_BUNDLE, IonBundle.class); + registerBinary(SEQUENCE_WRAPPER, IonSequenceWrapper.class); + registerBinary(MAP_WRAPPER, IonMapWrapper.class); + + reservedMarkChecking = true; + } + + + /** + * Register new {@link IonBinary} class for writing/loading. + * + * @param mark mark to be used 50..255, unless internal + * @param objClass class of the registered object + */ + public static void registerBinary(int mark, Class objClass) + { + // negative marks are allowed. + if (mark > 255) throw new IllegalArgumentException("Mark must be < 256."); + if (mark < 0) throw new IllegalArgumentException("Mark must be positive."); + + if (reservedMarkChecking && mark < 50) { + throw new IllegalArgumentException("Marks 0..49 are reserved."); + } + + if (registered[mark] != null) { + throw new IllegalArgumentException("Mark " + mark + " is already in use."); + } + + try { + objClass.getConstructor(); + } catch (NoSuchMethodException | SecurityException e) { + throw new IllegalArgumentException("Class " + Log.str(objClass) + " doesn't have an implicit constructor."); + } + + registered[mark] = objClass; + } + + + /** + * Load binary from file and cast. + */ + public static T fromFile(String path) throws IOException + { + return fromFile(new File(path)); + } + + + /** + * Load binary from file and cast. + */ + public static T fromFile(File file) throws IOException + { + try(InputStream in = new FileInputStream(file)) { + return fromStream(in); + } + } + + + /** + * Load bundled from file and unwrap. + */ + public static T fromFile(String path, Class objClass) throws IOException + { + return fromFile(new File(path), objClass); + } + + + /** + * Load bundled from file and unwrap. + */ + public static T fromFile(File file, Class objClass) throws IOException + { + try(InputStream in = new FileInputStream(file)) { + + return fromStream(in, objClass); + + } + } + + + public static void toFile(String path, IonBundled obj) throws IOException + { + toFile(new File(path), obj); + } + + + /** + * Wrap bundled and save to file. + */ + public static void toFile(File file, IonBundled obj) throws IOException + { + toFile(file, wrap(obj)); + } + + + /** + * Write binary to file with mark. + */ + public static void toFile(String path, IonBinary obj) throws IOException + { + toFile(new File(path), obj); + } + + + /** + * Write binary to file with mark. + */ + public static void toFile(File file, IonBinary obj) throws IOException + { + try(OutputStream out = new FileOutputStream(file)) { + + toStream(out, obj); + + out.flush(); + out.close(); + } catch (final Exception e) { + throw new IOException("Error writing to ION file.", e); + } + } + + + /** + * Load object from stream based on mark, try to cast. + */ + public static T fromStream(InputStream in) throws IOException + { + final IonInput inp = new IonInput(in); + + return (T) inp.readObject(); + } + + + /** + * Load bundled object from stream, unwrap. + */ + public static T fromStream(InputStream in, Class objClass) throws IOException + { + return unwrap(new IonInput(in).readBundle(), objClass); + } + + + /** + * Write object to output with a mark. + */ + public static void toStream(OutputStream out, IonBinary obj) throws IOException + { + new IonOutput(out).writeObject(obj); + } + + + /** + * Create new bundle and write the object to it. + */ + public static IonBundle wrap(IonBundled content) throws IOException + { + final IonBundle ib = new IonBundle(); + content.save(ib); + return ib; + } + + + /** + * Try to unwrap an object from bundle. The object class must have implicit + * accessible constructor. + * + * @param bundle unwrapped bundle + * @param objClass class of desired object + * @return the object unwrapped + * @throws IOException + */ + public static T unwrap(IonBundle bundle, Class objClass) throws IOException + { + try { + final T inst = objClass.newInstance(); + inst.load(bundle); + return inst; + } catch (InstantiationException | IllegalAccessException e) { + throw new IOException("Could not instantiate " + Log.str(objClass) + "."); + } + } + + + @SuppressWarnings("unchecked") + static Class getClassForMark(int mark) + { + return registered[mark]; + } + + + /** + * @return true if the mark is for a registered {@link IonBinary} object + */ + static boolean isMarkForBinary(int mark) + { + return registered[mark] != null; + } + + + /** + * Make sure object is registered in the table + * + * @throws IOException if not registered or class mismatch + */ + static void assertRegistered(IonBinary obj) throws IOException + { + final int mark = obj.getIonMark(); + + final Class clz = Ion.getClassForMark(mark); + + if (clz == null) { + throw new IOException("Not registered - mark: " + mark + ", class: " + Log.str(obj.getClass())); + } + + if (clz != obj.getClass()) { + throw new IOException("Class mismatch - mark: " + mark + ", class: " + Log.str(obj.getClass())); + } + } + +} diff --git a/src/mightypork/util/ion/IonBinary.java b/src/mightypork/util/ion/IonBinary.java new file mode 100644 index 0000000..a53d345 --- /dev/null +++ b/src/mightypork/util/ion/IonBinary.java @@ -0,0 +1,39 @@ +package mightypork.util.ion; + + +import java.io.IOException; + + +/** + * Binary ion object + * + * @author MightyPork + */ +public interface IonBinary { + + /** + * Load data from the input stream. + * + * @param in input stream + * @throws IOException + */ + void load(IonInput in) throws IOException; + + + /** + * Store data to output stream (in such way that the load method will later + * be able to read it). + * + * @param out Output stream + * @throws IOException + */ + void save(IonOutput out) throws IOException; + + + /** + * Get Ion mark byte. + * + * @return Ion mark byte. + */ + public short getIonMark(); +} diff --git a/src/mightypork/util/ion/IonBundle.java b/src/mightypork/util/ion/IonBundle.java new file mode 100644 index 0000000..53aefd7 --- /dev/null +++ b/src/mightypork/util/ion/IonBundle.java @@ -0,0 +1,284 @@ +package mightypork.util.ion; + + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + + +/** + * Ion data bundle - simplified Map + * + * @author MightyPork + */ +public class IonBundle implements IonBinary { + + private final Map backingMap = new HashMap<>(); + + + public void putBundled(String key, IonBundled saved) throws IOException + { + final IonBundle ib = new IonBundle(); + saved.save(ib); + put(key, ib); + } + + + public T getBundled(String key, Class objClass) throws IOException + { + return Ion.unwrap(get(key, (IonBundle) null), objClass); + } + + + public void loadBundled(String key, IonBundled loaded) throws IOException + { + IonBundle ib = get(key, null); + + if (ib == null) ib = new IonBundle(); + + loaded.load(ib); + } + + + public Map getMap(String key) + { + final Map m = new HashMap<>(); + loadMap(key, m); + return m; + } + + + public void loadMap(String key, Map map) + { + final IonMapWrapper imw = get(key, null); + if (imw == null) return; + map.clear(); + imw.fill(map); + } + + + public Collection getSequence(String key) + { + final Collection s = new ArrayList<>(); + loadSequence(key, s); + return s; + } + + + public void loadSequence(String key, Collection sequence) + { + final IonSequenceWrapper isw = get(key, null); + if (isw == null) return; + sequence.clear(); + isw.fill(sequence); + } + + + /** + *

+ * Get an object. + *

+ *

+ * If not found or of type incompatible with fallback, fallback is returned. + *

+ * + * @param key + * @param fallback value + * @return value + */ + public T get(String key, T fallback) + { + try { + final T itm = (T) backingMap.get(key); + if (itm == null) return fallback; + return itm; + } catch (final ClassCastException e) { + return fallback; + } + } + + + public void put(String key, IonBinary value) + { + if (key == null || value == null) return; + backingMap.put(key, value); + } + + + public void put(String key, boolean value) + { + backingMap.put(key, value); + } + + + public void put(String key, char value) + { + backingMap.put(key, value); + } + + + public void put(String key, short value) + { + backingMap.put(key, value); + } + + + public void put(String key, int value) + { + backingMap.put(key, value); + } + + + public void put(String key, long value) + { + backingMap.put(key, value); + } + + + public void put(String key, double value) + { + backingMap.put(key, value); + } + + + public void put(String key, float value) + { + backingMap.put(key, value); + } + + + public void put(String key, String value) + { + backingMap.put(key, value); + } + + + public void put(String key, boolean[] value) + { + backingMap.put(key, value); + } + + + public void put(String key, char[] value) + { + backingMap.put(key, value); + } + + + public void put(String key, short[] value) + { + backingMap.put(key, value); + } + + + public void put(String key, int[] value) + { + backingMap.put(key, value); + } + + + public void put(String key, long[] value) + { + backingMap.put(key, value); + } + + + public void put(String key, double[] value) + { + backingMap.put(key, value); + } + + + public void put(String key, float[] value) + { + backingMap.put(key, value); + } + + + public void put(String key, String[] value) + { + backingMap.put(key, value); + } + + + @SuppressWarnings("rawtypes") + public void putSequence(String key, Collection c) + { + backingMap.put(key, new IonSequenceWrapper(c)); + } + + + @SuppressWarnings("rawtypes") + public void putMap(String key, Map m) + { + backingMap.put(key, new IonMapWrapper(m)); + } + + + @Override + public short getIonMark() + { + return Ion.DATA_BUNDLE; + } + + + @Override + public void load(IonInput in) throws IOException + { + in.readMap(backingMap); + } + + + @Override + public void save(IonOutput out) throws IOException + { + out.writeMap(backingMap); + } + + + public int size() + { + return backingMap.size(); + } + + + public boolean isEmpty() + { + return backingMap.isEmpty(); + } + + + public void clear() + { + backingMap.clear(); + } + + + public Object remove(Object key) + { + return backingMap.remove(key); + } + + + @Override + public String toString() + { + return backingMap.toString(); + } + + + @Override + public boolean equals(Object o) + { + return backingMap.equals(o); + } + + + @Override + public int hashCode() + { + return 47 ^ backingMap.hashCode(); + } +} diff --git a/src/mightypork/util/ion/IonBundled.java b/src/mightypork/util/ion/IonBundled.java new file mode 100644 index 0000000..ae2915f --- /dev/null +++ b/src/mightypork/util/ion/IonBundled.java @@ -0,0 +1,18 @@ +package mightypork.util.ion; + + +import java.io.IOException; + + +/** + * Bundled ion object + * + * @author MightyPork + */ +public interface IonBundled { + + void load(IonBundle bundle) throws IOException; + + + void save(IonBundle bundle) throws IOException; +} diff --git a/src/mightypork/util/ion/IonInput.java b/src/mightypork/util/ion/IonInput.java new file mode 100644 index 0000000..d14c714 --- /dev/null +++ b/src/mightypork/util/ion/IonInput.java @@ -0,0 +1,439 @@ +package mightypork.util.ion; + + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import mightypork.util.error.CorruptedDataException; + + +/** + * Ion input stream + * + * @author MightyPork + */ +public class IonInput { + + private final DataInput in; + + + public IonInput(InputStream in) + { + this.in = new DataInputStream(in); + } + + + /** + * Read int 0-255. Suitable when the int was written using + * writeIntByte() method. + * + * @return int + * @throws IOException + */ + public int readIntByte() throws IOException + { + return in.readUnsignedByte(); + } + + + /** + * Read an int 0-65535. Suitable when the int was written using + * writeIntShort() method. + * + * @return int + * @throws IOException + */ + public int readIntShort() throws IOException + { + return in.readUnsignedShort(); + } + + + public boolean readBoolean() throws IOException + { + return in.readBoolean(); + } + + + public byte readByte() throws IOException + { + return in.readByte(); + } + + + public short readShort() throws IOException + { + return in.readShort(); + } + + + public char readChar() throws IOException + { + return in.readChar(); + } + + + public int readInt() throws IOException + { + return in.readInt(); + } + + + public long readLong() throws IOException + { + return in.readLong(); + } + + + public float readFloat() throws IOException + { + return in.readFloat(); + } + + + public double readDouble() throws IOException + { + return in.readDouble(); + } + + + public String readString() throws IOException + { + return in.readUTF(); + } + + + public boolean[] readBooleans() throws IOException + { + final int length = readLength(); + final boolean[] arr = new boolean[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readBoolean(); + } + return arr; + } + + + public byte[] readBytes() throws IOException + { + final int length = readLength(); + final byte[] arr = new byte[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readByte(); + } + return arr; + } + + + public char[] readChars() throws IOException + { + final int length = readLength(); + final char[] arr = new char[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readChar(); + } + return arr; + } + + + public short[] readShorts() throws IOException + { + final int length = readLength(); + final short[] arr = new short[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readShort(); + } + return arr; + } + + + public int[] readInts() throws IOException + { + final int length = readLength(); + final int[] arr = new int[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readInt(); + } + return arr; + } + + + public long[] readLongs() throws IOException + { + final int length = readLength(); + final long[] arr = new long[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readLong(); + } + return arr; + } + + + public float[] readFloats() throws IOException + { + final int length = readLength(); + final float[] arr = new float[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readFloat(); + } + return arr; + } + + + public double[] readDoubles() throws IOException + { + final int length = readLength(); + final double[] arr = new double[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readDouble(); + } + return arr; + } + + + public String[] readStrings() throws IOException + { + final int length = readLength(); + final String[] arr = new String[length]; + for (int i = 0; i < length; i++) { + arr[i] = in.readUTF(); + } + return arr; + } + + + public Object[] readObjects() throws IOException + { + final int length = readLength(); + final Object[] arr = new Object[length]; + for (int i = 0; i < length; i++) { + arr[i] = readObject(); + } + return arr; + } + + + public IonBundle readBundle() throws IOException + { + return (IonBundle) readObject(); + } + + + private int readMark() throws IOException + { + return readIntByte(); + } + + + private int readLength() throws IOException + { + return readIntShort(); + } + + + /** + *

+ * Read object based on mark; if null mark is found, returns default value. + *

+ *

+ * If, however, an object of invalid or different type is found, an + * exception will be thrown. + *

+ * + * @param def default value. + * @return the loaded object + * @throws IOException + */ + @SuppressWarnings("unchecked") + public T readObject(T def) throws IOException + { + try { + final Object o = readObject(); + return (T) (o == null ? def : o); + } catch (final Exception e) { + throw new IOException("Could not load object.", e); + } + } + + + /** + * Read single object, preceded by a mark. + * + * @return the loaded object + * @throws IOException + */ + public Object readObject() throws IOException + { + final int mark = readMark(); + if (Ion.isMarkForBinary(mark)) { + IonBinary loaded; + + try { + + loaded = Ion.getClassForMark(mark).newInstance(); + + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException("Couldn not load object with mark: " + mark, e); + } + + loaded.load(this); + return loaded; + } + + switch (mark) { + case Ion.NULL: + return null; + + case Ion.BOOLEAN: + return readBoolean(); + + case Ion.BYTE: + return readByte(); + + case Ion.CHAR: + return readChar(); + + case Ion.SHORT: + return readShort(); + + case Ion.INT: + return readInt(); + + case Ion.LONG: + return readLong(); + + case Ion.FLOAT: + return readFloat(); + + case Ion.DOUBLE: + return readDouble(); + + case Ion.STRING: + return readString(); + + case Ion.BOOLEAN_ARRAY: + return readBooleans(); + + case Ion.BYTE_ARRAY: + return readBytes(); + + case Ion.CHAR_ARRAY: + return readChars(); + + case Ion.SHORT_ARRAY: + return readShorts(); + + case Ion.INT_ARRAY: + return readInts(); + + case Ion.LONG_ARRAY: + return readLongs(); + + case Ion.FLOAT_ARRAY: + return readFloats(); + + case Ion.DOUBLE_ARRAY: + return readDoubles(); + + case Ion.STRING_ARRAY: + return readStrings(); + + default: + throw new CorruptedDataException("Invalid mark: " + mark); + } + } + + + /** + * Reads mark and returns true if the mark is ENTRY, false if the mark is + * END. Throws an exception otherwise. + * + * @return mark was ENTRY + * @throws IOException when the mark is neither ENTRY or END. + */ + public boolean hasNextEntry() throws IOException + { + final int mark = readMark(); + if (mark == Ion.ENTRY) return true; + if (mark == Ion.END) return false; + + throw new CorruptedDataException("Unexpected mark in sequence: " + mark); + } + + + /** + * Read a sequence of elements into an ArrayList + * + * @return the collection + * @throws IOException + */ + public Collection readSequence() throws IOException + { + return readSequence(new ArrayList()); + } + + + /** + * Load entries into a collection. The collection is cleaned first. + * + * @param filled collection to populate + * @return the collection + * @throws IOException + */ + @SuppressWarnings("unchecked") + public Collection readSequence(Collection filled) throws IOException + { + try { + filled.clear(); + while (hasNextEntry()) { + filled.add((T) readObject()); + } + return filled; + } catch (final ClassCastException e) { + throw new CorruptedDataException("Unexpected element type in sequence.", e); + } + } + + + /** + * Read element pairs into a HashMap + * + * @return the map + * @throws IOException + */ + public Map readMap() throws IOException + { + return readMap(new HashMap()); + } + + + /** + * Load data into a map. The map is cleaned first. + * + * @param filled filled map + * @return the map + * @throws IOException + */ + @SuppressWarnings("unchecked") + public Map readMap(Map filled) throws IOException + { + try { + filled.clear(); + while (hasNextEntry()) { + final K key = (K) readObject(); + final V value = (V) readObject(); + + filled.put(key, value); + } + return filled; + } catch (final ClassCastException e) { + throw new CorruptedDataException("Unexpected element type in map.", e); + } + } +} diff --git a/src/mightypork/util/ion/IonMapWrapper.java b/src/mightypork/util/ion/IonMapWrapper.java new file mode 100644 index 0000000..481a7ab --- /dev/null +++ b/src/mightypork/util/ion/IonMapWrapper.java @@ -0,0 +1,55 @@ +package mightypork.util.ion; + + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class IonMapWrapper implements IonBinary { + + private final Map map; + + + public IonMapWrapper() + { + map = new HashMap(); + } + + + public IonMapWrapper(Map saved) + { + map = saved; + } + + + @Override + public void load(IonInput in) throws IOException + { + map.clear(); + in.readMap(map); + } + + + @Override + public void save(IonOutput out) throws IOException + { + out.writeMap(map); + } + + + public void fill(Map o) + { + o.clear(); + o.putAll(map); + } + + + @Override + public short getIonMark() + { + return Ion.MAP_WRAPPER; + } + +} diff --git a/src/mightypork/util/ion/IonOutput.java b/src/mightypork/util/ion/IonOutput.java new file mode 100644 index 0000000..145d720 --- /dev/null +++ b/src/mightypork/util/ion/IonOutput.java @@ -0,0 +1,383 @@ +package mightypork.util.ion; + + +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; + +import mightypork.util.logging.Log; + + +/** + * Ion output stream + * + * @author MightyPork + */ +public class IonOutput { + + private final DataOutput out; + + + public IonOutput(OutputStream out) + { + this.out = new DataOutputStream(out); + } + + + public void writeBoolean(boolean a) throws IOException + { + out.writeBoolean(a); + } + + + public void writeByte(int a) throws IOException + { + out.writeByte(a); + } + + + public void writeShort(int a) throws IOException + { + out.writeShort(a); + } + + + public void writeChar(int a) throws IOException + { + out.writeChar(a); + } + + + public void writeInt(int a) throws IOException + { + out.writeInt(a); + } + + + public void writeIntShort(int a) throws IOException + { + out.writeShort(a); + } + + + public void writeIntByte(int a) throws IOException + { + out.writeByte(a); + } + + + public void writeLong(long a) throws IOException + { + out.writeLong(a); + } + + + public void writeFloat(float a) throws IOException + { + out.writeFloat(a); + } + + + public void writeDouble(double a) throws IOException + { + out.writeDouble(a); + } + + + public void writeBytes(String a) throws IOException + { + out.writeBytes(a); + } + + + public void writeString(String a) throws IOException + { + out.writeUTF(a); + } + + + public void writeBooleans(boolean[] arr) throws IOException + { + writeLength(arr.length); + for (final boolean a : arr) { + out.writeBoolean(a); + } + } + + + public void writeBytes(byte[] arr) throws IOException + { + writeLength(arr.length); + for (final byte a : arr) { + out.writeByte(a); + } + } + + + public void writeChars(char[] arr) throws IOException + { + writeLength(arr.length); + for (final char a : arr) { + out.writeChar(a); + } + } + + + public void writeShorts(short[] arr) throws IOException + { + writeLength(arr.length); + for (final short a : arr) { + out.writeShort(a); + } + } + + + public void writeInts(int[] arr) throws IOException + { + writeLength(arr.length); + for (final int a : arr) { + out.writeInt(a); + } + } + + + public void writeLongs(long[] arr) throws IOException + { + writeLength(arr.length); + for (final long a : arr) { + out.writeLong(a); + } + } + + + public void writeFloats(float[] arr) throws IOException + { + writeLength(arr.length); + for (final float a : arr) { + out.writeFloat(a); + } + } + + + public void writeDoubles(double[] arr) throws IOException + { + writeLength(arr.length); + for (final double a : arr) { + out.writeDouble(a); + } + } + + + public void writeStrings(String[] arr) throws IOException + { + writeLength(arr.length); + for (final String a : arr) { + out.writeUTF(a); + } + } + + + public void writeBundle(IonBundle bundle) throws IOException + { + writeObject(bundle); + } + + + /** + * Write array of objects. Supported are Ionizables, primitive types, + * String, and their arrays. + * + * @param arr array to write + * @throws IOException on IO error or on invalid object type. + */ + public void writeObjects(Object[] arr) throws IOException + { + writeLength(arr.length); + for (final Object a : arr) { + writeObject(a); + } + } + + + public void writeSequence(Collection sequence) throws IOException + { + for (final T element : sequence) { + writeMark(Ion.ENTRY); + writeObject(element); + } + writeMark(Ion.END); + } + + + public void writeMap(Map map) throws IOException + { + for (final Entry e : map.entrySet()) { + if (e.getValue() == null) { + continue; + } + + writeMark(Ion.ENTRY); + writeObject(e.getKey()); + writeObject(e.getValue()); + } + writeMark(Ion.END); + } + + + private void writeMark(int mark) throws IOException + { + writeIntByte(mark); + } + + + private void writeLength(int length) throws IOException + { + writeIntShort(length); + } + + + /** + * Write an object. Supported are Ionizable, primitive types, String, and + * their arrays. + * + * @param obj obj to write + * @throws IOException on IO error or invalid object type. + */ + public void writeObject(Object obj) throws IOException + { + if (obj == null) { + writeMark(Ion.NULL); + return; + } + + if (obj instanceof IonBinary) { + + final IonBinary ionObject = (IonBinary) obj; + + Ion.assertRegistered(ionObject); + + writeMark(ionObject.getIonMark()); + ionObject.save(this); + + return; + } + + if (obj instanceof IonBundled) { + throw new IOException("Bundled objects cannot be written to ION stream directly."); + } + + if (obj instanceof Boolean) { + writeMark(Ion.BOOLEAN); + writeBoolean((Boolean) obj); + return; + } + + if (obj instanceof Byte) { + writeMark(Ion.BYTE); + writeByte((Byte) obj); + return; + } + + if (obj instanceof Character) { + writeMark(Ion.CHAR); + writeChar((Character) obj); + return; + } + + if (obj instanceof Short) { + writeMark(Ion.SHORT); + writeShort((Short) obj); + return; + } + + if (obj instanceof Integer) { + writeMark(Ion.INT); + writeInt((Integer) obj); + return; + } + + if (obj instanceof Long) { + writeMark(Ion.LONG); + writeLong((Long) obj); + return; + } + + if (obj instanceof Float) { + writeMark(Ion.FLOAT); + writeFloat((Float) obj); + return; + } + + if (obj instanceof Double) { + writeMark(Ion.DOUBLE); + writeDouble((Double) obj); + return; + } + + if (obj instanceof String) { + writeMark(Ion.STRING); + writeString((String) obj); + return; + } + + if (obj instanceof boolean[]) { + writeMark(Ion.BOOLEAN_ARRAY); + writeBooleans((boolean[]) obj); + return; + } + + if (obj instanceof byte[]) { + writeMark(Ion.BYTE_ARRAY); + writeBytes((byte[]) obj); + return; + } + + if (obj instanceof char[]) { + writeMark(Ion.CHAR_ARRAY); + writeChars((char[]) obj); + return; + } + + if (obj instanceof short[]) { + writeMark(Ion.SHORT_ARRAY); + writeShorts((short[]) obj); + return; + } + + if (obj instanceof int[]) { + writeMark(Ion.INT_ARRAY); + writeInts((int[]) obj); + return; + } + + if (obj instanceof long[]) { + writeMark(Ion.LONG_ARRAY); + writeLongs((long[]) obj); + return; + } + + if (obj instanceof float[]) { + writeMark(Ion.FLOAT_ARRAY); + writeFloats((float[]) obj); + return; + } + + if (obj instanceof double[]) { + writeMark(Ion.DOUBLE_ARRAY); + writeDoubles((double[]) obj); + return; + } + + if (obj instanceof String[]) { + writeMark(Ion.STRING_ARRAY); + writeStrings((String[]) obj); + return; + } + + throw new IOException("Object " + Log.str(obj) + " could not be be written to stream."); + } +} diff --git a/src/mightypork/util/ion/IonSequenceWrapper.java b/src/mightypork/util/ion/IonSequenceWrapper.java new file mode 100644 index 0000000..becb53f --- /dev/null +++ b/src/mightypork/util/ion/IonSequenceWrapper.java @@ -0,0 +1,55 @@ +package mightypork.util.ion; + + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class IonSequenceWrapper implements IonBinary { + + private Collection collection = new ArrayList(); + + + public IonSequenceWrapper() + { + collection = new ArrayList(); + } + + + public IonSequenceWrapper(Collection saved) + { + collection = saved; + } + + + @Override + public void load(IonInput in) throws IOException + { + collection.clear(); + in.readSequence(collection); + } + + + @Override + public void save(IonOutput out) throws IOException + { + out.writeSequence(collection); + } + + + public void fill(Collection o) + { + o.clear(); + o.addAll(collection); + } + + + @Override + public short getIonMark() + { + return Ion.SEQUENCE_WRAPPER; + } + +}