org.modelibra.Entity.java Source code

Java tutorial

Introduction

Here is the source code for org.modelibra.Entity.java

Source

/*
 * Modelibra
 *
 * 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.modelibra;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.modelibra.action.EntityAction;
import org.modelibra.config.CombinationConfig;
import org.modelibra.config.ConceptConfig;
import org.modelibra.config.ModelConfig;
import org.modelibra.config.NeighborConfig;
import org.modelibra.config.NeighborsConfig;
import org.modelibra.config.PropertyConfig;
import org.modelibra.exception.ActionRuntimeException;
import org.modelibra.exception.ConfigRuntimeException;
import org.modelibra.exception.ModelibraRuntimeException;

/**
 * <p>
 * Entity abstract class to implement common characteristics of all concepts.
 * </p>
 * 
 * <p>
 * Each entity has oid and code properties. The oid property is generated by
 * Modelibra. The oid property cannot be configured. If you do not want to use
 * the code property, do not configure it.
 * </p>
 * 
 * <p>
 * An entity may have a user defined id (unique combination) that can be simple
 * or composite. It may contain propertie and/or neighbors. The id is configured
 * by marking properties and/or neighbors with the unique configuration element
 * set to true.
 * </p>
 * 
 * @author Dzenan Ridjanovic
 * @author Vedad Kirlic
 * @version 2009-02-22
 */
@SuppressWarnings("serial")
public abstract class Entity<T extends IEntity<T>> extends Observable implements IEntity<T> {

    private static Log log = LogFactory.getLog(Entity.class);

    private IDomainModel model;

    private ConceptConfig conceptConfig;

    private Oid oid;

    private String code;

    private UniqueCombination uniqueCombination;

    private IndexCombination indexCombination;

    /**
     * Constructs an entity without the domain model and concept configuration.
     * Used only in configuration classes.
     */
    public Entity() {

    }

    /**
     * Constructs an entity within the given domain model.
     * 
     * @param model
     *            domain model
     */
    public Entity(IDomainModel model) {
        this.model = model;
        if (model == null) {
            String msg = "Entity.constructor -- model is null.";
            throw new ModelibraRuntimeException(msg);
        }
        ModelConfig modelConfig = model.getModelConfig();
        String conceptCode = getClass().getSimpleName();
        conceptConfig = model.getModelConfig().getConceptConfig(conceptCode);
        if (conceptConfig == null) {
            String msg = "Entity.constructor -- concept code is not valid: "
                    + modelConfig.getDomainConfig().getCode() + "." + modelConfig.getCode() + "." + conceptCode;
            throw new ConfigRuntimeException(msg);
        }
        setOid(new Oid());
    }

    /**
     * Sets the entity domain model.
     * 
     * @param model
     *            domain model
     */
    public void setModel(IDomainModel model) {
        String conceptCode = getClass().getSimpleName();
        ConceptConfig conceptConfig = model.getModelConfig().getConceptConfig(conceptCode);
        if (conceptConfig != null) {
            this.model = model;
            this.conceptConfig = conceptConfig;
            List<String> internalChildNeighborCodes = getConceptConfig().getInternalChildNeighborCodes();
            for (String code : internalChildNeighborCodes) {
                Entities<?> internalChildNeghbor = (Entities<?>) getChildNeighbor(code);
                internalChildNeghbor.setModel(model);
            }
        } else {
            String msg = "Entity.setModel -- concept code is not valid: "
                    + model.getModelConfig().getDomainConfig().getCode() + "." + model.getModelConfig().getCode()
                    + "." + conceptCode;
            throw new ConfigRuntimeException(msg);
        }
    }

    /**
     * Gets the model.
     * 
     * @return model
     */
    public IDomainModel getModel() {
        return model;
    }

    /**
     * Gets the concept configuration.
     * 
     * @return concept configuration
     */
    public ConceptConfig getConceptConfig() {
        return conceptConfig;
    }

    /**
     * Sets the oid.
     * 
     * @param oid
     *            oid
     */
    public void setOid(Oid oid) {
        this.oid = oid;
    }

