* The guts of Unpacker.
*/
protected <T> FlatPackEntity<T> 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<HasUuid, JsonObject> 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<T> toReturn = (FlatPackEntity<T>) 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();
EntityDescription desc = typeContext.getEntityDescription(simpleName);
if (desc == null) {
if (ignoreUnresolvableTypes) {
reader.skipValue();
continue;
} else {
throw new UnsupportedOperationException("Cannot resolve type " + simpleName);
}
} else if (Modifier.isAbstract(desc.getEntityType().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(desc.getEntityType());
// 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);
if (entity != null) {
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<EntityMetadata> 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<HasUuid, JsonObject> entry : entityData.entrySet()) {
HasUuid entity = entry.getKey();
EntityCodex<HasUuid> codex = (EntityCodex<HasUuid>) typeContext
.getCodex(entity.getClass());
packReader.setPayload(entry.getValue());
visitors.getWalkers().walkImmutable(codex).accept(packReader, entity);
}
@SuppressWarnings("unchecked")
Codex<T> returnCodex = (Codex<T>) typeContext.getCodex(toReturn.getType());
toReturn.withValue(returnCodex.read(value, context));
for (Map.Entry<UUID, String> 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;
}