Ptidej-v5.2-master/POM Tests/data/ArgoUML_0.28/src/org/argouml/uml/ui/TabStyle.java
// $Id: TabStyle.java 16902 2009-03-19 20:41:32Z thn $
// Copyright (c) 1996-2008 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

package org.argouml.uml.ui;

import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Hashtable;

import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;

import org.apache.log4j.Logger;
import org.argouml.application.api.AbstractArgoJPanel;
import org.argouml.kernel.DelayedChangeNotify;
import org.argouml.kernel.DelayedVChangeListener;
import org.argouml.kernel.Project;
import org.argouml.kernel.ProjectManager;
import org.argouml.model.Model;
import org.argouml.swingext.UpArrowIcon;
import org.argouml.ui.StylePanel;
import org.argouml.ui.TabFigTarget;
import org.argouml.ui.targetmanager.TargetEvent;
import org.argouml.ui.targetmanager.TargetListener;
import org.argouml.uml.diagram.ArgoDiagram;
import org.argouml.uml.diagram.DiagramUtils;
import org.argouml.uml.diagram.ui.FigAssociationClass;
import org.argouml.uml.diagram.ui.FigClassAssociationClass;
import org.argouml.uml.util.namespace.Namespace;
import org.argouml.uml.util.namespace.StringNamespace;
import org.argouml.uml.util.namespace.StringNamespaceElement;
import org.tigris.gef.presentation.Fig;
import org.tigris.gef.presentation.FigEdge;

/**
 * Provides support for changing the appearance of a diagram element. For each
 * class of a diagram element, the TabStyle class attempts to find an according
 * class of StylePanel which contains the attributes to be modified in terms of
 * style.
 * <p>
 * The constructor of TabStyle takes an array argument which contains possible
 * base names for these style panels, or by default StylePanel and SP,
 * alternating between these two prefixes and the namespace of the Fig class or
 * <code>org.argouml.ui</code>. With this configuration, the stylepanel for
 * e.g. <code>org.argouml.uml.diagram.static.structure.ui.FigClass</code>,
 * will be looked at in the following places:
 * <ul>
 * <li>org.argouml.uml.diagram.static_structure.ui.StylePanelFigClass
 * <li>org.argouml.uml.diagram.static_structure.ui.SPFigClass
 * <li>org.argouml.ui.StylePanelFigClass
 * <li>org.argouml.ui.SPFigClass
 * </ul>
 * It continues to traverse the superclass structure until a matching class has
 * been found, e.g.
 * <ul>
 * <li>org.argouml.uml.diagram.ui.StylePanelFigNodeModelElement
 * <li>org.argouml.uml.diagram.ui.SPFigNodeModelElement
 * <li>org.argouml.ui.StylePanelFigNodeModelElement
 * <li>org.argouml.ui.SPFigNodeModelElement
 * </ul>
 * If a stylepanel had been found, it will be stored in a cache.<p>
 *
 * According the decision taken in issue 502, this tab is renamed "Presentation"
 * for the user. And the Presentation tab shall contain presentation options,
 * and no semantic UML properties (which belong in the "Properties" panel).
 * In contrast, the diagram pop-up menu for a model element
 * may access both presentation options as well as semantic UML properties. <p>
 *
 * Note also that the semantic properties of a UML model element exist in one
 * copy only but the presentation options exist in one copy per diagram
 * that the model element is showing in. E.g. a class could have
 * attributes hidden in one diagram and showing in another. So, for the user
 * it would be very logical to separate these 2 kinds of settings
 * on different tabs.
 *
 */
