com.cybozu.kintone.database.JsonParser.java Source code

Java tutorial

Introduction

Here is the source code for com.cybozu.kintone.database.JsonParser.java

Source

//   Copyright 2013 Cybozu
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
//   Google Gson
//   Copyright (c) 2008-2009 Google Inc. 
//
//   Licensed under the Apache License, Version 2.0 (the "License"); 
//   you may not use this file except in compliance with the License. 
//   You may obtain a copy of the License at 
//
//       http://www.apache.org/licenses/LICENSE-2.0 
//
//   Unless required by applicable law or agreed to in writing, software 
//   distributed under the License is distributed on an "AS IS" BASIS, 
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
//   See the License for the specific language governing permissions and 
//   limitations under the License.

package com.cybozu.kintone.database;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;

import com.cybozu.kintone.database.exception.TypeMismatchException;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonWriter;

/**
 * JsonParser class converts JSON string to Java object and also Java object to
 * JSON string. Although this class uses Google Gson library, developers don't
 * have to care about that. See the detail of Google Gson.
 * https://code.google.com/p/google-gson/
 */
public class JsonParser {

    public JsonParser() {

    }

    /**
     * Converts the json string to the error response object.
     * @param json
     *            a json string
     * @return error response object
     */
    public ErrorResponse jsonToErrorResponse(String json) {
        Gson gson = new Gson();
        try {
            return gson.fromJson(json, ErrorResponse.class);
        } catch (JsonSyntaxException e) {
            return null;
        }
    }

    /**
     * Converts the json string to the resultset object.
     * @param con
     *            a connection object
     * @param json
     *            a json string
     * @return resultset object
     * @throws IOException
     */
    public ResultSet jsonToResultSet(Connection con, String json) throws IOException {

        ResultSet rs = new ResultSet(con);
        com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
        JsonElement root = parser.parse(json);

        if (root.isJsonObject()) {
            JsonObject rootObject = root.getAsJsonObject();
            JsonArray records = rootObject.get("records").getAsJsonArray();
            for (JsonElement elem : records) {
                Record record = readRecord(elem);
                if (record != null) {
                    rs.add(record);
                }
            }

            if (!rootObject.get("totalCount").isJsonNull()) {
                rs.setTotalCount(rootObject.get("totalCount").getAsLong());

            }
        }

        return rs;
    }

    /**
     * Reads and parses each record element.
     * @param elem
     *            a json element represents a record object
     * @return the record object created
     * @throws IOException
     */
    private Record readRecord(JsonElement elem) throws IOException {

        Record record = new Record();

        if (elem.isJsonObject()) {
            JsonObject obj = elem.getAsJsonObject();
            Set<Map.Entry<String, JsonElement>> set = obj.entrySet();
            for (Map.Entry<String, JsonElement> entry : set) {
                Field field = readField(entry.getKey(), entry.getValue());
                if (field != null) {
                    record.addField(field.getName(), field);
                }
            }
        }

        return record;
    }

    /**
     * Reads and parses each field element.
     * @param fieldName
     *            the field name
     * @param elem
     *            a json element represents a record object
     * @return the field object created
     * @throws IOException
     */
    private Field readField(String fieldName, JsonElement fieldElem) throws IOException {

        Field field = null;

        if (!fieldElem.isJsonObject())
            return null;
        JsonObject obj = fieldElem.getAsJsonObject();

        FieldType type = FieldType.getEnum(obj.get("type").getAsString());
        JsonElement element = obj.get("value");

        Object object = null;
        String strVal = null;
        if (type == null || element == null)
            return null;

        if (!element.isJsonNull()) {
            switch (type) {
            case SINGLE_LINE_TEXT:
            case CALC:
            case MULTI_LINE_TEXT:
            case RICH_TEXT:
            case RADIO_BUTTON:
            case DROP_DOWN:
            case LINK:
            case STATUS:
            case RECORD_NUMBER:
            case NUMBER:
                object = element.getAsString();
                break;
            case __ID__:
            case __REVISION__:
                strVal = element.getAsString();
                try {
                    object = Long.valueOf(strVal);
                } catch (NumberFormatException e) {
                }
                break;
            case DATE:
            case TIME:
            case DATETIME:
            case CREATED_TIME:
            case UPDATED_TIME:
                object = element.getAsString();
                break;
            case CHECK_BOX:
            case MULTI_SELECT:
            case CATEGORY:
                object = jsonToStringArray(element);
                break;
            case FILE:
                object = jsonToFileArray(element);
                break;
            case CREATOR:
            case MODIFIER:
                if (element.isJsonObject()) {
                    Gson gson = new Gson();
                    object = gson.fromJson(element, UserDto.class);
                }
                break;
            case USER_SELECT:
            case STATUS_ASSIGNEE:
                object = jsonToUserArray(element);
                break;
            case SUBTABLE:
                object = jsonToSubtable(element);
                break;
            }
        }
        field = new Field(fieldName, type, object);

        return field;
    }

