edu.buffalo.cse.green.JavaModelListener.java Source code

Java tutorial

Introduction

Here is the source code for edu.buffalo.cse.green.JavaModelListener.java

Source

/* This file is part of Green.
 *
 * Copyright (C) 2005 The Research Foundation of State University of New York
 * All Rights Under Copyright Reserved, The Research Foundation of S.U.N.Y.
 * 
 * Green is free software, licensed under the terms of the Eclipse
 * Public License, version 1.0.  The license is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */

package edu.buffalo.cse.green;

import static org.eclipse.jdt.core.IJavaElement.COMPILATION_UNIT;
import static org.eclipse.jdt.core.IJavaElement.JAVA_PROJECT;
import static org.eclipse.jdt.core.IJavaElement.PACKAGE_FRAGMENT;
import static org.eclipse.jdt.core.IJavaElement.TYPE;
import static org.eclipse.jdt.core.IJavaElementDelta.ADDED;
import static org.eclipse.jdt.core.IJavaElementDelta.REMOVED;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jdt.internal.core.SourceField;
import org.eclipse.jdt.internal.core.SourceMethod;
import org.eclipse.jdt.internal.core.SourceType;

import edu.buffalo.cse.green.editor.DiagramEditor;
import edu.buffalo.cse.green.editor.controller.FieldPart;
import edu.buffalo.cse.green.editor.controller.MethodPart;
import edu.buffalo.cse.green.editor.controller.RootPart;
import edu.buffalo.cse.green.editor.model.AbstractModel;
import edu.buffalo.cse.green.editor.model.FieldModel;
import edu.buffalo.cse.green.editor.model.MethodModel;
import edu.buffalo.cse.green.editor.model.RootModel;
import edu.buffalo.cse.green.editor.model.TypeModel;

/**
 * Handles changes to the <code>JavaModel</code>, reflecting them in the
 * affected editors.
 * 
 * The elementChanged() method is passed in a <code>JavaElementDelta</code>
 * containing a tree structure with information about the change that occurred
 * in the <code>JavaModel</code>.
 * 
 * Helper methods are called that traverse the delta structure and find changes
 * that correspond to the addition, removal, or movement of certain element
 * types. Once these changes are detected, each editor is checked to see if the
 * changes affect it. If they do, the editor's model's references to the changed
 * elements are updated. Refresh methods are then called on the appropriate edit
 * parts to update the editor's display.
 * 
 * @author bcmartin
 */
public class JavaModelListener implements IElementChangedListener {
    private static JavaModelListener _listener = new JavaModelListener();
    private static Map<Class, RefactorHandler> map;

    static {
        // add the elements to consider changes for to a list
        map = new HashMap<Class, RefactorHandler>();
        map.put(JavaProject.class, ProjectRefactorHandler.instance());
        map.put(PackageFragment.class, PackageRefactorHandler.instance());
        map.put(CompilationUnit.class, CompilationUnitRefactorHandler.instance());
        map.put(SourceType.class, TypeRefactorHandler.instance());
        map.put(SourceField.class, FieldRefactorHandler.instance());
        map.put(SourceMethod.class, MethodRefactorHandler.instance());
    }

    // singleton
    private JavaModelListener() {
    }

    /**
     * @return The singleton instance of the listener.
     */
    public static JavaModelListener getListener() {
        return _listener;
    }

