org.modelibra.DomainModel.java Source code

Java tutorial

Introduction

Here is the source code for org.modelibra.DomainModel.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.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Observable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.modelibra.config.ConceptConfig;
import org.modelibra.config.ModelConfig;
import org.modelibra.exception.ConfigRuntimeException;
import org.modelibra.exception.ModelibraRuntimeException;
import org.modelibra.util.OutTester;

/**
 * <p>
 * A domain model can be accessed only through one or more entries. An entry is
 * an object of the IEntities type. The model is implemented as a map of
 * entries. An entry key is the concept configuration and the entry value is an
 * object of the IEntities type.
 * </p>
 * 
 * <p>
 * The model has a configuration that comes from an XML configuration file. The
 * model configuration contains a configuration of the model concepts. There are
 * three model configuration files: specific-domain-config.xml,
 * reusable-domain-config.xml and modelibra-domain-config.xml. Predefined domain
 * configurations are located in the modelibra-domian-config.xml. They can be
 * inherited by the specific domain configuration through the reusable domain
 * configuration. The reusable domain configuration may have user defined
 * concepts. A domain model cannot be created without at least one domain
 * configuration and one model configuration.
 * </p>
 * 
 * <p>
 * The model changes can be observed by an object of the Observer type. An
 * observer is used to persist domain model data. However, the model does not
 * have any knowledge of a persistence mechanism. The model changes are reported
 * to the observer only after the model is initialized (e.g., loaded from a
 * persistence store).
 * </p>
 * 
 * <p>
 * The model meta handling is done by the model property of the ModelMeta type.
 * An example of meta handling is a creation of the concept entry entities given
 * the full (completed with the package name) class name.
 * </p>
 * 
 * @author Dzenan Ridjanovic
 * @author Vedad Kirlic
 * @version 2009-02-10
 */
public class DomainModel extends Observable implements IDomainModel {

    private static final long serialVersionUID = 1020L;

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

    private ModelMeta modelMeta = new ModelMeta(this);

    private IDomain domain;

    private ModelConfig modelConfig;

    private IDomainModel referenceModel = this;

    private Map<ConceptConfig, IEntities<?>> entryMap = new HashMap<ConceptConfig, IEntities<?>>();

    private boolean initialized = false;

    /**
     * Constructs a domain model within the given domain.
     * 
     * @param domain
     *            domain
     */
    public DomainModel(IDomain domain) {
        this.domain = domain;
        if (domain == null) {
            throw new ModelibraRuntimeException("Domain.constructor -- domain is null.");
        }
        String modelCode = getClass().getSimpleName();
        modelConfig = domain.getDomainConfig().getModelConfig(modelCode);
        if (modelConfig == null) {
            String msg = "Model.constructor -- model code is not valid: " + domain.getDomainConfig().getCode() + "."
                    + modelCode;
            throw new ConfigRuntimeException(msg);
        }
        if (modelConfig.isAbstraction()) {
            String msg = "Model.constructor -- model is configured as an abstraction: "
                    + domain.getDomainConfig().getCode() + "." + modelConfig.getCode();
            throw new ConfigRuntimeException(msg);
        }
        if (domain.getDomainConfig().isDefaultConstruct()) {
            createEntries();
        }
        domain.getModels().add(this);
    }

    /**
     * Gets the domain.
     * 
     * @return domain
     */
    public IDomain getDomain() {
        return domain;
    }

    /**
     * Adds the entry.
     * 
     * @param entry
     *            entry entities
     */
    public void addEntry(IEntities<?> entry) {
        if (entry != null) {
            ConceptConfig conceptConfig = entry.getConceptConfig();
            if (conceptConfig != null) {
                IEntities<?> entryEntities = getEntry(conceptConfig);
                if (entryEntities == null) {
                    entryMap.put(entry.getConceptConfig(), entry);
                }
            }
        } else {
            String msg = "Model.addEntry -- entry is null.";
            throw new ModelibraRuntimeException(msg);
        }
    }

