Ptidej-v5.2-master/POM Tests/data/ArgoUML_0.28/src/org/argouml/uml/ui/TabConstraints.java
// $Id: TabConstraints.java 15968 2008-11-05 21:57:13Z tfmorris $
// Copyright (c) 1996-2007 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.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JToolBar;
import javax.swing.event.EventListenerList;

import org.apache.log4j.Logger;
import org.argouml.application.api.AbstractArgoJPanel;
import org.argouml.model.Model;
import org.argouml.ocl.ArgoFacade;
import org.argouml.ocl.OCLUtil;
import org.argouml.swingext.UpArrowIcon;
import org.argouml.ui.TabModelTarget;
import org.argouml.ui.targetmanager.TargetEvent;
import org.tigris.gef.presentation.Fig;
import org.tigris.toolbar.ToolBarManager;

import tudresden.ocl.OclException;
import tudresden.ocl.OclTree;
import tudresden.ocl.check.OclTypeException;
import tudresden.ocl.gui.ConstraintRepresentation;
import tudresden.ocl.gui.EditingUtilities;
import tudresden.ocl.gui.OCLEditor;
import tudresden.ocl.gui.OCLEditorModel;
import tudresden.ocl.gui.events.ConstraintChangeEvent;
import tudresden.ocl.gui.events.ConstraintChangeListener;
import tudresden.ocl.parser.OclParserException;
import tudresden.ocl.parser.analysis.DepthFirstAdapter;
import tudresden.ocl.parser.node.AConstraintBody;
import tudresden.ocl.parser.node.TName;

/**
  * Tab for OCL constraint editing.
  *
  * @author v1.0: Falk Finger
  * @author v2.0: Steffen Zschaler
  */
