/**
 * File:    Palette.java
 * Created: 13.03.2006
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Markus Bauer, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *    Marc Blachon, Bull SAS, Grenoble, France
 *      - getting ImageFactory from factoryRegistry
 *******************************************************************************/

package org.eclipse.jwt.we.editors.palette;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.provider.IItemLabelProvider;
import org.eclipse.gef.palette.CombinedTemplateCreationEntry;
import org.eclipse.gef.palette.ConnectionCreationToolEntry;
import org.eclipse.gef.palette.MarqueeToolEntry;
import org.eclipse.gef.palette.PaletteContainer;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.palette.PaletteEntry;
import org.eclipse.gef.palette.PaletteGroup;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.palette.PaletteSeparator;
import org.eclipse.gef.palette.PanningSelectionToolEntry;
import org.eclipse.gef.palette.ToolEntry;
import org.eclipse.gef.requests.CreationFactory;
import org.eclipse.gef.tools.CreationTool;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jwt.meta.model.application.Application;
import org.eclipse.jwt.meta.model.application.ApplicationPackage;
import org.eclipse.jwt.meta.model.application.WebServiceApplication;
import org.eclipse.jwt.meta.model.core.CorePackage;
import org.eclipse.jwt.meta.model.core.Package;
import org.eclipse.jwt.meta.model.core.PackageableElement;
import org.eclipse.jwt.meta.model.core.ReferenceableElement;
import org.eclipse.jwt.meta.model.data.Data;
import org.eclipse.jwt.meta.model.data.DataPackage;
import org.eclipse.jwt.meta.model.events.Event;
import org.eclipse.jwt.meta.model.events.EventsPackage;
import org.eclipse.jwt.meta.model.organisations.OrganisationsPackage;
import org.eclipse.jwt.meta.model.organisations.Role;
import org.eclipse.jwt.meta.model.processes.Activity;
import org.eclipse.jwt.meta.model.processes.ProcessesPackage;
import org.eclipse.jwt.we.Plugin;
import org.eclipse.jwt.we.PluginProperties;
import org.eclipse.jwt.we.editors.WEEditor;
import org.eclipse.jwt.we.figures.internal.ImageFactory;
import org.eclipse.jwt.we.figures.internal.ScalingImageDescriptor;
import org.eclipse.jwt.we.misc.factories.CreateReferenceFactory;
import org.eclipse.jwt.we.misc.factories.EcoreCopyFactory;
import org.eclipse.jwt.we.misc.factories.EcoreFactory;
import org.eclipse.jwt.we.misc.logging.Logger;
import org.eclipse.jwt.we.misc.views.Views;
import org.eclipse.jwt.we.model.view.Reference;
import org.eclipse.jwt.we.model.view.ViewPackage;
import org.eclipse.jwt.we.parts.EditPartAdapter;
import org.eclipse.swt.graphics.Image;

/**
 * The palette of the {@link WEEditor}.
 * 
 * <p>
 * The palette contain static and dynamic groups. The static groups will always
 * contain the same basic tools. The dynamic groups contain tools based on the
 * current state of the model.
 * </p>
 * 
 * <p>
 * The static entries are used to create new elements.
 * </p>
 * 
 * <p>
 * The dynamic entries are used to copy or link the model elements into the
 * editor. The entries are grouped by their packages. There exists one subentry
 * for each package. The package may be <code>null</code> and will then be
 * represented as a default package. Each package contains a list of model
 * elements. The model elements are then members of the package.
 * </p>
 * 
 * @version $Id: Palette.java,v 1.14 2009-12-02 12:18:05 chsaad Exp $
 * @author Markus Bauer (markusbauer@users.sourceforge.net), Programming
 *         distributed Systems Lab, University of Augsburg, Germany,
 *         www.ds-lab.org
 */
public class Palette extends PaletteRoot implements Adapter
{

	/**
	 * The WE editor.
	 */
	private WEEditor weeditor;

