com.architexa.diagrams.relo.jdt.parts.CodeUnitEditPart.java Source code

Java tutorial

Introduction

Here is the source code for com.architexa.diagrams.relo.jdt.parts.CodeUnitEditPart.java

Source

/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
/*
 * Created on Jun 13, 2004
 *
 */
package com.architexa.diagrams.relo.jdt.parts;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.Predicate;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search2.internal.ui.InternalSearchUI;
import org.eclipse.search2.internal.ui.text2.DefaultTextSearchQueryProvider;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TreeItem;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;

import com.architexa.diagrams.RSECore;
import com.architexa.diagrams.commands.AddNodeAndRelCmd;
import com.architexa.diagrams.commands.ColorActionCommand;
import com.architexa.diagrams.draw2d.IFigureWithContents;
import com.architexa.diagrams.eclipse.gef.MenuButton;
import com.architexa.diagrams.eclipse.gef.UndoableLabelSource;
import com.architexa.diagrams.jdt.Activator;
import com.architexa.diagrams.jdt.ArtifactContainer;
import com.architexa.diagrams.jdt.Filters;
import com.architexa.diagrams.jdt.IJavaElementContainer;
import com.architexa.diagrams.jdt.RJCore;
import com.architexa.diagrams.jdt.actions.AddCalleeHierarchy;
import com.architexa.diagrams.jdt.model.CodeUnit;
import com.architexa.diagrams.jdt.model.UserCreatedFragment;
import com.architexa.diagrams.jdt.ui.RSEOutlineInformationControl;
import com.architexa.diagrams.jdt.utils.JDTUISupport;
import com.architexa.diagrams.model.Artifact;
import com.architexa.diagrams.model.ArtifactFragment;
import com.architexa.diagrams.model.DerivedArtifact;
import com.architexa.diagrams.model.DirectedRel;
import com.architexa.diagrams.model.NamedRel;
import com.architexa.diagrams.parts.AnnoLabelCellEditorLocator;
import com.architexa.diagrams.parts.AnnoLabelDirectEditPolicy;
import com.architexa.diagrams.parts.BasicRootController;
import com.architexa.diagrams.parts.NavAidsEditPolicy;
import com.architexa.diagrams.parts.NavAidsSpec;
import com.architexa.diagrams.parts.PointPositionedDiagramPolicy;
import com.architexa.diagrams.parts.RelNavAidsSpec;
import com.architexa.diagrams.relo.commands.InteriorMoveCommand;
import com.architexa.diagrams.relo.figures.CodeUnitFigure;
import com.architexa.diagrams.relo.jdt.ReloJDTPlugin;
import com.architexa.diagrams.relo.jdt.actions.AddJavaDocAction;
import com.architexa.diagrams.relo.jdt.actions.EditJavaDocDialogAction;
import com.architexa.diagrams.relo.jdt.commands.DeleteCommand;
import com.architexa.diagrams.relo.jdt.commands.OpenInEditorCommand;
import com.architexa.diagrams.relo.jdt.parts.CompartmentedCodeUnitEditPart.CompartmentCUEditPart;
import com.architexa.diagrams.relo.jdt.services.PluggableEditPartSupport;
import com.architexa.diagrams.relo.modelBridge.ReloDoc;
import com.architexa.diagrams.relo.parts.ArtifactEditPart;
import com.architexa.diagrams.relo.parts.MoreItemsEditPart;
import com.architexa.diagrams.relo.parts.ReloController;
import com.architexa.diagrams.relo.ui.ColorScheme;
import com.architexa.diagrams.services.PluggableNavAids;
import com.architexa.diagrams.services.PluggableNavAids.INavAidSpecSource;
import com.architexa.diagrams.ui.ImageCache;
import com.architexa.diagrams.ui.MultiAddCommandAction;
import com.architexa.diagrams.ui.RSEContextMenuProvider;
import com.architexa.diagrams.ui.RSEEditor;
import com.architexa.diagrams.utils.MoreButtonUtils;
import com.architexa.org.eclipse.draw2d.Figure;
import com.architexa.org.eclipse.draw2d.IFigure;
import com.architexa.org.eclipse.draw2d.Label;
import com.architexa.org.eclipse.draw2d.ToolbarLayout;
import com.architexa.org.eclipse.draw2d.geometry.Dimension;
import com.architexa.org.eclipse.draw2d.geometry.Point;
import com.architexa.org.eclipse.draw2d.geometry.Rectangle;
import com.architexa.org.eclipse.gef.ConnectionEditPart;
import com.architexa.org.eclipse.gef.DefaultEditDomain;
import com.architexa.org.eclipse.gef.EditDomain;
import com.architexa.org.eclipse.gef.EditPart;
import com.architexa.org.eclipse.gef.EditPartViewer;
import com.architexa.org.eclipse.gef.EditPolicy;
import com.architexa.org.eclipse.gef.GraphicalEditPart;
import com.architexa.org.eclipse.gef.Request;
import com.architexa.org.eclipse.gef.RequestConstants;
import com.architexa.org.eclipse.gef.commands.Command;
import com.architexa.org.eclipse.gef.commands.CompoundCommand;
import com.architexa.org.eclipse.gef.requests.CreateConnectionRequest;
import com.architexa.org.eclipse.gef.requests.CreateRequest;
import com.architexa.org.eclipse.gef.requests.GroupRequest;
import com.architexa.org.eclipse.gef.requests.ReconnectRequest;
import com.architexa.org.eclipse.gef.tools.CellEditorLocator;
import com.architexa.org.eclipse.gef.tools.LabelDirectEditManager;
import com.architexa.org.eclipse.gef.tools.LabelSource;
import com.architexa.rse.BuildStatus;
import com.architexa.store.ReloRdfRepository;

/**
 * Java Related Functionality
 * 
 * @author vineet
 */
public class CodeUnitEditPart extends MoreItemsEditPart implements IJavaElementContainer, ArtifactContainer {
    static final Logger logger = ReloJDTPlugin.getLogger(CodeUnitEditPart.class);

    public final static String REQ_REDUCE = "minimize";
    public final static String REQ_EXPAND = "expand";

    // tooltips
    public final static String COLLAPSE = "collapse";
    public final static String EXPAND = "expand";
    public final static String HIDE = "hide";

    private final static boolean useCtrlOStyleMemberMenu = true;

    private String label;

