blob: 454cb5fc3f0a9ce203e3529677aafc3179e3e1f8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* 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. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.abdera2.activities.io.gson;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import javax.activation.MimeType;
import org.apache.abdera2.common.Discover;
import org.apache.abdera2.common.anno.AnnoUtil;
import org.apache.abdera2.common.geo.IsoPosition;
import org.apache.abdera2.common.http.EntityTag;
import org.apache.abdera2.common.iri.IRI;
import org.apache.abdera2.common.lang.Lang;
import org.apache.abdera2.activities.model.*;
import org.apache.abdera2.activities.model.objects.AccountObject;
import org.apache.abdera2.activities.model.objects.Address;
import org.apache.abdera2.activities.model.objects.ArticleObject;
import org.apache.abdera2.activities.model.objects.AudioObject;
import org.apache.abdera2.activities.model.objects.BadgeObject;
import org.apache.abdera2.activities.model.objects.BookmarkObject;
import org.apache.abdera2.activities.model.objects.CommentObject;
import org.apache.abdera2.activities.model.objects.EventObject;
import org.apache.abdera2.activities.model.objects.FileObject;
import org.apache.abdera2.activities.model.objects.GroupObject;
import org.apache.abdera2.activities.model.objects.ImageObject;
import org.apache.abdera2.activities.model.objects.Mood;
import org.apache.abdera2.activities.model.objects.NameObject;
import org.apache.abdera2.activities.model.objects.NoteObject;
import org.apache.abdera2.activities.model.objects.OrganizationObject;
import org.apache.abdera2.activities.model.objects.PersonObject;
import org.apache.abdera2.activities.model.objects.PlaceObject;
import org.apache.abdera2.activities.model.objects.ProductObject;
import org.apache.abdera2.activities.model.objects.QuestionObject;
import org.apache.abdera2.activities.model.objects.ReviewObject;
import org.apache.abdera2.activities.model.objects.ServiceObject;
import org.apache.abdera2.activities.model.objects.VideoObject;
import org.apache.abdera2.activities.protocol.ErrorObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
/**
* (De)serialization of ASBase object
*/
public class BaseAdapter
implements GsonTypeAdapter<ASBase> {
private final Map<String,Class<?>> map =
new ConcurrentHashMap<String,Class<?>>();
private final Map<String,Class<? extends ASObject>> objsmap =
new ConcurrentHashMap<String,Class<? extends ASObject>>();
public BaseAdapter() {
initPropMap();
}
@SuppressWarnings("unchecked")
private void initPropMap() {
map.put("verb",Verb.class);
map.put("url",IRI.class);
map.put("fileurl", IRI.class);
map.put("gadget", IRI.class);
map.put("updated", Date.class);
map.put("published", Date.class);
map.put("lang", Lang.class);
map.put("icon", MediaLink.class);
map.put("image", MediaLink.class);
map.put("totalitems", Integer.class);
map.put("duration", Integer.class);
map.put("height", Integer.class);
map.put("location", PlaceObject.class);
map.put("mood", Mood.class);
map.put("address", Address.class);
map.put("stream", MediaLink.class);
map.put("fullimage", MediaLink.class);
map.put("endtime", Date.class);
map.put("starttime", Date.class);
map.put("mimetype", MimeType.class);
map.put("rating", Double.class);
map.put("position", IsoPosition.class);
map.put("etag", EntityTag.class);
// From the replies spec
map.put("attending", Collection.class);
map.put("followers", Collection.class);
map.put("following", Collection.class);
map.put("friends", Collection.class);
map.put("friend-requests", Collection.class);
map.put("likes", Collection.class);
map.put("notAttending", Collection.class);
map.put("maybeAttending", Collection.class);
map.put("members", Collection.class);
map.put("replies", Collection.class);
map.put("reviews", Collection.class);
map.put("saves", Collection.class);
map.put("shares", Collection.class);
processType(
objsmap,map,
Address.class,
Activity.class,
ArticleObject.class,
AudioObject.class,
BadgeObject.class,
BookmarkObject.class,
Collection.class,
CommentObject.class,
EventObject.class,
FileObject.class,
GroupObject.class,
ImageObject.class,
NoteObject.class,
PersonObject.class,
PlaceObject.class,
ProductObject.class,
QuestionObject.class,
ReviewObject.class,
ServiceObject.class,
VideoObject.class,
ErrorObject.class,
NameObject.class,
AccountObject.class,
OrganizationObject.class);
}
private static void processType(
Map<String,Class<? extends ASObject>> map,
Map<String,Class<?>> propsmap,
Class<? extends ASObject>... _classes) {
for (Class<? extends ASObject> _class : _classes) {
String name = AnnoUtil.getName(_class);
map.put(name, _class);
if (_class.isAnnotationPresent(Properties.class)) {
Properties props = _class.getAnnotation(Properties.class);
for (Property prop : props.value()) {
String _propname = prop.name();
Class<?> _propclass = prop.to();
propsmap.put(_propname, _propclass);
}
}
}
}
public void addObjectMap(Class<? extends ASObject>... _class) {
processType(objsmap,map,_class);
}
public void addPropertyMap(String name, Class<?> _class) {
map.put(name,_class);
}
public JsonElement serialize(
ASBase asbase,
Type type,
JsonSerializationContext context) {
JsonObject el = new JsonObject();
for (String key : asbase) {
Object val = asbase.getProperty(key);
if (val != null) {
JsonElement value = null;
if (val instanceof Verb)
value = context.serialize(val, Verb.class);
else
value = context.serialize(val, val.getClass());
el.add(key, value);
}
}
return el;
}
public ASBase deserialize(
JsonElement el,
Type type,
JsonDeserializationContext context)
throws JsonParseException {
JsonObject obj = (JsonObject)el;
ASBase base = null;
if (type == Collection.class) {
base = new Collection<ASObject>();
} else if (type == Activity.class) {
base = new Activity();
} else if (type == MediaLink.class) {
base = new MediaLink();
} else if (type == PlaceObject.class) {
base = new PlaceObject();
} else if (type == Mood.class) {
base = new Mood();
} else if (type == Address.class) {
base = new Address();
} else {
JsonPrimitive ot = obj.getAsJsonPrimitive("objectType");
if (ot != null) {
String ots = ot.getAsString();
Class<? extends ASObject> _class = objsmap.get(ots);
if (_class != null) {
base = Discover.locate(_class, _class.getName());
try {
base = _class.newInstance();
} catch (Throwable t) {}
} else base = new ASObject(ots);
} else {
if (obj.has("verb") && (obj.has("actor") || obj.has("object") || obj.has("target"))) {
base = new Activity();
} else if (obj.has("items") && obj.has("totalItems")) {
base = new Collection<ASObject>();
} else {
base = new ASObject(); // anonymous object
}
}
}
for (Entry<String,JsonElement> entry : obj.entrySet()) {
String name = entry.getKey();
Class<?> _class = map.get(name.toLowerCase(Locale.US));
JsonElement val = entry.getValue();
if (val.isJsonPrimitive()) {
if (_class != null) {
base.setProperty(name, context.deserialize(val,_class));
} else {
JsonPrimitive prim = val.getAsJsonPrimitive();
if (prim.isBoolean())
base.setProperty(name, prim.getAsBoolean());
else if (prim.isNumber())
base.setProperty(name, prim.getAsNumber());
else {
base.setProperty(name, prim.getAsString());
}
}
} else if (val.isJsonArray()) {
List<Object> list = new ArrayList<Object>();
JsonArray arr = val.getAsJsonArray();
processArray(arr, _class, context, list);
base.setProperty(name, list);
} else if (val.isJsonObject()) {
if (map.containsKey(name)) {
base.setProperty(name, context.deserialize(val, map.get(name)));
} else
base.setProperty(name, context.deserialize(val, ASObject.class));
}
}
return base;
}
private void processArray(JsonArray arr, Class<?> _class, JsonDeserializationContext context, List<Object> list) {
for (JsonElement mem : arr) {
if (mem.isJsonPrimitive()) {
if (_class != null) {
list.add(context.deserialize(mem, _class));
} else {
JsonPrimitive prim2 = (JsonPrimitive) mem;
if (prim2.isBoolean())
list.add(prim2.getAsBoolean());
else if (prim2.isNumber())
list.add(prim2.getAsNumber());
else
list.add(prim2.getAsString());
}
} else if (mem.isJsonObject()) {
list.add(context.deserialize(mem, _class!=null?_class:ASObject.class));
} else if (mem.isJsonArray()) {
JsonArray array = mem.getAsJsonArray();
List<Object> objs = new ArrayList<Object>();
processArray(array,_class,context,objs);
list.add(objs);
}
}
}
public Class<ASBase> getAdaptedClass() {
return ASBase.class;
}
}