    /**
     * Converts json element to the sub table object.
     * @param element json element
     * @return sub table object
     * @throws IOException
     */
    private List<Record> jsonToSubtable(JsonElement element) throws IOException {
        List<Record> rs = new ArrayList<Record>();

        if (!element.isJsonArray())
            return null;

        JsonArray records = element.getAsJsonArray();
        for (JsonElement elem : records) {
            if (elem.isJsonObject()) {
                JsonObject obj = elem.getAsJsonObject();
                String id = obj.get("id").getAsString();
                JsonElement value = obj.get("value");
                Record record = readRecord(value);
                if (record != null) {
                    try {
                        record.setId(Long.valueOf(id));
                    } catch (NumberFormatException e) {
                    }
                    rs.add(record);
                }
            }
        }

        return rs;
    }

    /**
     * Converts json element to the string array object.
     * @param element json element
     * @return string array object
     * @throws IOException
     */
    private List<String> jsonToStringArray(JsonElement element) {
        if (!element.isJsonArray())
            return null;
        Type collectionType = new TypeToken<Collection<String>>() {
        }.getType();
        Gson gson = new Gson();

        return gson.fromJson(element, collectionType);
    }

    /**
     * Converts json element to the user array object.
     * @param element json element
     * @return user array object
     * @throws IOException
     */
    private List<UserDto> jsonToUserArray(JsonElement element) {
        if (!element.isJsonArray())
            return null;
        Type collectionType = new TypeToken<Collection<UserDto>>() {
        }.getType();
        Gson gson = new Gson();

        return gson.fromJson(element, collectionType);
    }

    /**
     * Converts json element to the file array object.
     * @param element json element
     * @return file array object
     * @throws IOException
     */
    private List<FileDto> jsonToFileArray(JsonElement element) {
        if (!element.isJsonArray())
            return null;
        Type collectionType = new TypeToken<Collection<FileDto>>() {
        }.getType();
        Gson gson = new Gson();

        return gson.fromJson(element, collectionType);
    }

    /**
     * Writes the field object with json writer.
     * @param writer json writer
     * @param field field object
     * @throws IOException
     */
    private void writeField(JsonWriter writer, Field field) throws IOException {
        writer.name(field.getName());
        writer.beginObject();
        writeFieldValue(writer, field);
        writer.endObject();
    }