	/**
	 * Classes of which to create dynamic groups.
	 */
	public static final Class[] PALETTE_DYNAMIC_GROUPS = new Class[]
	{ Activity.class, Application.class, WebServiceApplication.class, Role.class,
			Data.class, Event.class };

	/**
	 * The dynamic elements.
	 */
	private List<EObject> dynamicelements = new ArrayList<EObject>();

	/**
	 * Size for small icons in the palette.
	 */
	public static final Dimension ICON_SMALL_SIZE = ImageFactory.MODEL_TYPE_IMAGE_SIZE;

	/**
	 * Size for large icons in the palette.
	 */
	public static final Dimension ICON_LARGE_SIZE = new Dimension(24, 24);

	/**
	 * A Logger.
	 */
	private static final Logger logger = Logger.getLogger(Palette.class);

	/**
	 * The ResourceSet that contains the model.
	 */
	private ResourceSet resourceSet;

	/**
	 * AdapterFactory to adapt the model elements.
	 */
	private AdapterFactory adapterFactory;

	private PaletteGroup toolGroup = null;

	private PaletteDrawer activityGroup = null;

	private PaletteDrawer packageableElementsGroup = null;


	/**
	 * Creates a new palette.
	 * 
	 * @param resourceSet
	 *            The ResourceSet that contains the model.
	 * @param adapterFactory
	 *            AdapterFactory to adapt the model elements.
	 */
	public Palette(WEEditor weeditor, ResourceSet resourceSet,
			AdapterFactory adapterFactory)
	{
		createStaticGroups();
		// createDynamicGroups();
		setAdapterFactory(adapterFactory);
		setResourceSet(resourceSet);
		this.weeditor = weeditor;
	}


	/**
	 * @return Returns the resourceSet.
	 */
	public ResourceSet getResourceSet()
	{
		return resourceSet;
	}


	/**
	 * @param newResourceSet
	 *            The resourceSet to set.
	 */
	public void setResourceSet(ResourceSet newResourceSet)
	{
		if (resourceSet == newResourceSet)
		{
			return;
		}

		if (resourceSet != null)
		{
			resourceSet.eAdapters().remove(this);
		}

		resourceSet = newResourceSet;

		resourceSet.eAdapters().add(this);

		for (Iterator i = resourceSet.getResources().iterator(); i.hasNext();)
		{
			addResource((Resource) i.next());
		}
	}


	/**
	 * @return Returns the adapterFactory.
	 */
	public AdapterFactory getAdapterFactory()
	{
		return adapterFactory;
	}


	/**
	 * @param adapterFactory
	 *            The adapterFactory to set.
	 */
	public void setAdapterFactory(AdapterFactory adapterFactory)
	{
		this.adapterFactory = adapterFactory;
	}


	/**
	 * Returns an id for a model element. These ids are used to find the palette
	 * entries for a model element.
	 * 
	 * This method is based on {@link EcoreUtil#getID(EObject)}. It had to be
	 * changed because the ID must be the same even when the model element is
	 * removed from its container.
	 * 
	 * @param model
	 *            The model element.
	 * @return The id of the model.
	 */
	private String getId(EObject model)
	{
		if (model == null)
		{
			return "null"; //$NON-NLS-1$
		}

		StringBuffer result = new StringBuffer(model.getClass().getName());
		EClass eClass = model.eClass();
		if (eClass.getInstanceClassName() == null)
		{
			result.append('/');
			result.append(eClass.getEPackage().getNsURI());
			result.append('#');
			result.append(eClass.getName());
		}
		result.append('@');
		result.append(Integer.toHexString(model.hashCode()));

		return result.toString();
	}


	/**
	 * Returns the PaletteContainer that contains the given model types.
	 * 
	 * @param modelType
	 *            The type of the model.
	 * @return The corresponding PaletteContainer.
	 */
	private PaletteContainer getDynamicGroup(Class modelType)
	{

		boolean valid = false;
		for (Class group : PALETTE_DYNAMIC_GROUPS)
		{
			if (modelType == group)
			{
				valid = true;
				break;
			}
		}

		if (!valid)
		{
			return null;
		}

		return this;

		// for (Iterator i = getChildren().iterator(); i.hasNext();)
		// {
		// Object entry = i.next();
		//
		// if (entry instanceof PaletteContainer)
		// {
		// PaletteContainer group = (PaletteContainer) entry;
		// if (modelType.getName().equals(group.getId()))
		// {
		// return group;
		// }
		// }
		// }
		//
		// return null;
	}


