PageRenderTime 57ms CodeModel.GetById 37ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/java/com/laytonsmith/PureUtilities/SerializedPersistance.java

https://github.com/zml2008/commandhelper
Java | 347 lines | 242 code | 22 blank | 83 comment | 34 complexity | a2a2852d09136d973085b0b8856f61c6 MD5 | raw file
  1package com.laytonsmith.PureUtilities;
  2
  3import java.io.File;
  4import java.io.FileInputStream;
  5import java.io.FileNotFoundException;
  6import java.io.FileOutputStream;
  7import java.io.IOException;
  8import java.io.ObjectInputStream;
  9import java.io.ObjectOutputStream;
 10import java.io.PrintStream;
 11import java.io.Serializable;
 12import java.util.ArrayList;
 13import java.util.HashMap;
 14import java.util.Iterator;
 15import java.util.Map;
 16import java.util.logging.Level;
 17import java.util.logging.Logger;
 18import org.bukkit.plugin.Plugin;
 19
 20/**
 21 * This file allows for simple data storage across many different data sources. In general, the
 22 * most common methods used are getValue and setValue. Note that getValue, setValue, save, and
 23 * load are synchronized.
 24 * @author layton
 25 */
 26public class SerializedPersistance implements Persistance{
 27
 28    /**
 29     * This is the data structure that the registry is stored in
 30     */
 31    private HashMap<String, Serializable> data = new HashMap<String, Serializable>();
 32    private boolean isLoaded = false;
 33    /**
 34     * The storage location of the persistance database. Note that it is package private,
 35     * so it can be changed.
 36     */
 37    File storageLocation;
 38
 39    Object user;
 40    
 41    public SerializedPersistance(File database, Plugin user){
 42        storageLocation = database;
 43        this.user = user;
 44    }
 45
 46    /**
 47     * Private constructor, used for testing this class
 48     * @param database
 49     * @param user
 50     */
 51    private SerializedPersistance(File database, Object user){
 52        storageLocation = database;
 53        this.user = user;
 54    }
 55
 56    /**
 57     * Loads the database from disk. This is automatically called when setValue or getValue is called.
 58     * @throws Exception
 59     */
 60    public synchronized void load() throws Exception {
 61        try {
 62            FileInputStream fis = null;
 63            ObjectInputStream in = null;
 64            fis = new FileInputStream(storageLocation);
 65            in = new ObjectInputStream(fis);
 66            HashMap<String, Serializable> tempData = (HashMap<String, Serializable>) in.readObject();
 67            String[] myNamespace;
 68            if(user != null){
 69                myNamespace = ("plugin." + user.getClass().getCanonicalName()).split("\\.");
 70            } else {
 71                //We're running from the command line
 72                data = tempData;
 73                return;
 74            }
 75            Iterator i = tempData.entrySet().iterator();
 76            while (i.hasNext()) {
 77                String[] key = ((Map.Entry)i.next()).getKey().toString().split("\\.");
 78                boolean match = true;
 79                for(int j = 0; j < myNamespace.length; j++){
 80                    if(key.length > myNamespace.length){
 81                        if(!key[j].equals(myNamespace[j])){
 82                            match = false;
 83                            break;
 84                        }
 85                    }
 86                }
 87                if(match){
 88                    data.put(getNamespace(key), tempData.get(getNamespace(key)));
 89                }
 90            }
 91            in.close();
 92            isLoaded = true;
 93        } catch (FileNotFoundException ex){
 94            //ignore this one
 95        } catch (Exception ex) {
 96            throw ex;
 97        }
 98    }
 99
100    /**
101     * Causes the database to be saved to disk
102     * @throws IOException
103     */
104    public synchronized void save() throws Exception {
105        try {
106            HashMap<String, Serializable> tempData;
107            try{
108                FileInputStream fis = null;
109                ObjectInputStream in = null;
110                fis = new FileInputStream(storageLocation);
111                in = new ObjectInputStream(fis);
112                tempData = (HashMap<String, Serializable>) in.readObject();
113            } catch(FileNotFoundException ex){
114                tempData = new HashMap<String, Serializable>();
115            }
116            Iterator i = data.entrySet().iterator();
117            ArrayList<String> toRemove = new ArrayList<String>();
118            while (i.hasNext()) {
119                String key = ((Map.Entry)i.next()).getKey().toString();
120                if(data.get(key) == null){
121                    tempData.remove(key);
122                    toRemove.add(key);
123                } else{
124                    tempData.put(key, data.get(key));
125                }
126            }
127            for(String s : toRemove){
128                data.remove(s);
129            }
130            FileOutputStream fos = null;
131            ObjectOutputStream out = null;
132            storageLocation.getParentFile().mkdirs();
133            if(!storageLocation.exists())
134                storageLocation.createNewFile();
135            fos = new FileOutputStream(storageLocation);
136            out = new ObjectOutputStream(fos);
137            out.writeObject(tempData);
138            out.close();
139            System.out.println("Persistance saved into " + this.hashCode());
140        } catch (Exception ex) {
141            throw ex;
142        }
143    }
144
145
146    /**
147     * You should not usually use this method. Please see <code>setValue(String[] key, Serializable value)</code>
148     */
149    private synchronized Object setValue(String key, Serializable value) {
150        //defer loading until we actually try and use the data structure
151        if (isLoaded == false) {
152            System.out.println("Loading values for " + user.getClass().getCanonicalName());
153            try {
154                load();
155            } catch (Exception ex) {
156                Logger.getLogger("Minecraft").log(Level.SEVERE, null, ex);
157            }
158        }
159        key = "plugin." + user.getClass().getCanonicalName() + "." + key;
160        Serializable oldVal = data.get(key);
161        data.put(key, value);
162        return oldVal;
163    }
164
165
166    private synchronized Object getValue(String key) {
167        //defer loading until we actually try and use the data structure
168        if (isLoaded == false) {
169            System.out.println("Loading values for " + user.getClass().getCanonicalName());
170            try {
171                load();
172            } catch (Exception ex) {
173                Logger.getLogger(SerializedPersistance.class.getName()).log(Level.SEVERE, null, ex);
174            }
175        }
176        key = "plugin." + user.getClass().getCanonicalName() + "." + key;
177        if(data == null){
178            return null;
179        }
180        return data.get(key);
181    }
182
183    /**
184     * Adds or modifies the value of the key. Typically, this convention should be followed:
185     * <pre>
186     * key1.key2.key3...
187     * </pre>
188     * To make this usage easier, the function automatically namespaces the values for you. A sample
189     * usage might be:
190     * <pre>
191     * setValue(new String[]{"playerName", "value"}, value);
192     * </pre>
193     *
194     * When using namespaces in this way, the isNamespaceSet function becomes available to you.
195     * Since plugin values are global, you can use this to interact with other plugins. Caution should
196     * be used when interacting with other plugin's values though.
197     * @param key The key for this particular value
198     * @param value The value to store. If value is null, the key is simply removed.
199     * @return The object that was in this key, or null if the value did not exist.
200     */
201    public synchronized Object setValue(String[] key, Object value) {
202        return setValue(getNamespace(key), (Serializable) value);
203    }
204
205    /**
206     * Returns the value of a particular key
207     * @param key
208     * @return
209     */
210    public synchronized Object getValue(String[] key) {
211        return getValue(getNamespace(key));
212    }
213
214    /**
215     * Checks to see if a particular key is set. Unlike isNamespaceSet, this requires that
216     * the exact key be specified to see if it exists.
217     * @param key
218     * @return
219     */
220    public synchronized boolean isKeySet(String[] key) {
221        String k = getNamespace(key);
222        k = "plugin." + user.getClass().getCanonicalName() + "." + k;
223        return data.containsKey(k);
224    }
225
226    /**
227     * Returns whether or not a particular namespace value is set. For instance, if the
228     * value plugin.myPlugin.players.playerName.data is set, then the call to
229     * <code>isNamespaceSet(new String[]{"plugin", "myPlugin"})</code> would return
230     * <code>true</code>
231     * @param partialKey
232     * @return
233     */
234    public synchronized boolean isNamespaceSet(String[] partialKey) {
235        String m = getNamespace(partialKey);
236        m = "plugin." + user.getClass().getCanonicalName() + "." + m;
237        partialKey = m.split("\\.");
238        Iterator i = data.entrySet().iterator();
239        while (i.hasNext()) {
240            String key = ((Map.Entry)i.next()).getKey().toString();
241            String[] namespace = key.split("\\.");
242            boolean match = true;
243            for (int k = 0; k < partialKey.length; k++) {
244                if (namespace.length < k) {
245                    match = false;
246                    continue;
247                }
248                if (!namespace[k].equals(partialKey[k])) {
249                    match = false;
250                    continue;
251                }
252            }
253            if (match) {
254                return true;
255            }
256        }
257        return false;
258    }
259
260    /**
261     * Returns all the matched namespace entries.
262     * @param partialKey The partial name of the keys you wish to return
263     * @return An ArrayList of Map.Entries.
264     */
265    public synchronized ArrayList<Map.Entry> getNamespaceValues(String[] partialKey){
266
267        ArrayList<Map.Entry> matches = new ArrayList<Map.Entry>();
268        String m = getNamespace(partialKey);
269        m = "plugin." + user.getClass().getCanonicalName() + "." + m;
270        partialKey = m.split("\\.");
271        if(!isLoaded){
272            try {
273                load();
274            } catch (Exception ex) {
275                Logger.getLogger(SerializedPersistance.class.getName()).log(Level.SEVERE, null, ex);
276            }
277        }
278        Iterator i = data.entrySet().iterator();
279        while (i.hasNext()) {
280            Map.Entry entry = (Map.Entry)i.next();
281            String key = entry.getKey().toString();
282            String[] namespace = key.split("\\.");
283            boolean match = true;
284            for (int k = 0; k < partialKey.length; k++) {
285                if (namespace.length < partialKey.length) {
286                    match = false;
287                    continue;
288                }
289                if (!namespace[k].equals(partialKey[k])) {
290                    match = false;
291                    continue;
292                }
293            }
294            if (match) {
295                matches.add(entry);
296            }
297        }
298        return matches;
299    }
300
301    /**
302     * Combines the String array into a single string
303     * @param key
304     * @return
305     */
306    private synchronized static String getNamespace(String[] key) {
307        StringBuilder b = new StringBuilder();
308        for (int i = 0; i < key.length; i++) {
309            if (i > 0) {
310                b.append(".").append(key[i]);
311            } else {
312                b.append(key[i]);
313            }
314        }
315        return b.toString();
316    }
317
318    /**
319     * Prints all of the stored values to the specified print stream.
320     */
321    public synchronized void printValues(PrintStream out) {
322        try {
323            out.println("Printing all persisted values:");
324            load();
325            Iterator i = data.entrySet().iterator();
326            while (i.hasNext()) {
327                Map.Entry e = ((Map.Entry) i.next());
328                out.println(e.getKey()
329                        + ": " + data.get(e.getKey().toString()).toString());
330            }
331            out.println("Done printing persisted values");
332        } catch (Exception ex) {
333            Logger.getLogger(SerializedPersistance.class.getName()).log(Level.SEVERE, null, ex);
334        }
335    }
336
337    public static void main(String[] args) throws Exception{
338        SerializedPersistance p = new SerializedPersistance(new File("plugins/CommandHelper/persistance.ser"), new Object());
339        p.setValue(new String[]{"player", "wraithguard01", "name"}, "wraithguard01");
340        p.setValue(new String[]{"player", "wraithguard01", "age"}, "22");
341        p.setValue(new String[]{"player", "other", "name"}, "other");
342        System.out.println(p.getNamespaceValues(new String[]{"player", "wraithguard01", "age"}));
343        System.out.println();
344        p.save();
345    }
346
347}