    /**
     * Called when an IJavaElement changes. Used to reflect changes in the
     * editor, which has a view that is based on the <code>JavaModel</code>.
     * 
     * @param event - The change that occurred to the <code>JavaModel</code>.
     */
    @SuppressWarnings("unchecked")
    public void elementChanged(ElementChangedEvent event) {
        try {
            /* Goes through these classes looking for any that are added, moved
             * or removed. Calls methods that updates the editor to reflect any
             * changes found.
             */
            for (Class type : map.keySet()) {
                List<IJavaElementDelta> added = findAddedElements(event.getDelta(), type);
                List<IJavaElementDelta> removed = findRemovedElements(event.getDelta(), type);
                List<IJavaElementDelta> changed = findChangedElements(event.getDelta(), type);
                HashMap<IJavaElement, IJavaElement> moved = extractMovedElements(added, removed);

                // ignore updating the editors if no changes occurred
                if (added.size() == 0 && removed.size() == 0 && moved.size() == 0 && changed.size() == 0) {
                    continue;
                }

                List<DiagramEditor> editors = new ArrayList<DiagramEditor>(DiagramEditor.getEditors());

                // handle changes
                for (DiagramEditor editor : editors) {
                    RootModel root = editor.getRootModel();

                    // handle moves
                    for (IJavaElement sourceElement : moved.keySet()) {
                        IJavaElement targetElement = moved.get(sourceElement);
                        map.get(sourceElement.getClass()).handleMove(root, sourceElement, targetElement);
                    }

                    // handle removes
                    for (IJavaElementDelta removedElement : removed) {
                        map.get(removedElement.getElement().getClass()).handleRemove(root,
                                removedElement.getElement());
                    }

                    // handle adds
                    for (IJavaElementDelta addedElement : added) {
                        map.get(addedElement.getElement().getClass()).handleAdd(root, addedElement.getElement());
                    }

                    // handle changes (to modifiers, etc.)
                    for (IJavaElementDelta changedElement : changed) {
                        handleElementChange(changedElement);
                    }

                    editor.forceRefreshRelationships();
                }

            }
        } catch (Throwable t) {
            //TODO Incremental exploration throws Null Pointer.  Virtually unreproduceable.
            GreenException.critical(t);
        } finally {
            TypeRefactorHandler.REMOVED_TYPE = null;
        }
    }

    /**
     * Finds pairs of element changes that triggered "move" events. The types of
     * elements that are considered eligible for such an event are:
     * <code>IJavaProject</code>
     * <code>IPackageFragment</code>
     * <code>ICompilationUnit</code>
     * 
     * This is checked using the getMovedFromElement() and getMovedToElement()
     * methods in the delta.
     * 
     * There are other <code>IJavaElement</code> subclasses that may trigger
     * move events; however, in that context, move events do not pertain to the
     * <code>DiagramEditor</code>.
     * 
     * @param added - A list of elements that were added.
     * @param removed - A list of elements that were removed.
     * @return A mapping of all elements that triggered "move" events in the
     *         <code>IJavaElementDelta</code> hierarchy.
     */
    private HashMap<IJavaElement, IJavaElement> extractMovedElements(List<IJavaElementDelta> added,
            List<IJavaElementDelta> removed) {
        HashMap<IJavaElement, IJavaElement> moved = new HashMap<IJavaElement, IJavaElement>();
        List<IJavaElementDelta> notAdded = new ArrayList<IJavaElementDelta>();
        List<IJavaElementDelta> notRemoved = new ArrayList<IJavaElementDelta>();

        for (IJavaElementDelta toDelta : added) {
            // ignore this element if it wasn't moved to somewhere
            if (toDelta.getMovedFromElement() != null) {
                IJavaElement newElement = toDelta.getElement();

                // see if there's an element that has
                // the "newElement" returned by
                // toDelta.getMovedToElement()
                for (IJavaElementDelta fromDelta : removed) {
                    // if this element was moved from "newElement"...
                    if (sameElements(fromDelta.getMovedToElement(), newElement)) {
                        IJavaElement oldElement = fromDelta.getElement();

                        // and this element was moved from "oldElement"
                        if (sameElements(toDelta.getMovedFromElement(), oldElement)) {
                            /*
                             * remove both elements from their respective lists
                             * and put them in the move map
                             */
                            moved.put(oldElement, newElement);
                            notAdded.add(toDelta);
                            notRemoved.add(fromDelta);
                        }
                    }
                }
            }
        }

        added.removeAll(notAdded);
        removed.removeAll(notRemoved);

        return moved;
    }