    @Override
    protected IFigure createFigure(IFigure curFig, int newDL) {
        if (curFig == null) {
            ImageDescriptor id = this.getIconDescriptor(getCU().getArt(), getCU().queryType(getRepo()));
            Image img = ImageCache.calcImageFromDescriptor(id);
            label = getLabel();

            Label nameLbl = new Label(label, img);

            Figure lblFig = new Figure();
            lblFig.setLayoutManager(new ToolbarLayout(true));
            lblFig.add(nameLbl);
            lblFig.add(new Label("  "));

            curFig = new CodeUnitFigure(lblFig, null, null);
            curFig.setToolTip(new Label(CodeUnit.getLabelWithContext(getRepo(), (ArtifactFragment) getModel())));
        }

        return curFig;
    }

    public CodeUnit getCU() {
        return (CodeUnit) getModel();
    }

    @Override
    public void setModel(Object model) {
        // make sure that the model is of an easy to handle type
        if (!(model instanceof CodeUnit)) {
            logger.error("Can't deal with type: " + model.getClass());
        }
        super.setModel(model);
    }

    @Override
    public String getLabel(Artifact art, Artifact contextArt) {
        return CodeUnit.getLabel(getRepo(), art, contextArt);
    }

    @Override
    public ImageDescriptor getIconDescriptor(Artifact art, Resource resType) {
        ImageDescriptor icon = PluggableEditPartSupport.getIconDescriptor(getRepo(), art, resType);
        return CodeUnit.getDecoratedIcon(getRepo(), art, icon);
    }

    @Override
    public void activate() {
        super.activate();
        updateMembers(currDL);
    }

    private IJavaElement cachedIJE = null;

    private RSEOutlineInformationControl popup = null;

    public IJavaElement getJaveElement() {
        if (cachedIJE == null)
            cachedIJE = RJCore.resourceToJDTElement(this.getRepo(), getContainedArtifact().elementRes);
        return cachedIJE;
    }

    public Artifact getContainedArtifact() {
        return getArtifact().getArt();
    }

    /* (non-Javadoc)
     * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
     */
    @Override
    protected void createEditPolicies() {
        super.createEditPolicies();
        installEditPolicy(NavAidsEditPolicy.HANDLES_ROLE, new NavAidsEditPolicy());
        installDirectEditPolicy();
    }

    // content pane
    @Override
    public IFigure getContentPane() {
        IFigure fig = getFigure();
        while (fig instanceof IFigureWithContents)
            fig = ((IFigureWithContents) fig).getContentFig();
        return fig;
    }

    public String getLabel() {
        return getCU().getLabel(getRepo(), getCU().getParentArt().getArt()) + tag.getTrailer();
    }

    public String getLabel(Object context) {
        if (context instanceof CodeUnit) {
            return getCU().getLabel(getRepo(), ((CodeUnit) context).getArt()) + tag.getTrailer();
        } else {
            return getCU().getLabel(getRepo()) + tag.getTrailer();
        }
    }

    @Override
    public IFigure getMoreButton() {
        return getMoreBtn();
    }

    @Override
    public void buildMoreMenu(IMenuManager menu, MenuButton button) {
        if (!useCtrlOStyleMemberMenu) {
            super.buildMoreMenu(menu, button);
            return;
        }
        // Give menu a title label of "Members of MyClass:"
        String titleText = "Members of " + getLabel().trim() + ":";
        List<MultiAddCommandAction> actions = getAllMemberMenuActions();
        if (actions == null)
            return; // return if actions are being sent to server

        // Members could have multiple access levels or 
        // kinds, and members will not be library code
        String[] filters = new String[] { RSEOutlineInformationControl.FILTERS_ACCESS_LEVEL,
                RSEOutlineInformationControl.FILTERS_MEMBER_KIND };
        for (MultiAddCommandAction maca : actions) {
            Artifact art = RSEOutlineInformationControl.getArtifact(maca);
            // TODO: remove deplicated string here and inject this from EJCore
            // make sure to use this namespace part of the string and not 'web-root' since there can be nested web folders
            if (art.elementRes.toString().contains("jdtext-wkspc#")) {
                filters = new String[] { "" };
                break;
            }
        }

        createInformationControlMenu(titleText, actions, null, filters, button);
    }

    @Override
    public void buildNavAidMenu(List<MultiAddCommandAction> menuActions, NavAidsSpec spec, IMenuManager defaultMenu,
            DirectedRel rel) {

        String[] filters;
        if (RJCore.inherits.equals(rel.res))
            // Multiple possible access levels and kinds, so show all filters in the menu
            filters = RSEOutlineInformationControl.FILTERS_ALL;
        else
            // Everything in a calls nav aid or an overrides nav aid will be of kind
            // Method, so don't want filters for member kind because seeing check boxes 
            // for Class or Field can be confusing here
            filters = new String[] { RSEOutlineInformationControl.FILTERS_ACCESS_LEVEL,
                    RSEOutlineInformationControl.FILTERS_LIBRARY_CODE };

        createInformationControlMenu(getNavAidTitle(rel), menuActions, rel, filters, spec.decorationFig);
    }

    // Get a title appropriate for the nav aid menu depending on the rel and its direction
    private String getNavAidTitle(DirectedRel rel) {

        String cuName = getLabel().trim();
        String relName = rel.res.getLocalName().trim();
        String relNameCap = relName.substring(0, 1).toUpperCase() + relName.substring(1); // first letter capitalized

        String firstWord = "";
        String secondWord = "";
        String thirdWord = "";

        if (RJCore.inherits.equals(rel.res)) {
            if (rel.isFwd) {
                // inherits nav aid on top means this is a subclass, so make
                // menu title "MyClass inherits from:"
                firstWord = cuName;
                secondWord = relName;
                thirdWord = "from";
            } else {
                // inherits nav aid on bottom means this is a superclass, so make 
                // menu title "Inherits from MyClass:"
                firstWord = relNameCap;
                secondWord = "from";
                thirdWord = cuName;
            }
        } else if (RJCore.overrides.equals(rel.res)) {
            if (rel.isFwd) {
                // overrides nav aid on top means this member overrides something, so 
                // make menu title "foo() overrides:"
                firstWord = cuName;
                secondWord = relName;
            } else {
                // overrides nav aid on bottom means this member is overridden, so 
                // make menu title "Overrides foo():"
                firstWord = relNameCap;
                secondWord = cuName;
            }
        } else {
            // make menu title of nav aid on left "Members calling foo():" and on
            // right "Calls from foo():" to match eclipse Call Hierarchy messages
            firstWord = rel.isFwd ? relNameCap : "Members";
            secondWord = rel.isFwd ? "from" : "calling";
            thirdWord = cuName;
        }

        return (firstWord + " " + secondWord + " " + thirdWord).trim() + ":";
    }

