Ptidej-v5.2-master/POM Tests/data/ArgoUML_0.28/src/org/argouml/notation/NotationProvider.java
// $Id: NotationProvider.java 16400 2008-12-21 16:27:58Z tfmorris $
// Copyright (c) 2005-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.notation;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;

import org.apache.log4j.Logger;

import org.argouml.model.Model;

/**
 * A class that implements this abstract class manages a text
 * shown on a diagram. This means it is able to generate
 * text that represents one or more UML objects.
 * And when the user has edited this text, the model may be adapted
 * by parsing the text.
 * Additionally, a help text for the parsing is provided,
 * so that the user knows the syntax.
 * 
 * @author mvw@tigris.org
 */
public abstract class NotationProvider {

    private static final Logger LOG = Logger.getLogger(NotationProvider.class);
    
    private static final String LIST_SEPARATOR = ", ";
    
    /**
     * A collection of properties of listeners registered for this notation.
     * Each entry is a 2 element array containing the element and the property
     * name(s) for which a listener is registered. This facilitates easy removal
     * of a complex set of listeners.
     */
    private final Collection<Object[]> listeners = new ArrayList<Object[]>();

    /**
     * @return a i18 key that represents a help string
     *         giving an explanation to the user of the syntax
     */
    public abstract String getParsingHelp();

    
    /**
     * Utility function to determine the presence of a key. 
     * The default is false.
     * 
     * @param key the string for the key
     * @param map the Map to check for the presence 
     * and value of the key
     * @return true if the value for the key is true, otherwise false
     */
    public static boolean isValue(final String key, final Map map) {
        if (map == null) {
            return false;
        }
        Object o = map.get(key);
        if (!(o instanceof Boolean)) {
            return false;
        }
        return ((Boolean) o).booleanValue();
    }

    /**
     * Parses the given text, and adapts the modelElement and
     * maybe related elements accordingly.
     * 
     * @param modelElement the modelelement to adapt
     * @param text the string given by the user to be parsed
     * to adapt the model
     */
    public abstract void parse(Object modelElement, String text);

    /**
     * Generates a string representation for the given model element.
     * 
     * @param modelElement the base UML modelelement
     * @param args arguments that may determine the notation
     * @return the string written in the correct notation
     * @deprecated for 0.27.3 by tfmorris.  Use 
     * {@link #toString(Object, NotationSettings)}.
     */
    @Deprecated
    public abstract String toString(Object modelElement, Map args);


    /**
     * Generate a string representation for the given model element.
     * <p>
     * <em>WARNING:</em> All subclasses must implement this as if it were
     * abstract. It will become abstract in a future release after the
     * deprecation period for toString(Object, Map) expires.
     * 
     * @param modelElement the base UML element
     * @param settings settings that control rendering of the text
     * @return the string written in the correct notation
     */
    public String toString(Object modelElement,
            NotationSettings settings) {
        return toString(modelElement, Collections.emptyMap());
    }
    
    /**
     * Initialise the appropriate model change listeners 
     * for the given modelelement to the given listener.
     * Overrule this when you need more than 
     * listening to all events from the base modelelement.
     * 
     * @param listener the given listener
     * @param modelElement the modelelement that we provide 
     * notation for
     */
    public void initialiseListener(PropertyChangeListener listener, 
            Object modelElement) {
        addElementListener(listener, modelElement);
    }
    
    /**
     * Clean out the listeners registered before.
     * The default implementation is to remove all listeners 
     * that were remembered by the utility functions below.
     * 
     * @param listener the given listener
     * @param modelElement the modelelement that we provide 
     * notation for
     */
    public void cleanListener(final PropertyChangeListener listener, 
            final Object modelElement) {
        removeAllElementListeners(listener);
    }
    
    /**
     * Update the set of listeners based on the given event. <p>
     * 
     * The default implementation just removes all listeners, and then 
     * re-initialises completely - this is method 1. 
     * A more efficient way would be to dissect 
     * the propertyChangeEvent, and only adapt the listeners
     * that need to be adapted - this is method 2. <p>
     * 
     * Method 2 is explained by the code below that is commented out.
     * Method 1 is the easiest to implement, since at every arrival of an event,
     * we just remove all old listeners, and then inspect the current model, 
     * and add listeners where we need them. I.e. the advantage is 
     * that we only need to traverse the model structure in one location, i.e. 
     * the initialiseListener() method.
     * 
     * @param listener the given listener
     * @param modelElement the modelelement that we provide 
     * notation for
     * @param pce the received event, that we base the changes on
     */
    public void updateListener(final PropertyChangeListener listener, 
            Object modelElement,
            PropertyChangeEvent pce) {
        // e.g. for an operation:
        // if pce.getSource() == modelElement
        // && event.propertyName = "parameter"
        //     if event instanceof AddAssociationEvent
        //         Get the parameter instance from event.newValue
        //         Call model to add listener on parameter on change 
        //             of "name", "type"
        //     else if event instanceof RemoveAssociationEvent
        //         Get the parameter instance from event.oldValue
        //         Call model to remove listener on parameter on change 
        //             of "name", "type"
        //     end if
        // end if 
        if (Model.getUmlFactory().isRemoved(modelElement)) {
            LOG.warn("Encountered deleted object during delete of " 
                    + modelElement);
            return;
        }
        cleanListener(listener, modelElement);
        initialiseListener(listener, modelElement);
    }
    
    /*
     * Add an element listener and remember the registration.
     * 
     * @param element
     *            element to listen for changes on
     * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String)
     */
    protected final void addElementListener(PropertyChangeListener listener, 
            Object element) {
        if (Model.getUmlFactory().isRemoved(element)) {
            LOG.warn("Encountered deleted object during delete of " + element);
            return;
        }
        Object[] entry = new Object[] {element, null};
        if (!listeners.contains(entry)) {
            listeners.add(entry);
            Model.getPump().addModelEventListener(listener, element);
        } else {
            LOG.warn("Attempted duplicate registration of event listener"
                    + " - Element: " + element + " Listener: " + listener);
        }
    }
    
    /*
     * Utility function to add a listener for a given property name 
     * and remember the registration.
     * 
     * @param element
     *            element to listen for changes on
     * @param property
     *            name of property to listen for changes of
     * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener,
     *      Object, String)
     */
    protected final void addElementListener(PropertyChangeListener listener, 
            Object element, String property) {
        if (Model.getUmlFactory().isRemoved(element)) {
            LOG.warn("Encountered deleted object during delete of " + element);
            return;
        }
        Object[] entry = new Object[] {element, property};
        if (!listeners.contains(entry)) {
            listeners.add(entry);
            Model.getPump().addModelEventListener(listener, element, property);
        } else {
            LOG.debug("Attempted duplicate registration of event listener"
                    + " - Element: " + element + " Listener: " + listener);
        }
    }

    /*
     * Utility function to add a listener for an array of property names 
     * and remember the registration.
     * 
     * @param element
     *            element to listen for changes on
     * @param property
     *            array of property names (Strings) to listen for changes of
     * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener,
     *      Object, String)
     */
    protected final void addElementListener(PropertyChangeListener listener, 
            Object element, String[] property) {
        if (Model.getUmlFactory().isRemoved(element)) {
            LOG.warn("Encountered deleted object during delete of " + element);
            return;
        }
        Object[] entry = new Object[] {element, property};
        if (!listeners.contains(entry)) {
            listeners.add(entry);
            Model.getPump().addModelEventListener(listener, element, property);
        } else {
            LOG.debug("Attempted duplicate registration of event listener"
                    + " - Element: " + element + " Listener: " + listener);
        }
    }
    
    /*
     * Utility function to remove an element listener 
     * and adapt the remembered list of registration.
     * 
     * @param element
     *            element to listen for changes on
     * @see org.argouml.model.ModelEventPump#addModelEventListener(PropertyChangeListener, Object, String)
     */
    protected final void removeElementListener(PropertyChangeListener listener, 
            Object element) {
        listeners.remove(new Object[] {element, null});
        Model.getPump().removeModelEventListener(listener, element);
    }
    
    /*
     * Utility function to unregister all listeners 
     * registered through addElementListener.
     * 
     * @see #addElementListener(Object, String)
     */
    protected final void removeAllElementListeners(
            PropertyChangeListener listener) {
        for (Object[] lis : listeners) {
            Object property = lis[1];
            if (property == null) {
                Model.getPump().removeModelEventListener(listener, lis[0]);
            } else if (property instanceof String[]) {
                Model.getPump().removeModelEventListener(listener, lis[0],
                        (String[]) property);
            } else if (property instanceof String) {
                Model.getPump().removeModelEventListener(listener, lis[0],
                        (String) property);
            } else {
                throw new RuntimeException(
                        "Internal error in removeAllElementListeners");
            }
        }
        listeners.clear();
    }
    
    protected StringBuilder formatNameList(Collection modelElements) {
        return formatNameList(modelElements, LIST_SEPARATOR);
    }

    protected StringBuilder formatNameList(Collection modelElements, 
            String separator) {
        StringBuilder result = new StringBuilder();
        for (Object element : modelElements) {
            String name = Model.getFacade().getName(element);
            // TODO: Any special handling for null names? append will use "null"
            result.append(name).append(separator);
        }
        if (result.length() >= separator.length()) {
            result.delete(result.length() - separator.length(), 
                    result.length());
        }
        return result;
    }
}