    /**
     * Iterates over all subbranches of the <code>IJavaElementDelta</code> and
     * determines what elements have been added.
     * 
     * @param parentDelta - The tree (or subtree) of changes.
     * @param type - The class of elements to check for.
     * @return A list of added element deltas.
     */
    private List<IJavaElementDelta> findAddedElements(IJavaElementDelta parentDelta, Class type) {
        List<IJavaElementDelta> changes = new ArrayList<IJavaElementDelta>();

        // check for added element
        if (parentDelta.getKind() == ADDED) {
            if (type.isInstance(parentDelta.getElement())) {
                changes.add(parentDelta);
            }
        }

        // traverse all changed branches
        // this code shouldn't need altering
        for (IJavaElementDelta delta : parentDelta.getAffectedChildren()) {
            changes.addAll(findAddedElements(delta, type));
        }

        return changes;
    }

    /**
     * Iterates over all subbranches of the <code>IJavaElementDelta</code> and
     * determines what elements have changed that are present in the
     * <code>DiagramEditor</code>.
     * 
     * @param parentDelta - The delta to look through.
     * @param type - The class to match.
     * @return A mapping of moved elements.
     */
    private List<IJavaElementDelta> findChangedElements(IJavaElementDelta parentDelta, Class type) {
        IJavaElementDelta delta;
        List<IJavaElementDelta> changes = new ArrayList<IJavaElementDelta>();

        // adds deltas representing the removed elements of
        // the specified type to the list of changes
        for (int i = 0; i < parentDelta.getChangedChildren().length; i++) {
            delta = parentDelta.getChangedChildren()[i];

            if (type.isInstance(delta.getElement())) {
                if (delta.getChangedChildren().length == 0) {
                    changes.add(delta);
                }
            }
        }

        // traverse all changed branches
        // this code shouldn't need altering
        for (int i = 0; i < parentDelta.getChangedChildren().length; i++) {
            delta = parentDelta.getChangedChildren()[i];
            changes.addAll(findChangedElements(delta, type));
        }

        return changes;
    }

    /**
     * Iterates over all subbranches of the <code>IJavaElementDelta</code> and
     * determines what elements have been removed that are present in the
     * <code>DiagramEditor</code>.
     * 
     * @param parentDelta - The tree (or subtree) of changes.
     * @param type - The class of elements to check for.
     * @return A list of removed element deltas.
     */
    private List<IJavaElementDelta> findRemovedElements(IJavaElementDelta parentDelta, Class type) {
        List<IJavaElementDelta> changes = new ArrayList<IJavaElementDelta>();

        // check for removed element
        if (parentDelta.getKind() == REMOVED) {
            if (type.isInstance(parentDelta.getElement())) {
                changes.add(parentDelta);
            }
        }

        // traverse all changed branches
        // this code shouldn't need altering
        for (IJavaElementDelta delta : parentDelta.getAffectedChildren()) {
            changes.addAll(findRemovedElements(delta, type));
        }

        return changes;
    }

    /**
     * Updates the display when some change occurs to a Java element that
     * doesn't involve removal, addition, or movement.
     * 
     * @param elementDelta - The delta of the element that changed.
     */
    private void handleElementChange(IJavaElementDelta elementDelta) {
        IJavaElement element = elementDelta.getElement();

        // update the modifiers of the element (if they changed)
        if ((elementDelta.getFlags() & IJavaElementDelta.F_MODIFIERS) != 0) {
            for (DiagramEditor editor : DiagramEditor.getEditors()) {
                RootPart rootEditPart = editor.getRootPart();
                RootModel root = (RootModel) rootEditPart.getModel();
                AbstractModel abstractModel = root.getModelFromElement(element);

                if (abstractModel != null) {
                    if (abstractModel instanceof FieldModel) {
                        FieldModel fModel = (FieldModel) abstractModel;
                        FieldPart fEditPart = (FieldPart) rootEditPart.getPartFromModel(fModel);
                        fEditPart.updateIcon();
                    } else if (abstractModel instanceof MethodModel) {
                        MethodModel mModel = (MethodModel) abstractModel;
                        MethodPart mEditPart = (MethodPart) rootEditPart.getPartFromModel(mModel);
                        mEditPart.updateIcon();
                    }
                }
            }
        }
    }