public class TabStyle extends AbstractArgoJPanel implements TabFigTarget,
        PropertyChangeListener, DelayedVChangeListener {

    private static final Logger LOG = Logger.getLogger(TabStyle.class);

    private Fig target;

    private boolean shouldBeEnabled = false;

    private JPanel blankPanel = new JPanel();

    private Hashtable<Class, TabFigTarget> panels = 
        new Hashtable<Class, TabFigTarget>();

    private JPanel lastPanel = null;

    /**
     * The stylepanel shown by the tab style.
     */
    private StylePanel stylePanel = null;

    private String[] stylePanelNames;

    private EventListenerList listenerList = new EventListenerList();

    /**
     * The constructor.
     *
     * @param tabName the name of the tab
     * @param spn style panel names
     */
    public TabStyle(String tabName, String[] spn) {
        super(tabName);
        this.stylePanelNames = spn;
        setIcon(new UpArrowIcon());
        setLayout(new BorderLayout());
    }

    /**
     * Construct a default stylepanel with basenames <code>StylePanel</code>
     * and <code>SP</code>, resulting in the lookup order described above.
     */
    public TabStyle() {
        this("tab.style", new String[] {"StylePanel", "SP"});
    }

    /**
     * Adds a style panel to the internal list. This allows a plugin to add and
     * register a new style panel at run-time. This property style will then be
     * displayed in the details pane whenever an element of the given metaclass
     * is selected.
     *
     * @param c
     *            the metaclass whose details show be displayed in the property
     *            panel p
     * @param s
     *            an instance of the style panel for the metaclass m
     */
    public void addPanel(Class c, StylePanel s) {
        panels.put(c, s);
    }

    /**
     * Sets the target of the style tab.
     * 
     * @param t
     *            is the new target
     */
    public void setTarget(Object t) {
        if (target != null) {
            target.removePropertyChangeListener(this);
            if (target instanceof FigEdge) {
                // In this case, the bounds are determined by the FigEdge
                ((FigEdge) target).getFig().removePropertyChangeListener(this);
            }
            if (target instanceof FigAssociationClass) {
                // In this case, the bounds (of the box) are determined 
                // by the FigClassAssociationClass
                FigClassAssociationClass ac = 
                    ((FigAssociationClass) target).getAssociationClass();
                // A newly created AssociationClass may not have all its parts
                // created by the time we are called
                if (ac != null) {
                    ac.removePropertyChangeListener(this);
                }
            }
        }

        // TODO: Defer most of this work if the panel isn't visible - tfm

        // the responsibility of determining if the given target is a
        // correct one for this tab has been moved from the
        // DetailsPane to the member tabs of the details pane. Reason for
        // this is that the details pane is configurable and cannot
        // know what's the correct target for some tab.
        if (!(t instanceof Fig)) {
            if (Model.getFacade().isAModelElement(t)) {
                ArgoDiagram diagram = DiagramUtils.getActiveDiagram();
                if (diagram != null) {
                    t = diagram.presentationFor(t);
                }
                if (!(t instanceof Fig)) {
                    Project p = ProjectManager.getManager().getCurrentProject();
                    Collection col = p.findFigsForMember(t);
                    if (col == null || col.isEmpty()) {
                        return;
                    }
                    t = col.iterator().next();
                }
                if (!(t instanceof Fig)) {
                    return;
                }
            } else {
                return;
            }

        }

        target = (Fig) t;
        if (target != null) {
            target.addPropertyChangeListener(this);
            // TODO: This shouldn't know about the specific type of Fig that 
            // is being displayed.  That couples it too strongly to things it
            // shouldn't need to know about - tfm - 20070924
            if (target instanceof FigEdge) {
                // In this case, the bounds are determined by the FigEdge
                ((FigEdge) target).getFig().addPropertyChangeListener(this);
            }
            if (target instanceof FigAssociationClass) {
                // In this case, the bounds (of the box) are determined 
                // by the FigClassAssociationClass
                FigClassAssociationClass ac = 
                    ((FigAssociationClass) target).getAssociationClass();
                // A newly created AssociationClass may not have all its parts
                // created by the time we are called
                if (ac != null) {
                    ac.addPropertyChangeListener(this);
                }
            }
        }
        if (lastPanel != null) {
            remove(lastPanel);
            if (lastPanel instanceof TargetListener) {
                removeTargetListener((TargetListener) lastPanel);
            }
        }
        if (t == null) {
            add(blankPanel, BorderLayout.NORTH);
            shouldBeEnabled = false;
            lastPanel = blankPanel;
            return;
        }
        shouldBeEnabled = true;
        stylePanel = null;
        Class targetClass = t.getClass();

        stylePanel = findPanelFor(targetClass);

        if (stylePanel != null) {
            removeTargetListener(stylePanel);
            addTargetListener(stylePanel);
            stylePanel.setTarget(target);
            add(stylePanel, BorderLayout.NORTH);
            shouldBeEnabled = true;
            lastPanel = stylePanel;
        } else {
            add(blankPanel, BorderLayout.NORTH);
            shouldBeEnabled = false;
            lastPanel = blankPanel;
        }
        validate();
        repaint();
    }

    /*
     * @see org.argouml.ui.TabTarget#refresh()
     */
    public void refresh() {
        setTarget(target);
    }

    /**
     * Find the stylepanel for a given target class.
     *
     * @param targetClass
     *            the target class
     * @return a Stylepanel object or <code>null</code> on error
     */
    public StylePanel findPanelFor(Class targetClass) {
        Class panelClass = null;
        TabFigTarget p = panels.get(targetClass);
        if (p == null) {
            Class newClass = targetClass;
            while (newClass != null && panelClass == null) {
                panelClass = panelClassFor(newClass);
                newClass = newClass.getSuperclass();
            }
            if (panelClass == null) {
                return null;
            }
            try {
                p = (TabFigTarget) panelClass.newInstance();
            } catch (IllegalAccessException ignore) {
                LOG.error(ignore);
                return null;
            } catch (InstantiationException ignore) {
                LOG.error(ignore);
                return null;
            }
            panels.put(targetClass, p);
        }
        LOG.debug("found style for " + targetClass.getName() + "("
                + p.getClass() + ")");
        return (StylePanel) p;

    }

    /**
     * Get the class for the required stylepanel.
     *
     * @param targetClass the class of the current selected target.
     * @return the panel class for the class given or
     * null if none available.
     */
    public Class panelClassFor(Class targetClass) {
        if (targetClass == null) {
            return null;
        }

        StringNamespace classNs = (StringNamespace) StringNamespace
                .parse(targetClass);

        StringNamespace baseNs = (StringNamespace) StringNamespace.parse(
                "org.argouml.ui.", Namespace.JAVA_NS_TOKEN);

        StringNamespaceElement targetClassElement =
        	(StringNamespaceElement) classNs.peekNamespaceElement();

        LOG.debug("Attempt to find style panel for: " + classNs);

        classNs.popNamespaceElement();

        String[] bases = new String[] {
                classNs.toString(), baseNs.toString() 
        };
        for (String stylePanelName : stylePanelNames) {
            for (String baseName : bases) {
                String name = baseName + "." + stylePanelName
                        + targetClassElement;
                Class cls = loadClass(name);
                if (cls != null) {
                    return cls;
                }
            }
        }
        return null;
    }

    private Class loadClass(String name) {
        try {
            Class cls = Class.forName(name);
            return cls;
        } catch (ClassNotFoundException ignore) {
            LOG.debug("ClassNotFoundException. Could not find class:"
                    + name);
        }
        return null;
    }

    /**
     * @return the style panel names
     */
    protected String[] getStylePanelNames() {
        return stylePanelNames;
    }

    /*
     * @see org.argouml.ui.TabTarget#getTarget()
     */
    public Object getTarget() {
        return target;
    }

    /*
     * @see org.argouml.ui.TabTarget#shouldBeEnabled(java.lang.Object)
     */
    public boolean shouldBeEnabled(Object targetItem) {

        if (!(targetItem instanceof Fig)) {
            if (Model.getFacade().isAModelElement(targetItem)) {
                ArgoDiagram diagram = DiagramUtils.getActiveDiagram();
                if (diagram == null) {
                    shouldBeEnabled = false;
                    return false;
                }

                Fig f = diagram.presentationFor(targetItem);
                if (f == null) {
                    shouldBeEnabled = false;
                    return false;
                }
                targetItem = f;
            } else {
                shouldBeEnabled = false;
                return false;
            }
        }

        shouldBeEnabled = true;

        // TODO: It would be better to defer this initialization until the panel
        // actually needs to be displayed. Perhaps optimistically always return
        // true and figure out later if we've got something to display - tfm -
        // 20070110
        Class targetClass = targetItem.getClass();
        stylePanel = findPanelFor(targetClass);
        targetClass = targetClass.getSuperclass();

        if (stylePanel == null) {
            shouldBeEnabled = false;
        }

        return shouldBeEnabled;
    }

    /*
     * @see org.argouml.ui.targetmanager.TargetListener#targetAdded(org.argouml.ui.targetmanager.TargetEvent)
     */
    public void propertyChange(PropertyChangeEvent pce) {
        DelayedChangeNotify delayedNotify = new DelayedChangeNotify(this, pce);
        SwingUtilities.invokeLater(delayedNotify);
    }

    /*
     * @see org.argouml.kernel.DelayedVChangeListener#delayedVetoableChange(java.beans.PropertyChangeEvent)
     */
    public void delayedVetoableChange(PropertyChangeEvent pce) {
        if (stylePanel != null) {
            stylePanel.refresh(pce);
        }
    }

    /*
     * @see TargetListener#targetAdded(TargetEvent)
     */
    public void targetAdded(TargetEvent e) {
        setTarget(e.getNewTarget());
        fireTargetAdded(e);
    }

    /*
     * @see TargetListener#targetRemoved(TargetEvent)
     */
    public void targetRemoved(TargetEvent e) {
        // how to handle empty target lists?
        // probably the TabProps should only show an empty pane in that
        // case
        setTarget(e.getNewTarget());
        fireTargetRemoved(e);
    }

    /*
     * @see TargetListener#targetSet(TargetEvent)
     */
    public void targetSet(TargetEvent e) {
        setTarget(e.getNewTarget());
        fireTargetSet(e);
    }

    /**
     * Adds a listener.
     *
     * @param listener
     *            the listener to add
     */
    private void addTargetListener(TargetListener listener) {
        listenerList.add(TargetListener.class, listener);
    }

    /**
     * Removes a target listener.
     *
     * @param listener
     *            the listener to remove
     */
    private void removeTargetListener(TargetListener listener) {
        listenerList.remove(TargetListener.class, listener);
    }

    /**
     * @param targetEvent
     */
    private void fireTargetSet(TargetEvent targetEvent) {
        //          Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == TargetListener.class) {
                // Lazily create the event:
                ((TargetListener) listeners[i + 1]).targetSet(targetEvent);
            }
        }
    }

    /**
     * @param targetEvent
     */
    private void fireTargetAdded(TargetEvent targetEvent) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();

        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == TargetListener.class) {
                // Lazily create the event:
                ((TargetListener) listeners[i + 1]).targetAdded(targetEvent);
            }
        }
    }

    /**
     * @param targetEvent
     */
    private void fireTargetRemoved(TargetEvent targetEvent) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == TargetListener.class) {
                // Lazily create the event:
                ((TargetListener) listeners[i + 1]).targetRemoved(targetEvent);
            }
        }
    }

}