    /**
     * Gets the oid.
     * 
     * @return the oid
     */
    public Oid getOid() {
        return oid;
    }

    /**
     * Sets the code.
     * 
     * @param code
     *            code
     */
    public void setCode(String code) {
        this.code = code;
    }

    /**
     * Gets the code.
     * 
     * @return code
     */
    public String getCode() {
        return code;
    }

    /**
     * Derives the entity unique combination (id) using the concept's unique
     * configuration.
     * 
     * @return unique combination
     */
    public UniqueCombination getUniqueCombination() {
        uniqueCombination = new UniqueCombination();
        ConceptConfig conceptConfig = getConceptConfig();
        CombinationConfig idConfig = conceptConfig.getUniqueConfig();
        if (idConfig.isNotEmpty()) {
            if (!idConfig.isPropertyEmpty()) {
                for (String propertyCode : idConfig.getPropertyCodeList()) {
                    Object property = getProperty(propertyCode);
                    if (property != null) {
                        uniqueCombination.addProperty(propertyCode, property);
                    }
                }
            }

            if (!idConfig.isNeighborEmpty()) {
                for (String neighborCode : idConfig.getNeighborCodeList()) {
                    IEntity<?> neighbor = (IEntity<?>) getParentNeighbor(neighborCode);
                    if (neighbor != null) {
                        uniqueCombination.addNeighbor(neighborCode, neighbor);
                    }
                }
            }
            if (uniqueCombination.isEmpty()) {
                String msg = "Entity.getUniqueCombination -- concept has an empty unique combination: "
                        + model.getModelConfig().getDomainConfig().getCode() + "."
                        + model.getModelConfig().getCode() + "." + conceptConfig.getCode();
                throw new ConfigRuntimeException(msg);
            }
        }
        return uniqueCombination;
    }

    /**
     * Derives the entity index combination using the concept index
     * configuration.
     * 
     * @return entity index combination
     */
    public IndexCombination getIndexCombination() {
        indexCombination = new IndexCombination();
        ConceptConfig conceptConfig = getConceptConfig();
        CombinationConfig ixConfig = conceptConfig.getIndexConfig();
        if (ixConfig.isNotEmpty()) {
            if (!ixConfig.isPropertyEmpty()) {
                for (String propertyCode : ixConfig.getPropertyCodeList()) {
                    Object property = getProperty(propertyCode);
                    if (property != null) {
                        indexCombination.addProperty(propertyCode, property);
                    }
                }
            }

            if (!ixConfig.isNeighborEmpty()) {
                for (String neighborCode : ixConfig.getNeighborCodeList()) {
                    IEntity<?> neighbor = (IEntity<?>) getParentNeighbor(neighborCode);
                    if (neighbor != null) {
                        indexCombination.addNeighbor(neighborCode, neighbor);
                    }
                }
            }
            if (indexCombination.isEmpty()) {
                String msg = "Entity.getIndexCombination -- concept has an empty index combination: "
                        + model.getModelConfig().getDomainConfig().getCode() + "."
                        + model.getModelConfig().getCode() + "." + conceptConfig.getCode();
                throw new ConfigRuntimeException(msg);
            }
        }
        return indexCombination;
    }

    /**
     * Sets the entity property given a property code. Meta handling is used.
     * 
     * @param propertyCode
     *            property code
     * @param propertyValue
     *            property value
     */
    public void setProperty(String propertyCode, Object propertyValue) {
        if (propertyCode.equals("oid")) {
            Oid oid = (Oid) propertyValue;
            setOid(oid);
        } else if (propertyCode.equals("code")) {
            String code = (String) propertyValue;
            setCode(code);
        } else {
            DomainModel model = (DomainModel) getModel();
            if (model != null) {
                ModelMeta modelMeta = model.getModelMeta();
                modelMeta.setProperty(this, propertyCode, propertyValue);
            }
        }
    }

