[Java] Reinventing the Wheel

Accname

2D-Graphics enthusiast
Reaction score
1,462
Hi guys.

This is just a collection of generic utility libraries which I wrote out of boredom. None of these are something completely new. They all do some kind of functionality for which there are already several established libraries.

I just reinvented the wheel because I felt like it and it was fun.
More are to come as I write proper documentations for the libraries and add correct error handling.

All of these can, of course, be freely used by anybody. I dont care.
 

Accname

2D-Graphics enthusiast
Reaction score
1,462
Key-Value-Format
A file format for simple key-value items​
  • What is this?
    • A file format + a library to use the file format with
  • What is it good for?
    • This format (and the library) can be used to create simple key-value files in a tree structured hierarchy
    • The format is simple and human-readable, the library is easy to use and very reliable
    • The library is extremely small and efficient and very versatile
  • What can it be used for?
    • Basically anything that similar formats are used for. You can use this format (and the library) to store certain information on disc and later read and evaluate them in your program.
    • Examples include: Settings, Calculation-Results, Game-States, Databases, etc
  • What are similar formats?
    • XML, HTML, etc
Definition:
A .kvf file consists of any number of items. An item can have any number of children, which are also items.
Every item can also have any number of key-value-pairs.

A key-value-pair consists of two strings: a key and a value. In the .kvf format a key-value-pair is either represented by a "lonely" key or by assigning a value to a key. A lonely key is assumed to have an empty string as its value.

An item is represented in the following way:
An opening symbol '<', any number of key-value-pairs, and then a closing symbol '>'.
Following the definition of an item is the definition of its children which is represented as:
An opening curly bracket '{', any number of items, and then a closing curly bracket '}'.

A lonely key is represented by a string of arbitrary size that consists only of alphabetical letters.
A key-value assignment is represented by the key, a string of arbitrary size that consists only of alphabetical letters, followed by an equality sign '=' and then the value which can be any string of arbitrary size enclosed within quotation marks '"'. The hindmost quotation mark should not be directly preceeded by a backslash, otherwise it will be considered part of the value.

Any whitespaces within a .kvf file are ignored unless these are part of a value (enclosed by quotation marks).
A value can consist of any characters except quotation marks unless these are directly preceeded by a backslash '\'.
A backslash can be used as part of the value by having two backslashes directly next to each other.

A .kvf file can also include comments which have to be encapsulated by the comment symbol '/'. Comments may contain any characters and any number of whitespaces.
When the .kvf document is parsed comments will be ignored completely.

Examples of files:
A single item.
Code:
< >

A single item with a lonely key.
Code:
< HelloWorld >

A list with its children being numbers.
Code:
< list > {
    / This is a comment! /
    < num = "6" / Also a comment / >
    < / You can put these anywhere! / num = "9" >
    < num = "42" >
} / Comments! /

A more complex item, a hero for an RPG:
Code:
< hero
    name = "Awesome McCool"
    hp = "512.67"
    mp = "256.34"
    str = "128"
    def = "64"
    spd="32"
> {
    <item id = "longsword">
    <item id = "buckler">
    <item id = "chainmail">
    <item id = "potionofHealing">
}

A list of countries and notable towns.
Code:
 / These are countries and some of their most notable cities /
<France europe> {
    <Paris capital population =  "2250000" area = "2844.8" >
    <Marseille population = "1600000" area = "1204" >
    <Lyon population = "2110000" area = "954.19" >
}
<Italy europe> {
    <Rome capital population = "2640000" area = "1285.31" >
    <Milan population = "1310000" area = "181.76" >
    <Naples population = "950000" area = "117.27" >
}
<Germany europe> {
    <Berlin capital population = "3460000" area = "887,70" >
    <Hamburg population = "1780000" area = "755,16" >
    <Munich population = "1350000" area = "310,69" >
}
<USA america> {
    <Washington capital population = "630000" area = "177" >
    <NewYorkCity population = "8330000" area = "1214,4" >
}

