Package simtools.ui

Source Code of simtools.ui.GenericMapper$ExpressionDialog

/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info:  http://jsynoptic.sourceforge.net/index.html
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2003, by :
*     Corporate:
*         Astrium SAS
*         EADS CRC
*     Individual:
*         Nicolas Brodu
*
* $Id: GenericMapper.java,v 1.17 2008/04/10 16:39:29 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
* 07-Oct-03: Implemented all edit dialogs (NB);
  * 03-Nov-03 : Separated generic and color part (NB)
*
*/
package simtools.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;

import simtools.data.DataException;
import simtools.data.DataSource;
import simtools.util.ListenerManager;
import simtools.util.NumberStringComparator;

/**
* This class maps data sources and index to generic java Objects.
* The mapping is user defined, and based on the value of the data source.
*
* The data source shall not be saved into the object. Rather, this class goal is to implement
* a mapping independent of the data source given.
*
*/
public class GenericMapper implements Serializable, Cloneable {

  static final long serialVersionUID = -3336873194029698696L;

  public static ResourceBundle resources = ResourceFinder.get(GenericMapper.class);

  /** Associate a mappedValue for a data value of any type
   */
  protected TreeMap map;
  protected Vector intervals;
  protected String name;
  protected Object defaultValue; // null if not specified
 
  protected transient ListenerManager listeners = new ListenerManager();

  public Object clone() throws CloneNotSupportedException {
    GenericMapper m = (GenericMapper)super.clone();
    // Don't clone key and values, as they are not modified but only deleted/inserted
    m.map = (TreeMap)map.clone();
    // Intervals are cloned on write.
    m.intervals = (Vector)intervals.clone();
    m.listeners = new ListenerManager();
    m.name = (name != null)? resources.getString("CopyOf") + name : null;
    return m;
  }
 
  protected void setMapperValues(GenericMapper m){
    map = m.map;
    intervals = m.intervals;
  }

  /**
   * @return all values the mapper can provide
   */
  protected ArrayList getAllMapperValues(){
    ArrayList ret = new ArrayList();
    // Update map values
    Set mapKeys = map.keySet();
    for(Iterator it=mapKeys.iterator();it.hasNext();){
      ret.add(map.get(it.next()));
    }
    // Update interval values
    for(int i=0;i<intervals.size();i++){
      ret.add(((Interval)intervals.get(i)).value);
    }   
    return ret;
  }
 
  public void addListener(MapperListener l) {
    listeners.add(l);
  }

  public void removeListener(MapperListener l) {
    listeners.remove(l);
  }

  protected void notifyListeners() {
      synchronized(listeners) {
        int n = listeners.size(); // only one call outside loop
        for (int i=0; i<n; ++i) {
            MapperListener ml = (MapperListener)listeners.get(i);
            if (ml!=null) ml.mappingChanged(this);
        }
      }
  }

  /** Associate a value for a data interval.
   * The data interval can be of type double, long, or String,
   * and the comparison will use the best possible conversion.
   * Ex: you can specify an interval 0xABCDEF0123456789 &lt; x &lt; 0xABCDEF0123456791
   * and 0xABCDEF0123456790 will match (which would not be possible with the
   * precision of "double" data)
   */
  public static class Interval implements Cloneable, Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 1724762219212741694L;
    protected int type; // 0, 1, 2, 3 = double, long, string, degenerated
    protected double dmin, dmax;
    protected long lmin, lmax;
    protected String smin, smax;
    protected boolean boundedMin, boundedMax;
    protected boolean infiniteMin, infiniteMax;

    /** The value associated with this interval */
    public Object value;

    /**
     * Creates an interval of "double" data with a simple bound
     * @param isMore Values in the interval are greater than the v argument 
     * @param v The value to consider as the interval limit 
     * @param bounded If the value itself is in the interval
     * @param value The value to associate with this interval 
     */ 
    public Interval(boolean isMore, double v, boolean bounded, Object value) {
      type = 0;
      this.value = value;
      if (isMore) {
        dmin = v; dmax = Double.POSITIVE_INFINITY;
        lmin = (long)dmin; lmax = Long.MAX_VALUE;
        this.boundedMin = bounded; this.boundedMax = true;
        infiniteMax = true;
      } else {
        dmin = Double.NEGATIVE_INFINITY; dmax = v;
        lmin = Long.MIN_VALUE; lmax = (long)dmax;
        this.boundedMin = true; this.boundedMax = bounded;
        infiniteMin = true;
      }
    }

    public Interval(double min, boolean boundedMin, double max, boolean boundedMax, Object value) {
      type = 0;
      dmin = min; dmax = max;
      lmin = (long)min; lmax = (long)max;
      this.boundedMin = boundedMin; this.boundedMax = boundedMax;
      this.value = value;
    }

