Author: tfmorris
Date: 2008-04-14 12:34:05-0700
New Revision: 14341

Added:
   trunk/src/app/src/org/argouml/util/SortedListModel.java   (contents, props 
changed)
Modified:
   trunk/src/app/src/org/argouml/uml/ui/AbstractActionAddModelElement2.java
   trunk/src/app/src/org/argouml/uml/ui/UMLAddDialog.java
   trunk/src/app/src/org/argouml/uml/ui/model_management/PropPanelPackage.java

Log:
Issue 4988 - Don't modify caller's list.  Eliminate redundant copies of lists 
and depend on list model.  Keep lists in sorted order. 

Also restore API compatibility for return type of UMLAddDialog.getSelected(), 
but reduce visibility of several public methods.

Modified: 
trunk/src/app/src/org/argouml/uml/ui/AbstractActionAddModelElement2.java
Url: 
http://argouml.tigris.org/source/browse/argouml/trunk/src/app/src/org/argouml/uml/ui/AbstractActionAddModelElement2.java?view=diff&rev=14341&p1=trunk/src/app/src/org/argouml/uml/ui/AbstractActionAddModelElement2.java&p2=trunk/src/app/src/org/argouml/uml/ui/AbstractActionAddModelElement2.java&r1=14340&r2=14341
==============================================================================
--- trunk/src/app/src/org/argouml/uml/ui/AbstractActionAddModelElement2.java    
(original)
+++ trunk/src/app/src/org/argouml/uml/ui/AbstractActionAddModelElement2.java    
2008-04-14 12:34:05-0700
@@ -91,7 +91,7 @@
                              isExclusive());

         int result = dialog.showDialog(ArgoFrame.getInstance());

         if (result == JOptionPane.OK_OPTION) {

-            doIt(dialog.getSelectedList());

+            doIt(dialog.getSelected());

         }

     }

     


Modified: trunk/src/app/src/org/argouml/uml/ui/UMLAddDialog.java
Url: 
http://argouml.tigris.org/source/browse/argouml/trunk/src/app/src/org/argouml/uml/ui/UMLAddDialog.java?view=diff&rev=14341&p1=trunk/src/app/src/org/argouml/uml/ui/UMLAddDialog.java&p2=trunk/src/app/src/org/argouml/uml/ui/UMLAddDialog.java&r1=14340&r2=14341
==============================================================================
--- trunk/src/app/src/org/argouml/uml/ui/UMLAddDialog.java      (original)
+++ trunk/src/app/src/org/argouml/uml/ui/UMLAddDialog.java      2008-04-14 
12:34:05-0700
@@ -1,5 +1,5 @@
 // $Id$
-// Copyright (c) 1996-2007 The Regents of the University of California. All
+// 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
@@ -38,9 +38,9 @@
 import java.util.List;
 import java.util.Vector;
 
+import javax.swing.AbstractListModel;
 import javax.swing.BorderFactory;
 import javax.swing.Box;
-import javax.swing.DefaultListModel;
 import javax.swing.JButton;
 import javax.swing.JDialog;
 import javax.swing.JLabel;
@@ -49,45 +49,32 @@
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.ListCellRenderer;
+import javax.swing.ListModel;
 import javax.swing.ListSelectionModel;
 import javax.swing.SwingUtilities;
 import javax.swing.WindowConstants;
 
 import org.argouml.application.helpers.ResourceLoaderWrapper;
 import org.argouml.i18n.Translator;
