Created a generalized key-value store,MetaDataMap, for a map of MetaData.
Moved MetaData to wsdef from wsf. Added a Gson type adapter for Id type.
This commit is contained in:
parent
8cbdd8a030
commit
281ed6f866
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* 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.webservice.definition.rest;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata associated with a repository for a rest resource
|
||||||
|
*
|
||||||
|
* @author inder
|
||||||
|
*
|
||||||
|
* @param <R> The resource
|
||||||
|
*/
|
||||||
|
public final class MetaData<R extends RestResource<R>> {
|
||||||
|
|
||||||
|
private final Map<String, String> map;
|
||||||
|
|
||||||
|
public static <RS extends RestResource<RS>> MetaData<RS> create() {
|
||||||
|
return new MetaData<RS>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
private static MetaData<?> createTypeUnsafe(Map<String, String> values) {
|
||||||
|
return new MetaData(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetaData() {
|
||||||
|
this(new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MetaData(Map<String, String> values) {
|
||||||
|
this.map = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String key) {
|
||||||
|
return (String) map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putString(String key, String value) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBoolean(String key) {
|
||||||
|
String value = map.get(key);
|
||||||
|
return value == null ? false : Boolean.parseBoolean(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBoolean(String key, boolean value) {
|
||||||
|
map.put(key, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(String key) {
|
||||||
|
map.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gson Type adapter for {@link MetaData}. The serialized representation on wire is just a
|
||||||
|
* Map<String, String>
|
||||||
|
*/
|
||||||
|
public static final class GsonTypeAdapter implements JsonSerializer<MetaData<?>>,
|
||||||
|
JsonDeserializer<MetaData<?>>{
|
||||||
|
|
||||||
|
private static final Type MAP_TYPE = new TypeToken<Map<String, String>>(){}.getType();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetaData<?> deserialize(JsonElement json, Type typeOfT,
|
||||||
|
JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
Map<String, String> map = context.deserialize(json, MAP_TYPE);
|
||||||
|
return MetaData.createTypeUnsafe(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(MetaData<?> src, Type typeOfSrc,
|
||||||
|
JsonSerializationContext context) {
|
||||||
|
return context.serialize(src.map, MAP_TYPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,5 +23,4 @@ package com.google.gson.webservice.definition.rest;
|
||||||
* @param <R> the rest resource type
|
* @param <R> the rest resource type
|
||||||
*/
|
*/
|
||||||
public interface RestResource<R> extends HasId<R> {
|
public interface RestResource<R> extends HasId<R> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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.webservice.typeadapters;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
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.rest.Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type adapter for converting an Id to its serialized form
|
||||||
|
*
|
||||||
|
* @author inder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class IdTypeAdapter implements JsonSerializer<Id<?>>, JsonDeserializer<Id<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Id<?> src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Id<?> 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 Id.get(json.getAsLong(), typeOfId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* 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.webservice.typeadapters;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.webservice.definition.rest.Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link IdTypeAdapter}
|
||||||
|
*
|
||||||
|
* @author inder
|
||||||
|
*/
|
||||||
|
public class IdTypeAdapterTest extends TestCase {
|
||||||
|
private static final Id<Student> STUDENT1_ID = Id.get(5L, Student.class);
|
||||||
|
private static final Id<Student> STUDENT2_ID = Id.get(6L, Student.class);
|
||||||
|
private static final Student STUDENT1 = new Student(STUDENT1_ID, "first");
|
||||||
|
private static final Student STUDENT2 = new Student(STUDENT2_ID, "second");
|
||||||
|
private static final Type TYPE_COURSE_HISTORY =
|
||||||
|
new TypeToken<Course<HistoryCourse>>(){}.getType();
|
||||||
|
private static final Id<Course<HistoryCourse>> COURSE_ID = Id.get(10L, TYPE_COURSE_HISTORY);
|
||||||
|
|
||||||
|
private Gson gson;
|
||||||
|
private Course<HistoryCourse> course;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() {
|
||||||
|
gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(Id.class, new IdTypeAdapter())
|
||||||
|
.create();
|
||||||
|
course = new Course<HistoryCourse>(COURSE_ID, 4,
|
||||||
|
new Assignment<HistoryCourse>(null, null), createList(STUDENT1, STUDENT2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSerializeId() {
|
||||||
|
String json = gson.toJson(course, TYPE_COURSE_HISTORY);
|
||||||
|
System.out.println(json);
|
||||||
|
assertTrue(json.contains(String.valueOf(COURSE_ID.getValue())));
|
||||||
|
assertTrue(json.contains(String.valueOf(STUDENT1_ID.getValue())));
|
||||||
|
assertTrue(json.contains(String.valueOf(STUDENT2_ID.getValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeserializeId() {
|
||||||
|
String json = "{courseId:1,students:[{id:1,name:'first'},{id:6,name:'second'}],"
|
||||||
|
+ "numAssignments:4,assignment:{}}";
|
||||||
|
Course<HistoryCourse> target = gson.fromJson(json, TYPE_COURSE_HISTORY);
|
||||||
|
assertEquals(1, target.getStudents().get(0).id.getValue());
|
||||||
|
assertEquals(6, target.getStudents().get(1).id.getValue());
|
||||||
|
assertEquals(1, target.getId().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class Student {
|
||||||
|
Id<Student> id;
|
||||||
|
String name;
|
||||||
|
|
||||||
|
private Student() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
public Student(Id<Student> id, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class Course<T> {
|
||||||
|
final List<Student> students;
|
||||||
|
private final Id<Course<T>> courseId;
|
||||||
|
private final int numAssignments;
|
||||||
|
private final Assignment<T> assignment;
|
||||||
|
|
||||||
|
private Course() {
|
||||||
|
this(null, 0, null, new ArrayList<Student>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Course(Id<Course<T>> courseId, int numAssignments,
|
||||||
|
Assignment<T> assignment, List<Student> players) {
|
||||||
|
this.courseId = courseId;
|
||||||
|
this.numAssignments = numAssignments;
|
||||||
|
this.assignment = assignment;
|
||||||
|
this.students = players;
|
||||||
|
}
|
||||||
|
public Id<Course<T>> getId() {
|
||||||
|
return courseId;
|
||||||
|
}
|
||||||
|
List<Student> getStudents() {
|
||||||
|
return students;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class Assignment<T> {
|
||||||
|
private final Id<Assignment<T>> id;
|
||||||
|
private final T data;
|
||||||
|
|
||||||
|
private Assignment() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
public Assignment(Id<Assignment<T>> id, T data) {
|
||||||
|
this.id = id;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static class HistoryCourse {
|
||||||
|
int numClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> List<T> createList(T ...items) {
|
||||||
|
return Arrays.asList(items);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,24 +15,33 @@
|
||||||
*/
|
*/
|
||||||
package com.google.gson.wsf.server.rest;
|
package com.google.gson.wsf.server.rest;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.gson.webservice.definition.rest.Id;
|
||||||
|
import com.google.gson.webservice.definition.rest.MetaData;
|
||||||
import com.google.gson.webservice.definition.rest.RestResource;
|
import com.google.gson.webservice.definition.rest.RestResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata associated with a repository for a rest resource
|
* A map of resources to their MetaData
|
||||||
*
|
*
|
||||||
* @author inder
|
* @author inder
|
||||||
*
|
*
|
||||||
* @param <R> The resource
|
* @param <R> the rest resource for whic the metadata is being stored
|
||||||
*/
|
*/
|
||||||
public final class MetaData<R extends RestResource<R>> {
|
public class MetaDataMap<R extends RestResource<R>> {
|
||||||
|
private final Map<Id<R>, MetaData<R>> map;
|
||||||
|
|
||||||
final boolean freshlyAssignedId;
|
public MetaDataMap() {
|
||||||
|
this.map = new HashMap<Id<R>, MetaData<R>>();
|
||||||
public MetaData(boolean freshlyAssignedId) {
|
|
||||||
this.freshlyAssignedId = freshlyAssignedId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFreshlyAssignedId() {
|
public MetaData<R> get(Id<R> resourceId) {
|
||||||
return freshlyAssignedId;
|
MetaData<R> metaData = map.get(resourceId);
|
||||||
|
if (metaData == null) {
|
||||||
|
metaData = MetaData.create();
|
||||||
|
map.put(resourceId, metaData);
|
||||||
|
}
|
||||||
|
return metaData;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,6 +18,7 @@ package com.google.gson.wsf.server.rest;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.gson.webservice.definition.rest.Id;
|
import com.google.gson.webservice.definition.rest.Id;
|
||||||
|
import com.google.gson.webservice.definition.rest.MetaData;
|
||||||
import com.google.gson.webservice.definition.rest.RestResource;
|
import com.google.gson.webservice.definition.rest.RestResource;
|
||||||
import com.google.inject.internal.Maps;
|
import com.google.inject.internal.Maps;
|
||||||
import com.google.inject.internal.Preconditions;
|
import com.google.inject.internal.Preconditions;
|
||||||
|
@ -30,12 +31,14 @@ import com.google.inject.internal.Preconditions;
|
||||||
* @param <R> Type variable for the resource
|
* @param <R> Type variable for the resource
|
||||||
*/
|
*/
|
||||||
public class RepositoryInMemory<R extends RestResource<R>> implements Repository<R> {
|
public class RepositoryInMemory<R extends RestResource<R>> implements Repository<R> {
|
||||||
|
private static final String METADATA_KEY_IS_FRESHLY_ASSIGNED_ID = "isFreshlyAssignedId";
|
||||||
|
|
||||||
private final IdMap<R> resources;
|
private final IdMap<R> resources;
|
||||||
private final Map<Id<R>, MetaData<R>> metaDataMap;
|
private final MetaDataMap<R> metaDataMap;
|
||||||
|
|
||||||
public RepositoryInMemory(Class<? super R> classOfResource) {
|
public RepositoryInMemory(Class<? super R> classOfResource) {
|
||||||
this.resources = IdMap.create(classOfResource);
|
this.resources = IdMap.create(classOfResource);
|
||||||
this.metaDataMap = Maps.newHashMap();
|
this.metaDataMap = new MetaDataMap<R>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,7 +51,7 @@ public class RepositoryInMemory<R extends RestResource<R>> implements Repository
|
||||||
if (metaData == null) {
|
if (metaData == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return metaData.isFreshlyAssignedId();
|
return metaData.getBoolean(METADATA_KEY_IS_FRESHLY_ASSIGNED_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +67,7 @@ public class RepositoryInMemory<R extends RestResource<R>> implements Repository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resource = resources.put(resource);
|
resource = resources.put(resource);
|
||||||
metaDataMap.remove(resource.getId());
|
metaDataMap.get(resource.getId()).remove(METADATA_KEY_IS_FRESHLY_ASSIGNED_ID);
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +91,7 @@ public class RepositoryInMemory<R extends RestResource<R>> implements Repository
|
||||||
if (resource.getId() == null) {
|
if (resource.getId() == null) {
|
||||||
Id<R> id = resources.getNextId();
|
Id<R> id = resources.getNextId();
|
||||||
resource.setId(id);
|
resource.setId(id);
|
||||||
metaDataMap.put(id, new MetaData<R>(true));
|
metaDataMap.get(id).putBoolean(METADATA_KEY_IS_FRESHLY_ASSIGNED_ID, true);
|
||||||
}
|
}
|
||||||
return resource.getId();
|
return resource.getId();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user