    /**
     * Creates an interval of "long" data with a simple bound
     * @param isMore Values in the interval are greater than the v argument 
     * @param v The value to consider as the interval limit 
     * @param bounded If the value itself is in the interval
     * @param value The value to associate with this interval 
     */ 
    public Interval(boolean isMore, long v, boolean bounded, Object value) {
      type = 1;
      this.value = value;
      if (isMore) {
        dmin = v; dmax = Double.POSITIVE_INFINITY;
        lmin = v; lmax = Long.MAX_VALUE;
        this.boundedMin = bounded; this.boundedMax = true;
        infiniteMax = true;
      } else {
        dmin = Double.NEGATIVE_INFINITY; dmax = v;
        lmin = Long.MIN_VALUE; lmax = v;
        this.boundedMin = true; this.boundedMax = bounded;
        infiniteMin = true;
      }
    }

    public Interval(long min, boolean boundedMin, long max, boolean boundedMax, Object value) {
      type = 1;
      dmin = (double)min; dmax = (double)max;
      lmin = min; lmax = max;
      this.boundedMin = boundedMin; this.boundedMax = boundedMax;
      this.value = value;
    }

    /**
     * Creates an interval of "String" data with a simple bound
     * @param isMore Values in the interval are greater than the v argument 
     * @param v The value to consider as the interval limit 
     * @param bounded If the value itself is in the interval
     * @param value The value to associate with this interval 
     */ 
    public Interval(boolean isMore, String v, boolean bounded, Object value) {
      type = 2;
      this.value = value;
      if (isMore) {
        smin = v; smax = null;
        this.boundedMin = bounded; this.boundedMax = false;
        infiniteMax = true;
      } else {
        smin = null; smax = v;
        this.boundedMin = false; this.boundedMax = bounded;
        infiniteMin = true;
      }
    }

    public Interval(String min, boolean boundedMin, String max, boolean boundedMax, Object value) {
      type = 2;
      smin = min; smax = max;
      this.boundedMin = boundedMin; this.boundedMax = boundedMax;
      this.value = value;
    }

    /**
     * Creates a degenarated interval always including any value
     */ 
    public Interval(Object value) {
      type = 3;
      this.value = value;
      infiniteMax = true;
      infiniteMin = true;
      boundedMin = true;
      boundedMax = true;
    }


    public boolean contains(double value) {
      if (type==3) return true;
      if (type==2) return false;
      if (value == dmin) return boundedMin;
      if (value == dmax) return boundedMax;
      if (infiniteMin && infiniteMax) return true;
      if (infiniteMin) return value < dmax;
      if (infiniteMax) return value > dmin;
      if ((value > dmin) && (value<dmax)) return true;
      return false;     
    }

    public boolean contains(long value) {
      if (type==3) return true;
      if (type==2) return false;
      if (value == lmin) return boundedMin;
      if (value == lmax) return boundedMax;
      if (infiniteMin && infiniteMax) return true;
      if (infiniteMin) return value < lmax;
      if (infiniteMax) return value > lmin;
      if ((value > lmin) && (value<lmax)) return true;
      return false;     
    }

    public boolean contains(String value) {
      if (type==3) return true;
      if (type!=2) return false;
      if (value.compareTo(smin)==0) return boundedMin;
      if (value.compareTo(smax)==0) return boundedMax;
      if (infiniteMin && infiniteMax) return true;
      if (infiniteMin) return value.compareTo(smax) < 0;
      if (infiniteMax) return value.compareTo(smin) > 0;
      if ((value.compareTo(smin) > 0) && (value.compareTo(smax) < 0)) return true;
      return false;     
    }

   
    public String toString() {
      if (type==3) return resources.getString("AnyValue");
      String ret = "";
      if (!infiniteMin) {
        switch(type) {
          case 0: ret += dmin; break;
          case 1: ret += lmin; break;
          case 2: ret += smin; break;
        }
        ret += " <";
        if (boundedMin) ret+= "=";
        ret += " ";
      }
      ret += resources.getString("Value");
      if (!infiniteMax) {
        ret += " <";
        if (boundedMax) ret+= "=";
        ret += " ";
        switch(type) {
          case 0: ret += dmax; break;
          case 1: ret += lmax; break;
          case 2: ret += smax; break;
        }
      }
      return ret;
    }

    public Object clone() throws CloneNotSupportedException {
      return super.clone();
    }

    public Number getMin() throws NumberFormatException{
        Number ret = null;
        switch(type) {
        case 0: ret = new Double(dmin); break;
        case 1: ret = new Long(lmin); break;
        case 2: ret = new Double(smin); break;
        }
        return ret;
    }
    public Number getMax() throws NumberFormatException{
            Number ret = null;
            switch(type) {
            case 0: ret = new Double(dmax); break;
            case 1: ret = new Long(lmax); break;
            case 2: ret = new Double(smax); break;
            }
            return ret;
        }

        public boolean isBoundedMin() {
            return boundedMin;
        }

        public boolean isBoundedMax() {
            return boundedMax;
        }

