/**
* Copyright (c) 2013 Puppet Labs, Inc. and other contributors, as listed below.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License, Version 2.0
* which accompanies this distribution, and is available at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Contributors:
* Puppet Labs
*/
package com.puppetlabs.puppetdb.javaclient.impl;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.inject.Provider;
/**
* A provider of {@link Gson} instances.
*/
public class GsonProvider implements Provider<Gson> {
/**
* TODO: Deprecate this adapter when support for Java < 1.7 is dropped. The RFC-822 format is implemented from Java 1.7 and up
* A json adapter capable of serializing/deserializing a timestamp with RFC-822 style timezone
*/
public static class DateJsonAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
/**
* Convert the given date into a RFC-822 style timestamp
*
* @param date
* The date to be converted
* @return The string form of the date
*/
public static String dateToString(Date date) {
String target;
synchronized(ISO_8601_TZ) {
target = ISO_8601_TZ.format(date);
}
Matcher m = RFC_822_PTRN.matcher(target);
if(m.matches()) {
String tz = m.group(2);
if("+0000".equals(tz))
tz = "Z";
else
tz = tz.substring(0, 3) + ':' + tz.substring(3, 5);
target = m.group(1) + tz;
}
return target;
}
@Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String source = json.getAsString();
Matcher m = ISO_8601_PTRN.matcher(source);
if(m.matches()) {
String tz = m.group(2);
if("Z".equals(tz))
tz = "+0000";
else
tz = tz.substring(0, 3) + tz.substring(4, 6);
source = m.group(1) + tz;
}
synchronized(ISO_8601_TZ) {
try {
return ISO_8601_TZ.parse(source);
}
catch(ParseException e) {
throw new JsonParseException(e);
}
}
}
@Override
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(dateToString(src));
}
}
private static final Pattern ISO_8601_PTRN = Pattern.compile("^(\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d(?:\\.\\d+)?)(Z|(?:[+-]\\d\\d:\\d\\d))$");
private static final Pattern RFC_822_PTRN = Pattern.compile("^(\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d(?:\\.\\d+)?)([+-]\\d\\d\\d\\d)$");
private static final SimpleDateFormat ISO_8601_TZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
private static final GsonBuilder gsonBuilder;
private static final Gson gson;
static {
gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Date.class, new DateJsonAdapter());
gson = gsonBuilder.create();
}
/**
* Creates a JSON representation for the given object using an internal
* synchronized {@link Gson} instance.
*
* @param object
* The object to produce JSON for
* @return JSON representation of the given <code>object</code>
*/
public static String toJSON(Object object) {
synchronized(gson) {
return gson.toJson(object);
}
}
@Override
public Gson get() {
return gsonBuilder.create();
}
}