    /**
     * Compares two elements by their handle identifiers.
     * 
     * @param e1 - An element to compare.
     * @param e2 - An element to compare.
     * @return True if the elements have the same handles, false otherwise. 
     */
    public static boolean sameElements(IJavaElement e1, IJavaElement e2) {
        if (e1 == null || e2 == null)
            return false;

        return (e1.getHandleIdentifier().equals(e2.getHandleIdentifier()));
    }
}

/**
 * Provides the listener with a polymorphic way to update Green's editor by
 * mapping different <code>IJavaElement</code> instances onto the classes that
 * handle their refactoring.
 * 
 * @author bcmartin
 * @param <E> - The element handled by the given implementor.
 */
interface RefactorHandler<E extends IJavaElement> {
    /**
     * Handles the addition of an <code>IJavaElement</code> to the workspace.
     * 
     * @param root
     * @param element
     */
    public void handleAdd(RootModel root, E element);

    /**
     * Handles movement of an <code>IJavaElement</code> within the workspace.
     * 
     * @param root
     * @param sourceElement
     * @param targetElement
     */
    public void handleMove(RootModel root, E sourceElement, E targetElement);

    /**
     * Handles the removal of an <code>IJavaElement</code> from the workspace.
     * 
     * @param root
     * @param element
     */
    public void handleRemove(RootModel root, E element);
}

/**
 * Handles project refactoring.
 * 
 * @author bcmartin
 * @param <E> - The <code>IJavaProject</code> implementation to use.
 */
class ProjectRefactorHandler<E extends IJavaProject> implements RefactorHandler<E> {
    private static final ProjectRefactorHandler<IJavaProject> INSTANCE = new ProjectRefactorHandler<IJavaProject>();