    protected void createInformationControlMenu(String menuTitle, List<MultiAddCommandAction> menuActions,
            DirectedRel addButtonsActionsRel, String[] filters, IFigure menuButton) {

        if (popup != null && popup.active)
            return;
        popup = new RSEOutlineInformationControl(getBrowseModel().getRepo(), menuTitle, null, filters);
        popup.setInput(menuActions);

        // Get the actions for the buttons that add multiple items at once
        List<IAction> addButtonsActions = getAddButtonsActions(menuActions, popup, addButtonsActionsRel);
        popup.setButtonInput(addButtonsActions);

        // menu should open along the right hand side of the member / nav aid button
        EditPartViewer parentViewer = getViewer();
        Control parent = parentViewer.getControl();
        Rectangle figBounds = menuButton.getBounds().getCopy();
        menuButton.translateToAbsolute(figBounds);
        org.eclipse.swt.graphics.Point menuLocation = parent.toDisplay(figBounds.getTopRight().x + 1,
                figBounds.getTopRight().y - 1);
        popup.setLocation(menuLocation);

        // make sure menu is proper size to show all components but not stretch too wide
        popup.pack();
        popup.setInitSize();
        popup.open();
    }

    private List<IAction> getAddButtonsActions(List<MultiAddCommandAction> menuActions,
            RSEOutlineInformationControl navAidMenu, DirectedRel rel) {
        List<IAction> addActions = new ArrayList<IAction>();

        // Option that will add all enabled and visible calls in the menu to the diagram
        IAction addAllAction = getAddAllItemsAction(navAidMenu, getRootController());
        if (addAllAction != null)
            addActions.add(addAllAction);

        if (rel != null && RJCore.calls.equals(rel.res)) {
            // Option that will incrementally add all of the enabled and visible 
            // calls this method makes, every subsequent call those called 
            // methods make, every subsequent call those methods make, etc.
            IAction calleeHierAction = getAddCalleeHierarchyAction(navAidMenu, getRootController());
            addActions.add(calleeHierAction);

            // lifecycle action will have an icon, so give the Add All button
            // in the calls nav aid an icon too with a similar appearance in 
            // order to help users understand the difference between the two options
            addAllAction.setImageDescriptor(Activator.getImageDescriptor("icons/addAll_callees.png"));
        }

        // If there is not at least one enabled call in the list, continue showing
        // these buttons so the menu always looks consistent but disable the buttons
        boolean atLeastOneAddableItem = false;
        for (MultiAddCommandAction action : menuActions) {
            if (action.isEnabled()) {
                atLeastOneAddableItem = true;
                break;
            }
        }
        if (!atLeastOneAddableItem) {
            for (IAction addAction : addActions)
                addAction.setEnabled(false);
        }

        return addActions;
    }

    private IAction getAddAllItemsAction(final RSEOutlineInformationControl navAidMenu,
            final BasicRootController rc) {
        final String actionText = "Add All";
        IAction showAllAction = new Action(actionText) {
            @Override
            public void run() {
                CompoundCommand addAllCmd = getAddAllCmd(actionText, navAidMenu);
                runAddAll(addAllCmd, navAidMenu, rc);
            }
        };
        return showAllAction;
    }

    // Returns a compound command containing the commands of
    // visible and enabled (unfiltered and selectable) tree items
    private CompoundCommand getAddAllCmd(String addAllLabel, RSEOutlineInformationControl navAidMenu) {
        CompoundCommand addAllCmd = new CompoundCommand(addAllLabel);
        Map<Artifact, ArtifactFragment> addedArtToAFMap = new HashMap<Artifact, ArtifactFragment>();
        addCommandsToAddAllCmd(navAidMenu.getTreeViewer().getTree().getItems(), addAllCmd, addedArtToAFMap);
        return addAllCmd;
    }

    // Adds only the commands of visible and enabled tree items to the given compound cmd
    private void addCommandsToAddAllCmd(final TreeItem[] menuItems, CompoundCommand addAllCmd,
            Map<Artifact, ArtifactFragment> addedArtToAFMap) {
        for (TreeItem visibleItem : menuItems) {
            Object data = visibleItem.getData();
            if (data instanceof MultiAddCommandAction) {
                MultiAddCommandAction maca = (MultiAddCommandAction) data;
                if (maca.isEnabled())
                    MoreButtonUtils.addCommand(maca, addAllCmd, addedArtToAFMap);
            }

            // Only add expanded subtrees; if the user has collapsed a
            // subtree, we assume they are not of interest and don't add
            boolean isExpanded = visibleItem.getItemCount() == 0 || // consider expanded b/c not a parent node
                    visibleItem.getExpanded(); // is a subtree parent, so test if expanded
            if (isExpanded)
                addCommandsToAddAllCmd(visibleItem.getItems(), addAllCmd, addedArtToAFMap);
        }
    }

    private void runAddAll(CompoundCommand addAllCmd, RSEOutlineInformationControl navAidMenu,
            BasicRootController rc) {
        if (!addAllCmd.isEmpty()) {
            navAidMenu.dispose(); // close the menu
            rc.execute(addAllCmd); // and add the calls
        }
    }

