org.dd4t.databind.builder.BaseDataBinder.java Source code

Java tutorial

Introduction

Here is the source code for org.dd4t.databind.builder.BaseDataBinder.java

Source

/*
 * Copyright (c) 2015 Radagio
 *
 * 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.dd4t.databind.builder;

import org.apache.commons.lang3.StringUtils;
import org.dd4t.contentmodel.Component;
import org.dd4t.contentmodel.ComponentPresentation;
import org.dd4t.contentmodel.ComponentTemplate;
import org.dd4t.contentmodel.Field;
import org.dd4t.core.databind.BaseViewModel;
import org.dd4t.core.databind.ModelConverter;
import org.dd4t.databind.annotations.ViewModel;
import org.dd4t.databind.util.DataBindConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Base class serving as entry point for all ModelBuilders
 *
 * - Loads configs
 * - Preloads model classes
 * - Gives a unified / single design pattern to load concrete ModelConverters
 *
 * TODO: Build an XML dataConverter!
 *
 * @author R. Kempees
 * @since 17/11/14.
 */
public abstract class BaseDataBinder {
    private static final Logger LOG = LoggerFactory.getLogger(BaseDataBinder.class);
    protected static final ConcurrentMap<String, Class<? extends BaseViewModel>> VIEW_MODELS = new ConcurrentHashMap<>();

    protected ModelConverter converter;
    protected String viewModelMetaKeyName;
    protected String viewModelPackageRoot;
    protected boolean renderDefaultComponentModelsOnly;
    protected boolean renderDefaultComponentsIfNoModelFound;
    protected Class<ComponentPresentation> concreteComponentPresentationImpl;
    protected Class<ComponentTemplate> concreteComponentTemplateImpl;
    protected Class<Component> concreteComponentImpl;
    protected Class<Field> concreteFieldImpl;

    public void setConcreteComponentTemplateImpl(final Class<ComponentTemplate> concreteComponentTemplateImpl) {
        this.concreteComponentTemplateImpl = concreteComponentTemplateImpl;
    }

    public void setConcreteComponentPresentationImpl(
            final Class<ComponentPresentation> concreteComponentPresentationImpl) {
        this.concreteComponentPresentationImpl = concreteComponentPresentationImpl;
    }

    public void setConcreteComponentImpl(final Class<Component> concreteComponentImpl) {
        this.concreteComponentImpl = concreteComponentImpl;
    }

    public void setConverter(final ModelConverter converter) {
        this.converter = converter;
    }

    public void setViewModelMetaKeyName(final String viewModelMetaKeyName) {
        this.viewModelMetaKeyName = viewModelMetaKeyName;
    }

    public void setViewModelPackageRoot(final String viewModelPackageRoot) {
        this.viewModelPackageRoot = viewModelPackageRoot;
    }

    public void setRenderDefaultComponentModelsOnly(final boolean renderDefaultComponentModelsOnly) {
        this.renderDefaultComponentModelsOnly = renderDefaultComponentModelsOnly;
    }

    public void setRenderDefaultComponentsIfNoModelFound(final boolean renderDefaultComponentsIfNoModelFound) {
        this.renderDefaultComponentsIfNoModelFound = renderDefaultComponentsIfNoModelFound;
    }

    public boolean isRenderDefaultComponentModelsOnly() {
        return renderDefaultComponentModelsOnly;
    }

    public boolean isRenderDefaultComponentsIfNoModelFound() {
        return renderDefaultComponentsIfNoModelFound;
    }

    public Class<ComponentPresentation> getConcreteComponentPresentationImpl() {
        return concreteComponentPresentationImpl;
    }

    public Class<ComponentTemplate> getConcreteComponentTemplateImpl() {
        return concreteComponentTemplateImpl;
    }

    public Class<Component> getConcreteComponentImpl() {
        return concreteComponentImpl;
    }

    public void setConcreteFieldImpl(final Class<Field> concreteFieldImpl) {
        this.concreteFieldImpl = concreteFieldImpl;
    }

    public boolean renderDefaultComponentModelsOnly() {
        return renderDefaultComponentModelsOnly;
    }

    public boolean renderDefaultComponentsIfNoModelFound() {
        return renderDefaultComponentsIfNoModelFound;
    }

    public ModelConverter getConverter() {
        return converter;
    }

    public String getViewModelMetaKeyName() {
        return viewModelMetaKeyName;
    }

    public String getViewModelPackageRoot() {
        return viewModelPackageRoot;
    }

    public Class<Field> getConcreteFieldImpl() {
        return concreteFieldImpl;
    }