And my favorite:
Code:
/    This document contains information about RPG-Heroes.
    These heroes have several attributes and other personal information.
    Furthermore, heroes have an inventory which contains items. /
 
 
    < Hero name = "Newmoon" title = "Elven Archer" > {
        < attributes
            STR = "6"
            DEX = "16"
            GLE = "13"
            LUC = "10"
            STA = "15"
            INT = "13"
            CHR = "12"
        >
        < inventory > {
            < item type = "weapon" id = "Elven Bow" >
            < item type = "armor" id = "Elven Cloak" >
            < item type = "misc" id = "Antidote" >
            < item type = "misc" id = "Arrow" amount = "333">
        }
    }
    < Hero name = "Nimble" title = "Master Thief" > {
        < attributes
            STR = "8"
            DEX = "18"
            GLE = "15"
            LUC = "16"
            STA = "9"
            INT = "19"
            CHR = "15"
        >
        < inventory > {
            < item type = "weapon" id = "Dagger" >
            < item type = "armor" id = "Thiefs Cloth" >
            < item type = "armor" id = "Pants" >
            < item type = "misc" id = "Lockpick" amount = "5">
            < item type = "misc" id = "Gold" amount = "42">
        }
    }
    < Hero name = "Mark" title = "The Red" > {
        < attributes
            STR = "17"
            DEX = "15"
            GLE = "12"
            LUC = "10"
            STA = "17"
            INT = "8"
            CHR = "10"
        >
        < inventory > {
            < item type = "weapon" id = "Axe" >
            < item type = "weapon" id = "Warhammer" >
            < item type = "armor" id = "Leather Mail" >
        }
    }
    < Hero name = "Ambrose" title = "Apprentice Mage" > {
        <dead>
        < attributes
            STR = "5"
            DEX = "9"
            GLE = "17"
            LUC = "8"
            STA = "7"
            INT = "19"
            CHR = "19"
        >
        < inventory > {
            < item type = "weapon" id = "Staff" >
            < item type = "armor" id = "Wizard Robe" >
            < item type = "armor" id = "Leather Boots" >
            < item type = "misc" id = "Potion of Healing" >
        }
    }
    < Hero name = "Rogar" title = "The Barbarian" > {
        < attributes
            STR = "19"
            DEX = "12"
            GLE = "8"
            LUC = "9"
            STA = "18"
            INT = "9"
            CHR = "12"
        >
        < inventory > {
            < item type = "weapon" id = "Longsword" >
            < item type = "armor" id = "Leather Cuirass" >
        }
    }
    < Hero name = "Frodo" title = "Ring Bearer" > {
        < inventory > {
            < item type = "weapon" id = "Sting" >
            < item type = "armor" id = "Elven Cloak" >
            < item type = "misc" id = "The one Ring" >
        }
    }

Example of Code showing how to use the library:
Reading the Hero
Code:
    public static void main(String[] args) throws FileNotFoundException {
        KVFParser parser = new KVFParser(new File("Documents/RPGHero.kvf"));
        KVItem doc = parser.parse();
 
        KVItem heroAsItem = doc.getChildWithKey("hero");
        String heroName = heroAsItem.get("name").asString();
        double heroHp = heroAsItem.get("hp").asDouble();
        double heroMp = heroAsItem.get("mp").asDouble();
        int heroStr = heroAsItem.get("str").asInt();
        int heroDef = heroAsItem.get("def").asInt();
 
        List<String> heroItems = new ArrayList<>();
        for (KVItem itemAsItem : heroAsItem.getChildrenWithKey("item")) {
            heroItems.add(itemAsItem.get("id").asString());
        }
 
        PrintStream out = System.out;
 
        out.println(heroName);                // => Awesome McCool
        out.println("Hp: "+heroHp);            // => Hp: 512.67
        out.println("Mp: "+heroMp);            // => Mp: 256.34
        out.println("Str: "+heroStr);        // => Str: 128
        out.println("Def: "+heroDef);        // => Def: 64
        out.println("Items: "+heroItems);    // => Items: [longsword, buckler, chainmail, potionOfHealing]
    }

