/**
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright (C) 2007 Matthias Braeuer (braeuer.matthias@web.de). * All rights
* reserved. * * This work was done as a project at the Chair for Software
* Technology, * Dresden University Of Technology, Germany
* (http://st.inf.tu-dresden.de). * It is understood that any modification not
* identified as such is not * covered by the preceding statement. * * This work
* is free software; you can redistribute it and/or modify it * under the terms
* of the GNU Library General Public License as published * by the Free Software
* Foundation; either version 2 of the License, or * (at your option) any later
* version. * * This work is distributed in the hope that it will be useful, but
* WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public *
* License for more details. * * You should have received a copy of the GNU
* Library General Public License * along with this library; if not, you can
* view it online at * http://www.fsf.org/licensing/licenses/gpl.html. * * To
* submit a bug report, send a comment, or get the latest news on this *
* project, please visit the website: http://dresden-ocl.sourceforge.net. * For
* more information on OCL and related projects visit the OCL Portal: *
* http://st.inf.tu-dresden.de/ocl * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * *
*
* $Id$
*/
package org.dresdenocl.model.base;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dresdenocl.model.IModel;
import org.dresdenocl.model.IModelListener;
import org.dresdenocl.model.ModelAccessException;
import org.dresdenocl.model.ModelConstants;
import org.dresdenocl.model.ModelPlugin;
import org.dresdenocl.model.metamodel.IMetamodel;
import org.dresdenocl.pivotmodel.Constraint;
import org.dresdenocl.pivotmodel.Namespace;
import org.dresdenocl.pivotmodel.PivotModelFactory;
import org.dresdenocl.pivotmodel.PrimitiveType;
import org.dresdenocl.pivotmodel.PrimitiveTypeKind;
import org.dresdenocl.pivotmodel.Type;
import org.eclipse.emf.ecore.EObject;
/**
* <p>
* Abstract base implementation of the {@link IModel} interface.
* </p>
*
* @author Matthias Braeuer
*/
public abstract class AbstractModel implements IModel {
/** The {@link Logger} for this class. */
private static final Logger LOGGER = ModelPlugin
.getLogger(AbstractModel.class);
/** The {@link IModel}'s name as displayed to clients. */
private String displayName;
/**
* Indicates whether or not this {@link IModel} changed since notifying its
* {@link IModelListener}s for the last time.
*/
private boolean hasChanged = false;
/** The {@link IModelListener} of this {@link IModel}. */
private Set<IModelListener> listeners = new HashSet<IModelListener>();
/** The {@link IMetamodel} of this {@link IModel}. */
private IMetamodel metamodel;
private Map<EObject, EObject> m_allMappings =
new IdentityHashMap<EObject, EObject>();
public Map<EObject, EObject> getAllMappings() {
return m_allMappings;
}
public boolean addAllMappings(final Map<EObject, EObject> mappings) {
if (mappings == null) {
m_allMappings = new IdentityHashMap<EObject, EObject>();
}
else {
m_allMappings = new IdentityHashMap<EObject, EObject>(mappings);
}
return true;
}
/**
* <p>
* Constructor to be called by subclasses. The <code>displayName</code> is a
* name that should be used to identify this model in a graphical user
* interface. This may be the file name or another identifier.
* </p>
*
* @param displayName
* A name for this {@link IModel}.
* @param metamodel
* The {@link IMetamodel} for this {@link IModel}.
*/
protected AbstractModel(String displayName, IMetamodel metamodel) {
/* Use an empty string if display name is null. */
this.displayName = StringUtils.defaultString(displayName);
if (metamodel == null) {
throw new IllegalArgumentException(
"The metamodel reference must not be null."); //$NON-NLS-1$
}
// no else.
this.metamodel = metamodel;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.model.IModel#addListener(org.dresdenocl
* .pivot.modelbus.model.IModelListener)
*/
public boolean addListener(IModelListener listener) {
if (listener == null) {
throw new IllegalArgumentException("Parameter listener must not be null.");
}
// no else.
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "addListener(";
msg += "listener = " + listener; //$NON-NLS-1$ //$NON-NLS-2$
msg += ") - enter";
LOGGER.debug(msg);
}
// no else.
boolean result;
result = this.listeners.add(listener);
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER
.debug("addListener(IModelListener) - exit - return value=" + result); //$NON-NLS-1$
}
// no else.
return result;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.IModel#findNamespace(java.util.List)
*/
public Namespace findNamespace(List<String> pathName)
throws ModelAccessException {
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "findNamespace(";
msg += "pathName = " + pathName;
msg += ") - enter";
LOGGER.debug(msg); //$NON-NLS-1$ //$NON-NLS-2$
}
// no else.
/* The path name must not be null. */
if (pathName == null) {
throw new IllegalArgumentException("The path name must not be null."); //$NON-NLS-1$
}
// no else.
/* Clone the path name to avoid side effects. */
pathName = new ArrayList<String>(pathName);
/* By default search in the root name space. */
Namespace namespace = getRootNamespace();
if (pathName.size() > 0) {
/* Probably remove the root package from the path name. */
if (pathName.get(0).equals(ModelConstants.ROOT_PACKAGE_NAME)) {
pathName.remove(0);
}
// no else.
/* Iterate through the name space hierarchy. */
for (String namespaceName : pathName) {
/* Search for the next nested name space. */
namespace = namespace.lookupNamespace(namespaceName);
/* Probably cancel the search. */
if (namespace == null) {
break;
}
// no else.
}
// end for.
}
// no else (path name is empty).
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "findNamespace() - exit - ";
msg += "return value = " + namespace;
LOGGER.debug(msg); //$NON-NLS-1$
}
// no else.
return namespace;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.IModel#findType(java.util.List)
*/
public Type findType(List<String> pathName) throws ModelAccessException {
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("findType(pathName=" + pathName + ") - enter"); //$NON-NLS-1$ //$NON-NLS-2$
}
// no else.
/* Avoid illegal parameters. */
if (pathName == null || pathName.size() == 0) {
throw new IllegalArgumentException(
"The path name must not be null or empty."); //$NON-NLS-1$
}
// no else.
Type result = null;
/* Copy list to avoid side effects. */
pathName = new ArrayList<String>(pathName);
boolean isAbsolutePath = false;
/* Probably remove the root package from the pathName. */
if (pathName.get(0).equals(ModelConstants.ROOT_PACKAGE_NAME)) {
pathName.remove(0);
isAbsolutePath = true;
if (pathName.size() == 0) {
throw new IllegalArgumentException(
"The path name must not be the root package name."); //$NON-NLS-1$
}
// no else.
}
// no else.
List<Type> foundTypes;
foundTypes = new ArrayList<Type>();
/* Search for Types that match this path name. */
foundTypes.addAll(this.findTypeHere(this.getRootNamespace(), pathName,
!isAbsolutePath));
/* Check if more than one Type was found. */
if (foundTypes.size() > 1) {
String msg;
msg =
"More than one type with path name " + pathName + " were found: " + foundTypes; //$NON-NLS-1$//$NON-NLS-2$
LOGGER.warn(msg);
result = null;
}
/* Else check if at least one type has been found. */
else if (foundTypes.size() == 0) {
/* If the path has only one element check for a primitive type. */
if (pathName.size() == 1) {
String primitiveName;
PrimitiveType primitiveType;
primitiveName = pathName.get(0);
for (PrimitiveTypeKind aKind : PrimitiveTypeKind.VALUES) {
if (primitiveName.equals(aKind.getName())) {
primitiveType = PivotModelFactory.eINSTANCE.createPrimitiveType();
primitiveType.setKind(aKind);
primitiveType.setName(aKind.getName());
foundTypes.add(primitiveType);
}
// no else.
}
// end for.
}
// no else.
if (foundTypes.size() == 0) {
String msg;
msg =
"Type with path name " + pathName + " was not found: " + foundTypes; //$NON-NLS-1$//$NON-NLS-2$
LOGGER.warn(msg);
result = null;
}
else {
result = foundTypes.get(0);
}
}
/* Else return the found type. */
else {
result = foundTypes.get(0);
}
// end else.
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("findType() - exit - return value=" + result); //$NON-NLS-1$
}
// no else.
return result;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.model.IModel#getConstraints()
*/
public Collection<Constraint> getConstraints() throws ModelAccessException {
return this.getRootNamespace().getOwnedAndNestedRules();
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.IModel#getDisplayName()
*/
public String getDisplayName() {
return this.displayName;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.IModel#getMetamodel()
*/
public IMetamodel getMetamodel() {
return this.metamodel;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.model.IModel#hasChanged()
*/
public boolean hasChanged() {
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "hasChanged() - enter";
LOGGER.debug(msg);
}
// no else.
boolean result;
result = this.hasChanged;
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("hasChanged() - exit - return value=" + result); //$NON-NLS-1$
}
// no else.
return result;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.model.IModel#notifiyListeners()
*/
public synchronized boolean notifiyListeners() {
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "notifiyListeners() - enter";
LOGGER.debug(msg);
}
// no else.
boolean result;
result = this.hasChanged;
if (result) {
for (IModelListener listener : this.listeners) {
listener.modelChanged(this);
}
// end for.
this.hasChanged = false;
}
// no else.
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("notifiyListeners() - exit - return value=" + result); //$NON-NLS-1$
}
// no else.
return result;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.model.IModel#removeAllConstraints()
*/
public boolean removeAllConstraints() throws IllegalArgumentException,
ModelAccessException {
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "removeAllConstraints() - enter";
LOGGER.debug(msg);
}
// no else.
boolean result;
if (this.getConstraints().size() > 0) {
result = this.getRootNamespace().removeOwnedAndNestedRules();
}
else {
result = false;
}
if (result) {
this.setChanged();
}
// no else.
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("removeAllConstraints() - exit - return value=" + result); //$NON-NLS-1$
}
// no else.
return result;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.model.IModel#removeConstraints(java.util
* .Collection)
*/
public boolean removeConstraints(Collection<Constraint> constraints)
throws IllegalArgumentException, ModelAccessException {
if (constraints == null) {
throw new IllegalArgumentException(
"Parameter 'constraints' must not be null.");
}
// no else.
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "removeConstraints(";
msg += "constraints = " + constraints; //$NON-NLS-1$ //$NON-NLS-2$
msg += ") - enter";
LOGGER.debug(msg);
}
// no else.
boolean result;
result =
this.getRootNamespace().removeOwnedAndNestedRules(
new ArrayList<Constraint>(constraints));
if (result) {
this.setChanged();
}
// no else.
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("removeConstraints() - exit"); //$NON-NLS-1$
}
// no else.
return result;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.model.IModel#removeListener(tudresden.
* ocl20.pivot.modelbus.model.IModelListener)
*/
public boolean removeListener(IModelListener listener) {
if (listener == null) {
throw new IllegalArgumentException("Parameter listener must not be null.");
}
// no else.
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "removeListener(";
msg += "listener = " + listener; //$NON-NLS-1$ //$NON-NLS-2$
msg += ") - enter";
LOGGER.debug(msg);
}
// no else.
boolean result;
result = this.listeners.remove(listener);
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER
.debug("removeListener(IModelListener) - exit - return value=" + result); //$NON-NLS-1$
}
// no else.
return result;
}
/*
* (non-Javadoc)
* @see org.dresdenocl.modelbus.model.IModel#setChanged()
*/
public void setChanged() {
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "setChanged() - enter";
LOGGER.debug(msg);
}
// no else.
this.hasChanged = true;
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("setChanged() - exit"); //$NON-NLS-1$
}
// no else.
}
/**
* <p>
* A helper method that recursively looks for a {@link Type} with the given
* path name in the given {@link Namespace}.
* </p>
*
* @param namespace
* The {@link Namespace} to start the search in.
* @param pathName
* The path name to look for.
* @param searchAllNestedNamespaces
* Indicates whether a recursive search in all nested
* {@link Namespace}s with the full path name is required (for
* non-fully-qualified path names).
*
* @return A {@link List} containing all {@link Type}s found matching to the
* given pathName.
*/
private List<Type> findTypeHere(Namespace namespace, List<String> pathName,
boolean searchAllNestedNamespaces) {
/* Probably log the entry into this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "findTypeHere(";
msg += "namespace = " + namespace; //$NON-NLS-1$ //$NON-NLS-2$
msg += ", pathName = " + pathName; //$NON-NLS-1$ //$NON-NLS-2$
msg += ", searchAllNestedNamespaces = " + searchAllNestedNamespaces; //$NON-NLS-1$ //$NON-NLS-2$
msg += ") - enter";
LOGGER.debug(msg);
}
// no else.
List<Type> result;
result = new LinkedList<Type>();
/*
* Search in all nested name spaces with the given path name (in case it was
* not fully qualified).
*/
if (searchAllNestedNamespaces) {
for (Namespace nestedNamespace : namespace.getNestedNamespace()) {
result.addAll(findTypeHere(nestedNamespace, pathName, true));
}
}
// no else.
/* Get the first path segment. */
String firstPathSegment = pathName.get(0);
/* Look for a type in this name space if no more name segments left. */
if (pathName.size() == 1) {
Type aType;
aType = namespace.lookupType(firstPathSegment);
if (aType != null) {
result.add(aType);
}
// no else.
}
/* Else recursively look in nested name spaces. */
else {
namespace = namespace.lookupNamespace(firstPathSegment);
if (namespace != null) {
result.addAll(findTypeHere(namespace,
pathName.subList(1, pathName.size()), false));
}
}
/* Probably log the exit from this method. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("findTypeHere() - exit - return value=" + result); //$NON-NLS-1$
}
// no else.
return result;
}
}