    /**
     * Writes the field value with json writer.
     * @param writer json writer
     * @param field field object
     * @throws IOException
     */
    private void writeFieldValue(JsonWriter writer, Field field) throws IOException {
        writer.name("value");
        FieldType type = field.getFieldType();

        if (field.isEmpty()) {
            writer.value("");
        } else {

            switch (type) {
            case SINGLE_LINE_TEXT:
            case CALC:
            case MULTI_LINE_TEXT:
            case RICH_TEXT:
            case RADIO_BUTTON:
            case DROP_DOWN:
            case LINK:
            case STATUS:
                writer.value(field.getAsString());
                break;
            case NUMBER:
            case RECORD_NUMBER:
                writer.value(String.valueOf(field.getAsLong()));
                break;
            case DATE:
            case TIME:
            case DATETIME:
            case CREATED_TIME:
            case UPDATED_TIME:
                writer.value(field.getAsString());
                break;
            case CHECK_BOX:
            case MULTI_SELECT:
            case CATEGORY:
                writer.beginArray();
                Iterable<String> strList = field.getAsStringList();
                for (String strVal : strList) {
                    writer.value(strVal);
                }
                writer.endArray();
                break;
            case FILE:
                writer.beginArray();
                Iterable<FileDto> fileList = field.getAsFileList();
                for (FileDto file : fileList) {
                    writer.beginObject();
                    writer.name("fileKey").value(file.getFileKey());
                    writer.endObject();
                }
                writer.endArray();
                break;
            case USER_SELECT:
                writer.beginArray();
                Iterable<UserDto> userList = field.getAsUserList();
                for (UserDto user : userList) {
                    writer.beginObject();
                    writer.name("code").value(user.getCode());
                    writer.endObject();
                }
                writer.endArray();
                break;
            case CREATOR:
            case MODIFIER:
                writer.beginObject();
                writer.name("code").value(field.getAsUserInfo().getCode());
                writer.endObject();
                break;
            case SUBTABLE:
                writer.beginArray();
                List<Record> subtable = field.getAsSubtable();
                writeSubtable(writer, subtable);
                writer.endArray();
                break;
            default:
                writer.value("");
            }
        }
    }

    /**
     * Writes the subtable value to json.
     * @param writer
     *            a json writer
     * @param subtable
     *            a subtable object
     */
    private void writeSubtable(JsonWriter writer, Iterable<Record> subtable) throws IOException {
        for (Record record : subtable) {
            writer.beginObject();
            //writer.name("id").value(record.getId());
            writer.name("value");
            writer.beginObject();
            for (String fieldName : record.getFieldNames()) {
                Field field = record.getField(fieldName);
                try {
                    writeField(writer, field);
                } catch (TypeMismatchException e) {
                    e.printStackTrace();
                }
            }
            writer.endObject();
            writer.endObject();
        }
    }

    /**
     * Generates the json string for insert method.
     * @param app
     *            the application id
     * @param records
     *            the array of the record object
     * @return the json string
     * @throws IOException
     */
    public String recordsToJsonForInsert(long app, List<Record> records) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("records");

        writer.beginArray();
        for (Record record : records) {
            writer.beginObject();
            Set<Map.Entry<String, Field>> set = record.getEntrySet();
            for (Map.Entry<String, Field> entry : set) {
                Field field = entry.getValue();
                try {
                    writeField(writer, field);
                } catch (TypeMismatchException e) {
                    e.printStackTrace();
                }
            }
            writer.endObject();
        }
        writer.endArray();

        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Retrieves the array of the Long values from json.
     * @param json
     *            a json string
     * @return the array of the long value
     */
    public List<Long> jsonToLongArray(String json) {
        Gson gson = new Gson();
        Type listType = new TypeToken<ArrayList<Long>>() {
        }.getType();
        return gson.fromJson(json, listType);
    }

    /**
     * Retrieves the array of the ids from json string.
     * @param json
     *            a json string
     * @return the array of the id
     * @throws IOException
     */
    public List<Long> jsonToIDs(String json) throws IOException {
        com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
        JsonElement root = parser.parse(json);

        List<Long> ids = new ArrayList<Long>();
        if (root.isJsonObject()) {
            JsonArray jsonIds = root.getAsJsonObject().get("ids").getAsJsonArray();
            for (JsonElement elem : jsonIds) {
                Long id = new Long(elem.getAsString());
                ids.add(id);
            }
        }

        return ids;
    }

    /**
     * Generates the json string for update method.
     * @param app
     *            the application id
     * @param ids
     *            the array of the record id to be updated
     * @param record
     *            the values of updated records
     * @return
     *        json string
     * @throws IOException
     */
    public String recordsToJsonForUpdate(long app, List<Long> ids, Record record) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("records");