    private IAction getAddCalleeHierarchyAction(final RSEOutlineInformationControl navAidMenu,
            final BasicRootController rc) {
        IAction action = new AddCalleeHierarchy(this, getLabel().trim(),
                ((ArtifactFragment) getModel()).getParentArt().getArt().queryName(getRepo()).trim(), getRepo()) {

            @Override
            public void run() {

                // First set the action that will add to the diagram the first set of
                // calls in the hierarchy - the enabled and visible calls in the menu.
                // See {@link #setAddAllCallsAction(IAction)} for why setting at this 
                // point in the run() method.
                final CompoundCommand addAllCmd = getAddAllCmd(actionLbl, navAidMenu);
                IAction addAllAction = new Action() {
                    @Override
                    public void run() {
                        runAddAll(addAllCmd, navAidMenu, rc);
                    }
                };
                setAddAllCallsAction(addAllAction);

                super.run();
            }

            List<Resource> methodsAlreadyDone = new ArrayList<Resource>();

            @Override
            public boolean isMethodAlreadyDone(ArtifactFragment methodModel) {
                return methodsAlreadyDone.contains(methodModel.getArt().elementRes);
            }

            @Override
            public void addMethodDone(ArtifactFragment methodModel) {
                methodsAlreadyDone.add(methodModel.getArt().elementRes);
            }

            @Override
            public boolean makesCallNotInDiagram(EditPart methodEP, DirectedRel rel) {
                if (!(methodEP instanceof CodeUnitEditPart))
                    return false;
                CodeUnitEditPart cuep = (CodeUnitEditPart) methodEP;
                List<Artifact> calls = cuep.showableListModel(cuep.getRepo(), rel, null);
                return calls != null && calls.size() != 0;
            }

            @Override
            public void displayAllCallsMade(EditPart methodEP, DirectedRel rel) {
                if (!(methodEP instanceof CodeUnitEditPart))
                    return;
                CodeUnitEditPart cuep = (CodeUnitEditPart) methodEP;

                CompoundCommand addAllCmd = new CompoundCommand();
                BasicRootController rc = cuep.getRootController();
                List<Artifact> showableSetModel = cuep.showableListModel(cuep.getRepo(), rel, null);
                for (Artifact relArt : showableSetModel) {
                    String relArtLbl = cuep.getRelModelLabel(relArt);
                    MultiAddCommandAction action = cuep.getShowRelAction(rc, rel, relArt, relArtLbl);
                    if (action != null)
                        addAllCmd.add(action.getCommand(new HashMap<Artifact, ArtifactFragment>()));
                }
                rc.execute(addAllCmd);
            }

            @Override
            public List<EditPart> getNextLevelOfMethods(EditPart methodEP) {
                List<EditPart> targets = new ArrayList<EditPart>();
                if (!(methodEP instanceof CodeUnitEditPart))
                    return targets;

                CodeUnitEditPart cuep = (CodeUnitEditPart) methodEP;
                for (Object conn : cuep.getSourceConnections()) {
                    if (conn instanceof ConnectionEditPart
                            && ((ConnectionEditPart) conn).getTarget() instanceof CodeUnitEditPart)
                        targets.add(((ConnectionEditPart) conn).getTarget());
                }
                return targets;
            }
        };
        return action;
    }

    @Override
    /**
     * If a class contains a static or instance initializer, there will be a
     * stmt in the repo saying the class contains a <clinit> Resource. However,
     * the builder places an identical statement in the repo when the
     * class contains a static field, which is already handled separately
     * and we therefore want to remove from the given list. To identify 
     * the case where the child Artifact is an initializer, we can test 
     * whether there are any 'calls' stmts for it in the repo, since only 
     * initialization blocks will have these stmts. 
     * @return a list of the children Artifacts that are present as
     * <clinit> in the repository but do not actually represent initializer
     * blocks and therefore should not be included in the members button
     */
    public List<Artifact> getFakeInitializerChildren() {

        List<Artifact> fakes = new ArrayList<Artifact>();
        Set<Artifact> children = new HashSet<Artifact>(getArtifact().getArt().queryChildrenArtifacts(getRepo()));
        for (Artifact child : new ArrayList<Artifact>(children)) {
            if (!"<clinit>".equals(child.queryName(getRepo())))
                continue;

            boolean makesAnyCalls = getRepo().hasStatement(child.elementRes, RJCore.calls, null);
            if (!makesAnyCalls)
                fakes.add(child);
        }

        return fakes;
    }

    /*
     * other misc. functionality
     *
     */

    @Override
    public String getRelModelLabel(Object model) {
        if (!(model instanceof Artifact))
            return "{err}";
        String label = this.getLabel((Artifact) model, this.getArtifact().getArt());

        // change label for calls to anon class constructors
        if (RSECore.isAnonClassConstructorCall(label))
            label = label.replace("##", "#");

        // Mark library code. (If model is lib code, only reach 
        // here if user has pref set to show lib code in menus)
        if (!RSECore.isInitialized(getBrowseModel().getRepo(), ((Artifact) model).elementRes))
            label = label + ArtifactFragment.libraryAnnotation;
        return label;
    }

    @Override
    public List<NavAidsSpec> getSingleSelectHandlesSpecList(final NavAidsEditPolicy bdec) {
        final List<NavAidsSpec> decorations = new ArrayList<NavAidsSpec>(5);

        // let others plug into here
        Set<INavAidSpecSource> registeredNASS = PluggableNavAids.getRegisteredNavAidsSources();
        for (INavAidSpecSource iNavAidSpecSource : registeredNASS) {
            decorations.add(iNavAidSpecSource.getNavAids(decorations, this));
        }

        // Delete
        final NavAidsSpec deleteSpec = new NavAidsSpec() {
            @Override
            public void buildHandles() {
                IFigure btn;

                // TODO Need to rework the command infrastructure so that expand and
                // collapse buttons work correctly
                // currently collapse/ reduce does nothing

                //btn = getReqButton(CodeUnitEditPart.this, "collapse.gif", REQ_REDUCE, COLLAPSE);
                //if (btn != null) decorationFig.add(btn);

                //btn = getReqButton(CodeUnitEditPart.this, "expand.gif", REQ_EXPAND, EXPAND);
                //if (btn != null) decorationFig.add(btn);

                btn = getReqButton(CodeUnitEditPart.this, "remove.gif", RequestConstants.REQ_DELETE, HIDE);
                if (btn != null)
                    decorationFig.add(btn);
            }

            @Override
            public Point getHandlesPosition(IFigure containerFig) {
                // Place in upper right corner, to right of any other nav aid
                // (currently only other nav aid in this location is calls nav
                // aid) except the context-menu button, which is the rightmost,
                // so order would be like calls, delete, context-menu
                Point decPos = containerFig.getBounds().getTopRight();
                decPos.x = Math.max(decPos.x, firstNAS.decorationFig.getBounds().getTopRight().x);
                for (NavAidsSpec naSpec : decorations) {
                    if (naSpec.decorationFig == null)
                        continue;

                    if (naSpec instanceof RelNavAidsSpec
                            && RJCore.calls.equals(((RelNavAidsSpec) naSpec).getRel().res)
                            && ((RelNavAidsSpec) naSpec).getRel().isFwd) {
                        // delete nav aid is right of every other 
                        // nav aid - currently only calls nav aid
                        Rectangle callsBtnBounds = naSpec.decorationFig.getBounds().getCopy();
                        decPos.x = callsBtnBounds.x + callsBtnBounds.width;
                        if (bdec.getHost() instanceof ClassEditPart)
                            decPos.x = decPos.x - 8;
                    } else if (naSpec.decorationFig.containsPoint(decPos.getCopy().getTranslated(3, 3))
                            && !(bdec.getHost() instanceof ClassEditPart)) {
                        decPos.x = decPos.x + naSpec.decorationFig.getBounds().width;
                    }
                }
                return decPos;
            }
        };
        decorations.add(deleteSpec);

        // Context menu (a nav aid whose menu contains all the actions that are found 
        // in the selected item's context menu. Will make it easier for a user to 
        // realize and find the capabilities available to him and the actions 
        // that can be performed on a diagram item. 
        decorations.add(new NavAidsSpec() {
            @Override
            public void buildHandles() {
                IFigure btn = RSEContextMenuProvider.getContextMenuNavAid(getViewer());
                if (btn != null)
                    decorationFig.add(btn);
            }

            @Override
            public Point getHandlesPosition(IFigure containerFig) {
                // Place in upper right corner, to right of any other nav aid
                Point decPos = containerFig.getBounds().getTopRight();
                decPos.x = Math.max(decPos.x, firstNAS.decorationFig.getBounds().getTopRight().x);
                for (NavAidsSpec naSpec : decorations) {
                    if (naSpec.decorationFig == null)
                        continue;

                    if (naSpec.equals(deleteSpec)) {
                        // delete nav aid is right of every other nav aid (currently only
                        // calls nav aid), so place to right of delete so this is rightmost
                        Rectangle deleteBtnBounds = naSpec.decorationFig.getBounds().getCopy();
                        decPos.x = deleteBtnBounds.x + deleteBtnBounds.width;
                        decPos.translate(new Point(Display.getCurrent().getBounds().x, 0));
                    } else if (naSpec.decorationFig.containsPoint(decPos.getCopy().getTranslated(3, 3))
                            && !(bdec.getHost() instanceof ClassEditPart)) {
                        decPos.x = decPos.x + naSpec.decorationFig.getBounds().width;
                    }
                    //               decPos.x = decPos.x;
                }
                return decPos;
            }
        });

        return decorations;
    }