	/**
	 * Returns the PaletteEntry that contains the elements of a package.
	 * 
	 * @param dynamicGroup
	 *            The group that represents the model type.
	 * @param packageModel
	 *            The package. May be <code>null</code>.
	 * @return The PaletteEntry.
	 */
	private PaletteContainer getPackageEntry(PaletteContainer dynamicGroup,
			Package packageModel)
	{
		if (dynamicGroup == null)
		{
			return null;
		}

		String id = getId(packageModel);
		for (Iterator i = dynamicGroup.getChildren().iterator(); i.hasNext();)
		{
			Object entry = i.next();

			if (entry instanceof PaletteContainer)
			{
				PaletteContainer container = (PaletteContainer) entry;

				if (id.equals(container.getId()))
				{
					return container;
				}
			}
		}

		return null;
	}


	/**
	 * Returns the PaletteEntry that represents a model element.
	 * 
	 * @param packageEntry
	 *            The package entry that contains the model entry.
	 * @param model
	 *            The model.
	 * @return The entry of the model.
	 */
	private PaletteEntry getDynamicModelEntry(PaletteContainer packageEntry, EObject model)
	{
		if (packageEntry == null)
		{
			return null;
		}

		String id = getId(model);
		for (Iterator i = packageEntry.getChildren().iterator(); i.hasNext();)
		{
			PaletteEntry entry = (PaletteEntry) i.next();
			if (id.equals(entry.getId()))
			{
				return entry;
			}
		}

		return null;
	}


	/**
	 * Forces a refresh of the dynamic groups. Does this by first removing all
	 * elements and then readding all.
	 */
	public void refreshDynamicGroups()
	{
		try
		{
			List<EObject> buffer = new ArrayList<EObject>();
			for (EObject eb : dynamicelements)
			{
				buffer.add(eb);
			}
			for (EObject eb : buffer)
			{
				Package p = getPackage(eb);
				removeModel(eb, p);
			}
			for (EObject eb : buffer)
			{
				addModel(eb);
			}
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
		}
	}


