Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* #%L
* FlatPack serialization code
* %%
* Copyright (C) 2012 Perka 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.
* #L%
*/
package com.getperka.flatpack;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.security.Principal;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
import javax.inject.Provider;
import org.slf4j.Logger;
import com.getperka.flatpack.codexes.EntityCodex;
import com.getperka.flatpack.ext.Codex;
import com.getperka.flatpack.ext.DeserializationContext;
import com.getperka.flatpack.ext.TypeContext;
import com.getperka.flatpack.inject.FlatPackLogger;
import com.getperka.flatpack.inject.IgnoreUnresolvableTypes;
import com.getperka.flatpack.inject.PackScope;
import com.getperka.flatpack.util.FlatPackCollections;
import com.getperka.flatpack.util.IoObserver;
import com.getperka.flatpack.visitors.PackReader;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.internal.bind.JsonTreeReader;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
/**
* Allows {@link FlatPackEntity} instances to be restored from their serialized representations.
*
* @see FlatPack#getUnpacker()
*/
public class Unpacker {
@Inject
private Provider contexts;
@Inject
private Provider> metaCodex;
@IgnoreUnresolvableTypes
@Inject
private boolean ignoreUnresolvableTypes;
@Inject
private IoObserver ioObserver;
@Inject
@FlatPackLogger
private Logger logger;
@Inject
private PackScope packScope;
@Inject
private Provider packReaders;
@Inject
private TypeContext typeContext;
@Inject
private Visitors visitors;
protected Unpacker() {}
/**
* Read the properties of a single entity.
*
* @param the type of data to return
* @param entityType the type of data to return
* @param in a json object containing the output of a prior call to
* {@link Packer#append(HasUuid, Principal)
* @param principal the identity for which the unpacking is occurring
* @return the reified {@code entityType}
*/
public T read(Class entityType, JsonElement in, Principal principal) {
if (!in.isJsonObject()) {
throw new IllegalArgumentException("Expecting a JSON object");
}
packScope.enter().withPrincipal(principal);
try {
JsonObject chunk = in.getAsJsonObject();
EntityCodex codex = (EntityCodex) typeContext.getCodex(entityType);
DeserializationContext context = contexts.get();
T toReturn = codex.allocate(chunk, context);
PackReader packReader = packReaders.get();
packReader.setPayload(chunk);
visitors.visit(packReader, toReturn);
return toReturn;
} finally {
packScope.exit();
}
}
/**
* Read the properties of a single entity.
*
* @param the type of data to return
* @param entityType the type of data to return
* @param in a Reader containing the output of a prior call to
* {@link Packer#append(HasUuid, Principal, java.io.Writer)}
* @param principal the identity for which the unpacking is occurring
* @return the reified {@code entityType}
*/
public T read(Class entityType, Reader in, Principal principal) {
JsonParser parser = new JsonParser();
JsonReader reader = new JsonReader(in);
reader.setLenient(true);
JsonObject chunk = parser.parse(reader).getAsJsonObject();
return read(entityType, chunk, principal);
}
/**
* Reify a {@link FlatPackEntity} from an in-memory json representation.
*
* @param the type of data to return
* @param returnType a reference to {@code T}
* @param in the source of the serialized data
* @param principal the identity for which the unpacking is occurring
* @return the reified {@link FlatPackEntity}.
*/
public FlatPackEntity unpack(Type returnType, JsonElement in, Principal principal)
throws IOException {
packScope.enter().withPrincipal(principal);
try {
return doUnpack(returnType, new JsonTreeReader(in), principal);
} finally {
packScope.exit();
}
}
/**
* Reify a {@link FlatPackEntity} from its serialized form.
*
* @param the type of data to return
* @param returnType a reference to {@code T}
* @param in the source of the serialized data
* @param principal the identity for which the unpacking is occurring
* @return the reified {@link FlatPackEntity}.
*/
public FlatPackEntity unpack(Type returnType, Reader in, Principal principal)
throws IOException {
in = ioObserver.observe(in);
packScope.enter().withPrincipal(principal);
try {
return doUnpack(returnType, new JsonReader(in), principal);
} finally {
packScope.exit();
}
}
/**
* This method can be used with an anonymous subclass of {@link TypeReference} or with an empty
* entity returned by a {@link FlatPackEntity} factory method.
*
* @param the type of data to return
* @param returnType a reference to {@code T}
* @param in the source of the serialized data
* @param principal the identity for which the unpacking is occurring
* @return the reified {@link FlatPackEntity}.
* @see FlatPackEntity#collectionOf(Class)
* @see FlatPackEntity#mapOf(Class, Class)
* @see FlatPackEntity#stringMapOf(Class)
*/
public FlatPackEntity unpack(TypeReference returnType, Reader in,
Principal principal) throws IOException {
return unpack(returnType.getType(), in, principal);
}
/**
* The guts of Unpacker.
*/
protected FlatPackEntity doUnpack(Type returnType, JsonReader reader, Principal principal)
throws IOException {
// Hold temporary state for deserialization
DeserializationContext context = contexts.get();
/*
* Decoding is done as a two-pass operation since the runtime type of an allocated object cannot
* be swizzled. The per-entity data is held as a semi-reified JsonObject to be passed off to a
* Codex.
*/
Map entityData = FlatPackCollections.mapForIteration();
// Used to populate the entityData map
JsonParser jsonParser = new JsonParser();
/*
* The reader is placed in lenient mode as an aid to developers, however all output will be
* strictly formatted.
*/
reader.setLenient(true);
// The return value
@SuppressWarnings("unchecked")
FlatPackEntity toReturn = (FlatPackEntity) FlatPackEntity.create(returnType, null,
principal);
// Stores the single, top-level value in the payload for two-pass reification
JsonElement value = null;
if (reader.peek().equals(JsonToken.NULL)) {
return toReturn;
}
reader.beginObject();
while (JsonToken.NAME.equals(reader.peek())) {
String name = reader.nextName();
if ("data".equals(name)) {
// data : { "fooEntity" : [ { ... }, { ... } ]
reader.beginObject();
while (JsonToken.NAME.equals(reader.peek())) {
// Turn "fooEntity" into the actual FooEntity class
String simpleName = reader.nextName();
Class extends HasUuid> clazz = typeContext.getClass(simpleName);
if (clazz == null) {
if (ignoreUnresolvableTypes) {
reader.skipValue();
continue;
} else {
throw new UnsupportedOperationException("Cannot resolve type " + simpleName);
}
} else if (Modifier.isAbstract(clazz.getModifiers())) {
throw new UnsupportedOperationException("A subclass of " + simpleName
+ " must be used instead");
}
context.pushPath("allocating " + simpleName);
try {
// Find the Codex for the requested entity type
EntityCodex> codex = (EntityCodex>) typeContext.getCodex(clazz);
// Take the n-many property objects and stash them for later decoding
reader.beginArray();
while (!JsonToken.END_ARRAY.equals(reader.peek())) {
JsonObject chunk = jsonParser.parse(reader).getAsJsonObject();
HasUuid entity = codex.allocate(chunk, context);
toReturn.addExtraEntity(entity);
entityData.put(entity, chunk.getAsJsonObject());
}
reader.endArray();
} finally {
context.popPath();
}
}
reader.endObject();
} else if ("errors".equals(name)) {
// "errors" : { "path" : "problem", "path2" : "problem2" }
reader.beginObject();
while (JsonToken.NAME.equals(reader.peek())) {
String path = reader.nextName();
if (JsonToken.STRING.equals(reader.peek()) || JsonToken.NUMBER.equals(reader.peek())) {
toReturn.addError(path, reader.nextString());
} else {
reader.skipValue();
}
}
reader.endObject();
} else if ("metadata".equals(name)) {
reader.beginArray();
Codex metaCodex = typeContext.getCodex(EntityMetadata.class);
while (!JsonToken.END_ARRAY.equals(reader.peek())) {
JsonObject metaElement = jsonParser.parse(reader).getAsJsonObject();
PackReader packReader = packReaders.get();
packReader.setPayload(metaElement);
EntityMetadata meta = new EntityMetadata();
meta.setUuid(UUID.fromString(metaElement.get("uuid").getAsString()));
meta = visitors.getWalkers().walkSingleton(metaCodex).accept(packReader, meta);
toReturn.addMetadata(meta);
}
reader.endArray();
} else if ("value".equals(name)) {
// Just stash the value element in case it occurs first
value = jsonParser.parse(reader);
} else if ("warnings".equals(name)) {
// "warnings" : { "path" : "problem", "path2" : "problem2" }
reader.beginObject();
while (JsonToken.NAME.equals(reader.peek())) {
String path = reader.nextName();
if (JsonToken.STRING.equals(reader.peek()) || JsonToken.NUMBER.equals(reader.peek())) {
toReturn.addWarning(path, reader.nextString());
} else {
reader.skipValue();
}
}
reader.endObject();
} else if (JsonToken.STRING.equals(reader.peek()) || JsonToken.NUMBER.equals(reader.peek())) {
// Save off any other simple properties
toReturn.putExtraData(name, reader.nextString());
} else {
// Ignore random other entries
reader.skipValue();
}
}
reader.endObject();
reader.close();
PackReader packReader = packReaders.get();
for (Map.Entry entry : entityData.entrySet()) {
HasUuid entity = entry.getKey();
EntityCodex codex = (EntityCodex) typeContext
.getCodex(entity.getClass());
packReader.setPayload(entry.getValue());
visitors.getWalkers().walkImmutable(codex).accept(packReader, entity);
}
@SuppressWarnings("unchecked")
Codex returnCodex = (Codex) typeContext.getCodex(toReturn.getType());
toReturn.withValue(returnCodex.read(value, context));
for (Map.Entry entry : context.getWarnings().entrySet()) {
toReturn.addWarning(entry.getKey().toString(), entry.getValue());
}
// Process metadata
for (EntityMetadata meta : toReturn.getMetadata()) {
if (meta.isPersistent()) {
HasUuid entity = context.getEntity(meta.getUuid());
if (entity instanceof PersistenceAware) {
((PersistenceAware) entity).markPersistent();
}
}
}
context.runPostWork();
context.close();
return toReturn;
}
}