All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bson.codecs.pojo.ClassModelBuilder Maven / Gradle / Ivy

The newest version!
/*
 * 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 {@code ClassModels}.
 *
 * @param  The type of the class the ClassModel represents
 * @since 3.5
 * @see ClassModel
 */
public class ClassModelBuilder {
    static final String ID_PROPERTY_NAME = "_id";
    private final List> propertyModelBuilders = new ArrayList>();
    private IdGenerator idGenerator;
    private InstanceCreatorFactory instanceCreatorFactory;
    private Class type;
    private Map propertyNameToTypeParameterMap = emptyMap();
    private List conventions = DEFAULT_CONVENTIONS;
    private List annotations = emptyList();
    private boolean discriminatorEnabled;
    private String discriminator;
    private String discriminatorKey;
    private String idPropertyName;

    ClassModelBuilder(final Class type) {
        configureClassModelBuilder(this, notNull("type", type));
    }

    /**
     * Sets the IdGenerator for the ClassModel
     *
     * @param idGenerator the IdGenerator
     * @return this
     * @since 3.10
     */
    public ClassModelBuilder idGenerator(final IdGenerator idGenerator) {
        this.idGenerator = idGenerator;
        return this;
    }

    /**
     * @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
     *
     * @param instanceCreatorFactory the InstanceCreatorFactory
     * @return this
     */
    public ClassModelBuilder instanceCreatorFactory(final InstanceCreatorFactory instanceCreatorFactory) {
        this.instanceCreatorFactory = notNull("instanceCreatorFactory", instanceCreatorFactory);
        return this;
    }

    /**
     * @return the InstanceCreatorFactory for the ClassModel
     */
    public InstanceCreatorFactory getInstanceCreatorFactory() {
        return instanceCreatorFactory;
    }

    /**
     * Sets the type of the model
     *
     * @param type the type of the class
     * @return the builder to configure the class being modeled
     */
    public ClassModelBuilder type(final Class type) {
        this.type = notNull("type", type);
        return this;
    }

    /**
     * @return the type if set or null
     */
    public Class getType() {
        return type;
    }

    /**
     * Sets the conventions to apply to the model
     *
     * @param conventions a list of conventions
     * @return this
     */
    public ClassModelBuilder conventions(final List conventions) {
        this.conventions = notNull("conventions", conventions);
        return this;
    }

    /**
     * @return the conventions o apply to the model
     */
    public List getConventions() {
        return conventions;
    }

    /**
     * Sets the annotations for the model
     *
     * @param annotations a list of annotations
     * @return this
     */
    public ClassModelBuilder annotations(final List annotations) {
        this.annotations = notNull("annotations", annotations);
        return this;
    }

    /**
     * @return the annotations on the modeled type if set or null
     */
    public List getAnnotations() {
        return annotations;
    }

    /**
     * Sets the discriminator to be used when storing instances of the modeled type
     *
     * @param discriminator the discriminator value
     * @return this
     */
    public ClassModelBuilder discriminator(final String discriminator) {
        this.discriminator = discriminator;
        return this;
    }

    /**
     * @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
     *
     * @param discriminatorKey the discriminator key value
     * @return this
     */
    public ClassModelBuilder discriminatorKey(final String discriminatorKey) {
        this.discriminatorKey = discriminatorKey;
        return this;
    }

    /**
     * @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
     *
     * @param discriminatorEnabled true to enable the use of a discriminator
     * @return this
     */
    public ClassModelBuilder enableDiscriminator(final boolean discriminatorEnabled) {
        this.discriminatorEnabled = discriminatorEnabled;
        return this;
    }

    /**
     * @return true if a discriminator should be used when serializing, otherwise false
     */
    public Boolean useDiscriminator() {
        return discriminatorEnabled;
    }

    /**
     * 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 idPropertyName(final String idPropertyName) {
        this.idPropertyName = idPropertyName;
        return this;
    }

    /**
     * @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
     *
     * @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.
     *
     * @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;
    }

    /**
     * @return the properties on the modeled type
     */
    public List> getPropertyModelBuilders() {
        return Collections.unmodifiableList(propertyModelBuilders);
    }

    /**
     * Creates a new ClassModel instance based on the mapping data provided.
     *
     * @return the new instance
     */
    public ClassModel build() {
        List> propertyModels = new ArrayList>();
        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(type, propertyNameToTypeParameterMap, instanceCreatorFactory, discriminatorEnabled, discriminatorKey,
                discriminator, IdPropertyModelHolder.create(type, idPropertyModel, idGenerator), unmodifiableList(propertyModels));
    }

    @Override
    public String toString() {
        return format("ClassModelBuilder{type=%s}", type);
    }

    Map getPropertyNameToTypeParameterMap() {
        return propertyNameToTypeParameterMap;
    }

    ClassModelBuilder propertyNameToTypeParameterMap(final Map propertyNameToTypeParameterMap) {
        this.propertyNameToTypeParameterMap = unmodifiableMap(new HashMap(propertyNameToTypeParameterMap));
        return this;
    }

    ClassModelBuilder addProperty(final PropertyModelBuilder propertyModelBuilder) {
        propertyModelBuilders.add(notNull("propertyModelBuilder", propertyModelBuilder));
        return this;
    }

    private void validatePropertyModels(final String declaringClass, final List> propertyModels) {
        Map propertyNameMap = new HashMap();
        Map propertyReadNameMap = new HashMap();
        Map propertyWriteNameMap = new HashMap();

        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 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);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy