/* $Id: NotationProvider.java 18852 2010-11-20 19:27:11Z mvw $ ***************************************************************************** * Copyright (c) 2009-2010 Contributors - see below * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Michiel van der Wulp ***************************************************************************** * * Some portions of this file was previously release using the BSD License: */ // Copyright (c) 2005-2009 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 org.apache.log4j.Logger; import org.argouml.model.AddAssociationEvent; import org.argouml.model.DeleteInstanceEvent; import org.argouml.model.Model; import org.argouml.model.RemoveAssociationEvent; /** * 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 Michiel van der Wulp */ public abstract class NotationProvider implements PropertyChangeListener { private static final Logger LOG = Logger.getLogger(NotationProvider.class); private NotationRenderer renderer; /** * 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(); /** * 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); /** * Generate a string representation for the given model element. * * @param modelElement the base UML element * @param settings settings that control rendering of the text * @return the string written in the correct notation */ public abstract String toString(Object modelElement, NotationSettings settings); /** * Initialize 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 modelElement the modelelement that we provide * notation for */ public void initialiseListener(Object modelElement) { addElementListener(modelElement); } /** * Clean out the listeners registered before. * <p> * The default implementation is to remove all listeners * that were remembered by the utility functions below. */ public void cleanListener() { removeAllElementListeners(); } /** * Update the set of listeners based on the given event. <p> * * The default implementation just removes all listeners, and then * re-initializes 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 modelElement the modelelement that we provide * notation for * @param pce the received event, that we base the changes on */ public void updateListener(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(); initialiseListener(modelElement); } public void propertyChange(PropertyChangeEvent evt) { if (renderer != null) { Object owner = renderer.getOwner(this); if ((owner == evt.getSource()) && (evt instanceof DeleteInstanceEvent)) { return; } if (owner != null) { if (Model.getUmlFactory().isRemoved(owner)) { LOG.warn("Encountered deleted object during delete of " + owner); return; } renderer.notationRenderingChanged(this, toString(owner, renderer.getNotationSettings(this))); if (evt instanceof AddAssociationEvent || evt instanceof RemoveAssociationEvent) { initialiseListener(owner); } } } } /* * 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 an array of property names * and remember the registration. * * @param element element to listen for changes on */ public final void addElementListener(Object element) { addElementListener(this, element); } /* * 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 name of property to listen for changes of */ public final void addElementListener(Object element, String property) { addElementListener(this, element, property); } /* * 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 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 */ public final void addElementListener(Object element, String[] property) { addElementListener(this, element, property); } /* * 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 remove an element listener * and adapt the remembered list of registration. * * @param element element to listen for changes on */ public final void removeElementListener(Object element) { removeElementListener(this, 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(); } /** * Utility function to unregister all listeners * registered through addElementListener. */ public final void removeAllElementListeners() { removeAllElementListeners(this); } /** * @param nr the NotationRenderer */ void setRenderer(NotationRenderer nr) { renderer = nr; } }