    @Override
    public List<NavAidsSpec> getMultiSelectHandlesSpecList(NavAidsEditPolicy bdec) {
        final List<NavAidsSpec> decorations = new ArrayList<NavAidsSpec>(5);

        // TODO: Add multi delete button and get it working properly
        /*decorations.add(new NavAidsSpec() {
           @Override
           public void buildHandles() {
        IFigure btn;
               
           btn = getReqButton(CodeUnitEditPart.this, "remove.gif", RequestConstants.REQ_DELETE, NavAidsEditPolicy.REMOVE_ALL_SELECTED);
           if (btn != null) decorationFig.add(btn);
           }
        @Override
           public Point getHandlesPosition(IFigure containerFig) {
           while (containerFig instanceof IFigureWithContents) {
                if (containerFig instanceof CodeUnitFigure)
                    containerFig = ((CodeUnitFigure)containerFig).getLabel();
                else
                   containerFig = ((IFigureWithContents)containerFig).getContentFig();
           }
                
            return containerFig.getBounds().getTopRight(); 
        }         
        });*/
        return decorations;
    }

    public CodeUnitFigure getCodeUnitFigure() {
        IFigure fig = getFigure();
        while (fig instanceof IFigureWithContents) {
            if (fig instanceof CodeUnitFigure)
                return (CodeUnitFigure) fig;
            else
                fig = ((IFigureWithContents) fig).getContentFig();
        }
        return null;
    }

    @Override
    protected void refreshVisuals() {
        // set detail level flag in CodeUnit
        if (getModel() instanceof ArtifactFragment)
            CodeUnit.detailLevel = ((ArtifactFragment) getModel()).getRootArt().getDetailLevel();

        ColorScheme.init();
        updateColors();

        CodeUnitFigure cuf = getCodeUnitFigure();
        String localLabel = getLabel();
        this.label = getLabel();
        if (cuf != null) {

            // change label for Anon classes
            if (RSECore.isAnonClassName(localLabel)) {
                String label = RSECore.stripAnonNumber(localLabel);
                cuf.getLabel().setText(label);
            } else
                cuf.getLabel().setText(localLabel);

            Artifact art = getArtifact().getArt();
            ReloRdfRepository repo = getBrowseModel().getRepo();
            Image icon;
            if (getModel() instanceof UserCreatedFragment)
                icon = ((UserCreatedFragment) getModel()).getIcon(repo);
            else {
                ImageDescriptor id = this.getIconDescriptor(art, art.queryType(repo));
                icon = ImageCache.calcImageFromDescriptor(id);
            }

            if (com.architexa.diagrams.ColorScheme.SchemeV1) {
                cuf.getLabel().setIcon(icon);
            } else {
                cuf.getLabel().setIcon(null);
                // need to add some spacing when we arent showing an icon
                cuf.getLabel().setIconDimension(new Dimension(5, 5));
            }
            PointPositionedDiagramPolicy.getLocToFig(this.getArtifact(), (Figure) this.getFigure());
        }
    }

