This library provides an easy to use configuration API that maps class fields to readable configurations. It is possible to use any format, however by default the YAML format is provided with the library. Features: path and default value for every configuration entry is defined in code: Example comments on every field in the configuration: Example custom converter for your own data types: Example Type safety (due to 1.) Source: GitHub How to use the library Check Maven Central for the latest version. To see this library in action take a look at the CubeEngine project. If you have any suggestions or problems feel free to create an issue on GitHub.
I am a part of the project this was developed for. This library is really a pleasure to use! The config files look really nice, both in code and as config files. I'll upload an example later today to illustrate better how nice it is.
Not quite. Readablility makes the difference. Configs generated by this API are formatted to be readable. In addition comments can be added to any configuration path. All that isn't even the best part of this library. Have you ever done something like config.getString("a.supr.loooong.path") and wondered why you got null instead of the string due to the typo? That simply can't happen with this library, as the configuration paths are defined by code and therefore accesses are validated at compile time. the above example would look like this using above library instead of Bukkit's config API: config.a.supr.looong.path A typo would obviously result in a compilation error. Accidently getting a number from a path instead of a string? Not possible either as the field types are validated at compile time as well. More compile time checks ==> less time wasted with debugging strange bugs caused by typos in string constants.
Quick_Wango Hmm. I'll make sure to link this to the noobs then. Always having trouble with configs...
So I've created a simple example plugin to show off this lib. Source code: Spoiler (Move your mouse to the spoiler area to reveal the content) Show Spoiler Hide Spoiler Main class: Code:java import de.cubeisland.engine.configuration.ConfigurationFactory;import de.cubeisland.engine.configuration.codec.ConverterManager;import de.cubeisland.engine.configuration.convert.Converter;import de.cubeisland.engine.configuration.exception.ConversionException;import de.cubeisland.engine.configuration.exception.InvalidConfigurationException;import de.cubeisland.engine.configuration.node.Node;import de.cubeisland.engine.configuration.node.StringNode;import org.bukkit.Material;import org.bukkit.plugin.java.JavaPlugin; import java.io.File;import java.io.IOException;import java.util.logging.Level; public class ConfigPlugin extends JavaPlugin{ // We need a converter because The config API doesn't know about Bukkit classes public class MaterialConverter implements Converter<Material> { @Override public Node toNode(Material material, ConverterManager manager) throws ConversionException { return new StringNode(material.name()); } @Override public Material fromNode(Node node, ConverterManager manager) throws ConversionException { return Material.valueOf(node.asText()); } } public PluginConfig config; public void onEnable() { // The ConfigurationFactory is the core of the ConfigurationAPI ConfigurationFactory configFactory = new ConfigurationFactory(); // Because We'll use a custom class as key in the config(Material) // We have to register a Converter configFactory.getDefaultConverterManager().registerConverter(Material.class, new MaterialConverter()); // Try to load the config from file, or create it if it doesn't exist try { File dataFolder = this.getDataFolder(); if (!dataFolder.exists() && !dataFolder.mkdirs()) { throw new IOException("Could not create config folder"); } this.config = configFactory.load(PluginConfig.class, new File(dataFolder, "config.yml")); } catch (InvalidConfigurationException ex) { this.getLogger().log(Level.WARNING, "Could not load the config files!"); this.getLogger().log(Level.SEVERE, ex.getLocalizedMessage(), ex); this.getLogger().log(Level.WARNING, "Disabling plugin..."); this.getServer().getPluginManager().disablePlugin(this); return; } catch (IOException ex) { this.getLogger().log(Level.WARNING, "Could not load the config files!"); this.getLogger().log(Level.SEVERE, ex.getLocalizedMessage(), ex); this.getLogger().log(Level.WARNING, "Disabling plugin..."); this.getServer().getPluginManager().disablePlugin(this); return; } // We can now use the values in the config: this.getLogger().log(Level.INFO, config.helloMessage); this.getLogger().log(Level.INFO, "Block with id 20 is: " + config.ids.blocks.get(20)); } public void onDisable() { // We save the config when we shut down. This is necessary if we changed the config from the plugin. config.save(); } } Config Class: Code:java import de.cubeisland.engine.configuration.Section;import de.cubeisland.engine.configuration.YamlConfiguration;import de.cubeisland.engine.configuration.annotations.Comment;import de.cubeisland.engine.configuration.annotations.Name;import org.bukkit.Material; import java.util.HashMap;import java.util.Map; // This is the config class. It does not have to be an internal class!// It has to extend YamlConfigurationpublic class PluginConfig extends YamlConfiguration{ public String[] head() { return new String[] {"This is the head comment of the configuration file.", "It supports multiple lines!"}; } // This is a String value in the config. // In the config It'll be named 'hello-message', but you don't have to care about that // The value read from the config is inserted in this field public String helloMessage = "Hello there!"; // This is a integer value in the config. // It'll be named 'secret-number' in the config because of the [USER=35164]name[/USER] annotation // But you don't need to care about that. The value from the config is inserted back // in this field. // This field will also have a comment above it, because of the @Comment annotation. @Comment({"This is the key!", "Keep it secret!"}) [USER=35164]name[/USER]("secret-number") public int x = 3301; // This is a section in config // The library will initialize the object @Comment("Mappings of every block and item in the game") [USER=35164]name[/USER]("ids") public IdsSection ids; public class IdsSection implements Section { // These fields will be under the path 'ids.blocks' and 'ids.items' in the config @Comment("Mapping from every block in the game to it's id") public Map<Integer, Material> blocks = new HashMap<Integer, Material>(); @Comment("Mapping from every item in the game to it's id") public Map<Integer, Material> items = new HashMap<Integer, Material>(); // Insert the blocks and the id's in the maps { for (Material m : Material.values()) { if (m.isBlock()) { blocks.put(m.getId(), m); } else { items.put(m.getId(), m); } } } } public String[] tail() { return new String[] {"This is the end of the config.", "I hope you liked the ConfigurationAPI!"}; }} And the beutiful config file it creates. Keep in mind this is not modified in any way by me, the ConfigurationAPI creates them like this: Code: # This is the head comment of the configuration file. # It supports multiple lines! hello-message: Hello there! # This is the key! # Keep it secret! secret-number: 3301 # Mappings of every block and item in the game ids: # Mapping from every block in the game to it's id blocks: 0: AIR 1: STONE 2: GRASS 3: DIRT 4: COBBLESTONE 5: WOOD 6: SAPLING 7: BEDROCK 8: WATER 9: STATIONARY_WATER 10: LAVA 11: STATIONARY_LAVA 12: SAND 13: GRAVEL 14: GOLD_ORE 15: IRON_ORE 17: LOG 16: COAL_ORE 19: SPONGE 18: LEAVES 21: LAPIS_ORE 20: GLASS 23: DISPENSER 22: LAPIS_BLOCK 25: NOTE_BLOCK 24: SANDSTONE 27: POWERED_RAIL 26: BED_BLOCK 29: PISTON_STICKY_BASE 28: DETECTOR_RAIL 31: LONG_GRASS 30: WEB 34: PISTON_EXTENSION 35: WOOL 32: DEAD_BUSH 33: PISTON_BASE 38: RED_ROSE 39: BROWN_MUSHROOM 36: PISTON_MOVING_PIECE 37: YELLOW_FLOWER 42: IRON_BLOCK 43: DOUBLE_STEP 40: RED_MUSHROOM 41: GOLD_BLOCK 46: TNT 47: BOOKSHELF 44: STEP 45: BRICK 51: FIRE 50: TORCH 49: OBSIDIAN 48: MOSSY_COBBLESTONE 55: REDSTONE_WIRE 54: CHEST 53: WOOD_STAIRS 52: MOB_SPAWNER 59: CROPS 58: WORKBENCH 57: DIAMOND_BLOCK 56: DIAMOND_ORE 63: SIGN_POST 62: BURNING_FURNACE 61: FURNACE 60: SOIL 68: WALL_SIGN 69: LEVER 70: STONE_PLATE 71: IRON_DOOR_BLOCK 64: WOODEN_DOOR 65: LADDER 66: RAILS 67: COBBLESTONE_STAIRS 76: REDSTONE_TORCH_ON 77: STONE_BUTTON 78: SNOW 79: ICE 72: WOOD_PLATE 73: REDSTONE_ORE 74: GLOWING_REDSTONE_ORE 75: REDSTONE_TORCH_OFF 85: FENCE 84: JUKEBOX 87: NETHERRACK 86: PUMPKIN 81: CACTUS 80: SNOW_BLOCK 83: SUGAR_CANE_BLOCK 82: CLAY 93: DIODE_BLOCK_OFF 92: CAKE_BLOCK 95: LOCKED_CHEST 94: DIODE_BLOCK_ON 89: GLOWSTONE 88: SOUL_SAND 91: JACK_O_LANTERN 90: PORTAL 102: THIN_GLASS 103: MELON_BLOCK 100: HUGE_MUSHROOM_2 101: IRON_FENCE 98: SMOOTH_BRICK 99: HUGE_MUSHROOM_1 96: TRAP_DOOR 97: MONSTER_EGGS 110: MYCEL 111: WATER_LILY 108: BRICK_STAIRS 109: SMOOTH_STAIRS 106: VINE 107: FENCE_GATE 104: PUMPKIN_STEM 105: MELON_STEM 119: ENDER_PORTAL 118: CAULDRON 117: BREWING_STAND 116: ENCHANTMENT_TABLE 115: NETHER_WARTS 114: NETHER_BRICK_STAIRS 113: NETHER_FENCE 112: NETHER_BRICK 127: COCOA 126: WOOD_STEP 125: WOOD_DOUBLE_STEP 124: REDSTONE_LAMP_ON 123: REDSTONE_LAMP_OFF 122: DRAGON_EGG 121: ENDER_STONE 120: ENDER_PORTAL_FRAME 137: COMMAND 136: JUNGLE_WOOD_STAIRS 139: COBBLE_WALL 138: BEACON 141: CARROT 140: FLOWER_POT 143: WOOD_BUTTON 142: POTATO 129: EMERALD_ORE 128: SANDSTONE_STAIRS 131: TRIPWIRE_HOOK 130: ENDER_CHEST 133: EMERALD_BLOCK 132: TRIPWIRE 135: BIRCH_WOOD_STAIRS 134: SPRUCE_WOOD_STAIRS 152: REDSTONE_BLOCK 153: QUARTZ_ORE 154: HOPPER 155: QUARTZ_BLOCK 156: QUARTZ_STAIRS 157: ACTIVATOR_RAIL 158: DROPPER 159: STAINED_CLAY 144: SKULL 145: ANVIL 146: TRAPPED_CHEST 147: GOLD_PLATE 148: IRON_PLATE 149: REDSTONE_COMPARATOR_OFF 150: REDSTONE_COMPARATOR_ON 151: DAYLIGHT_DETECTOR 171: CARPET 170: HAY_BLOCK 173: COAL_BLOCK 172: HARD_CLAY # Mapping from every item in the game to it's id items: 275: STONE_AXE 274: STONE_PICKAXE 273: STONE_SPADE 272: STONE_SWORD 279: DIAMOND_AXE 278: DIAMOND_PICKAXE 277: DIAMOND_SPADE 276: DIAMOND_SWORD 283: GOLD_SWORD 282: MUSHROOM_SOUP 281: BOWL 280: STICK 287: STRING 286: GOLD_AXE 285: GOLD_PICKAXE 284: GOLD_SPADE 258: IRON_AXE 259: FLINT_AND_STEEL 256: IRON_SPADE 257: IRON_PICKAXE 262: ARROW 263: COAL 260: APPLE 261: BOW 266: GOLD_INGOT 267: IRON_SWORD 264: DIAMOND 265: IRON_INGOT 270: WOOD_PICKAXE 271: WOOD_AXE 268: WOOD_SWORD 269: WOOD_SPADE 305: CHAINMAIL_BOOTS 304: CHAINMAIL_LEGGINGS 307: IRON_CHESTPLATE 306: IRON_HELMET 309: IRON_BOOTS 308: IRON_LEGGINGS 311: DIAMOND_CHESTPLATE 310: DIAMOND_HELMET 313: DIAMOND_BOOTS 312: DIAMOND_LEGGINGS 315: GOLD_CHESTPLATE 314: GOLD_HELMET 317: GOLD_BOOTS 316: GOLD_LEGGINGS 319: PORK 318: FLINT 288: FEATHER 289: SULPHUR 290: WOOD_HOE 291: STONE_HOE 292: IRON_HOE 293: DIAMOND_HOE 294: GOLD_HOE 295: SEEDS 296: WHEAT 297: BREAD 298: LEATHER_HELMET 299: LEATHER_CHESTPLATE 300: LEATHER_LEGGINGS 301: LEATHER_BOOTS 302: CHAINMAIL_HELMET 303: CHAINMAIL_CHESTPLATE 343: POWERED_MINECART 342: STORAGE_MINECART 341: SLIME_BALL 340: BOOK 2264: RECORD_9 339: PAPER 2265: RECORD_10 338: SUGAR_CANE 2266: RECORD_11 337: CLAY_BALL 2267: RECORD_12 336: CLAY_BRICK 2260: RECORD_5 351: INK_SACK 2261: RECORD_6 350: COOKED_FISH 2262: RECORD_7 349: RAW_FISH 2263: RECORD_8 348: GLOWSTONE_DUST 2256: GOLD_RECORD 347: WATCH 2257: GREEN_RECORD 346: FISHING_ROD 2258: RECORD_3 345: COMPASS 2259: RECORD_4 344: EGG 326: WATER_BUCKET 327: LAVA_BUCKET 324: WOOD_DOOR 325: BUCKET 322: GOLDEN_APPLE 323: SIGN 320: GRILLED_PORK 321: PAINTING 334: LEATHER 335: MILK_BUCKET 332: SNOW_BALL 333: BOAT 330: IRON_DOOR 331: REDSTONE 328: MINECART 329: SADDLE 373: POTION 372: NETHER_STALK 375: SPIDER_EYE 374: GLASS_BOTTLE 369: BLAZE_ROD 368: ENDER_PEARL 371: GOLD_NUGGET 370: GHAST_TEAR 381: EYE_OF_ENDER 380: CAULDRON_ITEM 383: MONSTER_EGG 382: SPECKLED_MELON 377: BLAZE_POWDER 376: FERMENTED_SPIDER_EYE 379: BREWING_STAND_ITEM 378: MAGMA_CREAM 356: DIODE 357: COOKIE 358: MAP 359: SHEARS 352: BONE 353: SUGAR 354: CAKE 355: BED 364: COOKED_BEEF 365: RAW_CHICKEN 366: COOKED_CHICKEN 367: ROTTEN_FLESH 360: MELON 361: PUMPKIN_SEEDS 362: MELON_SEEDS 363: RAW_BEEF 408: HOPPER_MINECART 402: FIREWORK_CHARGE 403: ENCHANTED_BOOK 400: PUMPKIN_PIE 401: FIREWORK 406: QUARTZ 407: EXPLOSIVE_MINECART 404: REDSTONE_COMPARATOR 405: NETHER_BRICK_ITEM 395: EMPTY_MAP 394: POISONOUS_POTATO 393: BAKED_POTATO 392: POTATO_ITEM 399: NETHER_STAR 398: CARROT_STICK 397: SKULL_ITEM 396: GOLDEN_CARROT 387: WRITTEN_BOOK 386: BOOK_AND_QUILL 385: FIREBALL 384: EXP_BOTTLE 391: CARROT_ITEM 390: FLOWER_POT_ITEM 389: ITEM_FRAME 388: EMERALD 417: IRON_BARDING 419: DIAMOND_BARDING 418: GOLD_BARDING 421: NAME_TAG 420: LEASH # This is the end of the config. # I hope you liked the ConfigurationAPI! Even though this is really easy to use it is not aimed at noobs especially. The goal of it was to have configs that create nicer files, has easier support for child configs, and cleaner and easier to use in code. It allows for some quite advanced features. EDIT by Moderator: merged posts, please use the edit button instead of double posting.
Of course you can make configs like this with Bukkit's config API, but if you are going to make many configurations, or some big ones, you'll get a lot more code duplication that you get with this library. And I know that at least I am so lazy that I don't bother to find a way to create comments in my configs. With this Lib, comments become cheap and easy to use.