    /**
     * Replaces the entry. Sets new entry for conceptConfig key if there are
     * entry values set for that key.
     * 
     * @param entry
     *            entry entities
     */
    public void replaceEntry(IEntities<?> entry) {
        if (entry != null) {
            ConceptConfig conceptConfig = entry.getConceptConfig();
            if (conceptConfig != null) {
                IEntities<?> entryEntities = getEntry(conceptConfig);
                if (entryEntities != null) {
                    entryMap.put(entry.getConceptConfig(), entry);
                }
            }
        } else {
            String msg = "Model.replaceEntry -- entry is null.";
            throw new ModelibraRuntimeException(msg);
        }
    }

    /**
     * Creates an entry.
     * 
     * @param conceptConfig
     *            concept configuration
     * @return entry entities
     */
    private IEntities<?> createEntry(ConceptConfig conceptConfig) {
        IEntities<?> entry = null;
        if (conceptConfig.isAbstraction()) {
            String msg = "Model.createEntry -- concept is configured as an abstraction : "
                    + domain.getDomainConfig().getCode() + "." + modelConfig.getCode() + "."
                    + conceptConfig.getCode();
            throw new ConfigRuntimeException(msg);
        } else if (conceptConfig.isEntry()) {
            String entitiesClassName = conceptConfig.getEntitiesClass();
            getModelMeta().createEntities(entitiesClassName);
            // Entities constructor added the entry.
        } else {
            String msg = "Model.createEntry -- concept is not entry: " + domain.getDomainConfig().getCode() + "."
                    + modelConfig.getCode() + "." + conceptConfig.getCode();
            throw new ConfigRuntimeException(msg);
        }
        return entry;
    }

    /**
     * Creates entries.
     */
    private void createEntries() {
        for (ConceptConfig conceptConfig : modelConfig.getConceptsConfig()) {
            createEntry(conceptConfig);
        }
    }

    /**
     * Gets the model meta.
     * 
     * @return model meta
     */
    public ModelMeta getModelMeta() {
        return modelMeta;
    }

    /**
     * Sets to <code>true</code> if the domain model is initialized with the
     * model data. If the model is persistent, the setInitialized method should
     * be used only by the persistence mechanism to indicate that the loading of
     * the model is done.
     * 
     * @param initialized
     *            <code>true</code> if the domain model is initialized with the
     *            model data
     */
    public void setInitialized(boolean initialized) {
        if (initialized) {
            endInitialize();
        }
        this.initialized = initialized;
    }

    /**
     * Checks if the domain model is initialized with the model data.
     * 
     * @return <code>true</code> if the domain model is initialized with the
     *         model data
     */
    public boolean isInitialized() {
        return initialized;
    }

    /**
     * Allows to finish the initialization of the model. For example, after the
     * model is loaded, all external children are initialized as well. Called by
     * the setInitialized method.
     */
    protected void endInitialize() {
        Iterator<IEntities<?>> iterator = getEntryList().iterator();
        while (iterator.hasNext()) {
            IEntities<?> entities = iterator.next();
            getModelMeta().initializeExternalChildNeighbors(entities);
        }
    }

    /**
     * Checks if sessions will be used.
     * 
     * @return <code>true</code> if sessions will be used
     */
    public boolean isSession() {
        return getModelConfig().isSession();
    }

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

    /**
     * Sets the domain model configuration.
     * 
     * @param modelConfig
     *            domain model configuration
     */
    public void setModelConfig(ModelConfig modelConfig) {
        this.modelConfig = modelConfig;
    }

    /**
     * Gets the model configuration.
     * 
     * @return model configuration
     */
    public ModelConfig getModelConfig() {
        return modelConfig;
    }

    /**
     * Sets the reference (e.g., validation type) model.
     * 
     * @param referenceModel
     *            reference model
     */
    public void setReferenceModel(IDomainModel referenceModel) {
        this.referenceModel = referenceModel;
    }

    /**
     * Gets the reference (e.g., validation type) model.
     * 
     * @return reference model
     */
    public IDomainModel getReferenceModel() {
        return referenceModel;
    }