    private ProjectRefactorHandler() {
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleAdd(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleAdd(RootModel root, E element) {
        // do nothing
    }

    /**
     * @return The singleton instance of this <code>RefactorHandler</code>.
     */
    public static RefactorHandler<IJavaProject> instance() {
        return INSTANCE;
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleMove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleMove(RootModel root, E sourceElement, E targetElement) {
        root.changeProjectElement(sourceElement, targetElement);

        try {
            List<IJavaElement> packages = root.getElementsOfKind(PACKAGE_FRAGMENT);

            // look through packages to match up source and destination elements
            for (IJavaElement packElement : packages) {
                IPackageFragment packFrag = (IPackageFragment) packElement;

                if (JavaModelListener.sameElements(packFrag.getAncestor(JAVA_PROJECT),
                        sourceElement.getPrimaryElement())) {
                    for (IPackageFragment packFrags : targetElement.getPackageFragments()) {
                        // check for the same name of the two packages
                        if (packFrag.getElementName().equals(packFrags.getElementName())) {
                            PackageRefactorHandler.instance().handleMove(root, packFrag, packFrags);
                        }
                    }
                }
            }

            root.dispose();
        } catch (JavaModelException e) {
            e.printStackTrace();
        }
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleRemove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleRemove(RootModel root, E element) {
        List<IJavaElement> packages = root.getElementsOfKind(PACKAGE_FRAGMENT);

        for (IJavaElement pack : packages) {
            // get the project that contains the package
            IJavaElement proj = pack.getAncestor(JAVA_PROJECT);

            // if the package belongs to the project, remove it
            if (JavaModelListener.sameElements(proj, element)) {
                PackageRefactorHandler.instance().handleRemove(root, (IPackageFragment) pack);
            }
        }
    }
}

/**
 * Handles package refactoring.
 * 
 * @author bcmartin
 * @param <E> - The <code>IPackageFragment</code> implementation to use.
 */
class PackageRefactorHandler<E extends IPackageFragment> implements RefactorHandler<E> {
    private static final PackageRefactorHandler<IPackageFragment> INSTANCE = new PackageRefactorHandler<IPackageFragment>();

    private PackageRefactorHandler() {
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleAdd(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleAdd(RootModel root, E element) {
        // do nothing
    }

    /**
     * @return The singleton instance of this <code>RefactorHandler</code>.
     */
    public static RefactorHandler<IPackageFragment> instance() {
        return INSTANCE;
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleMove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleMove(RootModel root, E sourceElement, E targetElement) {
        try {
            List<IJavaElement> cus = root.getElementsOfKind(COMPILATION_UNIT);
            ICompilationUnit[] newCUs = targetElement.getCompilationUnits();

            for (IJavaElement cuElement : cus) {
                ICompilationUnit iCU = (ICompilationUnit) cuElement;
                if (JavaModelListener.sameElements(iCU.getAncestor(PACKAGE_FRAGMENT), sourceElement)) {
                    for (ICompilationUnit cu : newCUs) {
                        if (cu.getElementName().equals(iCU.getElementName())) {
                            CompilationUnitRefactorHandler.instance().handleMove(root, iCU, cu);
                        }
                    }
                }
            }
        } catch (JavaModelException e) {
            e.printStackTrace();
        }
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleRemove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleRemove(RootModel root, E element) {
        List<IJavaElement> cus = root.getElementsOfKind(COMPILATION_UNIT);

        for (IJavaElement cu : cus) {
            if (JavaModelListener.sameElements(cu.getAncestor(PACKAGE_FRAGMENT), element)) {
                CompilationUnitRefactorHandler.instance().handleRemove(root, (ICompilationUnit) cu);
            }
        }
    }
}

/**
 * Handles compilation unit refactoring.
 * 
 * @author bcmartin
 * @param <E> - The <code>ICompilationUnit</code> implementation to use.
 */
class CompilationUnitRefactorHandler<E extends ICompilationUnit> implements RefactorHandler<E> {
    private static final CompilationUnitRefactorHandler<ICompilationUnit> INSTANCE = new CompilationUnitRefactorHandler<ICompilationUnit>();

    /**
     * @return The singleton instance of this <code>RefactorHandler</code>.
     */
    public static CompilationUnitRefactorHandler<ICompilationUnit> instance() {
        return INSTANCE;
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleAdd(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleAdd(RootModel root, E element) {
        // do nothing
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleMove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleMove(RootModel root, E sourceElement, E targetElement) {
        // get all types represented in the diagram
        String scuId = sourceElement.getHandleIdentifier();
        String tcuId = targetElement.getHandleIdentifier();

        String scuName = scuId.substring(scuId.indexOf('{') + 1, scuId.indexOf(".java"));
        String tcuName = tcuId.substring(tcuId.indexOf('{') + 1, tcuId.indexOf(".java"));

        List<IJavaElement> cuTypes = root.getElementsOfKind(TYPE);

        // see if any types belong to the compilation unit
        // that is undergoing the move event
        for (IJavaElement oType : cuTypes) {
            if (JavaModelListener.sameElements(sourceElement, oType.getAncestor(COMPILATION_UNIT))) {
                String oId = oType.getHandleIdentifier();
                String oName = oId.substring(oId.indexOf('['));

                oName = oName.replaceAll("\\[" + scuName, "[" + tcuName);

                IJavaElement nType = JavaCore.create(tcuId + oName);
                TypeModel oModel = (TypeModel) root.getModelFromElement(oType);

                TypeModel nModel = root.createTypeModel((IType) nType);
                if (oModel != null) {
                    // TODO We tried to catch a ResourceException,
                    // but it is caught in MemberModel
                    oModel.removeFromParent();
                    nModel.setLocation(oModel.getLocation());
                    nModel.setSize(oModel.getSize());
                }
            }
        }
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleRemove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleRemove(RootModel root, E element) {
        // ignore this if the CU removed is a working copy (not actual removal)
        if (element.isWorkingCopy())
            return;

        List<IJavaElement> types = new ArrayList<IJavaElement>(root.getElementsOfKind(TYPE));

        // remove any types that belong to the compilation unit
        for (IJavaElement type : types) {
            if (JavaModelListener.sameElements(element, type.getAncestor(COMPILATION_UNIT))) {
                if (root.getModelFromElement(type) != null) {
                    root.getModelFromElement(type).removeFromParent();
                }
            }
        }
    }
}

/**
 * Handles type refactoring.
 * 
 * @author bcmartin
 * @param <E> - The <code>IType</code> implementation to use.
 */
class TypeRefactorHandler<E extends IType> implements RefactorHandler<E> {
    private static final TypeRefactorHandler<IType> INSTANCE = new TypeRefactorHandler<IType>();
    public static TypeModel REMOVED_TYPE;

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleAdd(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleAdd(RootModel root, E element) {
        // if the compilation unit exists in the editor, add the type
        if (root.ancestorInEditor(element.getAncestor(COMPILATION_UNIT))) {
            TypeModel addedType = root.createTypeModel(element);

            if (REMOVED_TYPE != null) {
                addedType.setLocation(REMOVED_TYPE.getLocation());
                addedType.setSize(REMOVED_TYPE.getSize());
            }
        }
    }

    /**
     * @return The singleton instance of this <code>RefactorHandler</code>.
     */
    public static TypeRefactorHandler<IType> instance() {
        return INSTANCE;
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleMove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleMove(RootModel root, E sourceElement, E targetElement) {
        GreenException.illegalOperation("Unhandled element change");
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleRemove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleRemove(RootModel root, E element) {
        REMOVED_TYPE = (TypeModel) root.getModelFromElement(element);
    }
}

/**
 * Handles field refactoring.
 * 
 * @author bcmartin
 * @param <E> - The <code>IField</code> implementation to use.
 */
class FieldRefactorHandler<E extends IField> implements RefactorHandler<E> {
    private static final FieldRefactorHandler<IField> INSTANCE = new FieldRefactorHandler<IField>();

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleAdd(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleAdd(RootModel root, E element) {
        if (element.equals(element.getPrimaryElement())) {
            TypeModel type = (TypeModel) root.getModelFromElement(element.getAncestor(TYPE));

            if (type != null) {
                type.addChild(new FieldModel(element));
            }
        }
    }

    /**
     * @return The singleton instance of this <code>RefactorHandler</code>.
     */
    public static FieldRefactorHandler<IField> instance() {
        return INSTANCE;
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleMove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleMove(RootModel root, E sourceElement, E targetElement) {
        GreenException.illegalOperation("Unhandled element change");
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleRemove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleRemove(RootModel root, E element) {
        FieldModel fieldModel = (FieldModel) root.getModelFromElement(element);

        // remove only stored copy
        if (fieldModel != null) {
            fieldModel.removeFromParent();
        }
    }
}

/**
 * Handles method refactoring.
 * 
 * @author bcmartin
 * @param <E> - The <code>IMethod</code> implementation to use.
 */
class MethodRefactorHandler<E extends IMethod> implements RefactorHandler<E> {
    private static final MethodRefactorHandler<IMethod> INSTANCE = new MethodRefactorHandler<IMethod>();

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleAdd(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleAdd(RootModel root, E element) {
        TypeModel type = (TypeModel) root.getModelFromElement(element.getAncestor(TYPE));

        if (type != null) {
            type.addChild(new MethodModel((IMethod) element));
        }
    }

    /**
     * @return The singleton instance of this <code>RefactorHandler</code>.
     */
    public static MethodRefactorHandler<IMethod> instance() {
        return INSTANCE;
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleMove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleMove(RootModel root, E sourceElement, E targetElement) {
        GreenException.illegalOperation("Unhandled element change");
    }

    /**
     * @see edu.buffalo.cse.green.RefactorHandler#handleRemove(edu.buffalo.cse.green.editor.model.RootModel, org.eclipse.jdt.core.IJavaElement)
     */
    public void handleRemove(RootModel root, E element) {
        MethodModel methodModel = (MethodModel) root.getModelFromElement(element);

        // remove only stored copy
        if (methodModel != null) {
            methodModel.removeFromParent();
        }
    }
}