    // implemented by individual packages/classes
    protected void updateColors() {
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.gef.EditPart#getCommand(org.eclipse.gef.Request)
     */
    @Override
    public Command getCommand(Request request) {
        if (request.getType().equals(REQ_EXPAND)) {
            return getExpandCmd();
        }
        if (request.getType().equals(RequestConstants.REQ_OPEN)) {

            if (getArtFrag() instanceof UserCreatedFragment)
                // user can rename his created frag by double clicking it
                return getDirectEditCommand();

            if (!(this instanceof PackageEditPart)) { // can't open package in editor
                // this should be an action, not a command, i.e. it should not go on
                // the undo/redo stack
                return new OpenInEditorCommand(getCU(), getRepo());
            }
        }
        if (request.getType().equals(RequestConstants.REQ_OPEN) && !(this instanceof PackageEditPart)) {
            // this should be an action, not a command, i.e. it should not go on
            // the undo/redo stack

            return new OpenInEditorCommand(getCU(), getRepo());
        }
        if (request.getType().equals(RequestConstants.REQ_DELETE)) {
            final CodeUnitEditPart cuep = CodeUnitEditPart.this;
            // ignore requests if the parent will be deleted anyway
            if (request instanceof GroupRequest && ((GroupRequest) request).getEditParts().contains(getParent()))
                return null;
            return new DeleteCommand(getRootController().getRootArtifact(), cuep.getArtifact(),
                    getRootController());
        }
        if (request.getType().equals(REQ_REDUCE)) {
            final CodeUnitEditPart cuep = CodeUnitEditPart.this;
            if (cuep.currDL == cuep.getMinimalDL())
                return null;
            return new Command() {
                @Override
                public void execute() {
                    cuep.suggestDetailLevelDecrease();
                }
            };
        }
        if (request.getType().equals(REQ_ADD)) {
            final CodeUnitEditPart cuep = CodeUnitEditPart.this;
            List<EditPart> selectedEPs = getGroupRequestEditParts((GroupRequest) request);
            ;
            CompoundCommand compoundMoveCmd = new CompoundCommand("Move Item");
            for (EditPart ep : selectedEPs) {
                final ArtifactFragment reqChildAF = (ArtifactFragment) ep.getModel();

                // if one of the moving EPs is a comment/rel/etc then ignore r
                if (!(ep instanceof CodeUnitEditPart))
                    continue;

                EditPart srcParentEP = ((CodeUnitEditPart) ep).getParent();
                ArtifactFragment targetAF = (ArtifactFragment) cuep.getParent().getModel();
                ArtifactFragment srcAF = (ArtifactFragment) srcParentEP.getModel();

                // moving to a specific spot in a compartment
                // ignore adds for items that are not within a class or are in a different compartment
                // these will be handled by the AbstractReloEditPart
                if (cuep.getParent().getParent().getModel() instanceof CodeUnit
                        && (targetAF.getClass() == srcAF.getClass())
                        && (((CodeUnit) cuep.getParent().getParent().getModel()).getArt()
                                .equals(((CodeUnit) srcParentEP.getParent().getModel()).getArt())
                                && cuep.getParent() instanceof CompartmentCUEditPart)) {
                    List<ArtifactFragment> compartmentChildren = ((CompartmentedCodeUnitEditPart) cuep.getParent()
                            .getParent()).getArtifact().getShownChildren();
                    int newIndex = compartmentChildren.indexOf(cuep.getArtifact());
                    int oldIndex = ((ArtifactFragment) srcParentEP.getParent().getModel()).getShownChildren()
                            .indexOf(reqChildAF);
                    ArtifactFragment parentAF = ((ArtifactFragment) cuep.getParent().getParent().getModel());
                    boolean move = false;
                    if (!((CodeUnit) cuep.getParent().getParent().getModel())
                            .equals(((CodeUnit) srcParentEP.getParent().getModel())))
                        move = true;
                    compoundMoveCmd.add(new InteriorMoveCommand(getRootController().getRootArtifact(), parentAF,
                            reqChildAF, newIndex, oldIndex, move));
                }
            }
            if (compoundMoveCmd.isEmpty())
                return super.getCommand(request);
            else
                return compoundMoveCmd;
        }
        // if we are moving connections make sure they are connecting the
        // correct editparts
        if (request.getType().equals(REQ_CONNECTION_START) || request.getType().equals(REQ_CONNECTION_END)
                || request.getType().equals(REQ_RECONNECT_SOURCE)
                || request.getType().equals(REQ_RECONNECT_TARGET)) {
            Object newConn = null;
            if (request instanceof CreateConnectionRequest)
                newConn = ((CreateRequest) request).getNewObject();
            if (request instanceof ReconnectRequest)
                newConn = ((ReconnectRequest) request).getConnectionEditPart().getModel();

            if (!(newConn instanceof NamedRel))
                return null;
            String connResource = ((NamedRel) newConn).relationRes.toString();
            if (connResource.contains(RSECore.namedRel.toString()))
                return super.getCommand(request);
            if (connResource.contains(RJCore.inherits.toString()) && this instanceof ClassEditPart)
                return super.getCommand(request);
            if (connResource.contains(RJCore.overrides.toString()) && this instanceof MethodEditPart)
                return super.getCommand(request);
            if (connResource.contains(RJCore.calls.toString()) && this instanceof MethodEditPart)
                return super.getCommand(request);

            return null;
        }
        return super.getCommand(request);
    }

    @SuppressWarnings("unchecked")
    private static List<EditPart> getGroupRequestEditParts(GroupRequest grpReq) {
        return (List<EditPart>) grpReq.getEditParts();
    }

    protected Command getExpandCmd() {
        final CodeUnitEditPart cuep = CodeUnitEditPart.this;
        if (cuep.currDL == cuep.getMaximumDL())
            return null;
        return new Command() {
            @Override
            public void execute() {
                //logger.info("COMMAND: Open execute start");
                //logger.error("Opening: " + getCU());
                //realizeParent();
                CompoundCommand relaizeParentCmd = new CompoundCommand();
                CodeUnitEditPart.this.realizeParent(relaizeParentCmd);
                relaizeParentCmd.execute();
                suggestDetailLevelIncrease();
                //logger.info("COMMAND: Open execute end");
            }

            @Override
            public void undo() {
                suggestDetailLevelDecrease();
                logger.info("Trying to undo open request!!");
            }
        };
    }

    // Subclassing LabelDirectEditManager so that we can access its
    // commit() method in order to end an edit when the user hits Enter
    public class CULabelDirectEditManager extends LabelDirectEditManager {
        public CULabelDirectEditManager(GraphicalEditPart source, Class<TextCellEditor> editorType,
                CellEditorLocator locator, IFigure directEditFigure) {
            super(source, editorType, locator, directEditFigure);
        }

        @Override
        protected void commit() {
            super.commit();
        }
    }

    protected CULabelDirectEditManager manager;
    protected String oldName;

    protected void installDirectEditPolicy() {
        if (!(this instanceof UndoableLabelSource))
            return;
        installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE,
                new AnnoLabelDirectEditPolicy((UndoableLabelSource) this, getTextChangeCmdName()));
    }

    protected String getTextChangeCmdName() {
        return "Edit Name";
    }

