blob: 590ffa300c85a46726948229164eb0c3f6807423 [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.
* <p>
* Contributors:
* Dmitry Kornilov - initial implementation
******************************************************************************/
package org.eclipse.persistence.json.bind.internal;
import org.eclipse.persistence.json.bind.internal.serializer.ContainerSerializerProvider;
import org.eclipse.persistence.json.bind.internal.serializer.ISerializerProvider;
import org.eclipse.persistence.json.bind.model.ClassCustomization;
import org.eclipse.persistence.json.bind.model.ClassModel;
import org.eclipse.persistence.json.bind.model.JsonbAnnotatedElement;
import java.util.Iterator;
import java.util.Objects;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* JSONB mappingContext. Created once per {@link javax.json.bind.Jsonb} instance. Represents a global scope.
* Holds internal model.
*
* Thread safe
*
* @author Dmitry Kornilov
* @author Roman Grigoriadi
*/
public class MappingContext {
private static class ParseClassModelFunction implements Function<Class, ClassModel> {
private ClassModel parentClassModel;
private ClassParser classParser;
private JsonbContext jsonbContext;
public ParseClassModelFunction(ClassModel parentClassModel, ClassParser classParser, JsonbContext jsonbContext) {
this.parentClassModel = parentClassModel;
this.classParser = classParser;
this.jsonbContext = jsonbContext;
}
@Override
public ClassModel apply(Class aClass) {
final JsonbAnnotatedElement<Class<?>> clsElement = jsonbContext.getAnnotationIntrospector().collectAnnotations(aClass);
final ClassCustomization customization = jsonbContext.getAnnotationIntrospector().introspectCustomization(clsElement);
final ClassModel newClassModel = new ClassModel(aClass, customization, parentClassModel, jsonbContext.getPropertyNamingStrategy());
classParser.parseProperties(newClassModel, clsElement);
return newClassModel;
}
}
private final JsonbContext jsonbContext;
private final ConcurrentHashMap<Class<?>, ClassModel> classes = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Class<?>, ContainerSerializerProvider> serializers = new ConcurrentHashMap<>();
private final ClassParser classParser;
/**
* Create mapping context which is scoped to jsonb runtime.
*
* @param jsonbContext required
*/
public MappingContext(JsonbContext jsonbContext) {
Objects.requireNonNull(jsonbContext);
this.jsonbContext = jsonbContext;
this.classParser = new ClassParser(jsonbContext);
}
/**
* Search for class model.
* Parse class and create one if not found.
* @param clazz clazz to search by or parse, not null.
*/
public ClassModel getOrCreateClassModel(Class<?> clazz) {
ClassModel classModel = classes.get(clazz);
if (classModel != null) {
return classModel;
}
final Stack<Class> newClassModels = new Stack<>();
for (Class classToParse = clazz; classToParse != Object.class; classToParse = classToParse.getSuperclass()) {
newClassModels.push(classToParse);
}
ClassModel parentClassModel = null;
while (!newClassModels.empty()) {
Class toParse = newClassModels.pop();
parentClassModel = classes.computeIfAbsent(toParse, new ParseClassModelFunction(parentClassModel, classParser, jsonbContext));
}
return classes.get(clazz);
}
/**
* Provided class class model is returned first by iterator.
* Following class models are sorted by hierarchy from provided class up to the Object.class.
*
* @param clazz class to start iteration of class models from
* @return iterator of class models
*/
public Iterator<ClassModel> classModelIterator(final Class<?> clazz) {
return new Iterator<ClassModel>() {
private Class<?> next = clazz;
@Override
public boolean hasNext() {
return next != Object.class;
}
@Override
public ClassModel next() {
final ClassModel result = classes.get(next);
next = next.getSuperclass();
return result;
}
};
}
/**
* Search for class model, without parsing if not found.
* @param clazz clazz to search by or parse, not null.
* @return model of a class if found.
*/
public ClassModel getClassModel(Class<?> clazz) {
return classes.get(clazz);
}
public ContainerSerializerProvider getSerializerProvider(Class<?> clazz) {
return serializers.get(clazz);
}
public void addSerializerProvider(Class<?> clazz, ContainerSerializerProvider serializerProvider) {
serializers.putIfAbsent(clazz, serializerProvider);
}
}