/**
* Copyright 2014 Opower, 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.opower.rest.client.generator.util;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.MapMaker;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ConcurrentMap;
public class StringConverters {
private static ConcurrentMap<Class<?>, StringConverter<?>> CONVERTERS;
public static <T> StringConverter<T> getStringConverter(Class<T> clazz) {
if(CONVERTERS == null) {
synchronized (StringConverters.class) {
if(CONVERTERS == null) {
CONVERTERS = init();
}
}
}
@SuppressWarnings("unchecked")
StringConverter<T> converterToUse = (StringConverter<T>)CONVERTERS.get(clazz);
return converterToUse;
}
private static ConcurrentMap<Class<?>, StringConverter<?>> init() {
ConcurrentMap<Class<?>, StringConverter<?>> map = new MapMaker().makeMap();
map.putAll(ImmutableMap.<Class<?>, StringConverter<?>>builder()
.put(Optional.absent().getClass(), new OptionalConverter())
.put(Optional.of(true).getClass(), new OptionalConverter())
.put(Cookie.class, new CookieStringConverter())
.put(EntityTag.class, new EntityTagDelegate())
.put(Locale.class, new LocaleDelegate())
.put(MediaType.class, new MediaTypeStringConverter())
.put(URI.class, new UriStringConverter())
.put(NewCookie.class, new NewCookieStringConverter())
.put(CacheControl.class, new CacheControlDelegate())
.build());
return map;
}
private static abstract class OneWayStringConverter<T> implements StringConverter<T> {
@Override
public T fromString(String string) {
throw new UnsupportedOperationException();
}
}
public static class OptionalConverter extends OneWayStringConverter<Optional<?>> {
@Override
public String toString(Optional<?> value) {
if (value.isPresent()) {
return value.get().toString();
}
return null;
}
}
public static class CookieStringConverter extends OneWayStringConverter<Cookie> {
public String toString(Cookie cookie) {
StringBuffer buf = new StringBuffer();
ServerCookie.appendCookieValue(buf, 0, cookie.getName(), cookie.getValue(), cookie.getPath(), cookie.getDomain(), null, -1, false);
return buf.toString();
}
}
public static class EntityTagDelegate extends OneWayStringConverter<EntityTag> {
public String toString(EntityTag value) {
String weak = value.isWeak() ? "W/" : "";
return weak + value.getValue();
}
}
public static class LocaleDelegate extends OneWayStringConverter<Locale> {
public String toString(Locale value) {
return toLanguageString(value);
}
/**
* HTTP 1.1 has different String format for language than what java.util.Locale does '-' instead of '_'
* as a separator
*
* @param value
* @return
*/
private String toLanguageString(Locale value) {
StringBuffer buf = new StringBuffer(value.getLanguage().toLowerCase());
if (value.getCountry() != null && !value.getCountry().equals(""))
buf.append("-").append(value.getCountry().toLowerCase());
return buf.toString();
}
}
public static class MediaTypeStringConverter implements StringConverter<MediaType> {
@Override
public MediaType fromString(String type) {
if (type == null) throw new IllegalArgumentException("MediaType value is null");
return parse(type);
}
public static MediaType parse(String type)
{
String params = null;
int idx = type.indexOf(";");
if (idx > -1)
{
params = type.substring(idx + 1).trim();
type = type.substring(0, idx);
}
String major = null;
String subtype = null;
String[] paths = type.split("/");
if (paths.length < 2 && type.equals("*"))
{
major = "*";
subtype = "*";
}
else if (paths.length != 2)
{
throw new IllegalArgumentException("Failure parsing MediaType string: " + type);
}
else if (paths.length == 2)
{
major = paths[0];
subtype = paths[1];
}
if (params != null && !params.equals(""))
{
HashMap<String, String> typeParams = new HashMap<String, String>();
int start = 0;
while (start < params.length())
{
start = setParam(typeParams, params, start);
}
return new MediaType(major, subtype, typeParams);
}
else
{
return new MediaType(major, subtype);
}
}
static int getEndName(String params, int start)
{
int equals = params.indexOf('=', start);
int semicolon = params.indexOf(';', start);
if (equals == -1 && semicolon == -1) return params.length();
if (equals == -1) return semicolon;
if (semicolon == -1) return equals;
int end = (equals < semicolon) ? equals : semicolon;
return end;
}
public static int setParam(HashMap<String, String> typeParams, String params, int start)
{
boolean quote = false;
boolean backslash = false;
int end = getEndName(params, start);
String name = params.substring(start, end).trim();
if (end < params.length() && params.charAt(end) == '=') end++;
StringBuilder buffer = new StringBuilder(params.length() - end);
int i = end;
for (; i < params.length(); i++)
{
char c = params.charAt(i);
switch (c)
{
case '"':
{
if (backslash)
{
backslash = false;
buffer.append(c);
}
else
{
quote = !quote;
}
break;
}
case '\\':
{
if (backslash)
{
backslash = false;
buffer.append(c);
}
break;
}
case ';':
{
if (!quote)
{
String value = buffer.toString().trim();
typeParams.put(name, value);
return i + 1;
}
else
{
buffer.append(c);
}
break;
}
default:
{
buffer.append(c);
break;
}
}
}
String value = buffer.toString().trim();
typeParams.put(name, value);
return i;
}
public String toString(MediaType type) {
String rtn = type.getType().toLowerCase() + "/" + type.getSubtype().toLowerCase();
if (type.getParameters() == null || type.getParameters().size() == 0) return rtn;
for (String name : type.getParameters().keySet()) {
String val = type.getParameters().get(name);
rtn += ";" + name + "=\"" + val + "\"";
}
return rtn;
}
}
public static class UriStringConverter extends OneWayStringConverter<URI> {
public String toString(URI uri) {
return uri.toASCIIString();
}
}
public static class NewCookieStringConverter extends OneWayStringConverter<NewCookie> {
protected void quote(StringBuilder b, String value) {
if (value.indexOf(' ') > -1) {
b.append('"');
b.append(value);
b.append('"');
} else {
b.append(value);
}
}
public String toString(NewCookie cookie) {
StringBuilder b = new StringBuilder();
b.append(cookie.getName()).append('=');
quote(b, cookie.getValue());
b.append(";").append("Version=").append(cookie.getVersion());
if (cookie.getComment() != null) {
b.append(";Comment=");
quote(b, cookie.getComment());
}
if (cookie.getDomain() != null) {
b.append(";Domain=");
quote(b, cookie.getDomain());
}
if (cookie.getPath() != null) {
b.append(";Path=");
quote(b, cookie.getPath());
}
if (cookie.getMaxAge() != -1) {
b.append(";Max-Age=");
b.append(cookie.getMaxAge());
}
if (cookie.isSecure())
b.append(";Secure");
return b.toString();
}
}
public static class CacheControlDelegate extends OneWayStringConverter<CacheControl> {
private StringBuffer addDirective(String directive, StringBuffer buffer) {
if (buffer.length() > 0) buffer.append(", ");
buffer.append(directive);
return buffer;
}
public String toString(CacheControl value) {
StringBuffer buffer = new StringBuffer();
if (value.isNoCache()) {
List<String> fields = value.getNoCacheFields();
if (fields.size() < 1) {
addDirective("no-cache", buffer);
} else {
for (String field : value.getNoCacheFields()) {
addDirective("no-cache", buffer).append("=\"").append(field).append("\"");
}
}
}
if (value.isMustRevalidate()) addDirective("must-revalidate", buffer);
if (value.isNoTransform()) addDirective("no-transform", buffer);
if (value.isNoStore()) addDirective("no-store", buffer);
if (value.isProxyRevalidate()) addDirective("rest-revalidate", buffer);
if (value.getSMaxAge() > -1) addDirective("s-maxage", buffer).append("=").append(value.getSMaxAge());
if (value.getMaxAge() > -1) addDirective("max-age", buffer).append("=").append(value.getMaxAge());
if (value.isPrivate()) {
List<String> fields = value.getPrivateFields();
if (fields.size() < 1) addDirective("private", buffer);
else {
for (String field : value.getPrivateFields()) {
addDirective("private", buffer).append("=\"").append(field).append("\"");
}
}
}
for (String key : value.getCacheExtension().keySet()) {
String val = value.getCacheExtension().get(key);
addDirective(key, buffer);
if (val != null && !"".equals(val)) {
buffer.append("=\"").append(val).append("\"");
}
}
return buffer.toString();
}
}
}