Property.java - My Fast Flatfile key-value data storage class

Discussion in 'Resources' started by Valrix, Aug 8, 2011.

Thread Status:
Not open for further replies.
  1. Offline

    Valrix

    Since I've had a few people go nuts about the class and said they wished they knew about it before, I've decided to finally put up the code and link to the class as a resource. The backstory is when I first started making plugins I saw a class made by @Nijikokun that stored basic key-value pairs for making a quick config file or something. From there I continuously tweaked and improved the code to improve the speed and reduce the number of I/O operations it did while reading and writing in UTF-8 and preserving comments. So, what was originally a quick script by Niji, has turned into a class found in every one of my plugins due to ease of use and how fast it saves and loads data. The beauty of the class is that you can add and remove methods as you see fit in order to suit what calls you'll make to the class. Anyway, here's the raw code followed by a link to download the zipped source file.

    Code:java
    1. package com.sparkedia.valrix.netstats;
    2.  
    3. import org.json.simple.JSONValue;
    4.  
    5. import java.io.*;
    6. import java.math.BigDecimal;
    7. import java.util.*;
    8. import java.util.logging.Level;
    9. import java.util.logging.Logger;
    10.  
    11. public final class Property {
    12. private Logger log;
    13. protected Netstats plugin;
    14. private LinkedHashMap<String, Object> properties = new LinkedHashMap<String, Object>();
    15. private String filename;
    16. private String pName;
    17.  
    18. public Property(String filename, Netstats plugin) {
    19. this.plugin = plugin;
    20. this.pName = plugin.pName;
    21. this.log = plugin.log;
    22. this.filename = filename;
    23. File file = new File(filename);
    24.  
    25. if (file.exists()) load();
    26. }
    27.  
    28. // Load data from file into ordered HashMap
    29. public void load() {
    30. BufferedReader br = null;
    31. try {
    32. br = new BufferedReader(new InputStreamReader(new FileInputStream(filename),"UTF-8"));
    33. String line;
    34. int cc = 0; // # of comments
    35. int lc = 0; // # of lines
    36. int delim;
    37.  
    38. // While there are lines to read (auto-breaks)
    39. while ((line = br.readLine()) != null) {
    40. // Is a comment, store it
    41. if (line.charAt(0) == '#' && lc != 0) {
    42. properties.put("#"+cc, line.substring(line.indexOf(' ')+1).trim());
    43. cc++;
    44. continue;
    45. }
    46. // Isn't a comment, store the key and value
    47. while ((delim = line.indexOf('=')) != -1) {
    48. String key = line.substring(0, delim).trim();
    49. String val = line.substring(delim+1).trim();
    50. properties.put(key, val);
    51. break;
    52. }
    53. }
    54. } catch (FileNotFoundException ex) {
    55. log.log(Level.SEVERE, '['+pName+"]: Couldn't find file "+filename, ex);
    56. } catch (IOException ex) {
    57. log.log(Level.SEVERE, '['+pName+"]: Unable to save "+filename, ex);
    58. } finally {
    59. // Close the reader
    60. try {
    61. if (br != null) br.close();
    62. } catch (IOException ex) {
    63. log.log(Level.SEVERE, '['+pName+"]: Unable to save "+filename, ex);
    64. }
    65. }
    66. }
    67.  
    68. // Save data from LinkedHashMap to file
    69. public void save() {
    70. BufferedWriter bw = null;
    71. try {
    72. // Construct the BufferedWriter object
    73. bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename),"UTF-8"));
    74. bw.write("# "+pName+" Properties File");
    75. bw.newLine();
    76.  
    77. // Save all the properties one at a time, only if there's data to write
    78. if (properties.size() > 0) {
    79. // Grab all the entries and create an iterator to run through them all
    80. Set<?> set = properties.entrySet();
    81. Iterator<?> i = set.iterator();
    82.  
    83. // While there's data to iterate through..
    84. while (i.hasNext()) {
    85. // Map the entry and save the key and value as variables
    86. Map.Entry<?, ?> me = (Map.Entry<?, ?>)i.next();
    87. String key = (String)me.getKey();
    88. String val = me.getValue().toString();
    89.  
    90. // If it starts with "#", it's a comment so write it as such
    91. if (key.charAt(0) == '#') {
    92. // Writing a comment to the file
    93. bw.write("# "+val);
    94. bw.newLine();
    95. } else {
    96. // Otherwise write the key and value pair as key=value
    97. bw.write(key+'='+val);
    98. bw.newLine();
    99. }
    100. }
    101. }
    102. } catch (FileNotFoundException ex) {
    103. log.log(Level.SEVERE, '['+pName+"]: Couldn't find file "+filename, ex);
    104. return;
    105. } catch (IOException ex) {
    106. log.log(Level.SEVERE, '['+pName+"]: Unable to save "+filename, ex);
    107. return;
    108. } finally {
    109. // Close the BufferedWriter
    110. try {
    111. if (bw != null) bw.close();
    112. } catch (IOException ex) {
    113. log.log(Level.SEVERE, '['+pName+"]: Unable to save "+filename, ex);
    114. }
    115. }
    116. }
    117.  
    118. // Turn the Property into a JSON string
    119. public String toJSON() {
    120. return JSONValue.toJSONString(properties);
    121. }
    122.  
    123. // Rebuild the current properties file using data from newMap
    124. public void rebuild(LinkedHashMap<String, Object> newMap) {
    125. properties.clear();
    126. properties.putAll(newMap);
    127. save();
    128. }
    129.  
    130. // Function to check if current properties file matches a referenced one by validating every key
    131. public boolean match(LinkedHashMap<String, Object> prop) {
    132. return (properties.keySet().containsAll(prop.keySet())) ? true : false;
    133. }
    134.  
    135. // Check if the key exists or not
    136. public boolean keyExists(String key) {
    137. return (properties.containsKey(key)) ? true : false;
    138. }
    139.  
    140. // Return all the keys that exist
    141. public Set<String> getKeys() {
    142. return properties.keySet();
    143. }
    144.  
    145. // Return all the stored values
    146. public Collection<Object> getValues() {
    147. return properties.values();
    148. }
    149.  
    150. // Check if the key no value
    151. public boolean isEmpty(String key) {
    152. return (properties.get(key).toString().length() == 0) ? true : false;
    153. }
    154.  
    155. // Increment the key by 1
    156. public void inc(String key) {
    157. BigDecimal v = new BigDecimal(properties.get(key).toString()).add(new BigDecimal("1"));
    158. properties.put(key, v.toString());
    159. }
    160.  
    161. // Add given number to given key
    162. public void add(String key, Number n) {
    163. BigDecimal v = new BigDecimal(properties.get(key).toString()).add(new BigDecimal(""+n.toString()));
    164. properties.put(key, v.toString());
    165. }
    166.  
    167. // Subtract given number from given key
    168. public void sub(String key, Number n) {
    169. BigDecimal v = new BigDecimal(properties.get(key).toString()).add(new BigDecimal(""+n.toString()));
    170. properties.put(key, v.toString());
    171. }
    172.  
    173. // Remove key from map
    174. public boolean remove(String key) {
    175. return (properties.remove(key) != null) ? true : false;
    176. }
    177.  
    178. // Set property value as a String
    179. public void setString(String key, String value) {
    180. properties.put(key, value);
    181. }
    182.  
    183. // Set property value as a Number
    184. public void setNumber(String key, Number value) {
    185. properties.put(key, String.valueOf(value));
    186. }
    187.  
    188. // Set property value as a Boolean
    189. public void setBool(String key, boolean value) {
    190. properties.put(key, String.valueOf(value));
    191. }
    192.  
    193. // Get property value as a string
    194. public String getString(String key) {
    195. return (properties.containsKey(key)) ? properties.get(key).toString() : "";
    196. }
    197.  
    198. // Get property value as a byte
    199. public byte getByte(String key) {
    200. return (properties.containsKey(key)) ? Byte.parseByte(properties.get(key).toString()) : 0;
    201. }
    202.  
    203. // Get property value as a short
    204. public short getShort(String key) {
    205. return (properties.containsKey(key)) ? Short.parseShort(properties.get(key).toString()) : 0;
    206. }
    207.  
    208. // Get property value as an int
    209. public int getInt(String key) {
    210. return (properties.containsKey(key)) ? Integer.parseInt(properties.get(key).toString()) : 0;
    211. }
    212.  
    213. // Get property value as a double
    214. public double getDouble(String key) {
    215. return (properties.containsKey(key)) ? Double.parseDouble(properties.get(key).toString()) : 0.0D;
    216. }
    217.  
    218. // Get property value as a long
    219. public long getLong(String key) {
    220. return (properties.containsKey(key)) ? Long.parseLong(properties.get(key).toString()) : 0L;
    221. }
    222.  
    223. // Get property value as a float
    224. public float getFloat(String key) {
    225. return (properties.containsKey(key)) ? Float.parseFloat(properties.get(key).toString()) : 0F;
    226. }
    227.  
    228. // Get property value as a boolean
    229. public boolean getBool(String key) {
    230. return (properties.containsKey(key)) ? Boolean.parseBoolean(properties.get(key).toString()) : false;
    231. }
    232. }
    233.  


    Example code:
    Code:java
    1. Property config = new Property(getServer().getDataFolder().toString()+"/config.txt", this);
    2. config.setNumber("one", 1);
    3. config.setBool("two", true);
    4. config.setString("three", "four");
    5. config.setString("#0", "This will be a comment between 'three' and 'four'");
    6. config.setNumber("four", 525600);
    7. config.save(); // You just call save() to write the data to disk
    8.  


    Source:
    Property.java

    Also: I'm sure there are optimizations that can still be done and I'll be adding those in over time, but please feel free to post what you'd do to make it better.
     
Thread Status:
Not open for further replies.

Share This Page