/*
 * Copyright 2008-present MongoDB, 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.
 */

package org.bson.codecs.pojo;

import org.bson.codecs.configuration.CodecConfigurationException;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static org.bson.assertions.Assertions.notNull;
import static org.bson.codecs.pojo.Conventions.DEFAULT_CONVENTIONS;
import static org.bson.codecs.pojo.PojoBuilderHelper.configureClassModelBuilder;
import static org.bson.codecs.pojo.PojoBuilderHelper.stateNotNull;

A builder for programmatically creating ClassModels.
Type parameters:
  • <T> – The type of the class the ClassModel represents
See Also:
Since:3.5
/** * A builder for programmatically creating {@code ClassModels}. * * @param <T> The type of the class the ClassModel represents * @since 3.5 * @see ClassModel */
public class ClassModelBuilder<T> { static final String ID_PROPERTY_NAME = "_id"; private final List<PropertyModelBuilder<?>> propertyModelBuilders = new ArrayList<PropertyModelBuilder<?>>(); private IdGenerator<?> idGenerator; private InstanceCreatorFactory<T> instanceCreatorFactory; private Class<T> type; private Map<String, TypeParameterMap> propertyNameToTypeParameterMap = emptyMap(); private List<Convention> conventions = DEFAULT_CONVENTIONS; private List<Annotation> annotations = emptyList(); private boolean discriminatorEnabled; private String discriminator; private String discriminatorKey; private String idPropertyName; ClassModelBuilder(final Class<T> type) { configureClassModelBuilder(this, notNull("type", type)); }
Sets the IdGenerator for the ClassModel
Params:
  • idGenerator – the IdGenerator
Returns:this
Since:3.10
/** * Sets the IdGenerator for the ClassModel * * @param idGenerator the IdGenerator * @return this * @since 3.10 */
public ClassModelBuilder<T> idGenerator(final IdGenerator<?> idGenerator) { this.idGenerator = idGenerator; return this; }
Returns:the IdGenerator for the ClassModel, or null if not set
Since:3.10
/** * @return the IdGenerator for the ClassModel, or null if not set * @since 3.10 */
public IdGenerator<?> getIdGenerator() { return idGenerator; }
Sets the InstanceCreatorFactory for the ClassModel
Params:
  • instanceCreatorFactory – the InstanceCreatorFactory
Returns:this
/** * Sets the InstanceCreatorFactory for the ClassModel * * @param instanceCreatorFactory the InstanceCreatorFactory * @return this */
public ClassModelBuilder<T> instanceCreatorFactory(final InstanceCreatorFactory<T> instanceCreatorFactory) { this.instanceCreatorFactory = notNull("instanceCreatorFactory", instanceCreatorFactory); return this; }
Returns:the InstanceCreatorFactory for the ClassModel
/** * @return the InstanceCreatorFactory for the ClassModel */
public InstanceCreatorFactory<T> getInstanceCreatorFactory() { return instanceCreatorFactory; }
Sets the type of the model
Params:
  • type – the type of the class
Returns:the builder to configure the class being modeled
/** * Sets the type of the model * * @param type the type of the class * @return the builder to configure the class being modeled */
public ClassModelBuilder<T> type(final Class<T> type) { this.type = notNull("type", type); return this; }
Returns:the type if set or null
/** * @return the type if set or null */
public Class<T> getType() { return type; }
Sets the conventions to apply to the model
Params:
  • conventions – a list of conventions
Returns:this
/** * Sets the conventions to apply to the model * * @param conventions a list of conventions * @return this */
public ClassModelBuilder<T> conventions(final List<Convention> conventions) { this.conventions = notNull("conventions", conventions); return this; }
Returns:the conventions o apply to the model
/** * @return the conventions o apply to the model */
public List<Convention> getConventions() { return conventions; }
Sets the annotations for the model
Params:
  • annotations – a list of annotations
Returns:this
/** * Sets the annotations for the model * * @param annotations a list of annotations * @return this */
public ClassModelBuilder<T> annotations(final List<Annotation> annotations) { this.annotations = notNull("annotations", annotations); return this; }
Returns:the annotations on the modeled type if set or null
/** * @return the annotations on the modeled type if set or null */
public List<Annotation> getAnnotations() { return annotations; }
Sets the discriminator to be used when storing instances of the modeled type
Params:
  • discriminator – the discriminator value
Returns:this
/** * Sets the discriminator to be used when storing instances of the modeled type * * @param discriminator the discriminator value * @return this */
public ClassModelBuilder<T> discriminator(final String discriminator) { this.discriminator = discriminator; return this; }
Returns:the discriminator to be used when storing instances of the modeled type or null if not set
/** * @return the discriminator to be used when storing instances of the modeled type or null if not set */
public String getDiscriminator() { return discriminator; }
Sets the discriminator key to be used when storing instances of the modeled type
Params:
  • discriminatorKey – the discriminator key value
Returns:this
/** * Sets the discriminator key to be used when storing instances of the modeled type * * @param discriminatorKey the discriminator key value * @return this */
public ClassModelBuilder<T> discriminatorKey(final String discriminatorKey) { this.discriminatorKey = discriminatorKey; return this; }
Returns:the discriminator key to be used when storing instances of the modeled type or null if not set
/** * @return the discriminator key to be used when storing instances of the modeled type or null if not set */
public String getDiscriminatorKey() { return discriminatorKey; }
Enables or disables the use of a discriminator when serializing
Params:
  • discriminatorEnabled – true to enable the use of a discriminator
Returns:this
/** * Enables or disables the use of a discriminator when serializing * * @param discriminatorEnabled true to enable the use of a discriminator * @return this */
public ClassModelBuilder<T> enableDiscriminator(final boolean discriminatorEnabled) { this.discriminatorEnabled = discriminatorEnabled; return this; }
Returns:true if a discriminator should be used when serializing, otherwise false
/** * @return true if a discriminator should be used when serializing, otherwise false */
public Boolean useDiscriminator() { return discriminatorEnabled; }
Designates a property as the _id property for this type. If another property is currently marked as the _id property, that setting is cleared in favor of the named property.
Params:
  • idPropertyName – the property name to use for the _id property, a null value removes the set idPropertyName.
Returns:this
/** * Designates a property as the {@code _id} property for this type. If another property is currently marked as the {@code _id} * property, that setting is cleared in favor of the named property. * * @param idPropertyName the property name to use for the {@code _id} property, a null value removes the set idPropertyName. * * @return this */
public ClassModelBuilder<T> idPropertyName(final String idPropertyName) { this.idPropertyName = idPropertyName; return this; }
Returns:the designated _id property name for this type or null if not set
/** * @return the designated {@code _id} property name for this type or null if not set */
public String getIdPropertyName() { return idPropertyName; }
Remove a property from the builder
Params:
  • propertyName – the actual property name in the POJO and not the documentPropertyName.
Returns:returns true if the property matched and was removed
/** * Remove a property from the builder * * @param propertyName the actual property name in the POJO and not the {@code documentPropertyName}. * @return returns true if the property matched and was removed */
public boolean removeProperty(final String propertyName) { return propertyModelBuilders.remove(getProperty(notNull("propertyName", propertyName))); }
Gets a property by the property name.
Params:
  • propertyName – the name of the property to find.
Returns:the property or null if the property is not found
/** * Gets a property by the property name. * * @param propertyName the name of the property to find. * @return the property or null if the property is not found */
public PropertyModelBuilder<?> getProperty(final String propertyName) { notNull("propertyName", propertyName); for (PropertyModelBuilder<?> propertyModelBuilder : propertyModelBuilders) { if (propertyModelBuilder.getName().equals(propertyName)) { return propertyModelBuilder; } } return null; }
Returns:the properties on the modeled type
/** * @return the properties on the modeled type */
public List<PropertyModelBuilder<?>> getPropertyModelBuilders() { return Collections.unmodifiableList(propertyModelBuilders); }
Creates a new ClassModel instance based on the mapping data provided.
Returns:the new instance
/** * Creates a new ClassModel instance based on the mapping data provided. * * @return the new instance */
public ClassModel<T> build() { List<PropertyModel<?>> propertyModels = new ArrayList<PropertyModel<?>>(); PropertyModel<?> idPropertyModel = null; stateNotNull("type", type); for (Convention convention : conventions) { convention.apply(this); } stateNotNull("instanceCreatorFactory", instanceCreatorFactory); if (discriminatorEnabled) { stateNotNull("discriminatorKey", discriminatorKey); stateNotNull("discriminator", discriminator); } for (PropertyModelBuilder<?> propertyModelBuilder : propertyModelBuilders) { boolean isIdProperty = propertyModelBuilder.getName().equals(idPropertyName); if (isIdProperty) { propertyModelBuilder.readName(ID_PROPERTY_NAME).writeName(ID_PROPERTY_NAME); } PropertyModel<?> model = propertyModelBuilder.build(); propertyModels.add(model); if (isIdProperty) { idPropertyModel = model; } } validatePropertyModels(type.getSimpleName(), propertyModels); return new ClassModel<T>(type, propertyNameToTypeParameterMap, instanceCreatorFactory, discriminatorEnabled, discriminatorKey, discriminator, IdPropertyModelHolder.create(type, idPropertyModel, idGenerator), unmodifiableList(propertyModels)); } @Override public String toString() { return format("ClassModelBuilder{type=%s}", type); } Map<String, TypeParameterMap> getPropertyNameToTypeParameterMap() { return propertyNameToTypeParameterMap; } ClassModelBuilder<T> propertyNameToTypeParameterMap(final Map<String, TypeParameterMap> propertyNameToTypeParameterMap) { this.propertyNameToTypeParameterMap = unmodifiableMap(new HashMap<String, TypeParameterMap>(propertyNameToTypeParameterMap)); return this; } ClassModelBuilder<T> addProperty(final PropertyModelBuilder<?> propertyModelBuilder) { propertyModelBuilders.add(notNull("propertyModelBuilder", propertyModelBuilder)); return this; } private void validatePropertyModels(final String declaringClass, final List<PropertyModel<?>> propertyModels) { Map<String, Integer> propertyNameMap = new HashMap<String, Integer>(); Map<String, Integer> propertyReadNameMap = new HashMap<String, Integer>(); Map<String, Integer> propertyWriteNameMap = new HashMap<String, Integer>(); for (PropertyModel<?> propertyModel : propertyModels) { if (propertyModel.hasError()) { throw new CodecConfigurationException(propertyModel.getError()); } checkForDuplicates("property", propertyModel.getName(), propertyNameMap, declaringClass); if (propertyModel.isReadable()) { checkForDuplicates("read property", propertyModel.getReadName(), propertyReadNameMap, declaringClass); } if (propertyModel.isWritable()) { checkForDuplicates("write property", propertyModel.getWriteName(), propertyWriteNameMap, declaringClass); } } if (idPropertyName != null && !propertyNameMap.containsKey(idPropertyName)) { throw new CodecConfigurationException(format("Invalid id property, property named '%s' can not be found.", idPropertyName)); } } private void checkForDuplicates(final String propertyType, final String propertyName, final Map<String, Integer> propertyNameMap, final String declaringClass) { if (propertyNameMap.containsKey(propertyName)) { throw new CodecConfigurationException(format("Duplicate %s named '%s' found in %s.", propertyType, propertyName, declaringClass)); } propertyNameMap.put(propertyName, 1); } }