Example for the cities:
Code:
    public static void main(String[] args) throws FileNotFoundException {
        KVFParser parser = new KVFParser(new File("Documents/Places.kvf"));
        KVItem doc = parser.parse();
        
        System.out.println(doc.deepToString());
        System.out.println();
        
        KVItem capitalOfFrance = doc.getChildWithKey("france").getChildWithKey("capital");
        System.out.println("Capital of France:");
        System.out.println(capitalOfFrance);
        
        KVItem capitalOfGermany = doc.getChildWithKey("GerMany").getChildWithKey("capital");
        System.out.println("Capital of Germany:");
        System.out.println(capitalOfGermany);
        
        KVItem capitalOfItaly = doc.getChildWithKey("Italy").getChildWithKey("capital");
        System.out.println("Capital of Italy:");
        System.out.println(capitalOfItaly);
        
        KVItem capitalOfBritain = doc.getChildWithKey("Britain").getChildWithKey("capital");
        System.out.println("Capital of Britain:");
        System.out.println(capitalOfBritain);
        
        List<KVItem> europeanCountries = doc.getChildrenWithKey("europe");
        System.out.println("European Countries:");
        System.out.println(europeanCountries);
        
        List<KVItem> capitalsOfNonEuropeanCountries = new ArrayList<>();
        for (KVItem countryAsItem : doc.getChildren()) {
            if (!countryAsItem.has("europe")) {
                capitalsOfNonEuropeanCountries.addAll(countryAsItem.getChildrenWithKey("capital"));
            }
        }
        System.out.println("Capitals of Non-European Countries:");
        System.out.println(capitalsOfNonEuropeanCountries);
        
        List<KVItem> capitalsOfAfricanCountries = new ArrayList<>();
        for (KVItem countryAsItem : doc.getChildren()) {
            if (countryAsItem.has("africa")) {
                capitalsOfAfricanCountries.addAll(countryAsItem.getChildrenWithKey("capital"));
            }
        }
        System.out.println("Capitals of African Countries:");
        System.out.println(capitalsOfAfricanCountries);
    }
    /* Output:
< > {
    < France europe > {
        < Paris capital population="2250000" area="2844.8" >
        < Marseille population="1600000" area="1204" >
        < Lyon population="2110000" area="954.19" >
    }
    < Italy europe > {
        < Rome capital population="2640000" area="1285.31" >
        < Milan population="1310000" area="181.76" >
        < Naples population="950000" area="117.27" >
    }
    < Germany europe > {
        < Berlin capital population="3460000" area="887,70" >
        < Hamburg population="1780000" area="755,16" >
        < Munich population="1350000" area="310,69" >
    }
    < USA america > {
        < Washington capital population="630000" area="177" >
        < NewYorkCity population="8330000" area="1214,4" >
    }
}

Capital of France:
< Paris capital population="2250000" area="2844.8" >
Capital of Germany:
< Berlin capital population="3460000" area="887,70" >
Capital of Italy:
< Rome capital population="2640000" area="1285.31" >
Capital of Britain:
< MissingItem >
European Countries:
[< France europe > { ... }, < Italy europe > { ... }, < Germany europe > { ... }]
Capitals of Non-European Countries:
[< Washington capital population="630000" area="177" >]
Capitals of African Countries:
[]

     */

Reading a document and writing parts of it back to disc:
Code:
    public static void main(String[] args) throws FileNotFoundException {
        long ts = System.nanoTime();
     
        KVFParser parser = new KVFParser(new File("Documents/Places.kvf"));
        KVItem doc = parser.parse();
     
        long since = System.nanoTime() - ts;
        System.out.println("Took: "+since / 1000);
        System.out.println("Item count: "+doc.getChildCount());
 
        doc.getChildWithKey("Italy").writeToFile(new File("Documents/Italy.kvf"));
        doc.getChildWithKey("France").writeToFile(new File("Documents/France.kvf"));
        doc.getChildWithKey("Germany").writeToFile(new File("Documents/Germany.kvf"));
    }

Creating a document and writing it to disc:
Code:
    public static void main(String[] args) throws FileNotFoundException {
        KVItem presidentList = new KVItem();
     
        KVItem nixon = new KVItem();
        nixon.set("firstname", "Richard");
        nixon.set("lastname", "Nixon");
        nixon.add("deceased");
        nixon.setParent(presidentList);
     
        KVItem reagan = new KVItem();
        reagan.set("firstname", "Ronald");
        reagan.set("lastname", "Reagan");
        reagan.add("deceased");
        reagan.setParent(presidentList);
     
        KVItem bushJr = new KVItem();
        bushJr.set("firstname", "George W.");
        bushJr.set("lastname", "Bush");
        bushJr.setParent(presidentList);
     
        KVItem obama = new KVItem();
        obama.set("firstname", "Barack");
        obama.set("lastname", "Obama");
        obama.setParent(presidentList);
     
        presidentList.writeToFile(new File("Documents/Presidents.kvf"));
    }
    /* Output:
    < > {
        < firstname="Richard" lastname="Nixon" deceased >
        < firstname="Ronald" lastname="Reagan" deceased >
        < firstname="George W." lastname="Bush" >
        < firstname="Barack" lastname="Obama" >
    }
    */

