com.ocs.dynamo.ui.container.hierarchical.ModelBasedHierarchicalContainer.java Source code

Java tutorial

Introduction

Here is the source code for com.ocs.dynamo.ui.container.hierarchical.ModelBasedHierarchicalContainer.java

Source

/*
   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 com.ocs.dynamo.ui.container.hierarchical;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.util.StringUtils;

import com.ocs.dynamo.constants.DynamoConstants;
import com.ocs.dynamo.dao.query.FetchJoinInformation;
import com.ocs.dynamo.domain.AbstractEntity;
import com.ocs.dynamo.domain.model.AttributeModel;
import com.ocs.dynamo.domain.model.AttributeType;
import com.ocs.dynamo.domain.model.EntityModel;
import com.ocs.dynamo.domain.model.EntityModelFactory;
import com.ocs.dynamo.domain.model.impl.ModelBasedFieldFactory;
import com.ocs.dynamo.service.BaseService;
import com.ocs.dynamo.service.MessageService;
import com.ocs.dynamo.ui.ServiceLocator;
import com.ocs.dynamo.ui.container.QueryType;
import com.ocs.dynamo.ui.container.ServiceContainer;
import com.vaadin.data.Container;
import com.vaadin.data.fieldgroup.FieldGroupFieldFactory;
import com.vaadin.ui.Component;
import com.vaadin.ui.Field;
import com.vaadin.ui.TableFieldFactory;

/**
 * Assumes that the following properties are set in message bundle:
 * <ul>
 * <li><rootentity model>.hierarchicalVisualPropertyIds the ordered common properties which will be
 * shared among all levels. When a level doesn't have that property it will be left blank when
 * displayed.
 * <li><(sub) entity model reference>.itemPropertyIdChild the property id that refers to the
 * (collection) of children. (optional)
 * <li><(sub) entity model reference>.itemPropertyIdParent the property id that refers to the
 * parent.
 * </ul>
 * The actual search will only search on the container in the lowest level by default.
 * 
 * @author Patrick Deenen (patrick.deenen@opencirclesolutions.nl)
 */
@SuppressWarnings("serial")
public class ModelBasedHierarchicalContainer<T> extends HierarchicalContainer {

    public static final String VISUAL_PROPERTY_IDS_MSG_KEY = "hierarchicalVisualPropertyIds";

    public class ModelBasedHierarchicalDefinition extends HierarchicalDefinition {

        private EntityModel<?> entityModel;

        private ModelBasedFieldFactory<AbstractEntity<?>> fieldFactory;

        public ModelBasedHierarchicalDefinition(EntityModel<?> entityModel, Indexed container, int level,
                Object itemPropertyIdParent, List<?> propertyIds) {
            super(container, level, entityModel.getIdAttributeModel().getName(), itemPropertyIdParent, propertyIds);
            this.entityModel = entityModel;
        }

        public EntityModel<?> getEntityModel() {
            return entityModel;
        }

        public void setEntityModel(EntityModel<?> entityModel) {
            this.entityModel = entityModel;
        }

        @SuppressWarnings("unchecked")
        public TableFieldFactory getFieldFactory(MessageService messageService) {
            if (fieldFactory == null) {
                fieldFactory = (ModelBasedFieldFactory<AbstractEntity<?>>) ModelBasedFieldFactory
                        .getInstance(entityModel, messageService);
            }
            return fieldFactory;
        }
    }

    public class HierarchicalFieldFactory implements FieldGroupFieldFactory, TableFieldFactory {

        private ModelBasedHierarchicalContainer<T> container;

        private MessageService messageService;

        /**
         * Create field factory delegator whichs uses the field factories in the definitions
         * 
         * @param container
         */
        public HierarchicalFieldFactory(ModelBasedHierarchicalContainer<T> container,
                MessageService messageService) {
            this.container = container;
            this.messageService = messageService;
        }