    protected static BaseViewModel getModelOrNullForExistingEntry(Map<String, BaseViewModel> models,
            Class modelClass) {
        for (BaseViewModel baseViewModel : models.values()) {
            LOG.debug(baseViewModel.getClass().getName() + "==" + modelClass.getName());
            if (baseViewModel.getClass().equals(modelClass)) {
                return baseViewModel;
            }
        }
        return null;
    }

    @PostConstruct
    protected abstract void init();

    protected void checkViewModelConfiguration() {
        if (StringUtils.isEmpty(viewModelMetaKeyName)) {
            this.viewModelMetaKeyName = DataBindConstants.VIEW_MODEL_DEFAULT_META_KEY;
            LOG.warn("Setting meta key to default: " + DataBindConstants.VIEW_MODEL_DEFAULT_META_KEY);
        }

        if (StringUtils.isEmpty(viewModelPackageRoot)) {
            this.viewModelPackageRoot = DataBindConstants.VIEW_MODEL_DEFAULT_NAMESPACE;
            LOG.warn("No package root configured for view models. Using the default package: "
                    + DataBindConstants.VIEW_MODEL_DEFAULT_NAMESPACE);
        }

        LOG.info("View model key name is: " + this.viewModelMetaKeyName);
        LOG.info("Root package for View models is: " + this.viewModelPackageRoot);
    }

    protected void scanAndLoadModels() {
        LOG.info("Init: scanning view models.");
        final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
                false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(ViewModel.class));
        for (BeanDefinition bd : scanner.findCandidateComponents(this.viewModelPackageRoot)) {
            try {
                final Class clazz = Class.forName(bd.getBeanClassName());
                if (clazz != null) {
                    LOG.debug("Loading class: " + clazz.getCanonicalName());
                    final ViewModel viewModelParameters = (ViewModel) clazz.getAnnotation(ViewModel.class);

                    if (hasProperModelInformation(viewModelParameters, clazz)) {
                        LOG.debug(
                                "Parameters: Viewmodel name(s):{}, Root Element name(s){}, Set Component: {}, Set raw: {} ",
                                new Object[] { viewModelParameters.viewModelNames(),
                                        viewModelParameters.rootElementNames(),
                                        viewModelParameters.setComponentObject(),
                                        viewModelParameters.setRawData() });
                        storeModelClass(viewModelParameters, clazz);
                    }
                }
                LOG.info("Init: Done scanning view models.");
            } catch (ClassNotFoundException e) {
                LOG.error("Unexpected exception", e);
            }
        }
    }

    private boolean hasProperModelInformation(ViewModel viewModelParameters, Class clazz) {

        if (viewModelParameters.rootElementNames().length == 0 && viewModelParameters.viewModelNames().length == 0
                && !viewModelParameters.setComponentObject()) {
            LOG.warn(
                    "No viewmodel and rootelement name configuration set and SetComponentObject is false. Not using this model: {}",
                    clazz.getCanonicalName());
            return false;
        }

        if (StringUtils.isEmpty(viewModelParameters.rootElementNames()[0])
                && StringUtils.isEmpty(viewModelParameters.viewModelNames()[0])
                && !viewModelParameters.setComponentObject()) {
            LOG.warn(
                    "No viewmodel and rootelement name configuration set and SetComponentObject is false. Not using this model: {}",
                    clazz.getCanonicalName());
            return false;
        }

        return true;
    }

    /**
     * Get the key with which a model class is registered, which is either
     * related models.
     *
     * We should be able to get multiple keys for the same view model
     *
     * We should be able to match both view models and schema root element names. So the
     */
    private static void storeModelClass(final ViewModel viewModelParameters, final Class model) {

        final List<String> modelNames = new ArrayList<>();
        String[] viewModelNames = viewModelParameters.viewModelNames();

        if (null != viewModelNames && viewModelNames.length > 0) {
            modelNames.addAll(Arrays.asList(viewModelNames));

        }
        String[] rootElementNames = viewModelParameters.rootElementNames();
        if (null != rootElementNames && rootElementNames.length > 0) {
            modelNames.addAll(Arrays.asList(rootElementNames));
        }
        storeModelClassForModelNames(modelNames, model);
    }

    private static void storeModelClassForModelNames(final List<String> viewModelNames, final Class model) {
        for (String viewModelName : viewModelNames) {
            LOG.info("Storing viewModelName: {}, for class: {}", viewModelName, model.toString());
            if (VIEW_MODELS.containsKey(viewModelName)) {
                LOG.warn("Key: {} already exists! Model for key is: {}", viewModelName, model.toString());
            } else {
                VIEW_MODELS.put(viewModelName, model);
            }
        }
    }
}