        writer.beginArray();
        for (long id : ids) {
            writer.beginObject();
            writer.name("id").value(id);
            if (record.hasRevision()) {
                writer.name("revision").value(record.getRevision());
            }
            writer.name("record");
            writer.beginObject();
            for (String fieldName : record.getFieldNames()) {
                Field field = record.getField(fieldName);
                try {
                    writeField(writer, field);
                } catch (TypeMismatchException e) {
                    e.printStackTrace();
                }
            }
            writer.endObject();
            writer.endObject();
        }
        writer.endArray();

        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string for update method.
     * @param app
     *            application id
     * @param record
     *            updated record
     * @return
     *        json string
     * @throws IOException
     */
    public String recordsToJsonForUpdate(long app, Record record) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("id").value(record.getId());
        if (record.hasRevision()) {
            writer.name("revision").value(record.getRevision());
        }
        writer.name("record");
        writer.beginObject();
        for (String fieldName : record.getFieldNames()) {
            Field field = record.getField(fieldName);
            try {
                writeField(writer, field);
            } catch (TypeMismatchException e) {
                e.printStackTrace();
            }
        }
        writer.endObject();

        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string for update method.
     * @param app
     *            the application id
     * @param records
     *            an array of the updated records
     * @return
     *        json string
     * @throws IOException
     */
    public String recordsToJsonForUpdate(long app, List<Record> records) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("records");

        writer.beginArray();
        for (Record record : records) {
            writer.beginObject();
            writer.name("id").value(record.getId());
            if (record.hasRevision()) {
                writer.name("revision").value(record.getRevision());
            }
            writer.name("record");
            writer.beginObject();
            for (String fieldName : record.getFieldNames()) {
                Field field = record.getField(fieldName);
                try {
                    writeField(writer, field);
                } catch (TypeMismatchException e) {
                    e.printStackTrace();
                }
            }
            writer.endObject();
            writer.endObject();
        }
        writer.endArray();

        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string for update method.
     * @param app
     *            application id
     * @param key
     *            key field name
     * @param record
     *            updated record
     * @return
     *        json string
     * @throws IOException
     */
    public String recordsToJsonForUpdateByKey(long app, String key, Record record) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        if (record.hasRevision()) {
            writer.name("revision").value(record.getRevision());
        }
        writer.name("updateKey");
        writer.beginObject();
        writer.name("field").value(key);
        writeFieldValue(writer, record.getField(key));
        writer.endObject();

        writer.name("record");
        writer.beginObject();
        for (String fieldName : record.getFieldNames()) {
            if (fieldName == key)
                continue;

            Field field = record.getField(fieldName);
            try {
                writeField(writer, field);
            } catch (TypeMismatchException e) {
                e.printStackTrace();
            }
        }
        writer.endObject();

        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string for update method.
     * @param app
     *            application id
     * @param key
     *            key field name
     * @param records
     *            an array of the updated records
     * @return
     *        json string
     * @throws IOException
     */
    public String recordsToJsonForUpdateByKey(long app, String key, List<Record> records) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("records");

        writer.beginArray();
        for (Record record : records) {
            writer.beginObject();
            if (record.hasRevision()) {
                writer.name("revision").value(record.getRevision());
            }
            writer.name("updateKey");
            writer.beginObject();
            writer.name("field").value(key);
            writeFieldValue(writer, record.getField(key));
            writer.endObject();

            writer.name("record");
            writer.beginObject();
            for (String fieldName : record.getFieldNames()) {
                if (fieldName == key)
                    continue;

                Field field = record.getField(fieldName);
                try {
                    writeField(writer, field);
                } catch (TypeMismatchException e) {
                    e.printStackTrace();
                }
            }
            writer.endObject();
            writer.endObject();
        }
        writer.endArray();

        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string for delete method.
     * @param app
     *            the application id
     * @param records
     *            an array of the records to be deleted
     * @return
     *        json string
     * @throws IOException
     */
    public String recordsToJsonForDelete(long app, List<Record> records) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);

        writer.name("ids");
        writer.beginArray();
        for (Record record : records) {
            writer.value(record.getId());
        }
        writer.endArray();

        writer.name("revisions");
        writer.beginArray();
        for (Record record : records) {
            writer.value(record.getRevision());
        }
        writer.endArray();

        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Retrieves the file key string from json string.
     * @param json
     *            a json string
     * @return the file key
     * @throws IOException
     */
    public String jsonToFileKey(String json) throws IOException {
        com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
        JsonElement root = parser.parse(json);

        String fileKey = null;
        if (root.isJsonObject()) {
            fileKey = root.getAsJsonObject().get("fileKey").getAsString();
        }

        return fileKey;
    }

    /**
     * Retrieves the revision string from json string.
     * @param json
     *            a json string
     * @return the revision number
     * @throws IOException
     */
    public long jsonToRevision(String json) throws IOException {
        com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
        JsonElement root = parser.parse(json);

        String revision = null;
        if (root.isJsonObject()) {
            revision = root.getAsJsonObject().get("revision").getAsString();
        }

        return Long.valueOf(revision);
    }

    /**
     * Retrieves the id string from json string.
     * @param json
     *            a json string
     * @return the id number
     * @throws IOException
     */
    public long jsonToId(String json) throws IOException {
        com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
        JsonElement root = parser.parse(json);

        String id = null;
        if (root.isJsonObject()) {
            id = root.getAsJsonObject().get("id").getAsString();
        }

        return Long.valueOf(id);
    }

    /**
     * Convert json string to AppDto.
     * @param json
     *            a json string
     * @return app object
     * @throws IOException
     */
    public AppDto jsonToApp(String json) throws IOException {
        com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
        JsonElement element = parser.parse(json);
        Type elementType = new TypeToken<AppDto>() {
        }.getType();
        Gson gson = new Gson();

        return gson.fromJson(element, elementType);
    }

    /**
     * Convert json string to AppDto array.
     * @param json
     *            a json string
     * @return the list of app object
     * @throws IOException
     */
    public List<AppDto> jsonToApps(String json) throws IOException {
        com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
        JsonElement root = parser.parse(json);

        if (!root.isJsonObject())
            return null;

        JsonArray apps = root.getAsJsonObject().get("apps").getAsJsonArray();

        if (!apps.isJsonArray())
            return null;

        Type collectionType = new TypeToken<Collection<AppDto>>() {
        }.getType();
        Gson gson = new Gson();

        return gson.fromJson(apps, collectionType);
    }

    /**
     * Generates the json string to update assignees.
     * @param id
     *            record id
     * @param code
     *            array of the code of the assigned users
     * @param revision
     *            revision number (-1 means "not set")
     * @return
     *        json string
     * @throws IOException
     */
    public String generateForUpdateAssignees(long app, long id, List<String> codes, long revision)
            throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("id").value(id);

        writer.name("assignees");
        writer.beginArray();
        for (String code : codes) {
            writer.value(code);
        }
        writer.endArray();

        if (revision >= 0) {
            writer.name("revision").value(revision);
        }
        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string to update status.
     * @param app
     *            application id
     * @param id
     *            record id
     * @param action
     *            action name
     * @param assignee
     *            login name of the assignee
     * @param revision
     *            revision number (-1 means "not set")
     * @return
     *        json string
     * @throws IOException
     */
    public String generateForUpdateStatus(long app, long id, String action, String assignee, long revision)
            throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("id").value(id);
        writer.name("action").value(action);
        if (assignee != null) {
            writer.name("assignee").value(assignee);
        }

        if (revision >= 0) {
            writer.name("revision").value(revision);
        }
        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string to update status(for bulk updating).
     * @param app
     *            application id
     * @param ids
     *            an array of the record id
     * @param actions
     *            an array of the action name
     * @param assignees
     *            an array of the login name of the assignee
     * @param revision
     *            an array of the revision number (-1 means "not set")
     * @return
     *        json string
     * @throws IOException
     */
    public String generateForUpdateStatus(long app, List<Long> ids, List<String> actions, List<String> assignees,
            List<Long> revisions) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);

        int size = ids.size();
        writer.name("records");
        writer.beginArray();
        for (int i = 0; i < size; i++) {
            writer.beginObject();
            writer.name("id").value(ids.get(i));
            writer.name("action").value(actions.get(i));
            if (assignees.get(i) != null) {
                writer.name("assignee").value(assignees.get(i));
            }

            if (revisions.get(i) >= 0) {
                writer.name("revision").value(revisions.get(i));
            }
            writer.endObject();
        }
        writer.endArray();

        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string to add comment.
     * @param app
     *            application id
     * @param record
     *            record id
     * @param mentions
     *            an array of mentions
     * @return
     *        json string
     * @throws IOException
     */
    public String generateForAddComment(long app, long record, String text, List<MentionDto> mentions)
            throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("record").value(record);

        writer.name("comment");
        writer.beginObject();

        writer.name("text").value(text);

        writer.name("mentions");
        writer.beginArray();
        for (MentionDto mention : mentions) {
            writer.beginObject();
            writer.name("code").value(mention.getCode());
            writer.name("type").value(mention.getType());
            writer.endObject();
        }
        writer.endArray();
        writer.endObject();
        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Generates the json string to delete comment.
     * @param app
     *            application id
     * @param record
     *            record id
     * @param id
     *            comment id
     * @return
     *        json string
     * @throws IOException
     */
    public String generateForDeleteComment(long app, long record, long id) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(baos));

        writer.beginObject();
        writer.name("app").value(app);
        writer.name("record").value(record);
        writer.name("comment").value(id);
        writer.endObject();

        writer.close();
        return new String(baos.toByteArray());
    }

    /**
     * Converts the json string to the commentset object.
     * @param json
     *            a json string
     * @return commentset object
     * @throws IOException
     */
    public CommentSet jsonToCommentSet(Connection con, String json) throws IOException {

        CommentSet cs = new CommentSet();
        com.google.gson.JsonParser parser = new com.google.gson.JsonParser();
        JsonElement root = parser.parse(json);

        if (root.isJsonObject()) {
            JsonObject obj = root.getAsJsonObject();
            JsonArray comments = obj.get("comments").getAsJsonArray();
            for (JsonElement elem : comments) {
                Comment comment = readComment(elem);
                if (comment != null) {
                    cs.add(comment);
                }
            }

            cs.setNewer(obj.get("newer").getAsBoolean());
            cs.setOlder(obj.get("older").getAsBoolean());
        }
        return cs;
    }

    private Date getDateTime(String strDate) {
        if (strDate == null || strDate.isEmpty())
            return null;
        try {
            DateFormat df;
            if (strDate.indexOf('.') > 0) {
                df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.000'Z'");
            } else {
                df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
            }
            df.setTimeZone(TimeZone.getTimeZone("UTC"));
            return df.parse(strDate);
        } catch (ParseException e) {
            throw new TypeMismatchException();
        }
    }

    /**
     * Reads and parses each comment element.
     * @param elem
     *            a json element represents a comment object
     * @return the comment object created
     * @throws IOException
     */
    private Comment readComment(JsonElement elem) throws IOException {

        Comment comment = null;
        Gson gson = new Gson();

        if (elem.isJsonObject()) {
            JsonObject obj = elem.getAsJsonObject();
            long id = obj.get("id").getAsLong();
            String text = obj.get("text").getAsString();
            Date createdAt = getDateTime(obj.get("createdAt").getAsString());

            Type userElementType = new TypeToken<UserDto>() {
            }.getType();

            UserDto user = gson.fromJson(obj.getAsJsonObject("creator"), userElementType);

            Type mentionsElementType = new TypeToken<Collection<MentionDto>>() {
            }.getType();

            List<MentionDto> mentions = gson.fromJson(obj.getAsJsonArray("mentions"), mentionsElementType);

            comment = new Comment(id, text, createdAt, user, mentions);
        }

        return comment;
    }
}