	/**
	 * Create tool entries for static groups.
	 * 
	 * It creates a tool group with selection and marquee tools, a group with
	 * tools to create activity elements and a group for PackageableElements
	 */
	public void createStaticGroups()
	{
		if (toolGroup == null)
		{
			toolGroup = new PaletteGroup(PluginProperties.palette_ToolsGroup_name);
			// Add a selection tool to the group
			ToolEntry tool = new PanningSelectionToolEntry();
			toolGroup.add(tool);
			setDefaultEntry(tool);
			// Add a marquee tool to the group
			toolGroup.add(new MarqueeToolEntry());
		}

		if (activityGroup == null)
		{
			activityGroup = new PaletteDrawer(
					PluginProperties.palette_ActivityElementsGroups_name);
		}
		activityGroup.getChildren().clear();

		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.ACTION);
		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.INITIAL_NODE);
		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.FINAL_NODE);
		addPaletteDrawer(activityGroup, EventsPackage.Literals.EVENT);
		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.FORK_NODE);
		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.JOIN_NODE);
		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.DECISION_NODE);
		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.MERGE_NODE);
		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.GUARD);
		activityGroup.add(new PaletteSeparator());
		addPaletteDrawer(activityGroup,
				ProcessesPackage.Literals.STRUCTURED_ACTIVITY_NODE);
		addPaletteDrawer(activityGroup, ProcessesPackage.Literals.ACTIVITY_LINK_NODE);
		activityGroup.add(new PaletteSeparator());
		addPaletteConnectionDrawer(activityGroup, ProcessesPackage.Literals.ACTIVITY_EDGE);
		addPaletteConnectionDrawer(activityGroup, ViewPackage.Literals.REFERENCE_EDGE);

		if (packageableElementsGroup == null)
		{
			packageableElementsGroup = new PaletteDrawer(
					PluginProperties.palette_PackageableElementsGroups_name);
			packageableElementsGroup.setInitialState(PaletteDrawer.INITIAL_STATE_CLOSED);
		}
		packageableElementsGroup.getChildren().clear();

		addPaletteDrawer(packageableElementsGroup,
				ApplicationPackage.Literals.APPLICATION);
		addPaletteDrawer(packageableElementsGroup,
				ApplicationPackage.Literals.WEB_SERVICE_APPLICATION);
		addPaletteDrawer(packageableElementsGroup, OrganisationsPackage.Literals.ROLE);
		addPaletteDrawer(packageableElementsGroup, DataPackage.Literals.DATA);
		packageableElementsGroup.add(new PaletteSeparator());

		this.remove(toolGroup);
		this.remove(activityGroup);
		this.remove(packageableElementsGroup);
		add(toolGroup);
		add(activityGroup);
		add(packageableElementsGroup);
	}


	/**
	 * Adds an object to a palettedrawer, if the object is allowed to be shown
	 * 
	 * @param pd
	 *            The palettedrawer
	 * @param obj
	 *            The object to be added
	 */
	private void addPaletteDrawer(PaletteDrawer pd, EClass obj)
	{
		if (Views.getInstance().displayObject(obj))
		{
			pd.add(createCreationToolEntry(obj));
		}
	}


	/**
	 * Adds an connection object to a palettedrawer, if the object is allowed to
	 * be shown
	 * 
	 * @param pd
	 *            The palettedrawer
	 * @param obj
	 *            The object to be added
	 */
	private void addPaletteConnectionDrawer(PaletteDrawer pd, EClass obj)
	{
		if (Views.getInstance().displayObject(obj))
		{
			pd.add(createConnectionCreationToolEntry(obj));
		}
	}


	/**
	 * Adds a new resource to the palette.
	 * 
	 * @param resource
	 *            The resource.
	 */
	private void addResource(Resource resource)
	{
		resource.eAdapters().add(this);

		for (Iterator i = resource.getContents().iterator(); i.hasNext();)
		{
			addModel((EObject) i.next());
		}
	}


	/**
	 * Removes a resource from the palette.
	 * 
	 * @param resource
	 *            The resource.
	 */
	private void removeResource(Resource resource)
	{
		resource.eAdapters().remove(this);

		for (Iterator i = resource.getContents().iterator(); i.hasNext();)
		{
			removeModel((EObject) i.next(), null);
		}
	}


	/**
	 * Adds a new model element to the palette.
	 * 
	 * @param model
	 *            The model.
	 */
	private void addModel(EObject model)
	{
		if (model == null || !Views.getInstance().displayObject(model))
		{
			return;
		}
		PaletteContainer group = getDynamicGroup(model.eClass().getInstanceClass());

		if (!dynamicelements.contains(model))
			dynamicelements.add(model);

		if (group != null)
		{
			Package packageModel = getPackage(model);
			PaletteContainer packageEntry = getPackageEntry(group, packageModel);

			if (packageEntry == null)
			{
				packageEntry = createPackageEntry(packageModel);
				group.add(packageEntry);
			}
			ToolEntry te = createAddToolEntry(model);
			if (getDynamicModelEntry(packageEntry, model) == null)
			{
				// add package entry to bottom
				packageEntry.add(te);

				// get the class type of the new entry
				Class teClass = null;
				if (((CreationFactory) ((CombinedTemplateCreationEntry) te).getTemplate())
						.getObjectType() == Reference.class)
				{
					teClass = ((Reference) ((CreationFactory) ((CombinedTemplateCreationEntry) te)
							.getTemplate()).getNewObject()).getReference().getClass();
				}
				else
				{
					teClass = (Class) ((CreationFactory) ((CombinedTemplateCreationEntry) te)
							.getTemplate()).getObjectType();
				}

				// move new package entry upwards to entries of the same type
				for (int i = packageEntry.getChildren().size() - 2; i >= 0; i--)
				{
					// get the class type of the current entry
					Class currentClass = null;
					if (((CreationFactory) ((CombinedTemplateCreationEntry) packageEntry
							.getChildren().get(i)).getTemplate()).getObjectType() == Reference.class)
					{
						currentClass = ((Reference) ((CreationFactory) ((CombinedTemplateCreationEntry) packageEntry
								.getChildren().get(i)).getTemplate()).getNewObject())
								.getReference().getClass();
					}
					else
					{
						currentClass = (Class) ((CreationFactory) ((CombinedTemplateCreationEntry) packageEntry
								.getChildren().get(i)).getTemplate()).getObjectType();
					}

					// if an old entry of the same type was found, move the new
					// entry
					// upwards
					if (currentClass == teClass)
					{
						for (int j = packageEntry.getChildren().size() - 1; j > i + 1; j--)
						{
							packageEntry.moveUp(te);
						}
						break;
					}
				}
			}
		}

		model.eAdapters().add(this);

		if (!model.eAdapters().contains(this))
		{
			model.eAdapters().add(this);
		}

		for (Iterator i = model.eContents().iterator(); i.hasNext();)
		{
			addModel((EObject) i.next());
		}
	}


	/**
	 * Removes a model element from the palette.
	 * 
	 * @param model
	 *            The model.
	 * @param oldPackage
	 *            If the package was changed this contains the old package.
	 */
	private void removeModel(EObject model, Package oldPackage)
	{

		if (model == null)
		{
			return;
		}

		if (dynamicelements.contains(model))
		{
			dynamicelements.remove(model);
		}
		PaletteContainer group = getDynamicGroup(model.eClass().getInstanceClass());

		if (group != null)
		{
			Package packageModel;
			if (oldPackage == null)
			{
				packageModel = getPackage(model);
			}
			else
			{
				packageModel = oldPackage;
			}

			PaletteContainer packageEntry = getPackageEntry(group, packageModel);

			if (packageEntry != null)
			{
				packageEntry.remove(getDynamicModelEntry(packageEntry, model));

				if (packageEntry.getChildren().isEmpty())
				{
					group.remove(packageEntry);
				}
			}
		}
		model.eAdapters().remove(this);
		for (Iterator i = model.eContents().iterator(); i.hasNext();)
		{
			removeModel((EObject) i.next(), null);
		}
	}


	/**
	 * Update an existing entry of a model element to reflect its changed
	 * values.
	 * 
	 * @param model
	 *            The model element.
	 */
	private void updateModel(EObject model)
	{
		if (model == null)
		{
			return;
		}
		PaletteContainer group = getDynamicGroup(model.eClass().getInstanceClass());

		if (group != null)
		{
			Package packageModel = getPackage(model);

			PaletteContainer packageEntry = getPackageEntry(group, packageModel);
			PaletteEntry dynamicModelEntry = getDynamicModelEntry(packageEntry, model);

			// bugfix: only if we found a corresponding package entry and model
			// entry
			if (packageEntry != null && dynamicModelEntry != null)
			{
				updatePackageEntry(packageEntry, packageModel);
				updateAddToolEntry(dynamicModelEntry, model);
			}
		}

		for (Iterator i = model.eContents().iterator(); i.hasNext();)
		{
			updateModel((EObject) i.next());
		}
	}


	/**
	 * Creates a new entry for a package.
	 * 
	 * @param packageModel
	 *            The package.
	 * @return The entry.
	 */
	private PaletteContainer createPackageEntry(Package packageModel)
	{
		PaletteDrawer packageEntry = new PaletteDrawer(""); //$NON-NLS-1$
		packageEntry.setId(getId(packageModel));
		packageEntry.setInitialState(PaletteDrawer.INITIAL_STATE_CLOSED);

		updatePackageEntry(packageEntry, packageModel);
		return packageEntry;
	}


	/**
	 * Updates the values of a package entry.
	 * 
	 * @param packageEntry
	 *            The entry of the package.
	 * @param packageModel
	 *            The package.
	 */
	private void updatePackageEntry(PaletteContainer packageEntry, Package packageModel)
	{
		ImageDescriptor smallIcon = null;
		ImageDescriptor largeIcon = null;

		// ask adapter for name and icon
		if (adapterFactory.isFactoryForType(IItemLabelProvider.class)
				&& packageModel != null)
		{
			IItemLabelProvider labelProvider = (IItemLabelProvider) adapterFactory.adapt(
					packageModel, IItemLabelProvider.class);

			smallIcon = new ScalingImageDescriptor(
					(ImageDescriptor) labelProvider.getImage(packageModel),
					ICON_SMALL_SIZE);
			largeIcon = new ScalingImageDescriptor(
					(ImageDescriptor) labelProvider.getImage(packageModel),
					ICON_LARGE_SIZE);

			if (!createsValidImage(smallIcon))
			{
				smallIcon = null;
			}
			if (!createsValidImage(largeIcon))
			{
				largeIcon = null;
			}
		}

		packageEntry.setLabel(getPackageName(packageModel));
		packageEntry.setSmallIcon(smallIcon);
		packageEntry.setLargeIcon(largeIcon);
	}


	/**
	 * Create a tool to create a new model element.
	 * 
	 * @param modelType
	 *            The type of the model.
	 * @return A CreationToolEntry.
	 */
	public ToolEntry createCreationToolEntry(EClass modelType)
	{
		EcoreFactory factory = new EcoreFactory(modelType);

		ImageDescriptor smallIcon = Plugin.getInstance().getFactoryRegistry()
				.getImageFactory(Views.getInstance().getSelectedView())
				.createImageDescriptor(PluginProperties.model_smallIcon(modelType));
		smallIcon = new ScalingImageDescriptor(smallIcon, ICON_SMALL_SIZE);

		ImageDescriptor largeIcon = Plugin.getInstance().getFactoryRegistry()
				.getImageFactory(Views.getInstance().getSelectedView())
				.createImageDescriptor(PluginProperties.model_smallIcon(modelType));
		largeIcon = new ScalingImageDescriptor(largeIcon, ICON_LARGE_SIZE);

		if (!createsValidImage(smallIcon))
		{
			smallIcon = null;
		}
		if (!createsValidImage(largeIcon))
		{
			largeIcon = null;
		}

		return new CombinedTemplateCreationEntry(
				PluginProperties.palette_CreationEntry_name(modelType),
				PluginProperties.palette_CreationEntry_description(modelType), factory,
				factory, smallIcon, largeIcon);
	}


	/**
	 * Create a tool to create a new connection.
	 * 
	 * @param modelType
	 *            The type of the model.
	 * @return A ToolEntry.
	 */
	public ToolEntry createConnectionCreationToolEntry(EClass modelType)
	{
		EcoreFactory factory = new EcoreFactory(modelType);

		return new ConnectionCreationToolEntry(
				PluginProperties.palette_CreationEntry_name(modelType),
				PluginProperties.palette_CreationEntry_description(modelType), factory,
				Plugin.getInstance()
						.getFactoryRegistry()
						.getImageFactory(Views.getInstance().getSelectedView())
						.createImageDescriptor(
								PluginProperties.model_smallIcon(modelType)), Plugin
						.getInstance()
						.getFactoryRegistry()
						.getImageFactory(Views.getInstance().getSelectedView())
						.createImageDescriptor(
								PluginProperties.model_largeIcon(modelType)));
	}


	/**
	 * Creates a tool to add an existing element.
	 * 
	 * @param modelElement
	 *            The model element to add.
	 * @return The tool.
	 */
	public ToolEntry createAddToolEntry(EObject modelElement)
	{
		// Create empty entry. The content is provided by the update method.
		ToolEntry entry = new CombinedTemplateCreationEntry(null, null, null, null, null,
				null);
		entry.setId(getId(modelElement));
		updateAddToolEntry(entry, modelElement);

		return entry;
	}


	/**
	 * Updates the values of a PaletteEntry created with
	 * {@link #createAddToolEntry(EObject)}.
	 * 
	 * @param paletteEntry
	 *            The entry.
	 * @param modelElement
	 *            The model.
	 */
	private void updateAddToolEntry(PaletteEntry paletteEntry, EObject modelElement)
	{
		CombinedTemplateCreationEntry creationEntry = (CombinedTemplateCreationEntry) paletteEntry;

		CreationFactory factory = null;
		if (modelElement instanceof ReferenceableElement)
		{
			factory = new CreateReferenceFactory((ReferenceableElement) modelElement);
		}
		else
		{
			factory = new EcoreCopyFactory(weeditor, modelElement);
		}

		String name = null;
		ImageDescriptor smallIcon = null;
		ImageDescriptor largeIcon = null;

		// ask adapter for name and icon
		if (adapterFactory.isFactoryForType(IItemLabelProvider.class))
		{
			IItemLabelProvider labelProvider = (IItemLabelProvider) adapterFactory.adapt(
					modelElement, IItemLabelProvider.class);

			name = labelProvider.getText(modelElement);

			smallIcon = new ScalingImageDescriptor(
					(ImageDescriptor) labelProvider.getImage(modelElement),
					ICON_SMALL_SIZE);
			largeIcon = new ScalingImageDescriptor(
					(ImageDescriptor) labelProvider.getImage(modelElement),
					ICON_LARGE_SIZE);

			if (!createsValidImage(smallIcon))
			{
				smallIcon = null;
			}
			if (!createsValidImage(largeIcon))
			{
				largeIcon = null;
			}
		}

		// model is unnamed
		if (name == null)
		{
			name = org.eclipse.jwt.meta.PluginProperties.model_Unnamed_name(modelElement);
		}

		creationEntry.setLabel(name);
		creationEntry.setDescription(PluginProperties.palette_DynamicEntry_description(
				name, modelElement));
		creationEntry.setSmallIcon(smallIcon);
		creationEntry.setLargeIcon(largeIcon);
		creationEntry.setToolProperty(CreationTool.PROPERTY_CREATION_FACTORY, factory);
		creationEntry.setTemplate(factory);
	}


	/**
	 * Finds the package of a model element.
	 * 
	 * @param model
	 *            The model element.
	 * @return The package, or <code>null</code> if no package can be found.
	 */
	private Package getPackage(EObject model)
	{
		Package packageModel = null;
		if (model instanceof PackageableElement)
		{
			packageModel = ((PackageableElement) model).getPackage();
		}
		else
		{
			// check if the container is a PackageableElement
			EObject container = model.eContainer();

			if (container != null && container instanceof PackageableElement)
			{
				packageModel = ((PackageableElement) container).getPackage();
			}
		}

		return packageModel;
	}


	/**
	 * Returns the name of an package that can be displayed.
	 * 
	 * @param element
	 *            The package element.
	 * @return The name for the package.
	 */
	private String getPackageName(Package element)
	{
		StringBuffer result = new StringBuffer();

		if (element == null)
		{
			// default package
			result.append(org.eclipse.jwt.meta.PluginProperties.model_DefaultPackage_name);
		}
		else
		{
			// go thru all superpackages
			while (element != null)
			{
				String packageName = element.getName();

				// assert a default name
				if (packageName == null || packageName.length() == 0)
				{
					packageName = org.eclipse.jwt.meta.PluginProperties
							.model_Unnamed_name(element);
				}

				// insert separator
				if (result.length() != 0)
				{
					result.insert(
							0,
							org.eclipse.jwt.meta.PluginProperties.model_PackageName_separator);
				}

				// insert name
				result.insert(0, packageName);

				element = element.getSuperpackage();
			}
		}

		return result.toString();
	}


	/**
	 * Checks if an ImageDescriptor creates and image or null.
	 * 
	 * @param imageDescriptor
	 *            The ImageDescriptor to check.
	 * @return <code>true</code>, if an image is return, <code>false</code>
	 *         otherwise.
	 */
	public boolean createsValidImage(ImageDescriptor imageDescriptor)
	{
		boolean result = false;

		Image image = Plugin.getDefault().getFactoryRegistry().getImageFactory()
				.getImage(imageDescriptor);
		if (image != null)
		{
			result = true;
		}

		return result;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.emf.common.notify.Adapter#notifyChanged(org.eclipse.emf.common
	 * .notify .Notification)
	 */
	public void notifyChanged(Notification notification)
	{
		try
		{
			// ResoureSet
			if (notification.getNotifier() == resourceSet)
			{
				if (notification.getFeatureID(ResourceSet.class) == ResourceSet.RESOURCE_SET__RESOURCES)
				{
					switch (notification.getEventType())
					{
						case Notification.ADD:
							addResource((Resource) notification.getNewValue());
							break;

						case Notification.REMOVE:
							removeResource((Resource) notification.getOldValue());
							break;
					}
				}
			}

			// Resource
			else if (notification.getNotifier() instanceof Resource)
			{
				if (notification.getFeatureID(Resource.class) == Resource.RESOURCE__CONTENTS)
				{
					switch (notification.getEventType())
					{
						case Notification.ADD:
							addModel((EObject) notification.getNewValue());
							break;

						case Notification.REMOVE:
							removeModel((EObject) notification.getOldValue(), null);
							break;

						case Notification.ADD_MANY:
						case Notification.REMOVE_MANY:
							throw new IllegalStateException("Event type not implemented."); //$NON-NLS-1$
					}
				}
			}

			// Model
			else if (notification.getNotifier() instanceof EObject)
			{
				switch (notification.getEventType())
				{
					case Notification.ADD:
					{
						EObject newValue = (EObject) notification.getNewValue();
						if (((EObject) notification.getNotifier()).eContents().contains(
								newValue))
						{
							addModel(newValue);
						}
						break;
					}

					case Notification.REMOVE:
						removeModel((EObject) notification.getOldValue(), null);
						break;

					case Notification.SET:
					case Notification.UNSET:
					{
						EObject model = (EObject) notification.getNotifier();
						if (notification.getNotifier() instanceof PackageableElement
								&& notification.getFeatureID(PackageableElement.class) == CorePackage.PACKAGEABLE_ELEMENT__PACKAGE)
						{
							removeModel(model, (Package) notification.getOldValue());
							addModel(model);
						}

						updateModel(model);
						break;
					}

					case Notification.ADD_MANY:
					{
						Object value = notification.getNewValue();
						for (Iterator i = ((Iterable) value).iterator(); i.hasNext();)
						{
							addModel((EObject) i.next());
						}
						break;
					}

					case Notification.REMOVE_MANY:
					{
						Object value = notification.getOldValue();
						for (Iterator i = ((Iterable) value).iterator(); i.hasNext();)
						{
							removeModel((EObject) i.next(), null);
						}
						break;
					}
				}
			}
		}
		catch (Exception e)
		{
			logger.warning(e);
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.common.notify.Adapter#getTarget()
	 */
	public Notifier getTarget()
	{
		return null;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.emf.common.notify.Adapter#setTarget(org.eclipse.emf.common
	 * .notify.Notifier )
	 */
	public void setTarget(Notifier newTarget)
	{
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.emf.common.notify.Adapter#isAdapterForType(java.lang.Object)
	 */
	public boolean isAdapterForType(Object type)
	{
		return false;
	}


	public void dispose()
	{
//		if (resourceSet != null)
//			for (Iterator iter = resourceSet.getAllContents(); iter.hasNext();)
//			{
//				Object obj = iter.next();
//
//				if (obj instanceof Notifier)
//				{
//					Set<Adapter> adapters = new HashSet<Adapter>();
//					for (Adapter a : ((Notifier) obj).eAdapters())
//						if (a == this)
//							adapters.add(a);
//					((Notifier) obj).eAdapters().removeAll(adapters);
//				}
//			}

		// unset
		resourceSet = null;
		toolGroup = null;
		adapterFactory = null;
		activityGroup = null;
		packageableElementsGroup = null;
	}
}