    /**
     * Gets the specific model entities.
     * 
     * @param entitiesCode
     *            entities code
     * @return entities
     */
    public IEntities<?> getSpecificModelEntities(String entitiesCode) {
        return (IEntities<?>) getModelMeta().getSpecificModelEntities(entitiesCode);
    }

    /**
     * Checks if the model is empty.
     * 
     * @return <code>true</code> if the model is empty
     */
    public boolean isEmpty() {
        for (Map.Entry<ConceptConfig, IEntities<?>> e : entryMap.entrySet()) {
            IEntities<?> entry = e.getValue();
            if (!entry.isEmpty()) {
                return false;
            }
        }
        return true;
    }

    /**
     * Gets the model entry entities for a given entry code.
     * 
     * @param entryCode
     *            entry concept entity code or entry concept entities code
     * @return entry entities
     */
    public IEntities<?> getEntry(String entryCode) {
        IEntities<?> entry = null;
        for (Map.Entry<ConceptConfig, IEntities<?>> e : entryMap.entrySet()) {
            ConceptConfig conceptConfig = e.getKey();
            if ((conceptConfig.getCode().equalsIgnoreCase(entryCode))
                    || (conceptConfig.getEntitiesCode().equalsIgnoreCase(entryCode))) {
                entry = (IEntities<?>) e.getValue();
                break;
            }
        }
        return entry;
    }

    /**
     * Gets the model entry entities for a given concept configuration.
     * 
     * @param conceptConfig
     *            concept configuration
     * @return entry entities
     */
    private IEntities<?> getEntry(ConceptConfig conceptConfig) {
        IEntities<?> entry = null;
        for (Map.Entry<ConceptConfig, IEntities<?>> e : entryMap.entrySet()) {
            ConceptConfig entryConceptConfig = e.getKey();
            if (entryConceptConfig.equals(conceptConfig)) {
                entry = (IEntities<?>) e.getValue();
                break;
            }
        }
        return entry;
    }

    /**
     * Gets the domain model list of entry entities.
     * 
     * @return domain model list of entry entities
     */
    public List<IEntities<?>> getEntryList() {
        List<IEntities<?>> entries = new ArrayList<IEntities<?>>();
        List<String> entryCodes = getEntryCodeList();
        for (String entryCode : entryCodes) {
            IEntities<?> entities = getEntry(entryCode);
            entries.add(entities);
        }
        return entries;
    }

    /**
     * Gets the domain model list of entry codes.
     * 
     * @return domain model list of entry codes
     */
    public List<String> getEntryCodeList() {
        List<String> entryCodes = new ArrayList<String>();
        for (Map.Entry<ConceptConfig, IEntities<?>> e : entryMap.entrySet()) {
            ConceptConfig conceptConfig = e.getKey();
            String conceptCode = conceptConfig.getCode();
            entryCodes.add(conceptCode);
        }
        Collections.sort(entryCodes);
        return entryCodes;
    }

    /**
     * Gets the domain model list of entry names (display names).
     * 
     * @return domain model list of entry names
     */
    public List<String> getEntryNameList() {
        List<String> entryNames = new ArrayList<String>();
        for (Map.Entry<ConceptConfig, IEntities<?>> e : entryMap.entrySet()) {
            ConceptConfig conceptConfig = e.getKey();
            String conceptName = conceptConfig.getConceptName();
            entryNames.add(conceptName);
        }
        Collections.sort(entryNames);
        return entryNames;
    }

    /**
     * Exports the base model to the taken model.
     * 
     * @param takenModel
     *            taken model
     * @param exportSensitive
     *            <code>true</code> if the sensitive information will be
     *            exported
     */
    public void export(IDomainModel takenModel, boolean sensitive) {
        ModelConfig baseModelConfig = getModelConfig();
        String baseModelCode = baseModelConfig.getCode();
        ModelConfig toModelConfig = takenModel.getModelConfig();
        String toModelCode = toModelConfig.getCode();
        log.info("--- Exporting " + baseModelCode + " to " + toModelCode + ". ---");
        List<IEntities<?>> baseModelEntryList = getEntryList();
        for (IEntities baseModelEntry : baseModelEntryList) {
            String baseModelEntryCode = baseModelEntry.getConceptConfig().getCode();
            IEntities toModelEntry = takenModel.getEntry(baseModelEntryCode);
            toModelEntry.getErrors().empty();
            baseModelEntry.export(toModelEntry, sensitive);
            List<String> errors = toModelEntry.getErrors().getErrorList();
            if (errors.size() > 0) {
                OutTester.outputCollection(errors, toModelEntry.getConceptConfig().getCode() + " Export Errors");
            }
        }
        log.info("--- Model exporting ends. ---");
    }