    /**
     * Gets the entity property given a property code. Meta handling is used.
     * 
     * @param propertyCode
     *            property code
     * @return property
     */
    public Object getProperty(String propertyCode) {
        Object property = null;
        if (propertyCode.equals("oid")) {
            property = this.getOid();
        } else if (propertyCode.equals("code")) {
            property = this.getCode();
        } else {
            DomainModel model = (DomainModel) getModel();
            if (model != null) {
                ModelMeta modelMeta = model.getModelMeta();
                property = modelMeta.getProperty(this, propertyCode);
            }
        }
        return property;
    }

    /**
     * Sets the entity neighbor object given a neighbor code. Meta handling is
     * used.
     * 
     * @param neighborCode
     *            neighbor code
     * @param neighbor
     *            neighbor object
     */
    public void setNeighbor(String neighborCode, Object neighbor) {
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            modelMeta.setNeighbor(this, neighborCode, neighbor);
        }
    }

    /**
     * Gets the entity neighbor object given a neighbor code. Meta handling is
     * used.
     * 
     * @param neighborCode
     *            neighbor code
     * @return neighbor object
     */
    public Object getNeighbor(String neighborCode) {
        Object neighbor = null;
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            neighbor = modelMeta.getNeighbor(this, neighborCode);
        }
        return neighbor;
    }

    /**
     * Sets the parent entity neighbor given a neighbor code. Meta handling is
     * used.
     * 
     * @param neighborCode
     *            neighbor code
     * @param neighborEntity
     *            neighbor entity
     */
    public void setParentNeighbor(String neighborCode, IEntity<? extends IEntity<?>> neighborEntity) {
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            modelMeta.setParentNeighbor(this, neighborCode, neighborEntity);
        }
    }

    /**
     * Gets the parent neighbor given a neighbor code. Meta handling is used.
     * 
     * @param neighborCode
     *            neighbor code
     * @return neighbor entity
     */
    public IEntity<?> getParentNeighbor(String neighborCode) {
        IEntity<?> neighborEntity = null;
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            neighborEntity = modelMeta.getParentNeighbor(this, neighborCode);
        }
        return neighborEntity;
    }

    /**
     * Sets the child entity neighbor given a neighbor code. Meta handling is
     * used.
     * 
     * @param neighborCode
     *            neighbor code
     * @param neighborEntities
     *            neighbor entities
     */
    public void setChildNeighbor(String neighborCode, IEntities<?> neighborEntities) {
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            modelMeta.setChildNeighbor(this, neighborCode, neighborEntities);
        }
    }

    /**
     * Gets the child entity neighbor given a neighbor code. Meta handling is
     * used.
     * 
     * @param neighborCode
     *            neighbor code
     * @return neighbor entities
     */
    public IEntities<?> getChildNeighbor(String neighborCode) {
        IEntities<?> neighborEntities = null;
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            neighborEntities = modelMeta.getChildNeighbor(this, neighborCode);
        }
        return neighborEntities;
    }

    /**
     * Updates the entity with the given entity.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity is updated with the given entity
     */
    public boolean update(T entity) {
        return update(entity, true);
    }

    /**
     * Updates the entity with a given entity. Meta handling is used. Action
     * notification is not used. Instead, the update action on entities is used
     * to undo the action.
     * 
     * @param entity
     *            entity
     * @param updateSensitive
     *            <code>true</code> if the sensitive information will be updated
     * @return <code>true</code> if the entity is updated with a given entity
     */
    public boolean update(T entity, boolean updateSensitive) {
        boolean updated = false;
        EntityAction entityAction = new EntityAction();
        entityAction.setEntity(this);
        entityAction.setParameter(entity);
        if (this == entity) {
            String msg = "Entity.update --  before update entity and after update entity cannot be the same object.";
            log.info(msg);
            ((Entity<?>) entity).output("Update");
            throw new ActionRuntimeException(msg);
        }

        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            updated = modelMeta.update(this, entity, updateSensitive);
        } else {

        }
        return updated;
    }

    /**
     * Updates only the properties of the entity with a given entity.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity is updated with a given entity
     */
    public boolean updateProperties(T entity) {
        return updateProperties(entity, true);
    }

    /**
     * Updates only the properties of the entity with a given entity. Meta
     * handling is used. Action notification is not used. Instead, the update
     * action on entities is used to undo the action.
     * 
     * @param entity
     *            entity
     * @param updateSensitive
     *            <code>true</code> if the sensitive properties will be updated
     * @return <code>true</code> if the entity is updated with a given entity
     */
    public boolean updateProperties(T entity, boolean updateSensitive) {
        boolean updated = false;
        EntityAction entityAction = new EntityAction();
        entityAction.setEntity(this);
        entityAction.setParameter(entity);
        if (this == entity) {
            String msg = "Entity.updateProperties --  before update entity and after update entity cannot be the same object.";
            log.info(msg);
            ((Entity<?>) entity).output("Update properties");
            throw new ActionRuntimeException(msg);
        }

        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            updated = modelMeta.updateProperties(this, entity, updateSensitive);
        } else {

        }
        return updated;
    }

    /**
     * Checks if the entity satisfies the selector.
     * 
     * @param selector
     *            selector
     * @return <code>true</code> if the entity satisfies the selector
     */
    public boolean isSelected(ISelector selector) {
        boolean selected = false;
        if (selector instanceof PropertySelector) {
            PropertySelector propertySelector = (PropertySelector) selector;
            return isSelected(propertySelector);
        } else if (selector instanceof ParentNeighborSelector) {
            ParentNeighborSelector parentNeighborSelector = (ParentNeighborSelector) selector;
            return isSelected(parentNeighborSelector);
        } else if (selector instanceof CompositeSelector) {
            CompositeSelector compositeSelector = (CompositeSelector) selector;
            return isSelected(compositeSelector);
        }
        return selected;
    }

    /**
     * Checks if the entity satisfies the property selector.
     * 
     * @param propertySelector
     *            property selector
     * @return <code>true</code> if the entity satisfies the property selector
     */
    private boolean isSelected(PropertySelector propertySelector) {
        boolean selected = false;
        String relationalOperator = propertySelector.getRelationalOperator();
        String propertyCode = propertySelector.getPropertyCode();
        Object value = propertySelector.getValue();
        if (relationalOperator.equals(PropertySelector.ALL)) {
            selected = true;
        } else {
            Object property = getProperty(propertyCode);
            if (property == null) {
                if (relationalOperator.equals(PropertySelector.NULL)) {
                    selected = true;
                }
            } else {
                Object[] values = propertySelector.getValues();
                if ((property instanceof String) && (value instanceof String)
                        && !propertySelector.isCaseSensitive()) {
                    property = ((String) property).toLowerCase();
                    value = new String(((String) value).toLowerCase());
                    String[] lowerCaseValues = new String[values.length];
                    for (int i = 0; i < values.length; i++) {
                        lowerCaseValues[i] = new String(((String) values[i]).toLowerCase());
                    }
                    values = lowerCaseValues;
                }
                if (relationalOperator.equals(PropertySelector.EQUAL)) {
                    if (property.equals(value)) {
                        selected = true;
                    }
                } else if (relationalOperator.equals(PropertySelector.CONTAIN)) {
                    if ((property instanceof String) && (value instanceof String)) {
                        String p = (String) property;
                        String s = (String) value;
                        if (p.contains(s)) {
                            selected = true;
                        }
                    } else if (property instanceof Collection) {
                        Collection<?> p = (Collection<?>) property;
                        if (p.contains(value)) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.CONTAIN_SOME)) {
                    // Object[] values = propertySelector.getValues();
                    if ((property instanceof String) && (values instanceof String[])) {
                        String p = (String) property;
                        String[] ps = (String[]) values;
                        boolean isSome = false;
                        for (String criteriaValue : ps) {
                            if (p.contains(criteriaValue)) {
                                isSome = true;
                                break;
                            }
                        }
                        if (isSome) {
                            selected = true;
                        }
                    } else if (property instanceof Collection) {
                        Collection<?> p = (Collection<?>) property;
                        boolean isSome = false;
                        for (Object criteriaValue : values) {
                            if (p.contains(criteriaValue)) {
                                isSome = true;
                                break;
                            }
                        }
                        if (isSome) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.CONTAIN_ALL)) {
                    // Object[] values = propertySelector.getValues();
                    if ((property instanceof String) && (values instanceof String[])) {
                        String p = (String) property;
                        String[] ps = (String[]) values;
                        boolean isAll = true;
                        for (String criteriaValue : ps) {
                            if (!p.contains(criteriaValue)) {
                                isAll = false;
                                break;
                            }
                        }
                        if (isAll) {
                            selected = true;
                        }
                    } else if (property instanceof Collection) {
                        Collection<?> p = (Collection<?>) property;
                        boolean isAll = true;
                        for (Object criteriaValue : values) {
                            if (!p.contains(criteriaValue)) {
                                isAll = false;
                                break;
                            }
                        }
                        if (isAll) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.BEGIN)) {
                    if ((property instanceof String) && (value instanceof String)) {
                        String p = (String) property;
                        String s = (String) value;
                        if (p.startsWith(s)) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.END)) {
                    if ((property instanceof String) && (value instanceof String)) {
                        String p = (String) property;
                        String s = (String) value;
                        if (p.endsWith(s)) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.MATCH)) {
                    if ((property instanceof String) && (value instanceof String)) {
                        String p = (String) property;
                        String s = (String) value;
                        if (p.matches(s)) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.IN)) {
                    // Object[] values = propertySelector.getValues();
                    boolean isIn = false;
                    for (Object criteriaValue : values) {
                        if (property.equals(criteriaValue)) {
                            isIn = true;
                            break;
                        }
                    }
                    if (isIn) {
                        selected = true;
                    }
                } else if (relationalOperator.equals(PropertySelector.LESS_THAN)) {
                    if (property instanceof Comparable) {
                        Comparable p = (Comparable) property;
                        if (p.compareTo(value) < 0) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.LESS_EQUAL)) {
                    if (property instanceof Comparable) {
                        Comparable p = (Comparable) property;
                        if (p.compareTo(value) <= 0) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.GREATER_THAN)) {
                    if (property instanceof Comparable) {
                        Comparable p = (Comparable) property;
                        if (p.compareTo(value) > 0) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.GREATER_EQUAL)) {
                    if (property instanceof Comparable) {
                        Comparable p = (Comparable) property;
                        if (p.compareTo(value) >= 0) {
                            selected = true;
                        }
                    }
                } else if (relationalOperator.equals(PropertySelector.BETWEEN)) {
                    Object[] borders = propertySelector.getValues();
                    Object min = borders[0];
                    Object max = borders[1];
                    if (property instanceof Comparable) {
                        Comparable p = (Comparable) property;
                        if ((p.compareTo(min) >= 0) && (p.compareTo(max) <= 0)) {
                            selected = true;
                        }

                    }
                }
            } // property != null
        } // selection criteria different form NONE and ALL
        return selected;
    }

    /**
     * Checks if the entity satisfies the parent neighbor selector.
     * 
     * @param parentNeighborSelector
     *            parent neighbor selector
     * @return <code>true</code> if the entity satisfies the parent neighbor
     *         selector
     */
    private boolean isSelected(ParentNeighborSelector parentNeighborSelector) {
        String parentNeighborCode = parentNeighborSelector.getParentNeighborCode();
        IEntity<?> parentNeighbor = getParentNeighbor(parentNeighborCode);
        PropertySelector parentNeighborPropertySelector = parentNeighborSelector
                .getParentNeighborPropertySelector();
        return parentNeighbor.isSelected(parentNeighborPropertySelector);
    }

    /**
     * Checks if the entity satisfies the composite selector.
     * 
     * @param compositeSelector
     *            composite selector
     * @return <code>true</code> if the entity satisfies the composite selector
     */
    private boolean isSelected(CompositeSelector compositeSelector) {
        String logicalOperator = compositeSelector.getLogicalOperator();
        ISelector leftSelector = compositeSelector.getLeftSelector();
        ISelector rightSelector = compositeSelector.getRightSelector();
        if (logicalOperator.equals(ISelector.NOT)) {
            return !isSelected(leftSelector);
        } else if (logicalOperator.equals(ISelector.AND)) {
            return isSelected(leftSelector) && isSelected(rightSelector);
        } else if (logicalOperator.equals(ISelector.OR)) {
            return isSelected(leftSelector) || isSelected(rightSelector);
        }
        return false;
    }

    /**
     * Copies the entity (properties and parent neighbors).
     * 
     * @return copied entity
     */
    public T copy() {
        return copy(true);
    }

    /**
     * Copies the entity. Meta handling is used.
     * 
     * @param copySensitive
     *            <code>true</code> if the sensitive information will be copied
     * @return copied entity
     */
    public T copy(boolean copySensitive) {
        T copiedEntity = null;
        DomainModel model = (DomainModel) getModel();
        if (model != null && getConceptConfig() != null) {
            ModelMeta modelMeta = model.getModelMeta();
            copiedEntity = modelMeta.createEntity(this);
            if (copiedEntity != null) {
                copiedEntity.setOid(getOid());
                ((Entity<T>) copiedEntity).update((T) this, copySensitive);
            }
        }
        return copiedEntity;
    }

    /**
     * Copies the entity by copying the whole internal tree with the entity as
     * the root. The copied entity may be a part of another model.
     * 
     * @param model
     *            another model
     * @return deep copied entity
     */
    public T deepCopy(IDomainModel model) {
        return deepCopy(model, true);
    }

    /**
     * Copies the entity by copying the whole internal tree with the entity as
     * the root. The copied entity may be a part of another model. Meta handling
     * is used.
     * 
     * @param model
     *            another model
     * @param copySensitive
     *            <code>true</code> if the sensitive information will be copied
     * @return deep copied entity
     */
    public T deepCopy(IDomainModel model, boolean copySensitive) {
        T copiedEntity = null;
        if (getConceptConfig() != null) {
            if (model != null) {
                DomainModel anotherModel = (DomainModel) model;
                ModelMeta modelMeta = anotherModel.getModelMeta();
                copiedEntity = modelMeta.createEntity(this);
                if (copiedEntity != null) {
                    copiedEntity.setOid(getOid());
                    ((Entity<T>) copiedEntity).updateProperties((T) this, copySensitive);

                    // reference properties (parentOid) ARE properties
                    // modelMeta.setExternalParentsByOids(copiedEntity,
                    // this);

                    NeighborsConfig neighborsConfig = getConceptConfig().getNeighborsConfig();
                    for (NeighborConfig neighborConfig : neighborsConfig) {
                        if (neighborConfig.isInternal() && neighborConfig.isChild()) {
                            String neighborCode = neighborConfig.getCode();
                            Entities baseEntityNeighborEntities = (Entities) getChildNeighbor(neighborCode);
                            IEntities copiedNeighborEntities = baseEntityNeighborEntities.deepCopy(model,
                                    copySensitive);
                            copiedEntity.setChildNeighbor(neighborCode, copiedNeighborEntities);
                        }
                    }
                }
            }
        }
        return copiedEntity;
    }

    /**
     * Copies only the properties of the entity.
     * 
     * @return copied entity
     */
    public T copyProperties() {
        return copyProperties(true);
    }

    /**
     * Copies only the properties of the entity. Meta handling is used.
     * 
     * @param copySensitive
     *            <code>true</code> if the sensitive properties will be copied
     * @return copied entity
     */
    public T copyProperties(boolean copySensitive) {
        T copiedEntity = null;
        if (getConceptConfig() != null) {
            DomainModel model = (DomainModel) getModel();
            if (model != null) {
                ModelMeta modelMeta = model.getModelMeta();
                copiedEntity = modelMeta.createEntity(this);
                if (copiedEntity != null) {
                    copiedEntity.setOid(getOid());
                    Entity<T> entity = (Entity<T>) copiedEntity;
                    entity.updateProperties((T) this, copySensitive);
                }
            }
        }
        return copiedEntity;
    }

    /**
     * Synchronizes the returned entitiy with the base entity. The returned
     * entity is a new version of the taken entity.
     * 
     * @param takenEntity
     *            taken entity
     * @param returnedEntity
     *            returned entity
     * @param synchronizeSensitive
     *            <code>true</code> if the sensitive information will be
     *            synchronized
     */
    public void synchronize(T takenEntity, T returnedEntity, boolean synchronizeSensitive) {
        // To be synchronized property by property and neighbor by neighbor
        // (parent by parent?).
        // If there is a difference between the base entity and the takenEntity,
        // the base entity wins.
        String msg = "Entity.synchronize -- not yet developed.";
        throw new ModelibraRuntimeException(msg);
    }

    /**
     * Checks if the entity has the same content (properties and neighbors) as
     * the given entity.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity has the same content as the given
     *         entity
     */
    public boolean equalContent(T entity) {
        boolean equal = equalProperties(entity);
        if (equal) {
            equal = equalNeighbors(entity);
        }
        return equal;
    }

    /**
     * Checks if the entity has the same properties as the given entity. Meta
     * handling is used.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity has the same properties as the
     *         given entity
     */
    public boolean equalProperties(T entity) {
        boolean equal = false;
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            equal = modelMeta.equalProperties(this, entity);
        }
        return equal;
    }

    /**
     * Checks if the entity has the same neighbors as the given entity.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity has the same neighbors as the
     *         given entity
     */
    public boolean equalNeighbors(T entity) {
        boolean equal = equalParentNeighbors(entity);
        if (equal) {
            equal = equalChildNeighbors(entity);
        }
        return equal;
    }

    /**
     * Checks if the entity has the same parent neighbors as the given entity.
     * Meta handling is used.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity has the same parent neighbors as
     *         the given entity
     */
    public boolean equalParentNeighbors(T entity) {
        boolean equal = false;
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            equal = modelMeta.equalParentNeighbors(this, entity);
        }
        return equal;
    }

    /**
     * Checks if the entity has the same child neighbors as the given entity.
     * Meta handling is used.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity has the same child neighbors as
     *         the given entity
     */
    public boolean equalChildNeighbors(T entity) {
        boolean equal = false;
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            equal = modelMeta.equalChildNeighbors(this, entity);
        }
        return equal;
    }

    /**
     * Checks if the entity has the same oid as the given entity. Used in the
     * overriden implementation of the equals method.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity has the same oid as the given
     *         entity
     */
    public boolean equalOid(T entity) {
        boolean equal = false;
        if (entity == null) {
            equal = false;
        } else if (entity == this) {
            equal = true;
        } else if (getOid() != null && getOid().equals(entity.getOid())) {
            equal = true;
        } else {
            equal = false;
        }
        return equal;
    }

    /**
     * Checks if the entity has the same unique combination (id) as the given
     * entity.
     * 
     * @param entity
     *            entity
     * @return <code>true</code> if the entity has the same unique combination
     *         (id) as the given entity
     */
    public boolean equalUnique(T entity) {
        boolean equal = true;
        if (entity == null) {
            equal = false;
        } else if (entity == this) {
            equal = true;
        } else {
            UniqueCombination idLeft = getUniqueCombination();
            UniqueCombination idRight = entity.getUniqueCombination();
            if (idLeft != null && idRight != null) {
                if (idLeft.size() == idRight.size()) {
                    for (String propertyCode : idLeft.getPropertyCodeList()) {
                        Object propertyLeft = idLeft.getProperty(propertyCode);
                        Object propertyRight = idRight.getProperty(propertyCode);
                        if (!propertyLeft.equals(propertyRight)) {
                            equal = false;
                            break;
                        }
                    }
                    for (String neighborCode : idLeft.getNeighborCodeList()) {
                        IEntity<?> neighborLeft = idLeft.getNeighbor(neighborCode);
                        IEntity<?> neighborRight = idRight.getNeighbor(neighborCode);
                        if (neighborLeft != null && neighborRight != null && !neighborLeft.equals(neighborRight)) {
                            equal = false;
                            break;
                        }
                    }
                } else {
                    equal = false;
                }
            } else {
                equal = false;
            }
        }
        return equal;
    }

    /**
     * Checks if the entity is equal to the given object. If the given object is
     * not of the IEntity type, two objects cannot be equal. Two entities are
     * equal if they have the same oid.
     * 
     * @param object
     *            object
     * @return <code>true</code> if the entity is equal to the given object.
     */
    @Override
    public boolean equals(Object object) {
        boolean equal = false;
        if (object == null) {
            equal = false;
        } else if (object == this) {
            equal = true;
        } else if (object instanceof IEntity) {
            IEntity<?> entity = (IEntity<?>) object;
            equal = equalOid((T) entity);
        } else {
            equal = false;
        }
        return equal;
    }

    /**
     * Two entity objects that have the same oids will have the same hash code.
     */
    @Override
    public int hashCode() {
        return oid.hashCode();
    }

    /**
     * Outputs entity. Used in testing. Meta handling is used.
     * 
     * @param title
     *            title
     */
    public void output(String title) {
        DomainModel model = (DomainModel) getModel();
        if (model != null) {
            ModelMeta modelMeta = model.getModelMeta();
            modelMeta.output(this, title);
        }
    }

    /**
     * Returns a string that represents this entity by using properties and/or
     * neighbors from unique combination (id). If there is no id set, entity oid
     * is returned.
     * 
     * @return entity as a string
     */
    @Override
    public String toString() {
        StringBuffer v = new StringBuffer();
        CombinationConfig uniqueConfig = getConceptConfig().getUniqueConfig();
        Iterator<String> neighborCodeListIterator = uniqueConfig.getNeighborCodeList().iterator();
        while (neighborCodeListIterator.hasNext()) {
            v.append(getNeighbor(neighborCodeListIterator.next()));
            if (neighborCodeListIterator.hasNext()) {
                v.append(", ");
            }
        }
        Iterator<String> propertyCodeListIterator = uniqueConfig.getPropertyCodeList().iterator();
        if (v.length() > 0 && propertyCodeListIterator.hasNext()) {
            v.append(" - ");
        }
        while (propertyCodeListIterator.hasNext()) {
            v.append(getProperty(propertyCodeListIterator.next()));
            if (propertyCodeListIterator.hasNext()) {
                v.append(", ");
            }
        }
        return v.length() == 0 ? oid.toString() : v.toString();
    }

    /**
     * Returns a string that represents this entity by using strings of
     * essential properties. If there are no essential properties toString()
     * invoked.
     * 
     * @return entity as a string of essential property string values
     */
    public String toEssentialPropertiesString() {
        String essentialPropertiesString = "";
        List<PropertyConfig> essentialPropertyConfigList = getConceptConfig().getPropertiesConfig()
                .getEssentialPropertyConfigList();
        if (essentialPropertyConfigList.isEmpty()) {
            essentialPropertiesString = toString();
        } else {
            for (PropertyConfig essentialPropertyConfig : essentialPropertyConfigList) {
                Object parentProperty = getProperty(essentialPropertyConfig.getCode());
                if (parentProperty != null) {
                    essentialPropertiesString = essentialPropertiesString + parentProperty.toString() + " ";
                } else {
                    essentialPropertiesString = toString();
                }
            }
        }
        return essentialPropertiesString;
    }

    /**
     * Compares two entities based on oids. If the result is less than 0 then
     * the first entity is less than the second, if it is equal to 0 they are
     * equal and if the result is greater than 0 then the first is greater than
     * the second.
     * 
     * @return compare integer
     */
    public int compareTo(T entity) {
        int result = oid.compareTo(entity.getOid());
        return result;
    }

    /**
     * Notifies observes.
     * 
     * @param arg
     *            arg
     */
    @Override
    public void notifyObservers(Object arg) {
        this.setChanged();
        super.notifyObservers(arg);
    }

}