        public Object getValue() {
            return value;
        }
  }

 
  public GenericMapper() {
    this(null);
  }
 
  public GenericMapper(String name) {
    this.name = name;
    map = new TreeMap(new NumberStringComparator());
    intervals = new Vector();
  }

  /**
   * Realizes the mapping, and returns the object for this data source and index
   * @param ds The data source to map
   * @param index The index in the data source (may be used, or not)
   * @return the mapped object, or null
   */
  public Object getMapping(DataSource ds, long index) {
    Object value;
    try {
      if (ds==null)
        return null;
      value = ds.getValue(index);
    } catch (DataException e) {
      return defaultValue; // No data => no value
    }
    return getMapping(value);
  }

  /**
   * Realizes the mapping, and returns the object for this data source and index
   * @param ds The data source to map
   * @param index The index in the data source (may be used, or not, and defaults to the last index is used)
   * @return the mapped object, or null
   */
  public Object getMapping(DataSource ds) {
    Object value;
    try {
      if (ds==null)
        return null;
      value = ds.getValue(ds.getLastIndex());
    } catch (DataException e) {
      return defaultValue; // No data => no value
    }
    return getMapping(value);
  }

 
  public Object getMapping(Object value) {
    // Look for exact value match
    Object p = map.get(value);
    if (p!=null) return p;

    int type = -1;
    if ((value instanceof Double) || (value instanceof Float)) {
      type = 0;
    }
    if ((value instanceof Long) || (value instanceof Integer) || (value instanceof Short) || (value instanceof Byte)) {
      type = 1;
    }
    if (value instanceof String) type = 2;
   
    // Unknown data type, cannot match an interval
    if (type==-1) return defaultValue;

    // Look in the intervals
    for (int i=0; i<intervals.size(); ++i) {
      Interval iv = (Interval)intervals.get(i);
      switch(type) {
        case 0:
          if (iv.contains(((Number)value).doubleValue())) return iv.value;
          else continue;
        case 1:
          if (iv.contains(((Number)value).longValue())) return iv.value;
          else continue;
        case 2:
          if (iv.contains((String)value)) return iv.value;
      }
    }
    return defaultValue;
  }
 
  /** The value to return when nothing match */
  public void setDefaultValue(Object defaultValue) {
    this.defaultValue = defaultValue;
  }

  /** The value returned when nothing match */
  public Object getDefaultValue() {
    return defaultValue;
  }

  /** Associate a mappedValue with a value */
  public void setMapping(Object value, Object mappedValue) {
    map.put(value,mappedValue);
    notifyListeners();
  }

  /** Associate a mappedValue with a double value interval (bounds included) */
  public void setMapping(double minvalue, double maxvalue, Object mappedValue) {
    setMapping(minvalue, true, maxvalue, true, mappedValue);
  }

  /** Associate a mappedValue with a double value interval. Interval bounds are specified */
  public void setMapping(double minvalue, boolean boundedMin, double maxvalue, boolean boundedMax, Object mappedValue) {
    intervals.add(new Interval(minvalue,boundedMin,maxvalue,boundedMax,mappedValue));
    notifyListeners();
  }

  /** Associate a mappedValue with a double value infinite range (bound included).
   * @param isMore if true, the interval is x &gt;= value, else it is x &lt;= value
   */
  public void setMapping(boolean isMore, double value, Object mappedValue) {
    setMapping(isMore, value, true, mappedValue);
  }

  /** Associate a mappedValue with a double value infinite range.
   * @param isMore if true, the interval is x &gt;= value, else it is x &lt;= value
   */
  public void setMapping(boolean isMore, double value, boolean boundIncluded, Object mappedValue) {
    intervals.add(new Interval(isMore, value, boundIncluded, mappedValue));
    notifyListeners();
  }

  /** Associate a mappedValue with a long value interval (bounds included) */
  public void setMapping(long minvalue, long maxvalue, Object mappedValue) {
    setMapping(minvalue, true, maxvalue, true, mappedValue);
  }

  /** Associate a mappedValue with a long value interval. Interval bounds are specified */
  public void setMapping(long minvalue, boolean boundedMin, long maxvalue, boolean boundedMax, Object mappedValue) {
    intervals.add(new Interval(minvalue,boundedMin,maxvalue,boundedMax,mappedValue));
    notifyListeners();
  }

  /** Associate a mappedValue with a long value infinite range (bound included).
   * @param isMore if true, the interval is x &gt;= value, else it is x &lt;= value
   */
  public void setMapping(boolean isMore, long value, Object mappedValue) {
    setMapping(isMore, value, true, mappedValue);
  }

  /** Associate a mappedValue with a long value infinite range.
   * @param isMore if true, the interval is x &gt;= value, else it is x &lt;= value
   */
  public void setMapping(boolean isMore, long value, boolean boundIncluded, Object mappedValue) {
    intervals.add(new Interval(isMore, value, boundIncluded, mappedValue));
    notifyListeners();
  }

  /** Associate a mappedValue with a String value interval (bounds included) */
  public void setMapping(String minvalue, String maxvalue, Object mappedValue) {
    setMapping(minvalue, true, maxvalue, true, mappedValue);
  }

  /** Associate a mappedValue with a String value interval. Interval bounds are specified */
  public void setMapping(String minvalue, boolean boundedMin, String maxvalue, boolean boundedMax, Object mappedValue) {
    intervals.add(new Interval(minvalue,boundedMin,maxvalue,boundedMax,mappedValue));
    notifyListeners();
  }

  /** Associate a mappedValue with a String value infinite range (bound included).
   * @param isMore if true, the interval is x &gt;= value, else it is x &lt;= value
   */
  public void setMapping(boolean isMore, String value, Object mappedValue) {
    setMapping(isMore, value, true, mappedValue);
  }

  /** Associate a mappedValue with a String value infinite range.
   * @param isMore if true, the interval is x &gt;= value, else it is x &lt;= value
   */
  public void setMapping(boolean isMore, String value, boolean boundIncluded, Object mappedValue) {
    intervals.add(new Interval(isMore, value, boundIncluded, mappedValue));
    notifyListeners();
  }

  public String toString() {
    if (name!=null){
        return name;
    }
    return super.toString();
  }

  /**
   * Set mapper name
   * @param name
   */
  public void setName(String name){
      this.name = name;
  }

  /* (non-Javadoc)
   * @see java.lang.Object#equals(java.lang.Object)
   */
  public boolean equals(Object obj) {
    if (!(obj instanceof GenericMapper)) return false;
    GenericMapper c = (GenericMapper)obj;
    if ((name == null) && (c.name==null)) return super.equals(obj);
    if ((name == null) || (c.name==null)) return false;
    return name.equals(c.name);
  }

  public static GenericMapper createMapperDialog(JDialog owner) {
    GenericMapper cm = new GenericMapper();
    cm.editDialog(owner);
    return cm;
  }

  public MapperTableModel createModel() {
    return new MapperTableModel();
  }
 
  /** For subclasses to specialize */
  protected ExpressionMappingTable createTable(JDialog parent) {
    return new ExpressionMappingTable(parent);
  }

  /** For subclasses to specialize, create a new object of the specialized type */
  protected Object createNewValue() {
    return new Double(Math.random());
  }
 
  /**
   * Creates a panel to configure this Mapper.
   * @param owner An optional owner frame
   * @param listener An optional listener for the title and the OK button. If null, these components won't be displayed
   * <p>The action listener can be sent two action id:<ul>
   * <li>1 => the text field content changed. The new text is available in the action "command" string</li> 
   * <li>2 => the ok button was pressed</li>
   * </ul>the action source is the returned panel</p>
   */
  public JPanel createPanel(JDialog owner, ActionListener listener) {

    final ExpressionMappingTable table = createTable(owner);
    final JPanel pane = new JPanel(new BorderLayout());
    final ActionListener actionListener = listener; // make it visible for anonymous classes

    Box namePanel = Box.createHorizontalBox();
    final JTextField tfName = new JTextField(20);
    namePanel.add(new JLabel(resources.getString("MapperName:")));
    namePanel.add(Box.createHorizontalGlue());
    namePanel.add(tfName);
    tfName.setText((name==null) ? "" : name);
    if (listener!=null) pane.add(namePanel, BorderLayout.NORTH);
   
    Box leftPane = Box.createVerticalBox();
    leftPane.add(new JLabel(resources.getString("CurrentAssociations(double-clickToEdit)")));
    //Create the scroll pane and add the table to it.
    leftPane.add(new JScrollPane(table));
    pane.add(leftPane, BorderLayout.CENTER);

    final JButton newv, newi, del, up, down, ok;

    JPanel rightPane = new JPanel(new BorderLayout());
    JPanel buttons = new JPanel(new GridLayout(5,1));
    buttons.add(newv =new JButton(resources.getString("NewValue")));
    buttons.add(newi = new JButton(resources.getString("NewInterval")));
    buttons.add(del = new JButton(resources.getString("Delete")));
    buttons.add(up = new JButton(resources.getString("MoveUp")));
    buttons.add(down = new JButton(resources.getString("MoveDown")));
    rightPane.add(buttons, BorderLayout.NORTH);
    ok = new JButton(resources.getString("OK"));
    if (listener!=null) rightPane.add(ok, BorderLayout.SOUTH);
    pane.add(rightPane, BorderLayout.EAST);
    del.setEnabled(false);
    up.setEnabled(false);
    down.setEnabled(false);

   
    if (listener!=null) tfName.getDocument().addDocumentListener(new DocumentListener() {
      public void insertUpdate(DocumentEvent e) {
        updateName();
      }
      public void removeUpdate(DocumentEvent e) {
        updateName();
      }
      public void changedUpdate(DocumentEvent e) {
        updateName();
      }
      public void updateName() {
        name = tfName.getText();
        if (name.equals("")) name = null;
        actionListener.actionPerformed(new ActionEvent(pane, 1, (name==null) ? resources.getString("UnnamedMapper") : name));
      }
    });

    table.addSelectionListener(new ListSelectionListener() {
      public void valueChanged(ListSelectionEvent event) {
        int r = table.getSelectedRow();
        if (r==-1) {
          del.setEnabled(false);
          up.setEnabled(false);
          down.setEnabled(false);
          return;
        }
        del.setEnabled(true);
        Expression e = (Expression)table.tableModel.expressions.get(r);
        if (e.value instanceof Interval) {
          up.setEnabled((r>0) && (((Expression)table.tableModel.expressions.get(r-1)).value instanceof Interval));
          down.setEnabled(r<table.tableModel.expressions.size()-1);
        } else {
          up.setEnabled(false);
          down.setEnabled(false);
        }
      }
    });

    newv.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        Long key = null;
        for (int k = 0; k < Integer.MAX_VALUE; k++) {
          key = new Long(k);
          if (map.get(key)==null) break;
        }
        map.put(key, createNewValue());
        int r = 0;
        for (Iterator it = map.keySet().iterator(); it.hasNext(); r++) {
          if (it.next().equals(key)) break;
        }
        table.tableModel.update(0,map.size()+intervals.size()-1);
        table.tableModel.fireTableRowsInserted(r,r);
        table.setRowSelectionInterval(r,r);
      }
    });

    newi.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        intervals.add(new Interval(false,0,false,createNewValue()));
        int r = map.size()+intervals.size()-1;
        table.tableModel.fireTableRowsInserted(r,r);
        table.tableModel.update(0,r);
        table.setRowSelectionInterval(r,r);
      }
    });

    del.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        int r = table.getSelectedRow();
        if (r<0) return;
        Expression exp = (Expression)table.tableModel.expressions.get(r);
        if (r<map.size()) {
          map.remove(exp.value);
          table.tableModel.fireTableRowsDeleted(r,r);
          table.tableModel.update(0,map.size()+intervals.size()-1);
          return;
        }
        intervals.remove(exp.value);
        table.tableModel.fireTableRowsDeleted(r,r);
        table.tableModel.update(0,map.size()+intervals.size()-1);
      }
    });

    up.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        int r = table.getSelectedRow();
        int i = r - map.size();
        if ((i<1) || (i>=intervals.size())) return;
        Object o = intervals.get(i-1);
        Object p = intervals.get(i);
        intervals.set(i-1,p);
        intervals.set(i,o);
        table.tableModel.update(r-1,r);
        table.setRowSelectionInterval(r-1,r-1);
      }
    });

    down.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        int r = table.getSelectedRow();
        int i = r - map.size();
        if ((i<0) || (i>=intervals.size()-1)) return;
        Object o = intervals.get(i);
        Object p = intervals.get(i+1);
        intervals.set(i,p);
        intervals.set(i+1,o);
        table.tableModel.update(r,r+1);
        table.setRowSelectionInterval(r+1,r+1);
      }
    });

    if (listener!=null) ok.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        actionListener.actionPerformed(new ActionEvent(pane, 2,""));
      }
    });
   
    return pane;
  }
 
  public void editDialog(JDialog owner) {

    final JDialog dialog = new JDialog(owner,true);
    dialog.setTitle((name==null) ? resources.getString("UnnamedMapper") : name);

    JPanel content = createPanel(owner, new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        switch (e.getID()) {
          case 1: // text change
            dialog.setTitle(e.getActionCommand());
            break;
          case 2: // OK pressed
            dialog.dispose();
            break;
        }
      }
    });
   
    dialog.getContentPane().add(content);
    dialog.pack();
    dialog.show();
    notifyListeners(); // there is no cancel => always consider the mapping changed
  }
 
  /** Copied from Java Tutorial on tables, they had the very good idea to set up a colored cell table :))))
   */
  protected class ExpressionMappingTable extends JTable {

    /**
     *
     */
    private static final long serialVersionUID = -4343304998955053415L;
    protected MapperTableModel tableModel;
    public ExpressionMappingTable(JDialog parent) {

      setModel(tableModel = createModel());

      setPreferredScrollableViewportSize(new Dimension(300, 200));
      getColumnModel().getColumn(0).setPreferredWidth(250);
      getColumnModel().getColumn(1).setPreferredWidth(50);

      //Set up renderer and editor for the value column.
      setUpRenderer();
      setUpEditor();
      setUpExpressionEditor(parent);
     
      getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
     
    }

    public void addSelectionListener(ListSelectionListener l) {
      getSelectionModel().addListSelectionListener(l)
    }

    protected void setUpRenderer() {
    }

    protected void setUpEditor() {
    }

    protected void setUpExpressionEditor(JDialog parent) {
      //First, set up the button that brings up the dialog.
      final JButton button = new JButton("");
      button.setBackground(Color.white);
      button.setBorderPainted(false);
      button.setMargin(new Insets(0,0,0,0));

      //Now create an editor to encapsulate the button, and
      //set it up as the editor for all Color cells.
      final ExpressionEditor expressionEditor = new ExpressionEditor(button);
      setDefaultEditor(Expression.class, expressionEditor);

      final ExpressionDialog expressionDialog = new ExpressionDialog(parent);
      //Set up the dialog that the button brings up.
      expressionDialog.addOKListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          expressionEditor.currentExpression = expressionDialog.getExpression();
        }
      });
   
      //Here's the code that brings up the dialog.
      button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          button.setText(expressionEditor.currentExpression.toString());
          expressionDialog.setLocationRelativeTo(button);
          expressionDialog.edit(expressionEditor.currentExpression);
        }
      });
    }

  }


  /*
   * The editor button that brings up the dialog.
   * We extend DefaultCellEditor for convenience,
   * even though it mean we have to create a dummy
   * check box.  Another approach would be to copy
   * the implementation of TableCellEditor methods
   * from the source code for DefaultCellEditor.
   */
  protected static class ExpressionEditor extends DefaultCellEditor {
    /**
     *
     */
    private static final long serialVersionUID = 9202536695519603668L;
    protected Expression currentExpression;

    public ExpressionEditor(JButton b) {
        super(new JCheckBox()); //Unfortunately, the constructor
                    //expects a check box, combo box,
                    //or text field.
      editorComponent = b;
      setClickCountToStart(2); //This is usually 1 or 2.

      //Must do this so that editing stops when appropriate.
      b.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          fireEditingStopped();
        }
      });
    }

    protected void fireEditingStopped() {
      super.fireEditingStopped();
    }

    public Object getCellEditorValue() {
      return currentExpression;
    }

    public Component getTableCellEditorComponent(JTable table,
                           Object value,
                           boolean isSelected,
                           int row,
                           int column) {
      ((JButton)editorComponent).setText(value.toString());
      currentExpression = (Expression)value;
      return editorComponent;
    }
  }

  protected static class Expression implements Cloneable {
    public Object value;

    public Expression(Object o) {
      value = o;
    }
   
    public String toString() {
      if (value instanceof Interval) return ((Interval)value).toString();
      return resources.getString("Value=") + value;
    }

    public Object clone() throws CloneNotSupportedException {
      return super.clone();
    }

  } 

  protected class MapperTableModel extends AbstractTableModel {
    /**
     *
     */
    private static final long serialVersionUID = -8126260990866927148L;
    final String[] columnNames = {resources.getString("Expression"), resources.getString("Mapping")};
    protected Object[] mappedValues;
    protected Vector expressions;

    public MapperTableModel() {
      // converts the map into two arrays
      expressions = new Vector();
      if ((map!=null) && (map.keySet()!=null)) for (Iterator it = map.keySet().iterator(); it.hasNext(); ) {
        expressions.add(new Expression(it.next()));
      }
      if (intervals!=null) for (Iterator it = intervals.iterator(); it.hasNext(); ) {
        expressions.add(new Expression(it.next()));
      }
      if (map.values()!=null) mappedValues = map.values().toArray();
    }

    /**
     * Updates the model, called when the intervals Vector changed
     * @param startIndex
     * @param endIndex
     */
    public void update(int startIndex, int endIndex) {
      // converts the map into two arrays
      expressions = new Vector();
      for (Iterator it = map.keySet().iterator(); it.hasNext(); ) {
        expressions.add(new Expression(it.next()));
      }
      for (Iterator it = intervals.iterator(); it.hasNext(); ) {
        expressions.add(new Expression(it.next()));
      }
      mappedValues = map.values().toArray();
      fireTableRowsUpdated(startIndex, endIndex);
    }

    public int getColumnCount() {
      return columnNames.length;
    }

    public Class getColumnClass(int c) {
      if (c==0) return Expression.class;
      return Object.class;
    }
   
    public int getRowCount() {
      return expressions.size();
    }

    public String getColumnName(int col) {
      return columnNames[col];
    }

    public Object getValueAt(int row, int col) {
      if (col==0) return expressions.get(row);
      if (row<mappedValues.length) return mappedValues[row];
      return ((Interval)intervals.get(row-mappedValues.length)).value;
    }

    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    public boolean isCellEditable(int row, int col) {
      if (col==0) return true;
      return false;
    }

    public void setValueAt(Object value, int row, int col) {
      // first rows are backed by the map
      if (row<mappedValues.length) {
        // change in value => ouch, change the map key
        if (col==0) {
          Object oldvalue = ((Expression)expressions.get(row)).value;
          Object newvalue = mappedValues[row];
          map.remove(oldvalue);
          map.put(((Expression)value).value,newvalue);
          expressions = new Vector();
          for (Iterator it = map.keySet().iterator(); it.hasNext(); ) {
            expressions.add(new Expression(it.next()));
          }
          for (Iterator it = intervals.iterator(); it.hasNext(); ) {
            expressions.add(new Expression(it.next()));
          }
          mappedValues = map.values().toArray();
          this.fireTableRowsUpdated(0,mappedValues.length-1);
          return;
        }
        // change in mappedValue
        Object key = ((Expression)expressions.get(row)).value;
        if (!map.containsKey(key)) return; // shall not happen
        map.put(key,value); // put the new value
        mappedValues[row] = value; // update our own copy
        this.fireTableCellUpdated(row,col);
        return;
      }
      // last rows are backed by the intervals
      if (col==0) {
        intervals.set(row-mappedValues.length,((Expression)value).value);
        expressions.set(row,value);
        this.fireTableRowsUpdated(row,row);
        return;
      }
      // Clone on modification, useful for cloning the mapper and share the values as long as they
      // do not change => same for the treemap, the entries are re-inserted
      Interval iv = (Interval)intervals.get(row-mappedValues.length);
      try {
        iv = (Interval)iv.clone();
      } catch (CloneNotSupportedException e) {
      }
      iv.value = value;
      intervals.set(row-mappedValues.length,iv);
    }

  }

  public static class ExpressionDialog extends JDialog {

    /**
     *
     */
    private static final long serialVersionUID = 2382730947499900807L;
    JButton ok, cancel;
    Expression current;
    ActionCheckBox mincb, maxcb;
    JTextField minValue, maxValue, theValue;
    JCheckBox minInc, maxInc;
    int mint, maxt;
    long lmin, lmax;
    double dmin, dmax;
    String smin, smax;
    int type;

    Box valuePanel, intervalPanel;

    public ExpressionDialog(JDialog parent) {
      super(parent,true);
      setTitle(resources.getString("Editing:")+current);

      this.getContentPane().setLayout(new BoxLayout(this.getContentPane(),BoxLayout.Y_AXIS));

      valuePanel = Box.createHorizontalBox();
      valuePanel.add(new JLabel(resources.getString("Value=")));
      valuePanel.add(theValue = new JTextField(10));
      valuePanel.setVisible(false);
      this.getContentPane().add(valuePanel);

      Box box;

      intervalPanel = Box.createVerticalBox();
      box = Box.createHorizontalBox();
      box.add(mincb = new ActionCheckBox(resources.getString("Min"), true) {
        public void actionPerformed(ActionEvent e) {
          minValue.setEnabled(isSelected());
          minInc.setEnabled(isSelected());
        }
      });
      box.add(minValue = new JTextField(10));
      minValue.getDocument().addDocumentListener(new DocumentListener() {
        public void insertUpdate(DocumentEvent e) {
          updateType();
        }
        public void removeUpdate(DocumentEvent e) {
          updateType();
        }
        public void changedUpdate(DocumentEvent e) {
          updateType();
        }
      });
      box.add(minInc = new JCheckBox(resources.getString("BoundIncluded"), true));
      intervalPanel.add(box);

      box = Box.createHorizontalBox();
      box.add(maxcb = new ActionCheckBox(resources.getString("Max"), true) {
        public void actionPerformed(ActionEvent e) {
          maxValue.setEnabled(isSelected());
          maxInc.setEnabled(isSelected());
        }
      });
      box.add(maxValue = new JTextField(10));
      maxValue.getDocument().addDocumentListener(new DocumentListener() {
        public void insertUpdate(DocumentEvent e) {
          updateType();
        }
        public void removeUpdate(DocumentEvent e) {
          updateType();
        }
        public void changedUpdate(DocumentEvent e) {
          updateType();
        }
      });
      box.add(maxInc = new JCheckBox(resources.getString("BoundIncluded"), true));
      intervalPanel.add(box);
      intervalPanel.setVisible(false);
      this.getContentPane().add(intervalPanel);
      mincb.apply();
      maxcb.apply();

      box = Box.createHorizontalBox();
      box.add(Box.createHorizontalGlue());
      box.add(cancel = new JButton(resources.getString("Cancel")));
      box.add(ok = new JButton(resources.getString("OK")));

      this.getContentPane().add(box);
     
      ok.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e) {
          updateExpression();
          hide();
        }
      });

      cancel.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e) {
          hide();
        }
      });
      setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
      //pack();
      setResizable(false);
    }

    protected void updateType() {
      if (!(current.value instanceof Interval)) return;
      smin = minValue.getText();
      try {
        // If it can be parsed as an integer number, including hex, use that
        lmin = Long.decode(smin).longValue();
        dmin = lmin;
        mint = 1;
      } catch (NumberFormatException nfe) {
        try {
          // If it can be parsed as an integer number, including hex, use that
          dmin = Double.parseDouble(smin);
          mint = 0;
        } catch (NumberFormatException nfe2) {
          mint = 2; // and smin already set
        }
      }
      smax = maxValue.getText();
      try {
        // If it can be parsed as an integer number, including hex, use that
        lmax = Long.decode(smax).longValue();
        dmax = lmax;
        maxt = 1;
      } catch (NumberFormatException nfe) {
        try {
          // If it can be parsed as an integer number, including hex, use that
          dmax = Double.parseDouble(smax);
          maxt = 0;
        } catch (NumberFormatException nfe2) {
          maxt = 2; // and smax already set
        }
      }
      if (mincb.isSelected() && maxcb.isSelected()) {
        if ((mint==2) || (maxt==2)) type = 2; // one string => string
        else if ((mint==1) && (maxt==1)) type = 1; // both integers => integer
        else type = 0; // no string and floating point value => floating point
      } else if (mincb.isSelected()) {
        type = mint;
      } else if (maxcb.isSelected()) {
        type = maxt;
      } else type = 3;
    }

    protected void updateExpression() {
      if (current==null) return;
      if (current.value instanceof Interval) {
        if (minValue.getText().equals("")) {mincb.setSelected(false); mincb.apply();}
        if (maxValue.getText().equals("")) {maxcb.setSelected(false); maxcb.apply();}
        Object value = ((Interval)current.value).value;
        updateType();
        switch(type) {
          case 0:
            if (mincb.isSelected() && maxcb.isSelected())
              current.value = new Interval(dmin, minInc.isSelected(), dmax, maxInc.isSelected(), value);
            else if (mincb.isSelected())
              current.value = new Interval(true, dmin, minInc.isSelected(), value);
            else
              current.value = new Interval(false, dmax, maxInc.isSelected(), value);
            break;
          case 1:
            if (mincb.isSelected() && maxcb.isSelected())
              current.value = new Interval(lmin, minInc.isSelected(), lmax, maxInc.isSelected(), value);
            else if (mincb.isSelected())
              current.value = new Interval(true, lmin, minInc.isSelected(), value);
            else
              current.value = new Interval(false, lmax, maxInc.isSelected(), value);
            break;
          case 2:
            if (mincb.isSelected() && maxcb.isSelected())
              current.value = new Interval(smin, minInc.isSelected(), smax, maxInc.isSelected(), value);
            else if (mincb.isSelected())
              current.value = new Interval(true, smin, minInc.isSelected(), value);
            else
              current.value = new Interval(false, smax, maxInc.isSelected(), value);
            break;
          case 3:
            current.value = new Interval(value);
            break;
        }
        return; // Interval updated
      }
      // Not an interval
      String value = theValue.getText();
      try {
        // If it can be parsed as an integer number, including hex, use that
        current.value = Long.decode(value);
      } catch (NumberFormatException nfe) {
        try {
          // If it can be parsed as double, OK
          current.value = new Double(Double.parseDouble(value));
        } catch (NumberFormatException nfe2) {
          // Otherwise, keep string
          current.value = value;
        }
      }
    }

    /**
     * @param object
     */
    public void edit(Expression e) {
      copyExpression(e);
      show();
    }

    /**
     * @param object
     */
    public void copyExpression(Expression e) {
      try {
        current = (Expression)e.clone();
      } catch (CloneNotSupportedException e1) {
        current = e;
      }
      if (current.value instanceof Interval) {
        Interval iv = (Interval)current.value;
        mincb.setSelected(!iv.infiniteMin);
        mincb.apply();
        maxcb.setSelected(!iv.infiniteMax);
        maxcb.apply();
        switch(iv.type) {
          case 0:
            if (iv.infiniteMin) minValue.setText("");
            else minValue.setText(""+iv.dmin);
            if (iv.infiniteMax) maxValue.setText("");
            else maxValue.setText(""+iv.dmax);
            break;
          case 1:
            if (iv.infiniteMin) minValue.setText("");
            else minValue.setText(""+iv.lmin);
            if (iv.infiniteMax) maxValue.setText("");
            else maxValue.setText(""+iv.lmax);
            break;
          case 2:
            if (iv.infiniteMin) minValue.setText("");
            else minValue.setText(iv.smin);
            if (iv.infiniteMax) maxValue.setText("");
            else maxValue.setText(iv.smax);
            break;
          case 3:
            minValue.setText("");
            maxValue.setText("");
            break;
        }
        valuePanel.setVisible(false);
        intervalPanel.setVisible(true);
        minInc.setSelected(iv.boundedMin);
        maxInc.setSelected(iv.boundedMax);
      } else {
        theValue.setText(current.value.toString());
        intervalPanel.setVisible(false);
        valuePanel.setVisible(true);
      }
      setTitle(resources.getString("Editing:")+current);
      setResizable(true);
      pack();
      setResizable(false);
    }

    /**
     * @param listener
     */
    public void addOKListener(ActionListener listener) {
      ok.addActionListener(listener);
    }


    /**
     * @return
     */
    public Expression getExpression() {
      return current;
    }
  }

  private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
    listeners = new ListenerManager();
  }

    public Vector getIntervals() {
        return intervals;
    }

    public TreeMap getMap() {
        return map;
    }
 
}
TOP

Related Classes of simtools.ui.GenericMapper$ExpressionDialog

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.