    /**
     * Synchronizes the returned model with the base model (adds and updates).
     * The returned model is a new version of the taken model.
     * 
     * @param takenModel
     *            taken model
     * @param returnedModel
     *            returned model
     * @param synchronizeSensitive
     *            <code>true</code> if the sensitive information will be
     *            synchronized
     */
    public void synchronize(IDomainModel takenModel, IDomainModel returnedModel, boolean sensitive) {
        ModelConfig baseModelConfig = getModelConfig();
        String baseModelCode = baseModelConfig.getCode();
        ModelConfig returnedModelConfig = returnedModel.getModelConfig();
        String returnedModelCode = returnedModelConfig.getCode();
        log.info("--- Synchronization of " + returnedModelCode + " to " + baseModelCode + " begins. ---");
        List<IEntities<?>> returnedModelEntryList = returnedModel.getEntryList();
        for (IEntities returnedModelEntry : returnedModelEntryList) {
            String returnedModelEntryCode = returnedModelEntry.getConceptConfig().getCode();
            IEntities takenModelEntry = takenModel.getEntry(returnedModelEntryCode);
            IEntities baseModelEntry = getEntry(returnedModelEntryCode);
            baseModelEntry.getErrors().empty();
            baseModelEntry.synchronize(takenModelEntry, returnedModelEntry, sensitive);
            List<String> errors = baseModelEntry.getErrors().getErrorList();
            if (errors.size() > 0) {
                OutTester.outputCollection(errors,
                        baseModelEntry.getConceptConfig().getCode() + " Integrate Errors");
            }
        }
        log.info("--- Model synchronization ends. ---");
    }

    /**
     * The base model is cleaned of entities that are removed from the returned
     * model (in comparison with the taken model).
     * 
     * @param takenModel
     *            taken model
     * @param returnedModel
     *            returned model
     */
    public void clean(IDomainModel takenModel, IDomainModel returnedModel) {
        ModelConfig baseModelConfig = getModelConfig();
        String baseModelCode = baseModelConfig.getCode();
        ModelConfig returnedModelConfig = returnedModel.getModelConfig();
        String returnedModelCode = returnedModelConfig.getCode();
        log.info("--- Cleaning of " + baseModelCode + " with respect to " + returnedModelCode + " begins. ---");
        List<IEntities<?>> returnedModelEntryList = returnedModel.getEntryList();
        for (IEntities returnedModelEntry : returnedModelEntryList) {
            String returnedModelEntryCode = returnedModelEntry.getConceptConfig().getCode();
            IEntities takenModelEntry = takenModel.getEntry(returnedModelEntryCode);
            IEntities baseModelEntry = getEntry(returnedModelEntryCode);
            baseModelEntry.getErrors().empty();
            baseModelEntry.clean(takenModelEntry, returnedModelEntry);
            List<String> errors = baseModelEntry.getErrors().getErrorList();
            if (errors.size() > 0) {
                OutTester.outputCollection(errors, baseModelEntry.getConceptConfig().getCode() + " Clean Errors");
            }
        }
        log.info("--- Model cleaning ends. ---");
    }

    /**
     * Gets a new session.
     * 
     * @return new session
     */
    public ModelSession getNewSession() {
        ModelSession session = null;
        if (isSession()) {
            session = new ModelSession(this);
        } else {
            String msg = "Model.getNewSession -- model session is configured as false: "
                    + domain.getDomainConfig().getCode() + "." + modelConfig.getCode();
            throw new ConfigRuntimeException(msg);
        }
        return session;
    }

    /**
     * Closes the model.
     */
    public void close() {

    }

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

}