+import org.argouml.util.SortedListModel;
 
 /**
- * UMLAddDialog allows the user to do a multiple select from a list of choices
- * in a separate dialog. The dialog has two possible uses:
+ * UMLAddDialog is a modal dialog which allows the user to do a multiple select
+ * from a list of choices. The dialog has two possible uses:
  * <ol>
  * <li>As dialog as described above with a custom cellrenderer or a default
  * cellrenderer.
  * <li>As dialog with a UMLCellRenderer. Cells in the choices list and selected
  * list are presented with their name instead of their toString function.
  * </ol>
+ * <p>
+ * <em>NOTE:</em> An incompatible change to the API contract was made between
+ * 0.24 and 0.26.  A number of internal methods which had public visibility
+ * were made private.
  */
 public class UMLAddDialog extends JPanel implements ActionListener {
 
     /**
-     * The choices a user has
-     */
-    private List choices = null;
-
-    /**
-     * The preselected choices
-     */
-    private List preSelected = null;
-
-    /**
-     * The selected choices.
-     * TODO: This should be switched to a List when [EMAIL PROTECTED] 
#getSelected()}
-     * is removed.  It needs to remain a Vector for now to preserve the 
-     * semantics of [EMAIL PROTECTED] #getSelected()} because it returns the 
Vector
-     * itself, not a copy.
-     */
-    private Vector selected = null;
-
-    /**
      * The GUI list for the choices
      */
     private JList choicesList = null;
@@ -117,6 +104,8 @@
      */
     private int returnValue;
 
+    private boolean exclusive;
+
     /**
      * Constructs a UMLAddDialog with a UMLListCellRenderer. Modelelements are
      * represented with their names in the choices list and the selected list.
@@ -129,15 +118,16 @@
      *            The title of the dialog
      * @param multiselectAllowed
      *            True if the user may select multiple choices
-     * @param exclusive
+     * @param isExclusive
      *            True if choices in the selected list may not appear in the
      *            choices list. If true preselected choices are removed from 
the
      *            choices list.
      */
-    public UMLAddDialog(List theChoices, List preselected, String theTitle,
-            boolean multiselectAllowed, boolean exclusive) {
+    public UMLAddDialog(final List theChoices, final List preselected,
+            final String theTitle, final boolean multiselectAllowed,
+            final boolean isExclusive) {
         this(theChoices, preselected, theTitle, new UMLListCellRenderer2(true),
-                multiselectAllowed, exclusive);
+                multiselectAllowed, isExclusive);
     }
 
     /**
@@ -153,33 +143,29 @@
      *            The cellrenderer of the choices list and the selected list
      * @param multiselectAllowed
      *            True if the user may select multiple choices
-     * @param exclusive
+     * @param isExclusive
      *            True if choices in the selected list may not appear in the
      *            choices list. If true preselected choices are removed from 
the
      *            choices list.
      */
-    public UMLAddDialog(List theChoices, List preselected, String theTitle,
-            ListCellRenderer renderer, boolean multiselectAllowed,
-            boolean exclusive) {
+    public UMLAddDialog(final List theChoices, final List preselected,
+            final String theTitle, final ListCellRenderer renderer,
+            final boolean multiselectAllowed, final boolean isExclusive) {
         multiSelectAllowed = multiselectAllowed;
         if (theChoices == null) {
             throw new IllegalArgumentException(
                     "There should always be choices in UMLAddDialog");
         }
-        if (exclusive && preselected != null && !preselected.isEmpty()) {
-            theChoices.removeAll(preselected);
+        exclusive = isExclusive;
+        List choices = new ArrayList(theChoices);
+        if (isExclusive && preselected != null && !preselected.isEmpty()) {
+            choices.removeAll(preselected);
         }
-        choices = theChoices;
-        preSelected = preselected;
-        selected = new Vector();
         if (theTitle != null) {
             title = theTitle;
         } else {
             title = "";
         }
-        if (preselected != null) {
-            selected.addAll(preselected);
-        }
 
         setLayout(new BorderLayout());
 
@@ -187,7 +173,7 @@
         JPanel panelChoices = new JPanel(new BorderLayout());
         JPanel panelSelected = new JPanel(new BorderLayout());
 
-        choicesList = new JList(constructListModel(theChoices));
+        choicesList = new JList(constructListModel(choices));
         choicesList.setMinimumSize(new Dimension(150, 300));
         if (renderer != null) {
             choicesList.setCellRenderer(renderer);
@@ -216,7 +202,7 @@
         buttonBox.add(Box.createRigidArea(new Dimension(0, 5)));
         buttonBox.add(removeButton);
 
-        selectedList = new JList(constructListModel(selected));
+        selectedList = new JList(constructListModel(preselected));
         selectedList.setMinimumSize(new Dimension(150, 300));
         if (renderer != null) {
             selectedList.setCellRenderer(renderer);
@@ -279,18 +265,18 @@
      * Updates the add and remove button (sets enabled/disabled). Called
      * whenever the model is changed.
      */
-    public void update() {
-        if (choices.size() == 0) {
+    private void update() {
+        if (choicesList.getModel().getSize() == 0) {
             addButton.setEnabled(false);
         } else {
             addButton.setEnabled(true);
         }
-        if (selected.size() == 0) {
+        if (selectedList.getModel().getSize() == 0) {
             removeButton.setEnabled(false);
         } else {
             removeButton.setEnabled(true);
         }
-        if (selected.size() > 1 && !multiSelectAllowed) {
+        if (selectedList.getModel().getSize() > 1 && !multiSelectAllowed) {
             addButton.setEnabled(false);
             okButton.setEnabled(false);
         }
@@ -306,10 +292,10 @@
      * @param list the given list
      * @return DefaultListModel
      */
-    protected DefaultListModel constructListModel(List list) {
-        DefaultListModel model = new DefaultListModel();
-        for (Object o : list) {
-            model.addElement(o);
+    protected AbstractListModel constructListModel(List list) {
+        SortedListModel model = new SortedListModel();
+        if (list != null) {
+            model.addAll(list);
         }
         return model;
     }
@@ -353,135 +339,77 @@
     }
 
     /**
-     * Return the choices a user can make.
-     *
+     * Returns the choices a user can make.
      * @return List of choices
      */
-    public List getChoicesList() {
+    private List getChoices() {
         List result = new ArrayList();
-        getChoicesListInternal(result);
-        return result;
-    }
-    
-    /**
-     * Returns the choices a user can make.
-     *
-     * @return Vector
-     * @deprecated for 0.25.4 by tfmorris. Use [EMAIL PROTECTED] 
#getSelectedChoicesList()}
-     */
-    @Deprecated
-    public Vector getChoices() {
-        Vector result = new Vector();
-        getChoicesListInternal(result);
-        return result;
-    }
-
-    private void getChoicesListInternal(List result) {
         for (int index : choicesList.getSelectedIndices()) {
-            result.add(choices.get(index));
+            result.add(choicesList.getModel().getElementAt(index));
         }
-    }
-
-
-    /**
-     * Returns the selected elements in the selected list
-     *
-     * @return List of elements
-     */
-    public List getSelectedChoicesList() {
-        List result = new ArrayList();
-        getSelectedChoicesInternal(result);
         return result;
     }
     
     /**
      * Returns the selected elements in the selected list
      *
-     * @return Vector
-     * @deprecated for 0.25.4 by tfmorris. Use [EMAIL PROTECTED] 
#getSelectedChoicesList()}
+     * @return List
      */
-    @Deprecated
-    public Vector getSelectedChoices() {
-        Vector result = new Vector();
-        getSelectedChoicesInternal(result);
-        return result;
-    }
-
-    private void getSelectedChoicesInternal(List result) {
-        if (selectedList != null && selected != null) {
-            for (int index : selectedList.getSelectedIndices()) {
-                result.add(selected.get(index));
-            }
+    private List getSelectedChoices() {
+        List result = new ArrayList();
+        for (int index : selectedList.getSelectedIndices()) {
+            result.add(selectedList.getModel().getElementAt(index));
         }
+        return result;
     }
 
-    
     /**
-     * Returns the by the user selected elements. This method should be called
-     * if the selected choices are to be known.
+     * Returns the elements of the right-hand "selected" list.  Note
+     * that these are not the elements selected (ie highlighted) in the
+     * Swing sense, but rather the entire contents of the list containing
+     * the user selections.
      *
-     * @return Vector
-     * @deprecated for 0.25.4 by tfmorris.  Use [EMAIL PROTECTED] 
#getSelectedList()}.
+     * @return a Vector of selected elements.
      */
-    @Deprecated
     public Vector getSelected() {
-        // TODO: Because we return our internal data directly, we can't copy
-        // it from a List to a Vector and keep the same semantics
-        // return new Vector(selected);
-        return selected;
-    }
-
-    /**
-     * Returns the by the user selected elements. This method should be called
-     * if the selected choices are to be known.
-     *
-     * @return List
-     */
-    public List getSelectedList() {
-        return selected;
+        Vector result = new Vector();
+        ListModel list = selectedList.getModel();
+        for (int i = 0; i < list.getSize(); i++) {
+            result.add(list.getElementAt(i));
+        }
+        return result;
     }
-
+    
     /**
      * Adds the selected elements in the choices list to the selected list.
-     * Updates the GUI too.
+     * 
      */
-    public void addSelection() {
-        List theChoices = getChoicesList();
-        choices.removeAll(theChoices);
-        for (int i = 0; i < theChoices.size(); i++) {
-            ((DefaultListModel) choicesList.getModel())
-                    .removeElement(theChoices.get(i));
-        }
-        selected.addAll(theChoices);
-        for (int i = 0; i < theChoices.size(); i++) {
-            ((DefaultListModel) selectedList.getModel()).addElement(theChoices
-                    .get(i));
+    private void addSelection() {
+        List theChoices = getChoices();
+        if (exclusive) {
+            ((SortedListModel) choicesList.getModel()).removeAll(theChoices);
         }
+        ((SortedListModel) selectedList.getModel()).addAll(theChoices);
+
     }
 
     /**
      * Removes the selected elements in the selected list and adds them to the
-     * choices list. Updates the GUI too.
+     * choices list. The GUI will be updated by the ListModel listeners.
      */
-    public void removeSelection() {
-        List theChoices = getSelectedChoicesList();
-        selected.removeAll(theChoices);
-        for (int i = 0; i < theChoices.size(); i++) {
-            ((DefaultListModel) selectedList.getModel())
-                    .removeElement(theChoices.get(i));
-        }
-        choices.addAll(theChoices);
-        for (int i = 0; i < theChoices.size(); i++) {
-            ((DefaultListModel) choicesList.getModel()).addElement(theChoices
-                    .get(i));
+    private void removeSelection() {
+        List theChoices = getSelectedChoices();
+        ((SortedListModel) selectedList.getModel()).removeAll(theChoices);
+        if (exclusive) {
+            ((SortedListModel) choicesList.getModel()).addAll(theChoices);
         }
     }
 
     /**
-     * Called when the okbutton is pressed. Closes this dialog and sets the
-     * returnvalue to JOptionPane.OK_OPTION.
+     * Called when the OK button is pressed. Closes this dialog and sets the
+     * return value to JOptionPane.OK_OPTION.
      */
-    public void ok() {
+    private void ok() {
         if (dialog != null) {
             dialog.setVisible(false);
             returnValue = JOptionPane.OK_OPTION;
@@ -490,15 +418,10 @@
 
     /**
      * Called when the cancel button is pressed. Closes this dialog and sets 
the
-     * returnvalue to JOptionPane.CANCEL_OPTION. Resets the selected list to 
the
-     * originally preselection if there is one. Otherwise the selected list is
-     * emptied.
-     */
-    public void cancel() {
-        selected.clear();
-        if (preSelected != null) {
-            selected.addAll(preSelected);
-        }
+     * returnvalue to JOptionPane.CANCEL_OPTION. The state of any selections
+     * is indeterminate after cancel is called.
+     */
+    private void cancel() {
         if (dialog != null) {
             dialog.setVisible(false);
             returnValue = JOptionPane.CANCEL_OPTION;

Modified: 
trunk/src/app/src/org/argouml/uml/ui/model_management/PropPanelPackage.java
Url: 
http://argouml.tigris.org/source/browse/argouml/trunk/src/app/src/org/argouml/uml/ui/model_management/PropPanelPackage.java?view=diff&rev=14341&p1=trunk/src/app/src/org/argouml/uml/ui/model_management/PropPanelPackage.java&p2=trunk/src/app/src/org/argouml/uml/ui/model_management/PropPanelPackage.java&r1=14340&r2=14341
==============================================================================
--- trunk/src/app/src/org/argouml/uml/ui/model_management/PropPanelPackage.java 
(original)
+++ trunk/src/app/src/org/argouml/uml/ui/model_management/PropPanelPackage.java 
2008-04-14 12:34:05-0700
@@ -242,7 +242,7 @@
                         isExclusive());
             int result = dialog.showDialog(ArgoFrame.getInstance());
             if (result == JOptionPane.OK_OPTION) {
-                doIt(target, dialog.getSelectedList());
+                doIt(target, dialog.getSelected());
             }
         }
     }

Added: trunk/src/app/src/org/argouml/util/SortedListModel.java
Url: 
http://argouml.tigris.org/source/browse/argouml/trunk/src/app/src/org/argouml/util/SortedListModel.java?view=auto&rev=14341
==============================================================================
--- (empty file)
+++ trunk/src/app/src/org/argouml/util/SortedListModel.java     2008-04-14 
12:34:05-0700
@@ -0,0 +1,259 @@
+// $Id$
+// Copyright (c) 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.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.swing.AbstractListModel;
+
+
+/**
+ * A ListModel which keeps the list in sorted order.
+ * <p>
+ * This is a low performance implementation designed for use with small lists
+ * (such as typically appear in the GUI). It will resort the entire list after
+ * each addition.
+ */
+public class SortedListModel extends AbstractListModel implements List
+{
+    private List delegate = new ArrayList();
+
+    /**
+     * Returns the number of components in this list.
+     * <p>
+     * This method is identical to <code>size</code>, which implements the 
+     * <code>List</code> interface defined in the 1.2 Collections framework.
+     * This method exists in conjunction with <code>setSize</code> so that
+     * <code>size</code> is identifiable as a JavaBean property.
+     *
+     * @return  the number of components in this list
+     * @see #size()
+     */
+    public int getSize() {
+        return delegate.size();
+    }
+
+    /**
+     * Returns the component at the specified index.
+     * <blockquote>
+     * <b>Note:</b> Although this method is not deprecated, the preferred
+     *    method to use is <code>get(int)</code>, which implements the 
+     *    <code>List</code> interface defined in the 1.2 Collections framework.
+     * </blockquote>
+     * @param      index   an index into this list
+     * @return     the component at the specified index
+     * @exception  ArrayIndexOutOfBoundsException  if the <code>index</code> 
+     *             is negative or greater than the current size of this 
+     *             list
+     * @see #get(int)
+     */
+    public Object getElementAt(int index) {
+        return delegate.get(index);
+    }
+
+    public Object get(int index) {
+        return delegate.get(index);
+    }
+
+
+    public int size() {
+        return delegate.size();
+    }
+
+
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+
+
+    public boolean contains(Object elem) {
+        return delegate.contains(elem);
+    }
+
+
+    public int indexOf(Object elem) {
+        return delegate.indexOf(elem);
+    }
+
+    public int lastIndexOf(Object elem) {
+        return delegate.lastIndexOf(elem);
+    }
+
+
+    public boolean add(Object obj) {
+        boolean status = delegate.add(obj);
+        Collections.sort(delegate);
+        fireContentsChanged(this, 0, delegate.size() - 1);
+        return status;
+    }
+    
+
+    /**
+     * Add an element to the ListModel. Note: Although this accepts an index
+     * parameter for compatibility with the List API, the ordering of the list
+     * is determined by its contents, so the index is effectively ignored.
+     * 
+     * @param index ignored
+     * @param element element to be added
+     * @see java.util.List#add(int, java.lang.Object)
+     */
+    public void add(int index, Object element) {
+        delegate.add(index, element);
+        Collections.sort(delegate);
+        fireContentsChanged(this, 0, delegate.size() - 1);
+    }
+
+
+    /**
+     * Remove the element at the given index and add the new element in
+     * sorted order.
+     * 
+     * @param index index of element to be removed
+     * @param element new element to be added
+     * @return the element that was removed
+     * @see java.util.List#set(int, java.lang.Object)
+     */
+    public Object set(int index, Object element) {
+        Object oldValue = delegate.set(index, element);
+        Collections.sort(delegate);
+        fireContentsChanged(this, 0, delegate.size() - 1);
+        return oldValue;
+    }
+    
+    public boolean addAll(Collection c) {
+        boolean status = delegate.addAll(c);
+        Collections.sort(delegate);
+        fireContentsChanged(this, 0, delegate.size() - 1);
+        return status;
+    }
+
+    /**
+     * Add the collection of elements to the ListModel. Note: Although this
+     * accepts an index parameter for compatibility with the List API, the
+     * ordering of the list is determined by its contents, so the index is
+     * effectively ignored.
+     * 
+     * @param index ignored
+     * @param c collection of elements to be added
+     * @see java.util.List#add(int, java.lang.Object)
+     */
+    public boolean addAll(int index, Collection c) {
+        boolean status = delegate.addAll(index, c);
+        Collections.sort(delegate);
+        fireContentsChanged(this, 0, delegate.size() - 1);
+        return status;
+    }
+
+    public Object remove(int index) {
+        Object oldValue = delegate.remove(index);
+        fireIntervalRemoved(this, index, index);
+        return oldValue;
+    }
+    
+    public boolean remove(Object obj) {
+        int index = indexOf(obj);
+        boolean rv = delegate.remove(obj);
+        if (index >= 0) {
+            fireIntervalRemoved(this, index, index);
+        }
+        return rv;
+    }
+    
+    public boolean removeAll(Collection c) {
+        boolean status = false;
+        for (Object o : c) {
+            status = status | remove(o);
+        }
+        return status;
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
+    public Object[] toArray() {
+        return delegate.toArray();
+    }
+    
+
+    public Object[] toArray(Object[] a) {
+        return delegate.toArray(a);
+    }
+
+
+
+
+
+    public void clear() {
+        int index1 = delegate.size() - 1;
+        delegate.clear();
+        if (index1 >= 0) {
+            fireIntervalRemoved(this, 0, index1);
+        }
+    }
+
+
+
+    public boolean containsAll(Collection c) {
+        return delegate.containsAll(c);
+    }
+
+    public Iterator iterator() {
+        return delegate.iterator();
+    }
+
+    public ListIterator listIterator() {
+        return delegate.listIterator();
+    }
+
+    public ListIterator listIterator(int index) {
+        return delegate.listIterator(index);
+    }
+
+    /**
+     * Unimplemented optional operation.
+     * 
+     * @param c
+     * @return
+     * @see java.util.List#retainAll(java.util.Collection)
+     */
+    public boolean retainAll(Collection c) {
+        throw new UnsupportedOperationException();
+    }
+
+    public List subList(int fromIndex, int toIndex) {
+        return delegate.subList(fromIndex, toIndex);
+    }
+
+
+}
+

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to