[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: 406
  • src.zip
    13.5 KB · Views: 409
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      The Helper Discord

      Staff online

      • Ghan
        Administrator - Servers are fun

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top