    protected Command getDirectEditCommand() {
        if (!(this instanceof LabelSource))
            return null;
        final CodeUnitEditPart cuep = CodeUnitEditPart.this;
        EditDomain editDomain = getRoot().getViewer().getEditDomain();
        final RSEEditor editor = (RSEEditor) ((DefaultEditDomain) editDomain).getEditorPart();
        return new Command("Edit User Created Element") {
            @Override
            public void execute() {
                if (oldName == null)
                    oldName = getArtFrag().getArt().queryName(getRepo());
                if (manager == null)
                    manager = new CULabelDirectEditManager(cuep, TextCellEditor.class,
                            new AnnoLabelCellEditorLocator(getAnnoLabelFigure()), getAnnoLabelFigure()) {
                        @Override
                        protected void commit() {
                            super.commit();
                            // Typing finished, so update Resource and repo with new name
                            editor.rseInjectableCommentEditor.run();
                            getArtFrag().setInstanceName(getAnnoLabelText());
                        }
                    };
                manager.show();
                editor.rseInjectableCommentEditor.handleTextEditing(getAnnoLabelText(), (UndoableLabelSource) cuep,
                        manager, getAnnoLabelFigure());
            }
        };
    }

    public String getOldAnnoLabelText() {
        return oldName;
    }

    public void setOldAnnoLabelText(String oldName) {
        this.oldName = oldName;
    }

    public IFigure getAnnoLabelFigure() {
        return getCodeUnitFigure().getLabel();
    }

    public String getAnnoLabelText() {
        return getCodeUnitFigure().getLabel().getText();
    }

    public void setAnnoLabelText(String str) {
        if (str == null)
            return;

        if (str.contains("\n") || str.contains("\r") || str.contains("\t")) {
            // Not allowing multiple lines in text, so treating
            // user hitting Enter as the end of the edit
            str = str.replaceAll("\n", "");
            str = str.replaceAll("\r", "");
            str = str.replaceAll("\t", "");
            updateFigure(str);
            manager.commit();
            return;
        }
        updateFigure(str);
    }

    private void updateFigure(String newName) {
        ((Label) getAnnoLabelFigure()).setText(newName);
        ((Label) getAnnoLabelFigure()).setToolTip(new Label(newName));
    }

    //TODO Remove
    //   private void updateResource() {
    //      ReloRdfRepository repo = getRepo();
    //
    //      ArtifactFragment thisFrag = getArtFrag();
    //      Resource oldRes = thisFrag.getArt().elementRes;
    //
    //      String namePreEdit = getArtFrag().getArt().queryName(repo);
    //      String newName = getAnnoLabelText();
    //
    //      String newResString = oldRes.toString().replace(namePreEdit, newName);
    //      newResString = newResString.substring(newResString.indexOf("#")+1); // removing namespace
    //      Resource newRes = thisFrag.getRootArt().getBrowseModel().createResForUserCreatedFrag(newResString);
    //      thisFrag.setArt(new Artifact(newRes));
    //
    //      repo.startTransaction();
    //
    //      StatementIterator iter = repo.getStatements(oldRes, null, null);
    //      while(iter.hasNext()) {
    //         Statement stmt = iter.next();
    //         repo.addStatement(newRes, stmt.getPredicate(), stmt.getObject());
    //      }
    //      repo.removeStatements(oldRes, null, null);
    //
    //      iter = repo.getStatements(null, null, oldRes);
    //      while(iter.hasNext()) {
    //         Statement stmt = iter.next();
    //         repo.addStatement(stmt.getSubject(), stmt.getPredicate(), newRes);
    //      }
    //      repo.removeStatements((Resource)null, null, oldRes);
    //
    //      repo.commitTransaction();
    //   }

    /* (non-Javadoc)
      * @see org.eclipse.gef.EditPart#performRequest(org.eclipse.gef.Request)
      */
    @Override
    public void performRequest(Request req) {
        //System.err.println("performRequest: req.getType()= " + req.getType());
        Command command = getCommand(req);
        if (command != null) {
            if (command.canExecute()) {
                execute(command);
            }
            return;
        }

        super.performRequest(req);
    }

    /* (non-Javadoc)
    * @see org.eclipse.gef.EditPart#refresh()
    */
    @Override
    public void refresh() {
        //if (getModelSourceConnections().size()
        //      + getModelTargetConnections().size() > 0) {
        //   System.err.println(this + ",s:"
        //         + getModelSourceConnections().size() + ",t:"
        //         + getModelTargetConnections().size());
        //}
        super.refresh();
    }

    public List<ArtifactFragment> getNonDerivedModelChildren() {
        List<ArtifactFragment> retVal = new ArrayList<ArtifactFragment>(getModelChildren());
        ListIterator<ArtifactFragment> li = retVal.listIterator();
        while (li.hasNext()) {
            if (li.next() instanceof DerivedArtifact) {
                li.remove();
            }
        }
        return retVal;
    }

    @Override
    public void buildContextMenu(IMenuManager menu) {
        super.buildContextMenu(menu);

        // Don't need javadoc and Open/Find menu entries if this is a palette frag
        if (!(this.getModel() instanceof UserCreatedFragment)) {

            // javadoc actions:

            IAction action = new AddJavaDocAction(this);
            if (!((AddJavaDocAction) action).canRun((ArtifactFragment) getModel()))
                action.setEnabled(false);
            menu.appendToGroup(RSEContextMenuProvider.GROUP_RSE_TOOLS, action);

            action = new EditJavaDocDialogAction(this, this.getRepo());
            menu.appendToGroup(RSEContextMenuProvider.GROUP_RSE_TOOLS, action);
            //Disable if cannot run
            if (!((EditJavaDocDialogAction) action).canRun((ArtifactFragment) getModel()))
                action.setEnabled(false);

            // Put Open in Java Editor if jdt element
            // or Find in Workspace if not jdt element

            // JDT Wkspace Element: Open in a java editor
            action = new Action(JDTUISupport.getOpenInJavaEditorActionString(),
                    JDTUISupport.getOpenInJavaEditorActionIcon()) {
                @Override
                public void run() {
                    CodeUnitEditPart cuep = CodeUnitEditPart.this;
                    JDTUISupport.openInEditor(cuep.getCU(), cuep.getRepo());
                }
            };

            // Otherwise: open the search dialog
            if (!RJCore.isJDTWksp(getArtFrag().getArt().elementRes)) {
                action = new Action("Find in Workspace", Activator.getImageDescriptor("icons/jcu_obj.gif")) {
                    @Override
                    public void run() {
                        try {
                            ISearchQuery query = DefaultTextSearchQueryProvider.getPreferred()
                                    .createQuery(getArtFrag().getArt().queryName(getRepo()));
                            InternalSearchUI searchUI = InternalSearchUI.getInstance();
                            searchUI.addQuery(query);
                            searchUI.runSearchInForeground(
                                    new ProgressMonitorDialog(Display.getCurrent().getActiveShell()), query,
                                    searchUI.getSearchView());
                        } catch (CoreException e) {
                            logger.error("Error parsing name for search", e);
                            e.printStackTrace();
                        }
                    }
                };
            }
            menu.appendToGroup(RSEContextMenuProvider.GROUP_RSE_EDITORS, action);
        }

        if (!(this instanceof ClassEditPart))
            return;
        final ClassEditPart cep = (ClassEditPart) this;
        List<ArtifactEditPart> epList = new ArrayList<ArtifactEditPart>();
        epList.add(cep);
        addColorAction(menu, epList);

    }

