Util StorableHashMap [using serialisation]

Discussion in 'Resources' started by Chloe-chan, Oct 18, 2015.

?

Did this utility helped you out?

  1. Yes!

    33.3%
  2. Not as much as I expected.

    0 vote(s)
    0.0%
  3. Nope, waste of my time.

    66.7%
  4. Others...

    0 vote(s)
    0.0%
Thread Status:
Not open for further replies.
  1. Offline

    Chloe-chan

    First of all, this is a small class that may or may not benefit you. I have no idea how beneficial this utility can be for others, but at least it saves me from writing messy codes. Feel free to use and modify it to better suit you, just give me some credit if you did use it (or not... I can't check anyways :p). And this is my first post, so be lenient with me. :'(

    NOTE: It is useful to have some knowledge of Java before attempting to use this. You can make your own Storable interface to further improve on this. Java 1.7 is required too, for try-with-resources. :p

    Summary: This StorableHashMap functions the same as the java.util.HashMap, with the exception that you can store and retrieve the data upon shutdown or startup of the server (or JVM). This function, however, is limited to Objects that implements java.io.Serializable and org.bukkit.configuration.serialization.ConfigurationSerializable. You can obviously add in the Serializable interface to your objects. If you store objects that aren't Serializable, error will be thrown when loading and saving, which you can opt out of (which defeats the purpose of this anyways). You can copy most of this code, with modifications, to accommodate whichever data structure you wish to use.
    EDIT:
    This class now supports ConfigurationSerializable, using the Bukkit utilities, BukkitObjectOutputStream and BukkitObjectInputStream.

    Class file:
    StorableHashMap.java

    Code:
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.Serializable;
    import java.util.HashMap;
    
    import org.bukkit.util.io.BukkitObjectInputStream;
    import org.bukkit.util.io.BukkitObjectOutputStream;
    
    public class StorableHashMap<K, V> extends HashMap<K, V> implements Serializable
    {
        private static final long serialVersionUID = -3535879180214628774L;
    
        private transient final String extension = ".dat";
    
        private transient File parentFolder;
        private transient File saveFile;
    
        public StorableHashMap(File parentFolder, String fileName) throws IOException
        {
            super();
            initialise(parentFolder, fileName);
        }
    
        public StorableHashMap(int initialCapacity, File parentFolder, String fileName) throws IOException
        {
            super(initialCapacity);
            initialise(parentFolder, fileName);
        }
    
        public StorableHashMap(int initialCapacity, float loadFactor, File parentFolder, String fileName) throws IOException
        {
            super(initialCapacity, loadFactor);
            initialise(parentFolder, fileName);
        }
    
        public StorableHashMap(HashMap<K, V> m, File parentFolder, String fileName) throws IOException
        {
            super(m);
            initialise(parentFolder, fileName);
        }
    
        private void initialise(File parentFolder, String fileName) throws IOException
        {
            this.parentFolder = parentFolder;
            this.saveFile = new File(this.parentFolder, fileName + extension);
            createFile();
        }
    
        public boolean createFile() throws IOException
        {
            if (!parentFolder.exists() && !parentFolder.mkdirs())
                return false;
            if (!saveFile.exists() && !saveFile.createNewFile())
                return false;
    
            return true;
        }
    
        @SuppressWarnings("unchecked")
        public void loadFromFile() throws FileNotFoundException, IOException, ClassNotFoundException
        {
            if (saveFile.length() > 0)
            {
                try (FileInputStream fileIn = new FileInputStream(saveFile);
                        BukkitObjectInputStream objectIn = new BukkitObjectInputStream(fileIn))
                {
                    StorableHashMap<K, V> map = (StorableHashMap<K, V>) objectIn.readObject();
                    super.clear();
                    super.putAll(map);
                }
            }
        }
    
        public void saveToFile() throws FileNotFoundException, IOException
        {
            try (FileOutputStream fileOut = new FileOutputStream(saveFile);
                    BukkitObjectOutputStream objectOut = new BukkitObjectOutputStream(fileOut))
            {
                objectOut.writeObject(this);
            }
        }
    }
    Example:
    Create an instance of this HashMap

    Code:
    private StorableHashMap<UUID, Integer> playerLeaveCount;
    
    public ClassNameHere()
    {
        try
        {
            playerLeaveCount = new StorableHashMap<UUID, Integer>(getDataFolder(), "player-leave-count");
        } catch (IOException e)
        {
            getLogger().log(Level.WARNING, "Error creating save file!", e);
        }
    }
    First parameter is the parent folder to save this file under. As for this example, we are saving it under the plugin's data folder.
    Second parameter is the file name, with the default extension being .dat. As for this example, we are saving it as "player-leave-count.dat".

    Loading files on enable
    Code:
    @Override
    public void onEnable()
    {
        try
        {
            playerLeaveCount.loadFromFile();
        } catch (ClassNotFoundException | IOException e)
        {
            getLogger().log(Level.WARNING, "Error loading file!", e);
        }
    }
    Saving files on disable
    Code:
    @Override
    public void onDisable()
    {
        try
        {
            playerLeaveCount.saveToFile();
        } catch (IOException e)
        {
            getLogger().log(Level.WARNING, "Error saving file!", e);
        }
    }

    That's basically it. Please let me know if this helped you out... or if this post was a waste of your time. I'm open to all suggestions and constructive feedback! :D

    [EDIT 9th Nov] Added dependency for Serializable objects in the generic declarations, together with implementation of Serializable.

    [EDIT 19th Dec] Changed ObjectOutputStream and ObjectInputStream to BukkitObjectOutputStream and BukkitObjectInputStream respectively. Removed generic declaration extends serializable.
     
    Last edited: Dec 18, 2015
    cococow123, FabeGabeMC and rbrick like this.
  2. Offline

    ChipDev

    Thank you thank you thank you!
    Wow!
    Thank you if I didn't already say!
     
    Chloe-chan likes this.
  3. Offline

    rbrick

    Looks good, I can't think of any cases I would use it for (at the time being), but I will bookmark it
     
    Chloe-chan likes this.
  4. Offline

    FabeGabeMC

    @Chloe-chan
    This looks like an amazing util! I've yet to see something this good! Gonna be needing it soon ;)

    Btw I tip le [cake] to you <:
     
    Chloe-chan likes this.
  5. Offline

    cococow123

    @Chloe-chan is there a way I can load it by the filename? If I was having hashmaps for each player, it would be hard to load a specific player file.
     
  6. Offline

    Mrs. bwfctower

    @cococow123
    That will create a new StorableHashMap from the file 'player-leave-count'.
     
    Chloe-chan likes this.
  7. Offline

    cococow123

    @Mrs. bwfctower Duhh!! I should if thought of that.. So when I load it.. I put that b4 the load code.
     
  8. Offline

    Mrs. bwfctower

  9. Offline

    cococow123

    @Mrs. bwfctower thanks so much... For everything you helped me with.

    I now know not to hesitate to ask questions on the Bukkit Forums.
     
  10. Offline

    DoggyCode™

    Bookmarked
     
    Chloe-chan likes this.
  11. Offline

    cococow123

  12. Offline

    pie_flavor

    @Chloe-chan Tip you may not have known: There are BukkitObjectInputStream and BukkitObjectOutputStream classes. They extend ObjectInputStream and ObjectOutputStream, retaining all functionality, but also work with ConfigurationSerializable objects in addition to Serializable objects. This can be used in a wide variety of situations - for instance, ItemStack is ConfigurationSerializable. The ideal HashMap saver uses these classes, so that everything works.
     
  13. Offline

    Chloe-chan

    Thanks for letting me know. I'm quite busy with my school assignments recently and don't have the chance to improve it.
     
  14. Offline

    cococow123

    @Chloe-chan How can I display all the values and keys in chat?

    Example:

    "apple:5"
    "donut:3"
    "taco:8"
     
  15. Offline

    Chloe-chan

    Do you mean a String representation of all the objects stored ?
    If so, you can create a new method in the StorableHashMap to do that.

    Make sure the object you stored overrides the default toString() method.

    Code:
    public String[] getAllEntries()
    {
        ArrayList<String> entries = new ArrayList<String>();
    
        for (Entry<K, V> entrySet : super.entrySet())
            entries.add(entrySet.getKey().toString() + ": " + entrySet.getValue().toString());
    
        return entries.toArray(new String[entries.size()]);
    }
     
    cococow123 likes this.
  16. Offline

    cococow123

  17. Offline

    Chloe-chan

    I'll do it when I can.
     
    cococow123 likes this.
  18. @Chloe-chan
    public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable ?
     
  19. Offline

    Chloe-chan

    Yes, but this library (or however you want to call it) adds on top of it the various methods of serialising and de-serialising the mentioned HashMap. Yes, I've to admit it is useless for those who have used or learnt about serialisation. But it is a tool for those who don't know, to do the same.
     
Thread Status:
Not open for further replies.

Share This Page