blob: 60e814f4f1efa7945f0f8bffc9984ba0299970b6 [file] [log] [blame]
/*
* Copyright (C) 2010 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.google.gson.rest.definition;
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.gson.webservice.definition.internal.utils.Preconditions;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
/**
* An id for a rest resource
*
* @author inder
*
* @param <R> type variable for the rest resource
*/
public final class ValueBasedId<R> implements ID {
private final long value;
private final Type typeOfId;
private ValueBasedId(long value, Type typeOfId) {
this.value = value;
this.typeOfId = typeOfId;
}
@Override
public long getValue() {
return value;
}
public static long getValue(ValueBasedId<?> id) {
return id == null ? INVALID_ID : id.getValue();
}
public String getValueAsString() {
return String.valueOf(value);
}
public Type getTypeOfId() {
return typeOfId;
}
@Override
public int hashCode() {
return (int) value;
}
public static boolean isValid(ValueBasedId<?> id) {
return id != null && id.value != INVALID_ID;
}
/**
* A more efficient comparison method for ids that take into account of ids being nullable.
* Since the method is parameterized and both ids are of the same type, this method compares
* only id values, not their types. Note that this shortcut doesn't work if you pass raw ids
* to this method
*/
public static <T> boolean equals(/* @Nullable */ ValueBasedId<T> id1,
/* @Nullable */ ValueBasedId<T> id2) {
if ((id1 == null && id2 != null) || (id1 != null && id2 == null)) {
return false;
}
if (id1 == null && id2 == null) {
return true;
}
return id1.value == id2.value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
@SuppressWarnings("unchecked")
ValueBasedId<R> other = (ValueBasedId<R>)obj;
if (typeOfId == null) {
if (other.typeOfId != null) return false;
} else if (!equivalentTypes(typeOfId, other.typeOfId)) return false;
if (value != other.value) return false;
return true;
}
/**
* Returns true for equivalentTypes(Class<?>, Class)
* Visible for testing only
*/
@SuppressWarnings("rawtypes")
static boolean equivalentTypes(Type type1, Type type2) {
if (type1 instanceof ParameterizedType && type2 instanceof Class) {
return areEquivalentTypes((ParameterizedType)type1, (Class)type2);
} else if (type2 instanceof ParameterizedType && type1 instanceof Class) {
return areEquivalentTypes((ParameterizedType)type2, (Class)type1);
}
return type1.equals(type2);
}
/**
* Visible for testing only
*/
@SuppressWarnings("rawtypes")
static boolean areEquivalentTypes(ParameterizedType type, Class clazz) {
Class rawClass = (Class) type.getRawType();
if (!clazz.equals(rawClass)) {
return false;
}
for (Type typeVariable : type.getActualTypeArguments()) {
if (typeVariable instanceof WildcardType) {
continue;
}
// This is a real parameterized type, not just ?
return false;
}
return true;
}
public static <RS> ValueBasedId<RS> get(long value, Type typeOfId) {
return new ValueBasedId<RS>(value, typeOfId);
}
@Override
public String toString() {
String typeAsString = getSimpleTypeName(typeOfId);
return String.format("{value:%s,type:%s}", value, typeAsString);
}
@SuppressWarnings("rawtypes")
private static String getSimpleTypeName(Type type) {
if (type == null) {
return "null";
}
if (type instanceof Class) {
return ((Class)type).getSimpleName();
} else if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
StringBuilder sb = new StringBuilder(getSimpleTypeName(pType.getRawType()));
sb.append('<');
boolean first = true;
for (Type argumentType : pType.getActualTypeArguments()) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append(getSimpleTypeName(argumentType));
}
sb.append('>');
return sb.toString();
} else if (type instanceof WildcardType) {
return "?";
}
return type.toString();
}
/**
* Type adapter for converting an Id to its serialized form
*
* @author inder
*
*/
public static final class GsonTypeAdapter implements JsonSerializer<ValueBasedId<?>>,
JsonDeserializer<ValueBasedId<?>> {
@Override
public JsonElement serialize(ValueBasedId<?> src, Type typeOfSrc,
JsonSerializationContext context) {
return new JsonPrimitive(src.getValue());
}
@Override
public ValueBasedId<?> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
if (!(typeOfT instanceof ParameterizedType)) {
throw new JsonParseException("Id of unknown type: " + typeOfT);
}
ParameterizedType parameterizedType = (ParameterizedType) typeOfT;
// Since Id takes only one TypeVariable, the actual type corresponding to the first
// TypeVariable is the Type we are looking for
Type typeOfId = parameterizedType.getActualTypeArguments()[0];
return ValueBasedId.get(json.getAsLong(), typeOfId);
}
}
}