de.cgarbs.lib.json.JSONDataModel.java Source code

Java tutorial

Introduction

Here is the source code for de.cgarbs.lib.json.JSONDataModel.java

Source

/*
 * Copyright 2015 (C)  Christian Garbs <mitch@cgarbs.de>
 * Licensed under GNU GPL 3 (or later)
 */
package de.cgarbs.lib.json;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;

import de.cgarbs.lib.data.DataModel;
import de.cgarbs.lib.exception.DataException;
import de.cgarbs.lib.exception.JSONException;

/**
 * This adapter maps a DataModel to a JSON representation by
 * writing to a file/reading from a file.
 *
 * @author Christian Garbs <mitch@cgarbs.de>
 * @since 0.3.0
 */
public abstract class JSONDataModel extends JSONAdapter {
    static final String IDENTIFIER = "de.cgarbs.javalib.json.JSONDataModel representation";
    static final String IDENTIFIER_FIELD = "id";

    static final String VERSION = "1";
    static final String VERSION_FIELD = "version";

    static final String ATTRIBUTE_FIELD = "attributes";

    /**
     * Converts the DataModel to JSON format and writes the JSON data to the
     * specified file.
     *
     * @param model the DataModel to be converted to JSON
     * @param file the File to be written to
     * @throws DataException either IO errors or errors during the JSON conversion
     */
    public static void writeToFile(final DataModel model, final File file) throws DataException {
        PrintWriter out = null;
        try {
            out = new PrintWriter(file);
            out.write(convertToJSON(model));
            out.close();
        } catch (FileNotFoundException e) {
            throw wrappedAsDataException(DataException.ERROR.IO_ERROR, e);
        } catch (JSONException e) {
            throw wrappedAsDataException(DataException.ERROR.JSON_CONVERSION_ERROR, e);
        } finally {
            if (out != null) {
                out.close();
            }
        }
        if (out.checkError()) {
            // FIXME: PrintWriter throws no IOExceptions?!  how to find out what happened?
            throw new DataException(DataException.ERROR.IO_ERROR, "error writing to file");
        }
    }

    /**
     * Reads JSON data from the specified file and writes all attribute
     * values from the JSON data to the appropriate attributes in the
     * given DataModel.
     *
     * @param model the DataModel to be written to
     * @param file the File containing the JSON data to be read
     * @throws DataException either IO errors or errors during the JSON conversion
     */
    public static void readFromFile(final DataModel model, final File file) throws DataException {
        StringBuilder json = new StringBuilder();
        try {
            for (final Scanner sc = new Scanner(file); sc.hasNext();) {
                json.append(sc.nextLine());
            }
        } catch (FileNotFoundException e) {
            throw wrappedAsDataException(DataException.ERROR.IO_ERROR, e);
        }

        try {
            readFromJSON(model, getJSONParser().parse(json.toString(), getOrderedContainerFactory()));
        } catch (ParseException e) {
            throw wrappedAsDataException(DataException.ERROR.JSON_CONVERSION_ERROR, e);
        } catch (JSONException e) {
            throw wrappedAsDataException(DataException.ERROR.JSON_CONVERSION_ERROR, e);
        }
    }

    /**
     * Converts the DataModel to JSON
     * @param model the DataModel to convert
     * @return JSON representation of the DataModel
     * @throws JSONException when there is an error during the JSON conversion
     * @throws DataException when there is an error during value retrieval from the DataModel
     */
    static String convertToJSON(final DataModel model) throws JSONException, DataException {
        final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
        for (final String key : model.getAttributeKeys()) {
            attributes.put(key, prepareForJSON(model.getValue(key)));
        }

        final Map<String, Object> json = new LinkedHashMap<String, Object>();
        json.put(IDENTIFIER_FIELD, IDENTIFIER);
        json.put(VERSION_FIELD, VERSION);
        json.put(ATTRIBUTE_FIELD, attributes);

        return JSONValue.toJSONString(json);
    }

    /**
     * Copies the JSON data to the DataModel's attributs.
     * @param model the DataModel to write to
     * @param json the parsed JSON data to read
     * @throws JSONException when there is an error during the JSON conversion
     * @throws DataException when there is an error setting a value in die DataModel
     */
    static void readFromJSON(final DataModel model, final Object json) throws JSONException, DataException {
        if (!(json instanceof Map)) {
            throw newJSONToJavaError("root element is no map");
        }
        final Map<String, Object> jsonMap = (Map<String, Object>) json;

        final Object identifier = jsonMap.get(IDENTIFIER_FIELD);
        final Object version = jsonMap.get(VERSION_FIELD);
        final Object attributes = jsonMap.get(ATTRIBUTE_FIELD);

        // check null
        if (identifier == null) {
            throw newJSONToJavaError("identifier [" + IDENTIFIER_FIELD + "] is missing");
        }
        if (version == null) {
            throw newJSONToJavaError("version [" + VERSION_FIELD + "] is missing");
        }
        if (attributes == null) {
            throw newJSONToJavaError("attributes [" + ATTRIBUTE_FIELD + "] are missing");
        }

        // check values
        if (!IDENTIFIER.equals(identifier)) {
            throw newJSONToJavaError("wrong identifier [" + IDENTIFIER_FIELD + "]: expected <" + IDENTIFIER
                    + ">, but got <" + identifier.toString() + ">");
        }
        if (!VERSION.equals(version)) {
            throw newJSONToJavaError("wrong version [" + VERSION_FIELD + "]: expected <" + VERSION + ">, but got <"
                    + version.toString() + ">");
        }
        if (!(attributes instanceof Map)) {
            throw newJSONToJavaError("wrong attributes [" + ATTRIBUTE_FIELD + "]: expected a <Map> but got a <"
                    + attributes.getClass().toString() + ">");
        }

        final Map<String, Object> attributeMap = (Map<String, Object>) attributes;
        final Set<String> validAttributes = model.getAttributeKeys();
        for (final String key : attributeMap.keySet()) {
            if (validAttributes.contains(key)) {
                model.setValue(key, JSONAdapter.prepareForJava(attributeMap.get(key)));
            }
        }
    }

    /**
     * Generates a JSONException with {@link JSONException.ERROR#JSON_TO_JAVA}
     * and a given error text.
     * @param errorText the error text
     * @return the freshly constructed JSONException
     */
    private static JSONException newJSONToJavaError(final String errorText) {
        return new JSONException(JSONException.ERROR.JSON_TO_JAVA, errorText);
    }

    /**
     * Wraps another exception in a DataException.
     *
     * @param error the DataException error code to use
     * @param t the original exception
     * @return the freshly constructed DataException
     */
    private static DataException wrappedAsDataException(final DataException.ERROR error, final Throwable t) {
        return new DataException(error, t);
    }
}