/*
* Copyright (C) 2008-2009 by Michael Thiele & Claas Wilke (claaswilke@gmx.net)
* This file is part of the UML2 Meta Model of Dresden OCL2 for Eclipse. Dresden
* OCL2 for Eclipse is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version. Dresden OCL2 for Eclipse 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 Lesser General Public License for more details. You should have
* received a copy of the GNU Lesser General Public License along with Dresden
* OCL2 for Eclipse. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dresdenocl.metamodels.uml2.internal.model;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.osgi.util.NLS;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Type;
import org.dresdenocl.metamodels.uml2.UML2MetamodelPlugin;
import org.dresdenocl.model.IModel;
import org.dresdenocl.model.ModelAccessException;
import org.dresdenocl.model.ModelConstants;
import org.dresdenocl.model.base.AbstractModel;
import org.dresdenocl.model.metamodel.IMetamodel;
import org.dresdenocl.pivotmodel.Namespace;
import org.dresdenocl.pivotmodel.PivotModelFactory;
/**
* <p>
* If the root of the model is a single {@link org.eclipse.uml2.uml.Package}, a
* corresponding {@link UML2Namespace} adapter will be created. If there are
* several root packages, a new "virtual" root namespace will be created.
* </p>
*
* @author Michael Thiele
*
* @generated NOT
*/
public class UML2Model extends AbstractModel implements IModel {
/**
* <p>
* A {@link Logger} for this class.
* </p>
*
* @generated NOT
*/
private static final Logger LOGGER = UML2MetamodelPlugin
.getLogger(UML2Model.class);
/** The resource containing the corresponding UML2 model. */
private Resource resource;
/** The adapter for the top package of the associated UML2 model. */
private Namespace rootNamespace;
/** The {@link UML2AdapterFactory} of this {@link UML2Model}. */
private UML2AdapterFactory factory;
/**
* <p>
* Creates a new {@link UML2Model} adapting the given
* {@link org.eclipse.uml2.uml.Package}.
*
* @param resource
* The {@link Resource} containing the model.
*
* @generated NOT
*/
public UML2Model(Resource resource, IMetamodel metamodel) {
super(resource.getURI().toString(), metamodel);
/* Initialize. */
this.resource = resource;
}
/*
* (non-Javadoc)
*
* @see org.dresdenocl.model.IModel#dispose()
*/
public void dispose() {
/* Unload referenced resources and the resource. */
Set<Resource> resourcesToUnload = new HashSet<Resource>();
resourcesToUnload.add(this.resource);
for (EObject eObject : EcoreUtil.ExternalCrossReferencer.find(
this.resource).keySet()) {
resourcesToUnload.add(eObject.eResource());
}
// end for.
for (Resource resource : resourcesToUnload) {
if (resource != null && resource.isLoaded())
resource.unload();
// no else.
}
// end for.
/* Reset the root name space to avoid caching. */
this.rootNamespace = null;
/* Reset the factory as well. */
this.factory = null;
}
/**
* <p>
* This method lazily creates a {@link Namespace} adapter for the virtual
* root package in the associated UML2 model. Thus, any possible resource
* loading errors will not happen until this method is called for the first
* time.
* </p>
*
* @throws ModelAccessException
* if an error occurs when creating the adapter for the top
* namespace
*
* @see org.dresdenocl.model.IModel#getRootNamespace()
*
* @generated
*/
public Namespace getRootNamespace() throws ModelAccessException {
if (rootNamespace == null) {
rootNamespace = createRootNamespace();
}
return rootNamespace;
}
/**
* <p>
* Overridden to base equality check on the URI of the associated resource.
* </p>
*
* @see java.lang.Object#equals(java.lang.Object)
*
* @generated NOT
*/
@Override
public boolean equals(Object anObject) {
boolean result;
/* Check if the given Object is a UML2Model. */
if (anObject instanceof UML2Model) {
result = resource.getURI().equals(
((UML2Model) anObject).resource.getURI());
}
else {
result = false;
}
return result;
}
/**
* <p>
* Overridden to base the hash code on the URI of the associated resource.
* </p>
*
* @see java.lang.Object#hashCode()
*
* @generated NOT
*/
@Override
public int hashCode() {
return this.resource.getURI().hashCode();
}
/**
* <p>
* Returns a String representation of this {@link UML2Model}.
* </p>
*
* @see java.lang.Object#toString()
*
* @generated NOT
*/
@Override
public String toString() {
return this.resource.getURI().toString();
}
/**
* <p>
* Returns the {@link UML2AdapterFactory} of this {@link UML2Model}.</p<
*
* @return The {@link UML2AdapterFactory} of this {@link UML2Model}.
*/
public UML2AdapterFactory getFactory() {
return this.factory;
}
/**
* <p>
* Computes the {@link Package}s from referenced UML models of this
* {@link UML2Model} and adds them to the root {@link Namespace} as well.
* </p>
*/
private void addNamespacesForReferencedPackages() {
/* Iterate through all referenced EObjects. */
for (EObject eObject : EcoreUtil.ExternalCrossReferencer.find(resource)
.keySet()) {
EcoreUtil.resolveAll(eObject);
/* Check the containing package of each EClassifier. */
if (eObject instanceof Classifier) {
Classifier classifier;
classifier = (Classifier) eObject;
Package containerPackage;
containerPackage = classifier.getNearestPackage();
if (containerPackage == null
|| containerPackage instanceof Model
|| containerPackage instanceof Profile) {
// FIXME Claas: This will probably cause problems. Types do
// not know their right name space now if they should be
// located in the root name space directly. */
/* Adapt the type. */
org.dresdenocl.pivotmodel.Type importedType;
importedType = this.factory.createType(classifier);
/* Only add the type if it has not been added yet. */
if (!this.rootNamespace.getOwnedType().contains(
importedType)) {
this.rootNamespace.addType(importedType);
}
// no else.
}
else {
while (containerPackage.getNestingPackage() != null
&& !(containerPackage.getNestingPackage() instanceof Model)
&& !(containerPackage.getNestingPackage() instanceof Profile)) {
containerPackage = containerPackage.getNestingPackage();
}
/*
* Adapt the package. If a package with the same name
* already exists, the packages are merged.
*/
Namespace importedNamespace;
importedNamespace = this.factory.createNamespace(
containerPackage, this.rootNamespace);
/*
* Only add the name space if it has not been added yet (for
* merged name space, the name space has not to be added
* again).
*/
if (!this.rootNamespace.getNestedNamespace().contains(
importedNamespace)) {
this.rootNamespace
.addNestedNamespace(importedNamespace);
}
// no else.
}
}
/*
* Else check if the EObject is a package (can be imported via
* import statement in UML.
*/
else if (eObject instanceof Package) {
/* If the package is a model or a profile only add its content. */
if (eObject instanceof Model || eObject instanceof Profile) {
for (Package umlPackage : ((Package) eObject)
.getNestedPackages()) {
Namespace adaptedNamespace = this.factory
.createNamespace(umlPackage, this.rootNamespace);
if (adaptedNamespace != null
&& !this.rootNamespace.equals(adaptedNamespace) && !this.rootNamespace.getNestedNamespace()
.contains(adaptedNamespace))
this.rootNamespace
.addNestedNamespace(adaptedNamespace);
// no else.
}
// end for.
for (Type umlType : ((Package) eObject).getOwnedTypes()) {
org.dresdenocl.pivotmodel.Type adaptedType = this.factory
.createType(umlType);
if (adaptedType != null
&& !this.rootNamespace.getOwnedType().contains(
adaptedType))
this.rootNamespace.addType(adaptedType);
// no else.
}
// end for.
}
else {
Package importedPackage;
importedPackage = (Package) eObject;
if (importedPackage.getNestingPackage() == null
|| importedPackage.getNestingPackage() instanceof Model
|| importedPackage.getNestingPackage() instanceof Profile) {
/*
* Adapt the package. If a package with the same name
* already exists, the packages are merged.
*/
Namespace importedNamespace;
importedNamespace = this.factory.createNamespace(
importedPackage, this.rootNamespace);
/*
* Only add the name space if it has not been added yet
* (for merged name space, the name space has not to be
* added again).
*/
if (!this.rootNamespace.getNestedNamespace().contains(
importedNamespace)) {
this.rootNamespace
.addNestedNamespace(importedNamespace);
}
// no else.
}
// no else.
}
// end else.
}
// no else.
}
}
/**
* <p>
* Helper method that creates the adapter for the root namespace. If there
* is only one top-level namespace possible, then this method should just
* return the adapter for the top-level namespace, else it should create a
* new "virtual" root namespace.
* </p>
*
* @return A {@link Namespace} instance
*
* @throws ModelAccessException
* If an error occurs while loading the adapted UML2 model.
*
* @generated NOT
*/
private Namespace createRootNamespace() throws ModelAccessException {
if (this.rootNamespace == null) {
/** load the resource. */
if (!resource.isLoaded()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info(NLS.bind(
UML2ModelMessages.UML2Model_LoadingUML2Model,
resource.getURI()));
}
/* Try to load the resource. */
try {
resource.load(null);
}
catch (IOException e) {
throw new ModelAccessException(
"Error while loading resource from " + resource.getURI(), e); //$NON-NLS-1$
}
}
// no else.
/* Resolve all probably contained proxies of the resource. */
EcoreUtil.resolveAll(resource);
/* Get the root packages. */
List<EObject> rootPackages;
rootPackages = resource.getContents();
/* Create a new package to serve as the root package. */
this.rootNamespace = PivotModelFactory.eINSTANCE.createNamespace();
this.rootNamespace.setName(ModelConstants.ROOT_PACKAGE_NAME);
this.factory = new UML2AdapterFactory(this.rootNamespace);
/** Add all sub-packages and sub-types to the new root package. */
for (EObject eObject : rootPackages) {
/*
* Models should not be added themselves. Add their contained
* packages instead.
*/
if (eObject instanceof Model) {
for (Package umlPackage : ((Model) eObject)
.getNestedPackages()) {
Namespace adaptedNamespace = this.factory
.createNamespace(umlPackage, this.rootNamespace);
if (adaptedNamespace != null)
this.rootNamespace
.addNestedNamespace(adaptedNamespace);
// no else.
}
// end for.
for (Type umlType : ((Model) eObject).getOwnedTypes()) {
org.dresdenocl.pivotmodel.Type adaptedType = this.factory
.createType(umlType);
if (adaptedType != null)
this.rootNamespace.addType(adaptedType);
// no else.
}
// end for.
}
/*
* Profiles should not be added themselves. Add their contained
* packages instead.
*/
else if (eObject instanceof Profile) {
for (Package umlPackage : ((Profile) eObject)
.getNestedPackages()) {
Namespace adaptedNamespace = this.factory
.createNamespace(umlPackage, this.rootNamespace);
if (adaptedNamespace != null)
this.rootNamespace
.addNestedNamespace(adaptedNamespace);
// no else.
}
// end for.
for (Type umlType : ((Profile) eObject).getOwnedTypes()) {
org.dresdenocl.pivotmodel.Type adaptedType = this.factory
.createType(umlType);
if (adaptedType != null)
this.rootNamespace.addType(adaptedType);
// no else.
}
// end for.
}
else if (eObject instanceof Package) {
Namespace adaptedNamespace = this.factory.createNamespace(
(Package) eObject, this.rootNamespace);
if (adaptedNamespace != null)
this.rootNamespace.addNestedNamespace(adaptedNamespace);
// no else.
}
else if (eObject instanceof Type) {
org.dresdenocl.pivotmodel.Type adaptedType = this.factory
.createType((Type) eObject);
if (adaptedType != null)
this.rootNamespace.addType(adaptedType);
// no else.
}
// no else.
}
// end for.
this.addNamespacesForReferencedPackages();
}
// no else.
return this.rootNamespace;
}
}