public class TabConstraints extends AbstractArgoJPanel
    implements TabModelTarget, ComponentListener {

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

    /**
     * The actual editor pane.
     */
    private OCLEditor mOcleEditor;

    /**
     * The current target element.
     */
    private Object mMmeiTarget;

    /**
     * The constructor.
     */
    public TabConstraints() {
        super("tab.constraints");

        setIcon(new UpArrowIcon());
        setLayout(new BorderLayout(0, 0));

        mOcleEditor = new OCLEditor();
        mOcleEditor.setOptionMask(OCLEditor.OPTIONMASK_TYPECHECK
                   /*|  //removed to workaround problems with autosplit
                     OCLEditor.OPTIONMASK_AUTOSPLIT*/);
        mOcleEditor.setDoAutoSplit(false);
        setToolbarRollover(true);
        setToolbarFloatable(false);
        getOclToolbar().setName("misc.toolbar.constraints");

        add(mOcleEditor);
        
        addComponentListener(this);
    }

    /**
     * Set the toolbar rollover style.
     *
     * @param enable If true then button borders do not become visible
     * until mouse rolls over button.
     */
    private void setToolbarRollover(boolean enable) {
        if (!ToolBarManager.alwaysUseStandardRollover()) {
            getOclToolbar().putClientProperty(
                    "JToolBar.isRollover", Boolean.TRUE);
        }
    }

    /**
     * Set the toolbar floating style.
     *
     * @param enable If true then the toolbar can be floated and docked
     */
    private void setToolbarFloatable(boolean enable) {
        getOclToolbar().setFloatable(false);
    }

    /**
     * Get a reference to the toolbar object contained in the
     * OCLEditor component.  This is currently a nasty hack. We really
     * require an interface method on OCLEditor (Bob Tarling 8 Feb
     * 2003).
     *
     * @return The toolbar
     */
    private JToolBar getOclToolbar() {
        return (JToolBar) mOcleEditor.getComponent(0);
    }

    //TabModelTarget interface methods

    /**
     * Should this tab be activated for the current target element?<p>
     *
     * Argo only supports constraints for Classes and Features
     * (eg. Attributes and Operations) currently.
     *
     * {@inheritDoc}
     */
    public boolean shouldBeEnabled(Object target) {
        target = (target instanceof Fig) ? ((Fig) target).getOwner() : target;
        return (Model.getFacade().isAClass(target)
                || Model.getFacade().isAFeature(target));
    }

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

    /**
     * Refresh the tab because the target has changed.
     */
    public void refresh() {
        setTarget(mMmeiTarget);
    }

    /**
     * Set the target element to be displayed in this tab. Only model elements
     * will be accepted by the constraint tab.
     *
     * {@inheritDoc}
     */
    public void setTarget(Object oTarget) {
        oTarget =
	    (oTarget instanceof Fig) ? ((Fig) oTarget).getOwner() : oTarget;
        if (!(Model.getFacade().isAModelElement(oTarget))) {
            mMmeiTarget = null;
            return;
        }

        mMmeiTarget = oTarget;

        if (isVisible()) {
            setTargetInternal(mMmeiTarget);
        }
    }

    private void setTargetInternal(Object oTarget) {
        // Set editor's model
        if (oTarget != null) {
            mOcleEditor.setModel(new ConstraintModel(oTarget));
        }
    }

    /**
     * Adapter to provide information and a manipulation interface for a
     * target element's set of constraints to the constraint editor.
     */
    private static class ConstraintModel implements OCLEditorModel {

        /**
         * The target element being edited.
         */
        private Object theMMmeiTarget;

        /**
         * A list of all the constraints in m_nmeiTarget. This is necessary to
         * induce a consistent order on the constraints.
         */
        private ArrayList theMAlConstraints;

        /**
         * List of listeners.
         */
        private EventListenerList theMEllListeners = new EventListenerList();

        /**
         * Construct a new ConstraintModel.
         */
        public ConstraintModel(Object mmeiTarget) {
            super();

            theMMmeiTarget = mmeiTarget;

            theMAlConstraints =
		new ArrayList(Model.getFacade().getConstraints(theMMmeiTarget));
        }

        /**
         * Return the number of constraints in this model.
         */
        public int getConstraintCount() {
            return theMAlConstraints.size();
        }

        /**
         * Return the constraint with the specified index.
         *
         * @param nIdx the index of the constraint to be returned.
         *             0 <= nIdx < {@link #getConstraintCount}
         */
        public ConstraintRepresentation getConstraintAt(int nIdx) {
            return representationFor(nIdx);
        }

        /**
         * Remove the specified constraint from the model.
         *
         * @param nIdx the index of the constraint to be removed.
         *             0 <= nIdx < {@link #getConstraintCount}
         */
        public void removeConstraintAt(int nIdx) {
            if ((nIdx < 0) || (nIdx > theMAlConstraints.size())) {
                return;
            }

            Object mc = theMAlConstraints.remove(nIdx);

            if (mc != null) {
                Model.getCoreHelper().removeConstraint(theMMmeiTarget, mc);
            }

            fireConstraintRemoved(mc, nIdx);
        }

        /**
         * Add a fresh constraint to the model.<p>
         *
         * There are 2 restrictions on what can be parsed, given
         * the current OCL grammar:
         * <ol>
         *   <li>Class names must have a capital first letter.
         *   <li>Feature name must have a lower case first letter.
         * </ol>
         */
        public void addConstraint() {

            // check ocl parsing constraints
            Object mmeContext = OCLUtil
                    .getInnerMostEnclosingNamespace(theMMmeiTarget);
            String contextName = Model.getFacade().getName(mmeContext);
            String targetName = Model.getFacade().getName(theMMmeiTarget);
            if ((contextName == null
		 || contextName.equals (""))
		||  // this is to fix issue #2056
                (targetName == null
		 || targetName.equals (""))
		||   // this is to fix issue #2056
                !Character.isUpperCase(contextName.charAt(0))
		|| (Model.getFacade().isAClass (theMMmeiTarget)
		    && !Character.isUpperCase(targetName.charAt(0)))
		|| (Model.getFacade().isAFeature(theMMmeiTarget)
		    && !Character.isLowerCase(targetName.charAt(0)))) {
                // TODO: I18n
                JOptionPane.showMessageDialog(
		    null,
                    "The OCL Toolkit requires that:\n\n"
		    + "Class names have a capital first letter and\n"
		    + "Attribute or Operation names have "
		    + "a lower case first letter.",
                    "Require Correct name convention:",
                    JOptionPane.ERROR_MESSAGE);
                // do not create a constraint:
                return;
            }

            // null elements represent new constraints, which will be
            // added to the target the first time any actual editing
            // takes place.  This is done to ensure syntactical
            // correctness of constraints stored with the target.
            theMAlConstraints.add(null);

            fireConstraintAdded();
        }

        // TODO: - please add some javadoc - ugly classname also
        private class CR implements ConstraintRepresentation {
            /**
             * The constraint being represented.
             */
            private Object theMMcConstraint;

            /**
             * The constraint's index in the list of
             * constraints. Necessary only for new constraints, where
             * m_mcConstraint is still <tt>null</tt>.
             */
            private int theMNIdx = -1;

            public CR(Object mcConstraint, int nIdx) {
                super();

                theMMcConstraint = mcConstraint;
                theMNIdx = nIdx;
            }

            public CR(int nIdx) {
                this(null, nIdx);
            }

            /**
             * Get the name of the constraint.
             */
            public String getName() {
                if (theMMcConstraint == null) {
                    return "newConstraint";
                }
                return Model.getFacade().getName(theMMcConstraint);
            }

            /**
             * Get the constraint's body.
             */
            public String getData() {
                if (theMMcConstraint == null) {
                    return OCLUtil.getContextString(theMMmeiTarget);
                }
                return (String) Model.getFacade().getBody(
                        Model.getFacade().getBody(theMMcConstraint));
            }

            /**
             * Set the constraint's body text. For the exceptions the
             * detailed message must be human readable.
             *
             * @param sData the new body of the constraint
             *
             * @exception IllegalStateException if the constraint is
             *            not in a state to accept body changes.
             * @exception OclParserException if the specified constraint is not
             *     syntactically correct.
             * @exception OclTypeException if the specified constraint
             *            does not adhere by OCL type rules.
             */
            public void setData(String sData, EditingUtilities euHelper)
		throws OclParserException, OclTypeException {
                // Parse and check specified constraint.
                OclTree tree = null;

                try {
                    Object mmeContext = OCLUtil
                            .getInnerMostEnclosingNamespace(theMMmeiTarget);

                    try {
                        tree =
			    euHelper.parseAndCheckConstraint(
				sData,
				new ArgoFacade(mmeContext));
                    } catch (IOException ioe) {
                        // Ignored: Highly unlikely, and what would we
                        // do anyway?  log it
                        LOG.error("problem parsing And Checking Constraints",
				   ioe);
                        return;
                    }

                    // Split constraint body, if user wants us to
                    if (euHelper.getDoAutoSplit()) {
                        List lConstraints = euHelper.splitConstraint(tree);

                        if (lConstraints.size() > 0) {
                            removeConstraintAt(theMNIdx);

                            for (Iterator i = lConstraints.iterator();
				 i.hasNext();) {
                                OclTree ocltCurrent = (OclTree) i.next();

                                Object mc =
                                    Model.getCoreFactory()
				        .createConstraint();
                                Model.getCoreHelper().setName(mc, ocltCurrent
                                    .getConstraintName());
                                Model.getCoreHelper().setBody(mc,
                                        Model.getDataTypesFactory()
                                        	.createBooleanExpression(
                                        	        "OCL",
                                        	        ocltCurrent
                                        	        .getExpression()));
                                Model.getCoreHelper().addConstraint(
                                        theMMmeiTarget,
                                        mc);

                                // the constraint _must_ be owned by a namespace
                                if (Model.getFacade().getNamespace(
                                        theMMmeiTarget)
                                        != null) {
                                    Model.getCoreHelper().addOwnedElement(
                                            Model.getFacade().getNamespace(
                                                    theMMmeiTarget),
                                            mc);
                                } else if (Model.getFacade().getNamespace(
                                        mmeContext) != null) {
                                    Model.getCoreHelper().addOwnedElement(
                                            Model.getFacade().getNamespace(
                                                    mmeContext),
                                            theMMcConstraint);
                                }

                                theMAlConstraints.add(mc);
                                fireConstraintAdded();
                            }

                            return;
                        }
                    }

                    // Store constraint body
                    Object mcOld = null;

                    if (theMMcConstraint == null) {
                        // New constraint, first time setData is called
                        theMMcConstraint =
                            Model.getCoreFactory().createConstraint();

                        Model.getCoreHelper().setName(
                                theMMcConstraint,
                                "newConstraint");
                        Model.getCoreHelper().setBody(
                                theMMcConstraint,
                                Model.getDataTypesFactory()
                                	.createBooleanExpression("OCL", sData));

                        Model.getCoreHelper().addConstraint(theMMmeiTarget,
                                theMMcConstraint);

                        // the constraint _must_ be owned by a namespace
                        Object targetNamespace =
                            Model.getFacade().getNamespace(theMMmeiTarget);
                        Object contextNamespace =
                            Model.getFacade().getNamespace(mmeContext);
                        if (targetNamespace != null) {
                            Model.getCoreHelper().addOwnedElement(
                                    targetNamespace,
                                    theMMcConstraint);
                        } else if (contextNamespace != null) {
                            Model.getCoreHelper().addOwnedElement(
                                    contextNamespace,
                                    theMMcConstraint);
                        }

                        theMAlConstraints.set(theMNIdx, theMMcConstraint);
                    } else {
                        mcOld = Model.getCoreFactory().createConstraint();
                        Model.getCoreHelper().setName(
                                mcOld,
                                Model.getFacade().getName(theMMcConstraint));
                        Model.getCoreHelper().setBody(
                                mcOld,
                                Model.getDataTypesFactory()
                                	.createBooleanExpression("OCL",
                                	        (String) Model.getFacade()
                                	        	.getBody(
                                                Model.getFacade().getBody(
                                                        theMMcConstraint))));
                        Model.getCoreHelper().setBody(theMMcConstraint,
                                Model.getDataTypesFactory()
                                	.createBooleanExpression("OCL", sData));
                    }

                    fireConstraintDataChanged(theMNIdx, mcOld,
                            theMMcConstraint);

                } catch (OclTypeException pe) {
                    LOG.warn("There was some sort of OCL Type problem", pe);
                    throw pe;
                } catch (OclParserException pe1) {
                    LOG.warn("Could not parse the constraint", pe1);
                    throw pe1;
                } catch (OclException oclExc) {
                    // a runtime exception that occurs when some
                    // internal test fails
                    LOG.warn("There was some unidentified problem");
                    throw oclExc;
                }
            }

            /**
             * Set the constraint's name.
             */
            public void setName(
                final String sName,
                final EditingUtilities euHelper) {
                if (theMMcConstraint != null) {
                    // Check name for consistency with spec
                    if (!euHelper.isValidConstraintName(sName)) {
                        throw new IllegalArgumentException(
                                "Please specify a valid name.");
                    }

                    // Set name
                    Object mcOld =
                        Model.getCoreFactory().createConstraint();
                    Model.getCoreHelper().setName(mcOld,
                            Model.getFacade().getName(theMMcConstraint));
                    Object constraintBody =
                        Model.getFacade().getBody(theMMcConstraint);
                    Model.getCoreHelper().setBody(mcOld,
                            Model.getDataTypesFactory()
                                .createBooleanExpression(
                                        "OCL",
                                        (String) Model.getFacade().getBody(
                                                constraintBody)));

                    Model.getCoreHelper().setName(theMMcConstraint, sName);

                    fireConstraintNameChanged(theMNIdx, mcOld,
                            theMMcConstraint);

                    // Also set name in constraint body -- Added 03/14/2001
                    try {
                        OclTree tree = null;
                        Object mmeContext = OCLUtil
                                .getInnerMostEnclosingNamespace(theMMmeiTarget);

                        constraintBody =
                            Model.getFacade().getBody(theMMcConstraint);
                        tree =
                            euHelper.parseAndCheckConstraint(
                                (String) Model.getFacade().getBody(
                                        constraintBody),
                                new ArgoFacade(mmeContext));

                        if (tree != null) {
                            tree.apply(new DepthFirstAdapter() {
                                private int nameID = 0;
                                public void caseAConstraintBody(
                                        AConstraintBody node) {
                                    // replace name
                                    if (nameID == 0) {
                                        node.setName(new TName(sName));
                                    } else {
                                        node.setName(new TName(
                                                sName + "_" + nameID));
                                    }
                                    nameID++;
				}
                            });

                            setData(tree.getExpression(), euHelper);
                        }
                    } catch (Throwable t) {
                        // OK, so that didn't work out... Just ignore
                        // any problems and don't set the name in the
                        // constraint body better had log it.
                        LOG.error("some unidentified problem", t);
                    }
                } else {
                    throw new IllegalStateException(
                        "Please define and submit a constraint body first.");
                }
            }
        }

        /**
         * Create a representation adapter for the given constraint.
         */
        private CR representationFor(int nIdx) {
            if ((nIdx < 0) || (nIdx >= theMAlConstraints.size())) {
                return null;
            }

            Object mc = theMAlConstraints.get(nIdx);

            if (mc != null) {
                return new CR(mc, nIdx);
            }
            return new CR(nIdx);
        }

        /**
         * Add a listener to be informed of changes in the model.
         *
         * @param ccl the new listener
         */
        public void addConstraintChangeListener(ConstraintChangeListener ccl) {
            theMEllListeners.add(ConstraintChangeListener.class, ccl);
        }

        /**
         * Remove a listener to be informed of changes in the model.
         *
         * @param ccl the listener to be removed
         */
        public void removeConstraintChangeListener(
                ConstraintChangeListener ccl) {
            theMEllListeners.remove(ConstraintChangeListener.class, ccl);
        }

        protected void fireConstraintRemoved(
                Object mc, int nIdx) {
            // Guaranteed to return a non-null array
            Object[] listeners = theMEllListeners.getListenerList();

            ConstraintChangeEvent cce = null;

            // Process the listeners last to first, notifying
            // those that are interested in this event
            for (int i = listeners.length - 2; i >= 0; i -= 2) {
                if (listeners[i] == ConstraintChangeListener.class) {
                    // Lazily create the event:
                    if (cce == null) {
                        cce = new ConstraintChangeEvent(
                            this,
                            nIdx,
                            new CR(mc, nIdx),
                            null);
                    }
                    ((ConstraintChangeListener) listeners[i + 1])
                        .constraintRemoved(cce);
                }
            }
        }

        protected void fireConstraintAdded() {
            // Guaranteed to return a non-null array
            Object[] listeners = theMEllListeners.getListenerList();

            ConstraintChangeEvent cce = null;

            // Process the listeners last to first, notifying
            // those that are interested in this event
            for (int i = listeners.length - 2; i >= 0; i -= 2) {
                if (listeners[i] == ConstraintChangeListener.class) {
                    // Lazily create the event:
                    if (cce == null) {
                        int nIdx = theMAlConstraints.size() - 1;
                        cce =
                            new ConstraintChangeEvent(
                                    this,
                                    nIdx,
                                    null,
                                    representationFor(nIdx));
                    }
                    ((ConstraintChangeListener) listeners[i + 1])
                        .constraintAdded(cce);
                }
            }
        }

        protected void fireConstraintDataChanged(
                         int nIdx,
                         Object mcOld,
                         Object mcNew) {
            // Guaranteed to return a non-null array
            Object[] listeners = theMEllListeners.getListenerList();

            ConstraintChangeEvent cce = null;

            // Process the listeners last to first, notifying
            // those that are interested in this event
            for (int i = listeners.length - 2; i >= 0; i -= 2) {
                if (listeners[i] == ConstraintChangeListener.class) {
                    // Lazily create the event:
                    if (cce == null) {
                        cce = new ConstraintChangeEvent(
                            this,
                            nIdx,
                            new CR(mcOld, nIdx),
                            new CR(mcNew, nIdx));
                    }

                    ((ConstraintChangeListener) listeners[i + 1])
                        .constraintDataChanged(cce);
                }
            }
        }

        protected void fireConstraintNameChanged(
                         int nIdx,
                         Object mcOld,
                         Object mcNew) {
            // Guaranteed to return a non-null array
            Object[] listeners = theMEllListeners.getListenerList();

            ConstraintChangeEvent cce = null;

            // Process the listeners last to first, notifying
            // those that are interested in this event
            for (int i = listeners.length - 2; i >= 0; i -= 2) {
                if (listeners[i] == ConstraintChangeListener.class) {
                    // Lazily create the event:
                    if (cce == null) {
                        cce = new ConstraintChangeEvent(
                            this,
                            nIdx,
                            new CR(mcOld, nIdx),
                            new CR(mcNew, nIdx));
                    }

                    ((ConstraintChangeListener) listeners[i + 1])
                        .constraintNameChanged(cce);
                }
            }
        }
    }

    /*
     * @see org.argouml.ui.targetmanager.TargetListener#targetAdded(
     *         org.argouml.ui.targetmanager.TargetEvent)
     */
    public void targetAdded(TargetEvent e) {
        // TODO: Why is this ignored? - tfm - 20070110
    }

    /*
     * @see org.argouml.ui.targetmanager.TargetListener#targetRemoved(
     *         org.argouml.ui.targetmanager.TargetEvent)
     */
    public void targetRemoved(TargetEvent e) {
        setTarget(e.getNewTarget());
    }

    /*
     * @see org.argouml.ui.targetmanager.TargetListener#targetSet(
     *         org.argouml.ui.targetmanager.TargetEvent)
     */
    public void targetSet(TargetEvent e) {
        setTarget(e.getNewTarget());
    }
    
    /*
     * @see java.awt.event.ComponentListener#componentShown(java.awt.event.ComponentEvent)
     */
    public void componentShown(ComponentEvent e) {
        // Update our model with our saved target
        setTargetInternal(mMmeiTarget);
    }
    
    /*
     * @see java.awt.event.ComponentListener#componentHidden(java.awt.event.ComponentEvent)
     */
    public void componentHidden(ComponentEvent e) {
        // We have no model event listeners, so no need to do anything
    }

    public void componentMoved(ComponentEvent e) {
        // ignored
    }

    public void componentResized(ComponentEvent e) {
        // ignored
    }


}
