com.skcraft.launcher.persistence.Persistence.java Source code

Java tutorial

Introduction

Here is the source code for com.skcraft.launcher.persistence.Persistence.java

Source

/*
 * SK's Minecraft Launcher
 * Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> and contributors
 * Please see LICENSE.txt for license information.
 */

package com.skcraft.launcher.persistence;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.ByteSink;
import com.google.common.io.ByteSource;
import com.google.common.io.Closer;
import com.google.common.io.Files;
import lombok.NonNull;
import lombok.extern.java.Log;

import java.io.*;
import java.util.WeakHashMap;
import java.util.logging.Level;

/**
 * Simple persistence framework that can read an object from a file, bind
 * that object to that file, and allow any code having a reference to the
 * object make changes to the object and save those changes back to disk.
 * </p>
 * For example:
 * <pre>config = Persistence.load(file, Configuration.class);
 * config.changeSomething();
 * Persistence.commit(config);</pre>
 */
@Log
public final class Persistence {

    private static final ObjectMapper mapper = new ObjectMapper();
    private static final WeakHashMap<Object, ByteSink> bound = new WeakHashMap<Object, ByteSink>();

    private Persistence() {
    }

    /**
     * Bind an object to a path where the object will be saved.
     *
     * @param object the object
     * @param sink the byte sink
     */
    public static void bind(@NonNull Object object, @NonNull ByteSink sink) {
        synchronized (bound) {
            bound.put(object, sink);
        }
    }

    /**
     * Save an object to file.
     *
     * @param object the object
     * @throws java.io.IOException on save error
     */
    public static void commit(@NonNull Object object) throws IOException {
        ByteSink sink;
        synchronized (bound) {
            sink = bound.get(object);
            if (sink == null) {
                throw new IOException("Cannot persist unbound object: " + object);
            }
        }

        Closer closer = Closer.create();
        try {
            OutputStream os = closer.register(sink.openBufferedStream());
            mapper.writeValue(os, object);
        } finally {
            closer.close();
        }
    }

    /**
     * Save an object to file, and send all errors to the log.
     *
     * @param object the object
     */
    public static void commitAndForget(@NonNull Object object) {
        try {
            commit(object);
        } catch (IOException e) {
            log.log(Level.WARNING, "Failed to save " + object.getClass() + ": " + object.toString(), e);
        }
    }

    /**
     * Read an object from a byte source, without binding it.
     *
     * @param source byte source
     * @param cls the class
     * @param returnNull true to return null if the object could not be loaded
     * @param <V> the type of class
     * @return an object
     */
    public static <V> V read(ByteSource source, Class<V> cls, boolean returnNull) {
        V object;
        Closer closer = Closer.create();

        try {
            object = mapper.readValue(closer.register(source.openBufferedStream()), cls);
        } catch (IOException e) {
            if (!(e instanceof FileNotFoundException)) {
                log.log(Level.INFO, "Failed to load" + cls.getCanonicalName(), e);
            }

            if (returnNull) {
                return null;
            }

            try {
                object = cls.newInstance();
            } catch (InstantiationException e1) {
                throw new RuntimeException("Failed to construct object with no-arg constructor", e1);
            } catch (IllegalAccessException e1) {
                throw new RuntimeException("Failed to construct object with no-arg constructor", e1);
            }
        } finally {
            try {
                closer.close();
            } catch (IOException e) {
            }
        }

        return object;
    }

    /**
     * Read an object from file, without binding it.
     *
     * @param file the file
     * @param cls the class
     * @param returnNull true to return null if the object could not be loaded
     * @param <V> the type of class
     * @return an object
     */
    public static <V> V read(File file, Class<V> cls, boolean returnNull) {
        return read(Files.asByteSource(file), cls, returnNull);
    }

    /**
     * Read an object from file, without binding it.
     *
     * @param file the file
     * @param cls the class
     * @param <V> the type of class
     * @return an object
     */
    public static <V> V read(File file, Class<V> cls) {
        return read(file, cls, false);
    }

    /**
     * Read an object from file.
     *
     * @param file the file
     * @param cls the class
     * @param returnNull true to return null if the object could not be loaded
     * @param <V> the type of class
     * @return an object
     */
    public static <V> V load(File file, Class<V> cls, boolean returnNull) {
        ByteSource source = Files.asByteSource(file);
        ByteSink sink = new MkdirByteSink(Files.asByteSink(file), file.getParentFile());

        Scrambled scrambled = cls.getAnnotation(Scrambled.class);
        if (cls.getAnnotation(Scrambled.class) != null) {
            source = new ScramblingSourceFilter(source, scrambled.value());
            sink = new ScramblingSinkFilter(sink, scrambled.value());
        }

        V object = read(source, cls, returnNull);
        Persistence.bind(object, sink);
        return object;
    }

    /**
     * Read an object from file.
     *
     * <p>If the file does not exist or loading fails, construct a new instance of
     * the given class by using its no-arg constructor.</p>
     *
     * @param file the file
     * @param cls the class
     * @param <V> the type of class
     * @return an object
     */
    public static <V> V load(File file, Class<V> cls) {
        return load(file, cls, false);
    }

    /**
     * Write an object to file.
     *
     * @param file the file
     * @param object the object
     * @throws java.io.IOException on I/O error
     */
    public static void write(File file, Object object) throws IOException {
        file.getParentFile().mkdirs();
        mapper.writeValue(file, object);
    }

}