blob: e0272eadf4949dbe2444164c769ef5327b3ffe51 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Roman Grigoriadi
******************************************************************************/
package org.eclipse.persistence.json.bind.internal.unmarshaller;
import org.eclipse.persistence.json.bind.internal.AbstractSerializerBuilder;
import org.eclipse.persistence.json.bind.internal.ComponentMatcher;
import org.eclipse.persistence.json.bind.internal.JsonbContext;
import org.eclipse.persistence.json.bind.internal.ProcessingContext;
import org.eclipse.persistence.json.bind.internal.ReflectionUtils;
import org.eclipse.persistence.json.bind.internal.adapter.AdapterBinding;
import org.eclipse.persistence.json.bind.internal.adapter.DeserializerBinding;
import org.eclipse.persistence.json.bind.internal.properties.MessageKeys;
import org.eclipse.persistence.json.bind.internal.properties.Messages;
import org.eclipse.persistence.json.bind.internal.serializer.AbstractValueTypeDeserializer;
import org.eclipse.persistence.json.bind.internal.serializer.DefaultSerializers;
import org.eclipse.persistence.json.bind.internal.serializer.SerializerProviderWrapper;
import org.eclipse.persistence.json.bind.model.JsonBindingModel;
import org.eclipse.persistence.json.bind.model.PolymorphismAdapter;
import org.eclipse.persistence.json.bind.model.TypeWrapper;
import javax.json.JsonStructure;
import javax.json.bind.JsonbException;
import javax.json.bind.config.BinaryDataStrategy;
import javax.json.bind.serializer.JsonbDeserializer;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
/**
* Builder for currently processed items by unmarshaller.
*
* @author Roman Grigoriadi
*/
public class DeserializerBuilder extends AbstractSerializerBuilder<DeserializerBuilder> {
/**
* Value type of json event.
*/
private JsonValueType jsonValueType;
public DeserializerBuilder(JsonbContext jsonbContext) {
super(jsonbContext);
}
public DeserializerBuilder withJsonValueType(JsonValueType valueType) {
this.jsonValueType = valueType;
return this;
}
/**
* Build an fully initialized item.
*
* @return built item
*/
@SuppressWarnings("unchecked")
public JsonbDeserializer<?> build() {
runtimeType = resolveRuntimeType();
Class<?> rawType = ReflectionUtils.getRawType(getRuntimeType());
//First check if user deserializer is registered for such type
final ComponentMatcher componentMatcher = jsonbContext.getComponentMatcher();
Optional<DeserializerBinding<?>> userDeserializer =
componentMatcher.getDeserialzierBinding(getRuntimeType(), getModel());
if (userDeserializer.isPresent() &&
!(wrapper instanceof UserDeserializerDeserializer && ReflectionUtils.getRawType(wrapper.getRuntimeType()).isAssignableFrom(rawType))) {
return new UserDeserializerDeserializer<>(this, userDeserializer.get().getJsonbDeserializer());
}
//Second user adapter is registered.
final Optional<AdapterBinding> adapterInfoOptional = componentMatcher.getAdapterBinding(getRuntimeType(), getModel());
Optional<Class> rawTypeOptional = adapterInfoOptional.map(adapterInfo->{
runtimeType = adapterInfo.getToType();
wrapper = new AdaptedObjectDeserializer<>(adapterInfoOptional.get(), (AbstractContainerDeserializer<?>) wrapper);
return ReflectionUtils.getRawType(getRuntimeType());
});
rawType = rawTypeOptional.orElse(rawType);
//In case of Base64 json value would be string and recognition by JsonValueType would not work
if (isByteArray(rawType)) {
String strategy = jsonbContext.getBinaryDataStrategy();
switch (strategy) {
case BinaryDataStrategy.BYTE:
return new ByteArrayDeserializer(this);
default:
return new ByteArrayBase64Deserializer(getModel());
}
}
//Third deserializer is a supported value type that serializes to JSON_VALUE
if (isJsonValueType()) {
final Optional<AbstractValueTypeDeserializer<?>> supportedTypeDeserializer = getSupportedTypeDeserializer(rawType);
if (!supportedTypeDeserializer.isPresent()) {
throw new JsonbException(Messages.getMessage(MessageKeys.DESERIALIZE_VALUE_ERROR, getRuntimeType()));
}
return wrapAdapted(adapterInfoOptional, supportedTypeDeserializer.get());
}
JsonbDeserializer<?> deserializer;
if (jsonValueType == JsonValueType.ARRAY) {
if (JsonStructure.class.isAssignableFrom(rawType)) {
return wrapAdapted(adapterInfoOptional, new JsonArrayDeserializer(this));
} else if (rawType.isArray() || getRuntimeType() instanceof GenericArrayType) {
deserializer = createArrayItem(rawType.getComponentType());
return wrapAdapted(adapterInfoOptional, deserializer);
} else if (Collection.class.isAssignableFrom(rawType)) {
deserializer = new CollectionDeserializer<>(this);
return wrapAdapted(adapterInfoOptional, deserializer);
} else {
throw new JsonbException("Can't deserialize JSON array into: " + getRuntimeType());
}
} else if(jsonValueType == JsonValueType.OBJECT) {
if (JsonStructure.class.isAssignableFrom(rawType)) {
return wrapAdapted(adapterInfoOptional, new JsonObjectDeserializer(this));
} else if (Map.class.isAssignableFrom(rawType)) {
final JsonbDeserializer<?> mapDeserializer = new MapDeserializer(this);
return wrapAdapted(adapterInfoOptional, mapDeserializer);
} else if (rawType.isInterface()) {
throw new JsonbException(Messages.getMessage(MessageKeys.INFER_TYPE_FOR_UNMARSHALL, rawType.getName()));
} else {
if (adapterInfoOptional.isPresent()) {
runtimeType = adapterInfoOptional.get().getToType();
rawType = ReflectionUtils.getRawType(getRuntimeType());
}
classModel = getClassModel(rawType);
if (TypeWrapper.class.isAssignableFrom(rawType)) {
return wrapAdapted(adapterInfoOptional, new TypeWrapperDeserializer<>(this,
((PolymorphismAdapter<?>) adapterInfoOptional.get().getAdapter()).getAllowedClasses()));
}
deserializer = new ObjectDeserializer<>(this);
return wrapAdapted(adapterInfoOptional, deserializer);
}
}
throw new JsonbException("unresolved type for deserialization: " + getRuntimeType());
}
private boolean isJsonValueType() {
switch (jsonValueType) {
case NULL:
case BOOLEAN:
case NUMBER:
case STRING:
return true;
case OBJECT:
case ARRAY:
return false;
default:
throw new JsonbException("Unknown value type: " + jsonValueType);
}
}
private Optional<AbstractValueTypeDeserializer<?>> getSupportedTypeDeserializer(Class<?> rawType) {
final Optional<? extends SerializerProviderWrapper> supportedTypeDeserializerOptional = DefaultSerializers.getInstance().findValueSerializerProvider(rawType);
if (supportedTypeDeserializerOptional.isPresent()) {
return Optional.of(supportedTypeDeserializerOptional.get().getDeserializerProvider().provideDeserializer(getModel()));
}
return Optional.empty();
}
private JsonbDeserializer<?> wrapAdapted(Optional<AdapterBinding> adapterInfoOptional, JsonbDeserializer<?> item) {
final Optional<JsonbDeserializer<?>> adaptedDeserializerOptional = adapterInfoOptional.map(adapterInfo -> {
setAdaptedItemCaptor((AdaptedObjectDeserializer)wrapper, item);
return (JsonbDeserializer<?>)wrapper;
});
return adaptedDeserializerOptional.orElse(item);
}
private <T,A> void setAdaptedItemCaptor(AdaptedObjectDeserializer<T,A> decoratorItem, JsonbDeserializer<T> adaptedItem) {
decoratorItem.setAdaptedItem(adaptedItem);
}
private Type resolveRuntimeType() {
Type toResolve = genericType != null ? genericType : getModel().getType();
//Map Unknown objects to java.util.Map
if (toResolve == Object.class) {
return jsonValueType.getConversionType();
}
return ReflectionUtils.resolveType(wrapper, toResolve);
}
/**
* Instance is not created in case of array items, because, we don't know how long it should be
* till parser ends parsing.
*/
private JsonbDeserializer<?> createArrayItem(Class<?> componentType) {
if (componentType == byte.class) {
return new ByteArrayDeserializer(this);
} else if (componentType == short.class) {
return new ShortArrayDeserializer(this);
} else if (componentType == int.class) {
return new IntArrayDeserializer(this);
} else if (componentType == long.class) {
return new LongArrayDeserializer(this);
} else if (componentType == float.class) {
return new FloatArrayDeserializer(this);
} else if (componentType == double.class) {
return new DoubleArrayDeserializer(this);
} else {
return new ObjectArrayDeserializer(this);
}
}
private boolean isByteArray(Class<?> rawType) {
return rawType.isArray() && rawType.getComponentType() == Byte.TYPE;
}
}