        @Override
        public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) {
            ModelBasedHierarchicalDefinition def = null;
            if (itemId instanceof HierarchicalId) {
                def = this.container.getHierarchicalDefinitionByItemId(itemId);
            } else {
                def = this.container.getHierarchicalDefinition(0);
            }
            if (def != null) {
                Object p = this.container.unmapProperty(def, propertyId);
                if (p != null) {
                    return def.getFieldFactory(messageService).createField(def.getContainer(),
                            this.container.unwrapItemId(itemId), p, uiContext);
                }
            }
            return null;
        }

        @SuppressWarnings("rawtypes")
        @Override
        public <F extends Field> F createField(Class<?> dataType, Class<F> fieldType) {
            FieldGroupFieldFactory f = (FieldGroupFieldFactory) container.getHierarchicalDefinition(0)
                    .getFieldFactory(messageService);
            return f.createField(dataType, fieldType);
        }

        protected ModelBasedHierarchicalContainer<T> getContainer() {
            return container;
        }
    }

    /**
     * Default constructor
     */
    public ModelBasedHierarchicalContainer() {
        // Do nothing
    }

    /**
     * Construct a hierarchical container using given root entity model and services.
     * 
     * @param messageService
     *            used for custom definitions, see class description.
     * @param rootEntityModel
     *            The top level entity model, the other sub models will be defined dynamically.
     * @param services
     *            All services to be used for each level.
     */
    public ModelBasedHierarchicalContainer(MessageService messageService, EntityModel<T> rootEntityModel,
            List<BaseService<?, ?>> services, HierarchicalFetchJoinInformation[] joins) {
        generateHierarchy(messageService, ServiceLocator.getEntityModelFactory(), rootEntityModel, services, joins,
                QueryType.ID_BASED);
    }

    public ModelBasedHierarchicalContainer(MessageService messageService, EntityModelFactory entityModelFactory,
            EntityModel<T> rootEntityModel, List<BaseService<?, ?>> services,
            HierarchicalFetchJoinInformation[] joins, QueryType queryType) {
        generateHierarchy(messageService, entityModelFactory, rootEntityModel, services, joins, queryType);
    }

    /**
     * Construct this hierarchical container using given root entity model and services.
     * 
     * @param messageService
     *            used for custom definitions, see class description.
     * @param rootEntityModel
     *            The top level entity model, the other sub models will be defined dynamically.
     * @param services
     *            All services to be used for each level.
     * @param joins
     *            Join information for each level.
     */
    public HierarchicalContainer generateHierarchy(MessageService messageService,
            EntityModelFactory entityModelFactory, EntityModel<T> rootEntityModel, List<BaseService<?, ?>> services,
            HierarchicalFetchJoinInformation[] joins, QueryType queryType) {
        if (rootEntityModel != null) {
            // generate definitions
            EntityModel<?> pm = null;
            EntityModel<?> cm = rootEntityModel;
            for (int level = 0; level < services.size(); level++) {
                // Create container for hierarchy level
                Indexed container = createLevelContainer(level, services.get(level), cm, joins, queryType);

                // Initialize common properties
                List<String> propertyIds = new ArrayList<>();
                String msg = messageService.getEntityMessage(cm.getReference(), VISUAL_PROPERTY_IDS_MSG_KEY);
                if (!StringUtils.isEmpty(msg)) {
                    // Use properties from message bundle
                    String[] tokens = msg.split(",");
                    if (level == 0) {
                        setContainerPropertyIds(Arrays.asList(tokens));
                    }
                    for (String prop : tokens) {
                        if ("null".equalsIgnoreCase(prop) || "".equals(prop)
                                || !container.getContainerPropertyIds().contains(prop)) {
                            propertyIds.add(null);
                        } else {
                            propertyIds.add(prop);
                        }
                    }
                } else {
                    // Create the common properties from the root
                    for (AttributeModel am : cm.getAttributeModels()) {
                        if (am.isVisibleInTable()) {
                            propertyIds.add(am.getName());
                        }
                    }
                }

                // Create definition
                if (level > 0) {
                    // Find parent, look in message bundle and otherwise an
                    // educated guess
                    AttributeModel parent = findRelatedAttributeModel(messageService, cm, pm.getEntityClass(),
                            AttributeType.MASTER, "itemPropertyIdParent");
                    // Must have a parent
                    addDefinition(new ModelBasedHierarchicalDefinition(cm, container, level, parent.getName(),
                            propertyIds));
                } else {
                    // Root will not have a parent
                    addDefinition(new ModelBasedHierarchicalDefinition(cm, container, level, null, propertyIds));
                }

                if ((level + 1) < services.size()) {
                    // Next child/level, look in message bundle and otherwise an
                    // educated guess
                    pm = cm;
                    Class<?> pec = services.get(level + 1).getEntityClass();
                    AttributeModel child = findRelatedAttributeModel(messageService, pm, pec, AttributeType.DETAIL,
                            "itemPropertyIdChild");
                    if (child != null) {
                        cm = child.getNestedEntityModel();
                    } else {
                        cm = entityModelFactory.getModel(pm.getReference() + "_" + pec.getSimpleName(), pec);
                    }
                }
            }
        }
        return this;
    }

    /**
     * Create the container for a specific level
     * 
     * @return the created container
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected Indexed createLevelContainer(int level, BaseService<?, ?> service, EntityModel<?> entityModel,
            HierarchicalFetchJoinInformation[] joins, QueryType queryType) {
        List<HierarchicalFetchJoinInformation> ljoins = new ArrayList<>();
        if (joins != null) {
            for (HierarchicalFetchJoinInformation join : joins) {
                if (join.getLevel() == level) {
                    ljoins.add(join);
                }
            }
        }
        ServiceContainer container = new ServiceContainer(service, entityModel, false,
                DynamoConstants.EXTENDED_PAGE_SIZE, queryType, ljoins.toArray(new FetchJoinInformation[0]));
        // increase cache size to prevent endless re-queries
        container.getQueryView().setMaxCacheSize(DynamoConstants.CACHE_SIZE);

        return container;
    }

    protected AttributeModel findRelatedAttributeModel(MessageService messageService, EntityModel<?> entityModel,
            Class<?> entityClassOther, AttributeType attributeType, String messageKey) {
        AttributeModel related = null;
        String msg = messageService.getEntityMessage(entityModel.getReference(), messageKey);
        if (!StringUtils.isEmpty(msg) && entityModel.getAttributeModel(msg) != null) {
            related = entityModel.getAttributeModel(msg);
        } else {
            List<AttributeModel> ms = entityModel.getAttributeModelsForType(attributeType, entityClassOther);
            if (!ms.isEmpty()) {
                related = ms.get(0);
            }
        }
        return related;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ModelBasedHierarchicalDefinition getHierarchicalDefinition(int level) {
        return (ModelBasedHierarchicalDefinition) getHierarchy().get(level);
    }

    @SuppressWarnings("unchecked")
    @Override
    public ModelBasedHierarchicalDefinition getHierarchicalDefinitionByItemId(Object itemId) {
        return (ModelBasedHierarchicalDefinition) super.getHierarchicalDefinitionByItemId(itemId);
    }

    @Override
    public void addDefinition(HierarchicalDefinition definition) {
        if (!(definition instanceof ModelBasedHierarchicalContainer.ModelBasedHierarchicalDefinition)) {
            throw new UnsupportedOperationException(
                    "The use of a ModelBasedHierarchicalDefinition is mandatory for a ModelBasedHierarchicalContainer.");
        }
        super.addDefinition(definition);
    }

    @Override
    public void addDefinition(Indexed container, int level, Object itemPropertyId, Object itemPropertyIdParent,
            Object... propertyIds) {
        throw new UnsupportedOperationException(
                "The use of a ModelBasedHierarchicalDefinition is mandatory for a ModelBasedHierarchicalContainer.");
    }

    public void addDefinition(EntityModel<?> entityModel, Indexed container, int level, Object itemPropertyIdParent,
            Object... propertyIds) {
        super.addDefinition(new ModelBasedHierarchicalDefinition(entityModel, container, level,
                itemPropertyIdParent, Arrays.asList(propertyIds)));

    }
}