    public static String DEFAULT = "Default";

    public void addColorAction(IMenuManager menu, List<ArtifactEditPart> epList) {

        MenuManager subMenu = new MenuManager("Highlight");
        subMenu.add(getColorAction(ColorScheme.RED, epList));
        subMenu.add(getColorAction(ColorScheme.BLUE, epList));
        subMenu.add(getColorAction(ColorScheme.GREEN, epList));
        subMenu.add(new Separator());
        subMenu.add(getColorAction(DEFAULT, epList));

        // Highlight menu goes in the edit appearance section
        menu.appendToGroup(RSEContextMenuProvider.GROUP_RSE_EDIT_APPEARANCE, subMenu);
    }

    private IAction getColorAction(final String actnText, final List<ArtifactEditPart> listEP) {
        return new Action(actnText) {
            @Override
            public void run() {
                CompoundCommand cmd = new CompoundCommand("Coloring: " + actnText);
                BuildStatus.addUsage("Relo > " + cmd.getLabel());
                for (ArtifactEditPart cep : listEP)
                    cmd.add(new ColorActionCommand(actnText, (ArtifactFragment) ((EditPart) cep).getModel()));

                getViewer().getEditDomain().getCommandStack().execute(cmd);
                refresh();
            }
        };
    }

    // dir is true if this is a fwd relation (Supertypes) and false for reverse relations (subtypes)
    public IAction createHierarchyAction(final String label, final CodeUnitEditPart cuep,
            final DirectedRel directedRel) {
        return new Action(label) {
            @Override
            public void run() {
                try {
                    CodeUnit cu = cuep.getCU();
                    Map<Artifact, ArtifactFragment> addedArtToAF = new HashMap<Artifact, ArtifactFragment>();

                    List<CodeUnit> cuList;
                    if (directedRel.isFwd)
                        cuList = cu.getExtendedTypeList(cuep.getRepo());
                    else
                        cuList = cu.getInheritedTypeList(cuep.getRepo());

                    ArtifactFragment srcAF = cuep.getArtFrag();
                    CompoundCommand tgtCmd = new CompoundCommand(label);
                    ReloController rc = (ReloController) cuep.getRoot().getContents();

                    Predicate filter = Filters.getTypeFilter(cuep.getRepo(), RJCore.interfaceType);
                    for (final Artifact relCU : srcAF.getArt().queryArtList(getRepo(), directedRel, filter)) {
                        if (!rc.canAddRel(srcAF, directedRel, relCU))
                            continue;
                        AddNodeAndRelCmd addInterfaceCmd = new AddNodeAndRelCmd(rc, srcAF, directedRel, relCU,
                                addedArtToAF);
                        tgtCmd.add(addInterfaceCmd);
                        // layout at end, not here
                        // tgtCmd.add(rc.getLayoutCmd());
                        ((ReloDoc) rc.getRootArtifact()).showIncludedRelationships(tgtCmd,
                                addInterfaceCmd.getNewArtFrag());
                    }
                    for (ArtifactFragment tgtAF : cuList) {
                        recursiveAddHierarchy(tgtCmd, cuep, srcAF, tgtAF, directedRel, addedArtToAF);
                    }

                    ((ReloController) cuep.getRoot().getContents()).execute(tgtCmd);
                } catch (Exception e) {
                    logger.error("Unexpected exception", e);
                }
            }
        };
    }

    private void recursiveAddHierarchy(CompoundCommand tgtCmd, CodeUnitEditPart cuep, ArtifactFragment srcAF,
            ArtifactFragment tgtAF, DirectedRel directedRel, Map<Artifact, ArtifactFragment> addedArtToAF) {
        ReloController rc = (ReloController) cuep.getRoot().getContents();
        // Add Classes
        AddNodeAndRelCmd addCmd = new AddNodeAndRelCmd(rc, srcAF, directedRel, tgtAF.getArt(), addedArtToAF);
        tgtCmd.add(addCmd);
        ((ReloDoc) rc.getRootArtifact()).showIncludedRelationships(tgtCmd, addCmd.getNewArtFrag());
        // layout at end, not here
        // tgtCmd.add(rc.getLayoutCmd());

        ArtifactFragment newSrcAF = addCmd.getNewArtFrag();
        List<CodeUnit> cuList;
        if (directedRel.isFwd)
            cuList = ((CodeUnit) tgtAF).getExtendedTypeList(cuep.getRepo());
        else
            cuList = ((CodeUnit) tgtAF).getInheritedTypeList(cuep.getRepo());

        // Add Interfaces
        Predicate filter = Filters.getTypeFilter(cuep.getRepo(), RJCore.interfaceType);
        for (final Artifact relCU : srcAF.getArt().queryArtList(getRepo(), directedRel, filter)) {
            if (!rc.canAddRel(srcAF, directedRel, relCU))
                continue;
            AddNodeAndRelCmd addInterfaceCmd = new AddNodeAndRelCmd(rc, srcAF, directedRel, relCU, addedArtToAF);
            tgtCmd.add(addInterfaceCmd);
            tgtCmd.add(rc.getLayoutCmd());
            ((ReloDoc) rc.getRootArtifact()).showIncludedRelationships(tgtCmd, addInterfaceCmd.getNewArtFrag());
        }

        for (ArtifactFragment newTgtAF : cuList) {
            recursiveAddHierarchy(tgtCmd, cuep, newSrcAF, newTgtAF, directedRel, addedArtToAF);
        }
    }

    @Override
    protected Set<URI> getFilteredPredsForAutoBrowse() {
        Set<URI> filteredPreds = super.getFilteredPredsForAutoBrowse();
        filteredPreds.add(RJCore.access);
        return filteredPreds;
    }
}