Very complex example for the Gamers:
Code:
    public static void main(String[] args) throws FileNotFoundException {
        KVFParser parser = new KVFParser(new File("Documents/Heroes.kvf"));
        KVItem party = parser.parse();
     
        List<Hero> heroes = new ArrayList<>();
        for (KVItem heroAsItem : party.getChildrenWithKey("hero")) {
            heroes.add(heroFromItem(heroAsItem));
        }
     
        Hero missingHero = heroFromItem(party.getChildWithValue("name", "Gandalf"));
        heroes.add(missingHero);
     
        for (Hero hero : heroes) {
            System.out.println(hero);
            System.out.println("--------------------");
        }
    }
 
    public static Hero heroFromItem(KVItem heroAsItem) {
        Hero heroAsHero = new Hero(heroAsItem.get("Name").asString("NAME"), heroAsItem.get("Title").asString("TITLE"));
     
        AliveState heroState = AliveState.UNKNOWN;
        if (heroAsItem.exists()) {
            if (heroAsItem.getChildWithKey("dead").exists()) {
                heroState = AliveState.DEAD;
            } else {
                heroState = AliveState.ALIVE;
            }
        }
        heroAsHero.setState(heroState);
     
        int DEFAULT_ATTRIBUTE_VALUE = 0;
        KVItem attributes = heroAsItem.getChildWithKey("attributes");
        for (Attribute attr : Attribute.values()) {
            KVPair asPair = attributes.get(attr.name());
            int value = asPair.asInt(DEFAULT_ATTRIBUTE_VALUE);
         
            heroAsHero.setAttribute(attr, value);
        }
     
        KVItem inventory = heroAsItem.getChildWithKey("inventory");
        for (KVItem itemAsKVItem : inventory.getChildrenWithKey("item")) {
            String type = itemAsKVItem.get("type").asString();
            String id = itemAsKVItem.get("id").asString();
            int amount = itemAsKVItem.get("amount").asInt(Item.DEFAULT_AMOUNT);
         
            Item item = new Item(id, type);
            item.setAmount(amount);
         
            heroAsHero.addItem(item);
        }
     
        return heroAsHero;
    }
 
    public static class Hero {
     
        private final String name;
        private final String title;
        private final int[] attributes;
        private final List<Item> items;
        private AliveState state;
     
        public Hero(String name, String title) {
            this.name = name;
            this.title = title;
            this.attributes = new int[Attribute.values().length];
            this.items = new ArrayList<>();
        }
     
        public void setState(AliveState state) {
            this.state = state;
        }
     
        public AliveState getState() {
            return state;
        }
     
        public void setAttribute(Attribute attr, int value) {
            attributes[attr.ordinal()] = value;
        }
     
        public int getAttribute(Attribute attr) {
            return attributes[attr.ordinal()];
        }
     
        public void addItem(Item item) {
            if (item == null) {
                throw new NullPointerException();
            }
            items.add(item);
        }
     
        public List<Item> getItems() {
            return items;
        }
     
        public String toString() {
            return name+" "+title+"\n"
                + "State: "+getState().toString()+"\n"
                + "Str: "+getAttribute(Attribute.STR)+"\n"
                + "Dex: "+getAttribute(Attribute.DEX)+"\n"
                + "Gle: "+getAttribute(Attribute.GLE)+"\n"
                + "Luc: "+getAttribute(Attribute.LUC)+"\n"
                + "Sta: "+getAttribute(Attribute.STA)+"\n"
                + "Int: "+getAttribute(Attribute.INT)+"\n"
                + "Chr: "+getAttribute(Attribute.CHR)+"\n"
                + "Items: "+getItems();
        }
     
    }
 
    public static class Item {
     
        public static final int DEFAULT_AMOUNT = 1;
     
        private String id;
        private String type;
        private int amount;
     
        public Item(String id, String type) {
            this.id = id;
            this.type = type;
            amount = DEFAULT_AMOUNT;
        }
     
        public void setAmount(int amount) {
            if (amount <= 0) {
                throw new IllegalArgumentException();
            }
            this.amount = amount;
        }
     
        public String toString() {
            if (amount == DEFAULT_AMOUNT) {
                return " ("+type+") " + id;
            }
            return " ("+type+") " + id + " ["+amount+"]";
        }
    }
 
    public static enum AliveState {
        ALIVE,
        DEAD,
        UNDEAD,
        UNKNOWN;
    }
 
    public static enum Attribute {
        STR,
        DEX,
        GLE,
        LUC,
        STA,
        INT,
        CHR;
    }

About the library:
The code is not completely final yet. I know that the parser is not perfect at this point and has a few problems with the definition here and there, but all included examples are parsed perfectly fine.

The library has many more useful methods to traverse the hierarchy.

I look forward to constructive criticism and advice on the design.
 

Attachments

  • jar-file.zip
    21.5 KB · Views: 400
  • src.zip
    13.5 KB · Views: 404
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top