Beispiel #1
0
/**
 * This handles a variety of flavors of xml documents (e.g., thredds query capability, thredds
 * catalogs, idv menus) to create data choosers from. It provides a combobox to enter urls to xml
 * documents. It retrieves the xml and creates a {@link XmlHandler} based on the type of xml.
 * Currently this class handles two types of xml: Thredds catalog and Web Map Server (WMS)
 * capability documents. The XmlHandler does most of the work.
 *
 * <p>This class maintains the different xml docs the user has gone to coupled with the XmlHandler
 * for each doc. It uses this list to support navigating back and forth through the history of
 * documents.
 *
 * @author IDV development team
 * @version $Revision: 1.80 $Date: 2007/07/09 22:59:58 $
 */
public class XmlChooser extends IdvChooser implements ActionListener {

  /** Use this member to log messages (through calls to LogUtil) */
  static ucar.unidata.util.LogUtil.LogCategory log_ =
      ucar.unidata.util.LogUtil.getLogInstance(XmlChooser.class.getName());

  /** _more_ */
  public static final String PROP_CHOOSER_URL = "idv.chooser.url";

  /**
   * If there was some error in loading the xml we construct and xml document with the error tag and
   * popup the message in the GUI
   */
  public static final String TAG_ERROR = "error";

  /**
   * The xml attr name for any initial url from the choosers.xml segment that created this chooser
   */
  public static final String ATTR_URL = "url";

  /** Action command for choosing a file */
  private static final String CMD_BROWSE = "cmd.browse";

  /** Keeps track of the outstanding url request */
  protected int timestamp = 0;

  /**
   * List of {@link XmlHandler}s that we have create We keep this around so we can go back and forth
   * in the list
   */
  private List handlers = new ArrayList();

  /** The document we created the xml from */
  private Document document;

  /** The xml */
  private String xmlContents;

  /** The current index into the handlers list */
  private int historyIdx = -1;

  /** Back history button */
  private JButton backBtn;

  /** Forward history button */
  private JButton fwdBtn;

  /**
   * A {@link ucar.unidata.util.PreferenceList} that manages the url list. Saving new entries as a
   * user property, etc.
   */
  private PreferenceList urlListHandler;

  /** Combobox of urls. We get this from the urlListHandler */
  private JComboBox urlBox;

  /** A flag to know when to ignore urlBox selection events */
  private boolean okToDoUrlListEvents = true;

  /** Holds the current XmlHandler GUI */
  private JPanel handlerHolder;

  /** The main gui contents */
  private JPanel myContents;

  /** The first url we have */
  private String initialUrlPath;

  /** The data selector we use */
  private DataSelector dataSelector;

  /**
   * Create the <code>XmlChooser</code>
   *
   * @param mgr The <code>IdvChooserManager</code>
   * @param root The xml root that defines this chooser
   */
  public XmlChooser(IdvChooserManager mgr, Element root) {
    super(mgr, root);
    initialUrlPath = ((chooserNode != null) ? XmlUtil.getAttribute(chooserNode, ATTR_URL, "") : "");
  }

  /**
   * _more_
   *
   * @param dataSource _more_
   */
  public void setDataSource(DataSource dataSource) {
    super.setDataSource(dataSource);
    String tmp = (String) dataSource.getProperty(PROP_CHOOSER_URL);
    if (tmp != null) {
      initialUrlPath = tmp;
    }
  }

  /**
   * _more_
   *
   * @return _more_
   */
  private boolean haveHandler() {
    return ((historyIdx >= 0) && (historyIdx < handlers.size()));
  }

  /** _more_ */
  protected void updateStatus() {
    if (haveHandler()) {
      XmlHandler handler = (XmlHandler) handlers.get(historyIdx);
      handler.updateStatus();
    } else {
      setStatus("");
    }
  }

  /**
   * _more_
   *
   * @param have _more_
   */
  public void setHaveData(boolean have) {
    super.setHaveData(have);
    updateStatus();
  }

  /**
   * Overwrite base class method to do the update on first display.
   *
   * @return Return true. We do want to do an update on initial display.
   */
  protected boolean shouldDoUpdateOnFirstDisplay() {
    return true;
  }

  /**
   * Handle any Gui actions.
   *
   * @param ae The <code>ActionEvent</code>.
   */
  public void actionPerformed(ActionEvent ae) {
    String cmd = ae.getActionCommand();
    if (cmd.equals(CMD_BROWSE)) {
      String filename = FileManager.getReadFile(FILTER_XML);
      if (filename == null) {
        return;
      }
      urlBox.setSelectedItem(filename);
    } else if (cmd.equals(GuiUtils.CMD_OK)) {
      doLoad();
    } else {
      // Here, the base class ChooserPanel will check if this command
      // is the load or cancel command.
      super.actionPerformed(ae);
    }
  }

  /**
   * _more_
   *
   * @param properties _more_
   */
  public void initSubProperties(Hashtable properties) {
    properties.put(PROP_CHOOSERCLASSNAME, getClass().getName());
    properties.put(PROP_CHOOSER_URL, urlBox.getSelectedItem());
  }

  /**
   * _more_
   *
   * @param definingObject _more_
   * @param dataType _more_
   * @param properties _more_
   * @return _more_
   */
  protected boolean makeDataSource(Object definingObject, String dataType, Hashtable properties) {
    properties.put(PROP_CHOOSER_URL, urlBox.getSelectedItem());
    return super.makeDataSource(definingObject, dataType, properties);
  }

  /**
   * Create and return the Gui contents.
   *
   * @return The gui contents.
   */
  protected JComponent doMakeContents() {
    //        dataSelector = new DataSelector(getIdv(), new Dimension(400, 200),
    //                                        true);

    // Get the list of catalogs but remove the old catalog.xml entry
    urlListHandler = getPreferenceList(PREF_CATALOGLIST);
    final XmlChooser xmlChooser = this;
    ActionListener catListListener =
        new ActionListener() {
          public void actionPerformed(ActionEvent ae) {
            if (!okToDoUrlListEvents) {
              return;
            }
            xmlChooser.actionPerformed(ae);
          }
        };
    urlBox = urlListHandler.createComboBox(GuiUtils.CMD_UPDATE, catListListener, true);

    GuiUtils.setPreferredWidth(urlBox, 200);

    // top panel
    JButton browseButton = new JButton("Select File...");
    browseButton.setToolTipText("Choose a catalog from disk");
    browseButton.setActionCommand(CMD_BROWSE);
    browseButton.addActionListener(this);

    GuiUtils.setHFill();
    JPanel catListPanel =
        GuiUtils.doLayout(new Component[] {urlBox}, 1, GuiUtils.WT_Y, GuiUtils.WT_N);

    backBtn =
        GuiUtils.getImageButton(GuiUtils.getImageIcon("/auxdata/ui/icons/Left16.gif", getClass()));
    backBtn.setToolTipText("View previous selection");
    GuiUtils.makeMouseOverBorder(backBtn);
    backBtn.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent ae) {
            goBack();
          }
        });

    fwdBtn =
        GuiUtils.getImageButton(GuiUtils.getImageIcon("/auxdata/ui/icons/Right16.gif", getClass()));
    GuiUtils.makeMouseOverBorder(fwdBtn);
    fwdBtn.setToolTipText("View next selection");
    fwdBtn.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent ae) {
            goForward();
          }
        });

    checkButtons();
    JComponent bottomButtons = getDefaultButtons();
    handlerHolder = new JPanel();
    handlerHolder.setLayout(new BorderLayout());
    //        JPanel tmp = new JPanel();
    //        tmp.setPreferredSize(new Dimension(200,500));
    //        handlerHolder.add(tmp, BorderLayout.CENTER);

    if (getIdv().getArgsManager().getInitCatalogs().size() > 0) {
      initialUrlPath = (String) getIdv().getArgsManager().getInitCatalogs().get(0);
      urlBox.setSelectedItem(initialUrlPath);
    } else {
      if ((initialUrlPath != null) && (initialUrlPath.length() > 0)) {
        makeUiFromPath(initialUrlPath);
      } else {
        makeBlankTree();
      }
    }
    JPanel navButtons = GuiUtils.hbox(backBtn, fwdBtn);

    GuiUtils.tmpInsets = GRID_INSETS;
    JPanel catPanel =
        GuiUtils.doLayout(
            new Component[] {new JLabel("Catalogs:"), catListPanel, browseButton},
            3,
            GuiUtils.WT_NYN,
            GuiUtils.WT_N);
    JPanel topPanel = GuiUtils.leftCenter(navButtons, catPanel);
    myContents = GuiUtils.topCenterBottom(topPanel, handlerHolder, bottomButtons);
    //        myContents = GuiUtils.topCenter(getStatusComponent(), myContents);
    return myContents;
  }

  /**
   * Load the xml defined by the given xmlPath. Try to create a display from it.
   *
   * @param xmlPath The url pointing to the xml to display.
   * @return Was this successful
   */
  public boolean makeUiFromPath(String xmlPath) {
    return makeUiFromPath(xmlPath, ++timestamp);
  }

  /**
   * Load the xml defined by the given xmlPath. Try to create a display from it.
   *
   * @param xmlPath The url pointing to the xml to display.
   * @param myTimestamp Keeps track of which request this is.
   * @return Was this successful
   */
  public boolean makeUiFromPath(String xmlPath, int myTimestamp) {
    boolean ok = true;
    try {
      if (xmlPath.length() > 0) {
        showWaitCursor();
        xmlContents = IOUtil.readContents(xmlPath, NULL_STRING);
        showNormalCursor();
        if (myTimestamp != timestamp) {
          return false;
        }
        if (xmlContents == null) {
          // Clear out the tree
          xmlContents =
              XmlUtil.tag(TAG_ERROR, XmlUtil.attr("label", "Could not load url: " + xmlPath));
          ok = false;
        }
        // Check if its xml
        if (xmlContents.indexOf("<") >= 0) {
          document = XmlUtil.getDocument(xmlContents);
        }

        // If we failed to make an xml document then try to tack on the wms
        // capabilities request in case this is a wms url without one
        if ((document == null) || (document.getDocumentElement() == null)) {
          if (xmlPath.indexOf("?") < 0) {
            xmlPath = xmlPath + "?request=GetCapabilities&service=WMS";
          } else {
            xmlPath = xmlPath + "&request=GetCapabilities&service=WMS";
          }
          xmlContents = IOUtil.readContents(xmlPath, NULL_STRING);
          document = XmlUtil.getDocument(xmlContents);
        }

        if ((document == null) || (document.getDocumentElement() == null)) {
          throw new IllegalArgumentException("Could not process XML from:" + xmlPath);
        }
        makeUi(document, document.getDocumentElement(), xmlPath);
      } else {
        makeUi(null, null, xmlPath);
      }
    } catch (Exception exc) {
      if (myTimestamp != timestamp) {
        return false;
      }
      logException("Creating ui:" + xmlPath, exc);
      return false;
    }
    return ok;
  }

  /**
   * Generate a user interface from the given xml document (derived from the given path). The xml
   * can be a thredds query capability, any verion of a thredds catalog or an IDV menus xml file.
   *
   * @param doc the xml document
   * @param xmlRoot The root of the xml document to create a display for.
   * @param path The url path we got the xml from.
   */
  protected void makeUi(Document doc, Element xmlRoot, String path) {
    this.document = doc;
    setHaveData(false);
    if (xmlRoot == null) {
      return;
    }
    setSelected(path);
    XmlHandler handler = null;
    String tagName = XmlUtil.getLocalName(xmlRoot);

    if (tagName.equals(WmsUtil.TAG_WMS1) || tagName.equals(WmsUtil.TAG_WMS2)) {
      handler = new WmsHandler(this, xmlRoot, path);
    } else if (tagName.equals(TAG_ERROR)) {
      final String error = XmlUtil.getAttribute(xmlRoot, "label", "Error");
      LogUtil.userErrorMessage("Error: " + error);
      return;
    } else if (tagName.equals(CatalogUtil.TAG_CATALOG)) {
      handler = new ThreddsHandler(this, xmlRoot, path);
    } else if (tagName.equals("menus")) {
      handler = new MenuHandler(this, xmlRoot, path);
    } else {
      throw new IllegalArgumentException(
          "Unknown xml:"
              + ((xmlContents.length() > 100) ? xmlContents.substring(0, 100) : xmlContents)
              + " ...");
    }

    JComponent contents = handler.getContents();
    contents.setPreferredSize(new Dimension(200, 250));
    addToContents(contents);
    addToHistory(handler);
    updateStatus();
  }

  /**
   * Set the catalog list combobox to the given xmlPath.
   *
   * @param xmlPath The xmlPath to show in the combo box.
   */
  private void setSelected(String xmlPath) {
    okToDoUrlListEvents = false;
    urlBox.setSelectedItem(xmlPath);
    okToDoUrlListEvents = true;
  }

  /** Display the document defined in the history list by the current historyIdx. */
  private void go() {
    if (haveHandler()) {
      XmlHandler handler = (XmlHandler) handlers.get(historyIdx);
      setSelected(handler.getPath());
      addToContents(handler.getContents());
      checkButtons();
    }
  }

  /** Go back and display the previous document. */
  public void goBack() {
    historyIdx--;
    if (historyIdx < 0) {
      historyIdx = 0;
    }
    go();
  }

  /** Go forward and display the next document in the history list. */
  public void goForward() {
    historyIdx++;
    if (historyIdx >= handlers.size()) {
      historyIdx = handlers.size() - 1;
    }
    go();
  }

  /** Disable or enable the forward/back buttons. */
  private void checkButtons() {
    fwdBtn.setEnabled(historyIdx < handlers.size() - 1);
    backBtn.setEnabled(historyIdx > 0);
  }

  /** A holder of a String action and a properties table. */
  public static class PropertiedAction {

    /** The action */
    String action;

    /** The properties */
    Hashtable properties;

    /**
     * Create me with the given actio
     *
     * @param action The action
     */
    public PropertiedAction(String action) {
      this(action, new Hashtable());
    }

    /**
     * Create me with the given action and properties
     *
     * @param action The actio
     * @param properties The properties
     */
    public PropertiedAction(String action, Hashtable properties) {
      this.action = action;
      this.properties = properties;
    }
  }

  /**
   * A wrapper around @see{handleAction}, passing in an empty properties table.
   *
   * @param action The String action (Url, Idv command, etc.) to handle.
   */
  protected void handleAction(String action) {
    handleAction(action, new Hashtable());
  }

  /**
   * Process the given action (e.g., url, idv command) with the given properties. This just creates
   * a new PropertiedAction, adds it to a list and turns around and calls the handleActions method.
   *
   * @param action The String action (Url, Idv command, etc.) to handle.
   * @param properties The properties for this action.
   */
  protected void handleAction(String action, Hashtable properties) {
    handleActions(Misc.newList(new PropertiedAction(action, properties)));
  }

  /**
   * Process the given list of {@link PropertiedAction}s.
   *
   * @param actions The list of actions to process
   */
  protected void handleActions(final List actions) {
    // Run the call in another thread. For now use the ChooserRunnable. This
    // really does nothing but is a hook for when we have  cancel
    // load, etc, functionality
    Misc.run(
        new ChooserRunnable(this) {
          public void run() {
            showWaitCursor();
            try {
              handleActionsInThread(actions);
            } catch (Exception exc) {
              logException("Creating data source", exc);
            }
            showNormalCursor();
            if (getCanceled()) {}
          }
        });
  }

  /**
   * Actually does the work of handling the actions
   *
   * @param actions List of <code>PropertiedAction</code>s
   */
  protected void handleActionsInThread(List actions) {
    boolean didone = false;
    String invalidSource = null;
    for (int i = 0; i < actions.size(); i++) {
      PropertiedAction action = (PropertiedAction) actions.get(i);
      boolean isValidAction = idv.handleAction(action.action, action.properties);
      if (isValidAction) {
        didone = true;
      } else {
        if (invalidSource == null) {
          invalidSource = action.action;
        }
      }
    }

    if (didone) {
      closeChooser();
    } else {
      // If we did not do any and if there was one path that was an invalid data source
      // then try to build the gui with it.
      if (invalidSource != null) {
        if (!invalidSource.endsWith(".xml")) {
          //              LogUtil.userMessage ("Unknown url:" + invalidSource);
        } else {
          makeUiFromPath(invalidSource);
        }
      }
    }
  }

  /** Reload the current xml and update the display. */
  public void doUpdate() {
    if (okToDoUrlListEvents) {
      Misc.run(this, "doUpdateInner");
    }
  }

  /** Reload the current xml and update the display. */
  public void doUpdateInner() {
    String selected = urlBox.getSelectedItem().toString().trim();
    // Only save off the list on a successful load
    if (selected.length() == 0) {
      if (handlers.size() > 0) {
        goBack();
      } else {
        makeBlankTree();
      }
      return;
    }
    if (makeUiFromPath(selected)) {
      urlListHandler.saveState(urlBox);
    }
  }

  /** Load the currently selected xml element. */
  public void doLoadInThread() {
    showWaitCursor();
    try {

      Object handler = handlers.get(historyIdx);
      if (handler instanceof ThreddsHandler) {
        ((ThreddsHandler) handler).doLoad();
      } else if (handler instanceof WmsHandler) {
        ((WmsHandler) handler).doLoad();
      } else if (handler instanceof DatasetUI) {
        String action = ((DatasetUI) handler).processActionTemplate();
        if (action == null) {
          return;
        }
        handleAction(action);
      } else if (handler instanceof XmlTree) {
        //              processElement (((XmlTree)object).getSelectedElement ());
      }
    } catch (Exception exc) {
      logException("Loading data", exc);
    }
    showNormalCursor();
  }

  /**
   * Insert a new ui component into the panel.
   *
   * @param handler The handler associated with this component.
   */
  private void addToHistory(XmlHandler handler) {
    int howManyToRemove = handlers.size() - historyIdx - 1;
    for (int cnt = 0; cnt < howManyToRemove; cnt++) {
      Misc.removeLast(handlers);
    }
    handlers.add(handler);
    historyIdx = handlers.size() - 1;
    checkButtons();
  }

  /** Just creates an empty XmlTree */
  private void makeBlankTree() {
    XmlTree blankTree = new XmlTree(null, true, "");

    addToContents(GuiUtils.inset(GuiUtils.topCenter(new JPanel(), blankTree.getScroller()), 5));
  }

  /**
   * Remove the currently display gui and insert the given one.
   *
   * @param comp The new gui.
   */
  private void addToContents(JComponent comp) {
    handlerHolder.removeAll();
    comp.setPreferredSize(new Dimension(200, 300));
    handlerHolder.add(comp, BorderLayout.CENTER);
    if (myContents != null) {
      myContents.invalidate();
      myContents.validate();
      myContents.repaint();
    }
  }

  /**
   * Get the xml
   *
   * @return The xml
   */
  public String getXml() {
    return xmlContents;
  }

  /**
   * Get the xml doc
   *
   * @return The xml doc
   */
  public Document getDocument() {
    return document;
  }
}
Beispiel #2
0
/**
 * This class provides 2 facilities. First, there is a set of static methods: init
 * (XmlResourceCollection resources); Range getParamRange (String paramName); getParamColorTable
 * (String paramName); that provide basic lookup of parameter defaults. The init method is called
 * once. It is passed an XmlResourceCollection that holds the list of xml files to be used. The
 * first resource in the list is taken to be the user's "writable" resource. These resources are
 * read in, first to last, and a static collection of {@link ucar.unidata.idv.ui.ParamInfo}-s are is
 * created that is used to do the subsequent param default lookups.
 *
 * <p>This class is also used to provide an end-user editing facility.
 *
 * @author IDV development team
 * @version $Revision: 1.78 $Date: 2007/06/22 13:03:56 $
 */
public class ParamDefaultsEditor extends IdvManager implements ActionListener {

  /** Use this member to log messages (through calls to LogUtil) */
  static ucar.unidata.util.LogUtil.LogCategory log_ =
      ucar.unidata.util.LogUtil.getLogInstance(ParamDefaultsEditor.class.getName());

  /** The param xml tag name */
  public static final String TAG_PARAM = "param";

  /** The params xml tag name */
  public static final String TAG_PARAMS = "params";

  /** The unit xml attribute name */
  public static final String ATTR_UNIT = "unit";

  /** The name xml attribute name */
  public static final String ATTR_NAME = "name";

  /** The colortable xml attribute name */
  public static final String ATTR_COLORTABLE = "table";

  /** The range min xml attribute name */
  public static final String ATTR_RANGE_MIN = "range_min";

  /** The range max xml attribute name */
  public static final String ATTR_RANGE_MAX = "range_max";

  /** The contour info interval xml attribute name */
  public static final String ATTR_CI_INTERVAL = "ci_interval";

  /** The contour info base xml attribute name */
  public static final String ATTR_CI_BASE = "ci_base";

  /** The contour info min xml attribute name */
  public static final String ATTR_CI_MIN = "ci_min";

  /** The contour info max xml attribute name */
  public static final String ATTR_CI_MAX = "ci_max";

  /** The contour info dash xml attribute name */
  public static final String ATTR_CI_DASH = "ci_dash";

  /** The contour info label xml attribute name */
  public static final String ATTR_CI_LABEL = "ci_label";

  /** The contour info label xml attribute name */
  public static final String ATTR_CI_WIDTH = "ci_width";

  /** The contour info default dash value */
  public static final boolean DFLT_CI_DASH = ContourInfo.DEFAULT_DASH;

  /** The contour info default label value */
  public static final boolean DFLT_CI_LABEL = ContourInfo.DEFAULT_LABEL;

  /** The contour info default width value */
  public static final int DFLT_CI_WIDTH = ContourInfo.DEFAULT_LINE_WIDTH;

  /** The list of column headers */
  private static final String[] columns = {
    "Parameter", "Color table", "Range", "Contours (interval,base,min,max)", "Display unit"
  };

  /** The set of resources to be displayed */
  XmlResourceCollection resources;

  /** A list of ParamDefaultsTable objects (an inner class, derived from JTable) */
  ArrayList myTables;

  /** The tabbed pane which holds the JTables, one for each resource */
  JTabbedPane tableTabbedPane;

  /** Used to view color tables */
  private ColorTableEditor colorTableEditor;

  /**
   * This is the list of {@link ucar.unidata.idv.ui.ParamInfo} objects that are accessed by the get
   * state methods in this class that are used by {@link ucar.unidata.idv.DisplayConventions}
   */
  private List paramInfos = new ArrayList();

  /** A mapping of param name to ParamInfo */
  private Hashtable paramToInfo = new Hashtable();

  /**
   * Create the editor with the given collection of xml resources
   *
   * @param idv The IDV
   */
  public ParamDefaultsEditor(IntegratedDataViewer idv) {
    super(idv);
    this.resources = getResourceManager().getXmlResources(IdvResourceManager.RSC_PARAMDEFAULTS);
    init(resources);
    if (resources.size() == 0) {
      contents = GuiUtils.top(new JLabel("No resources defined"));
    } else {
      // Initialize the user's writable document (if not created yet)
      resources.getWritableDocument("<" + TAG_PARAMS + "/>");
      init();
    }
  }

  /**
   * A JTable that holds a list of {@link ParamInfo} objects and provides display and editing
   * capabilities
   */
  public class ParamDefaultsTable extends JTable {

    /** label */
    String label;

    /** A list of {@link ParamInfo} objects. */
    List myParamInfos;

    /** Does this represent the user's writable resource? */
    boolean isEditable;

    /** Keep the tableModel around */
    AbstractTableModel tableModel;

    /**
     * Create the table
     *
     * @param infos List of {@link ParamInfo}
     * @param editable Are the {@link ParamInfo}s editable
     */
    public ParamDefaultsTable(List infos, boolean editable) {
      this.myParamInfos = infos;
      this.isEditable = editable;

      // Construct the tableModel
      tableModel =
          new AbstractTableModel() {
            public boolean isCellEditable(int rowIndex, int columnIndex) {
              return false;
            }

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

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

            public int getRowCount() {
              return myParamInfos.size();
            }

            public Object getValueAt(int row, int col) {
              ParamInfo paramInfo = getInfo(row);
              if (col == 0) {
                return paramInfo.getName();
              }
              if (col == 1) {
                if (paramInfo.hasColorTableName()) {
                  return paramInfo.getColorTableName();
                }
                return "";
              }
              if (col == 2) {
                if (paramInfo.hasRange()) {
                  return paramInfo.getMin() + " - " + paramInfo.getMax();
                }
                return "";
              }
              if (col == 3) {
                if (paramInfo.hasContourInfo()) {
                  ContourInfo ci = paramInfo.getContourInfo();
                  return ""
                      + (ci.getIntervalDefined()
                          ? String.valueOf(ci.getInterval())
                          : ci.getLevelsString())
                      + ", "
                      + ci.getBase()
                      + ", "
                      + ci.getMin()
                      + ", "
                      + ci.getMax();
                }
                return "";
              }
              if (col == 4) {
                if (paramInfo.hasDisplayUnit()) {
                  return paramInfo.getDisplayUnit().toString();
                }
                return "";
              }
              return null;
            }

            public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
              ParamInfo paramInfo = getInfo(rowIndex);
              if (columnIndex == 0) {
                paramInfo.setName(aValue.toString());
              }
            }
          };
      setModel(tableModel);
      initMouseListener();
    }

    /**
     * Select the param info
     *
     * @param info info
     */
    public void selectParamInfo(ParamInfo info) {
      int index = myParamInfos.indexOf(info);
      if (index >= 0) {
        getSelectionModel().setSelectionInterval(index, index);
      }
    }

    /**
     * Return the list of {@link ParamInfo}-s held by this table
     *
     * @return List of param infos
     */
    public List getParamInfoList() {
      return myParamInfos;
    }

    /**
     * Get the list of selected param infos
     *
     * @return list of selected param infos
     */
    public List getSelectedParamInfoList() {
      int[] rows = getSelectedRows();
      List result = new ArrayList();
      for (int i = 0; i < rows.length; i++) {
        result.add(myParamInfos.get(i));
      }
      return result;
    }

    /**
     * Return the {@link ParamInfo}
     *
     * @param row
     * @return The ParamInfo
     */
    public ParamInfo getInfo(int row) {
      return (ParamInfo) myParamInfos.get(row);
    }

    /**
     * Utility method to add components to dialog list
     *
     * @param comps list to add to
     * @param name name
     * @param cbx enable checkbox
     * @param comp the component
     */
    private void addEditComponents(
        List comps, String name, final JCheckBox cbx, final JComponent comp) {
      cbx.addChangeListener(
          new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
              GuiUtils.enableTree(comp, cbx.isSelected());
            }
          });

      GuiUtils.enableTree(comp, cbx.isSelected());
      comps.add(GuiUtils.top(GuiUtils.inset(cbx, 5)));
      comps.add(GuiUtils.top(GuiUtils.inset(GuiUtils.rLabel(name), new Insets(8, 0, 0, 0))));
      JComponent right = GuiUtils.inset(comp, new Insets(3, 5, 0, 0));
      //            comps.add(GuiUtils.leftCenter(GuiUtils.top(GuiUtils.inset(cbx,
      //                    new Insets(2, 0, 0, 0))), GuiUtils.topLeft(right)));
      comps.add(GuiUtils.topLeft(right));
    }

    /**
     * Edit the param info
     *
     * @param paramInfo param info to edit
     * @return user pressed ok
     */
    public boolean editRow(ParamInfo paramInfo) {
      return editRow(paramInfo, false);
    }

    /**
     * Edit row
     *
     * @param paramInfo param info
     * @param removeOnCancel Should remove param info if user presses cancel_
     * @return ok
     */
    public boolean editRow(ParamInfo paramInfo, boolean removeOnCancel) {

      List comps = new ArrayList();
      ParamField nameFld = new ParamField(null, true);
      nameFld.setText(paramInfo.getName());
      JPanel topPanel = GuiUtils.hbox(GuiUtils.lLabel("Parameter:  "), nameFld);
      topPanel = GuiUtils.inset(topPanel, 5);

      comps.add(GuiUtils.inset(new JLabel("Defined"), new Insets(5, 0, 0, 0)));
      comps.add(GuiUtils.filler());
      comps.add(GuiUtils.filler());

      final JLabel ctPreviewLbl = new JLabel("");
      final JLabel ctLbl = new JLabel("");
      if (paramInfo.hasColorTableName()) {
        ctLbl.setText(paramInfo.getColorTableName());
        ColorTable ct =
            getIdv().getColorTableManager().getColorTable(paramInfo.getColorTableName());
        if (ct != null) {
          ctPreviewLbl.setIcon(ColorTableCanvas.getIcon(ct));
        } else {
          ctPreviewLbl.setIcon(null);
        }
      }
      String cbxLabel = "";
      final ArrayList menus = new ArrayList();
      getIdv()
          .getColorTableManager()
          .makeColorTableMenu(
              new ObjectListener(null) {
                public void actionPerformed(ActionEvent ae, Object data) {
                  ctLbl.setText(data.toString());
                  ColorTable ct = getIdv().getColorTableManager().getColorTable(ctLbl.getText());
                  if (ct != null) {
                    ctPreviewLbl.setIcon(ColorTableCanvas.getIcon(ct));
                  } else {
                    ctPreviewLbl.setIcon(null);
                  }
                }
              },
              menus);

      JCheckBox ctUseCbx = new JCheckBox(cbxLabel, paramInfo.hasColorTableName());
      final JButton ctPopup = new JButton("Change");
      ctPopup.addActionListener(
          new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
              GuiUtils.showPopupMenu(menus, ctPopup);
            }
          });
      addEditComponents(
          comps,
          "Color Table:",
          ctUseCbx,
          GuiUtils.hbox(ctPopup, GuiUtils.vbox(ctLbl, ctPreviewLbl), 5));

      JCheckBox rangeUseCbx = new JCheckBox(cbxLabel, paramInfo.hasRange());
      JTextField minFld = new JTextField("" + paramInfo.getMin(), 4);
      JTextField maxFld = new JTextField("" + paramInfo.getMax(), 4);
      JPanel rangePanel = GuiUtils.hbox(minFld, maxFld, 5);
      addEditComponents(comps, "Range:", rangeUseCbx, rangePanel);

      JCheckBox unitUseCbx = new JCheckBox(cbxLabel, paramInfo.hasDisplayUnit());
      String unitLabel = "";
      Unit unit = null;
      if (paramInfo.hasDisplayUnit()) {
        unit = paramInfo.getDisplayUnit();
      }

      JComboBox unitFld = getIdv().getDisplayConventions().makeUnitBox(unit, null);
      //            JTextField unitFld = new JTextField(unitLabel, 15);
      addEditComponents(comps, "Unit:", unitUseCbx, unitFld);

      ContourInfo ci = paramInfo.getContourInfo();
      JCheckBox contourUseCbx = new JCheckBox(cbxLabel, ci != null);
      if (ci == null) {
        ci = new ContourInfo();
      }
      ContourInfoDialog contDialog =
          new ContourInfoDialog("Edit Contour Defaults", false, null, false);
      contDialog.setState(ci);
      addEditComponents(comps, "Contour:", contourUseCbx, contDialog.getContents());

      GuiUtils.tmpInsets = new Insets(5, 5, 5, 5);
      JComponent contents = GuiUtils.doLayout(comps, 3, GuiUtils.WT_NNY, GuiUtils.WT_N);

      contents = GuiUtils.topCenter(topPanel, contents);
      contents = GuiUtils.inset(contents, 5);
      while (true) {
        if (!GuiUtils.showOkCancelDialog(null, "Parameter Defaults", contents, null)) {
          if (removeOnCancel) {
            myParamInfos.remove(paramInfo);
            tableChanged();
          }
          return false;
        }
        String what = "";
        try {
          if (contourUseCbx.isSelected()) {
            what = "setting contour defaults";
            contDialog.doApply();
            ci.set(contDialog.getInfo());
            paramInfo.setContourInfo(ci);
          } else {
            paramInfo.clearContourInfo();
          }
          if (unitUseCbx.isSelected()) {
            what = "setting display unit";
            Object selected = unitFld.getSelectedItem();
            String unitName = TwoFacedObject.getIdString(selected);
            if ((unitName == null) || unitName.trim().equals("")) {
              paramInfo.setDisplayUnit(null);
            } else {
              paramInfo.setDisplayUnit(ucar.visad.Util.parseUnit(unitName));
            }
          } else {
            paramInfo.setDisplayUnit(null);
          }

          if (ctUseCbx.isSelected()) {
            paramInfo.setColorTableName(ctLbl.getText());
          } else {
            paramInfo.clearColorTableName();
          }

          if (rangeUseCbx.isSelected()) {
            what = "setting range";
            paramInfo.setRange(
                new Range(Misc.parseNumber(minFld.getText()), Misc.parseNumber(maxFld.getText())));
          } else {
            paramInfo.clearRange();
          }

          paramInfo.setName(nameFld.getText().trim());
          break;
        } catch (Exception exc) {
          errorMsg("An error occurred " + what + "\n " + exc.getMessage());
          //              exc.printStackTrace();
        }
      }
      repaint();
      saveData();
      return true;
    }

    /**
     * Show the colortable in the color table editor for the given row
     *
     * @param row The given row
     */
    protected void viewColorTable(int row) {
      ColorTable ct = getColorTable(getInfo(row));
      if (colorTableEditor == null) {
        colorTableEditor = new ColorTableEditor(getIdv().getColorTableManager(), ct);
      } else {
        colorTableEditor.setColorTable(ct);
      }
      colorTableEditor.show();
    }

    /**
     * Helper to show an error message
     *
     * @param msg The message
     */
    protected void errorMsg(String msg) {
      javax.swing.JOptionPane.showMessageDialog(this, msg, "Error", JOptionPane.ERROR_MESSAGE);
    }

    /**
     * Tool tip for the table
     *
     * @param event The event
     * @return The tooltip text
     */
    public String getToolTipText(MouseEvent event) {
      return "Right-click to show popup menu, double click to edit row";
    }

    /** Add a mouselistener to this table */
    private void initMouseListener() {
      addMouseListener(
          new MouseAdapter() {

            public void mouseReleased(MouseEvent e) {
              final int row = rowAtPoint(e.getPoint());
              ParamInfo paramInfo = getInfo(row);
              if (!SwingUtilities.isRightMouseButton(e)) {
                if ((e.getClickCount() > 1) && (paramInfo != null)) {
                  if (isEditable) {
                    editRow(paramInfo);
                  } else {
                    copyToUsers(paramInfo);
                  }
                }
                return;
              }

              getSelectionModel().setSelectionInterval(row, row);
              JPopupMenu popup = new JPopupMenu();
              makePopupMenu(popup, row);
              popup.show((Component) e.getSource(), e.getX(), e.getY());
            }
          });
    }

    /**
     * Make the popup menu
     *
     * @param popup The popup menu
     * @param row The row the user clicked on
     */
    void makePopupMenu(JPopupMenu popup, final int row) {
      ParamInfo info = getInfo(row);
      if (isEditable) {
        makeEditableMenu(popup, row);
      } else {
        JMenuItem mi = new JMenuItem("Copy Row to Users Defaults");
        mi.addActionListener(
            new ActionListener() {
              public void actionPerformed(ActionEvent ae) {
                try {
                  copyToUsers(getInfo(row));
                } catch (Exception exc) {
                  LogUtil.printException(log_, "Copying row: " + row + " to users table.", exc);
                }
              }
            });
        popup.add(mi);
      }
      if (info != null) {
        popup.add(
            GuiUtils.makeMenuItem(
                "Export to Plugin", getIdv().getPluginManager(), "addObject", info));
      }
    }

    /**
     * Find the ParamInfo from the name
     *
     * @param name name
     * @return param info
     */
    public ParamInfo findByName(String name) {
      name = name.trim();
      for (int i = 0; i < myParamInfos.size(); i++) {
        ParamInfo paramInfo = (ParamInfo) myParamInfos.get(i);
        if (paramInfo.getName().equals(name)) {
          return paramInfo;
        }
      }
      return null;
    }

    /**
     * Add the ParamInfo into the table
     *
     * @param i
     */
    public void add(ParamInfo i) {
      if (!myParamInfos.contains(i)) {
        myParamInfos.add(i);
      }
      tableChanged();
    }

    /**
     * Add the ParamInfo into the table
     *
     * @param i
     */
    public void addBeginning(ParamInfo i) {
      myParamInfos.add(0, i);
      tableChanged();
    }

    /**
     * Make the edit menu
     *
     * @param popup The popup
     * @param row The row
     */
    void makeEditableMenu(JPopupMenu popup, final int row) {

      JMenuItem mi;
      popup.add(GuiUtils.makeMenuItem("Add New Field", this, "addNewRow"));
      ParamInfo paramInfo = getInfo(row);
      if (paramInfo == null) {
        return;
      }

      popup.add(GuiUtils.makeMenuItem("Edit Settings", this, "editRow", paramInfo));

      mi = new JMenuItem("Delete Settings For Parameter");
      mi.addActionListener(
          new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
              removeRow(row);
            }
          });
      popup.add(mi);
    }

    /** Add a new row */
    public void addNewRow() {
      ParamInfo paramInfo = new ParamInfo("", null, null, null, null);
      myParamInfos.add(paramInfo);
      tableChanged();
      editRow(paramInfo, true);
    }

    /**
     * Remove the given row
     *
     * @param row The given row
     */
    protected void removeRow(int row) {
      if (!GuiUtils.showYesNoDialog(
          null, "Are you sure you want to delete this row?", "Delete Confirmation")) {
        return;
      }
      myParamInfos.remove(row);
      tableChanged();
      saveData();
    }

    /**
     * Make the color table menu items
     *
     * @param row The given row
     * @return List of menu items
     */
    public ArrayList makeCTMenuItems(final int row) {
      ArrayList menus = new ArrayList();
      getIdv()
          .getColorTableManager()
          .makeColorTableMenu(
              new ObjectListener(null) {
                public void actionPerformed(ActionEvent ae, Object data) {
                  getInfo(row).setColorTableName(data.toString());
                  tableChanged();
                }
              },
              menus);
      return menus;
    }

    /** Table changed */
    public void tableChanged() {
      tableModel.fireTableStructureChanged();
    }

    /**
     * Map the name of the color table to the {@link ucar.unidata.util.ColorTable}
     *
     * @param name Name of the color table
     * @return The ColorTable
     */
    protected ColorTable getColorTableFromName(String name) {
      ColorTable ct = getIdv().getColorTableManager().getColorTable(name);
      if (ct == null) {
        ct = getIdv().getColorTableManager().getDefaultColorTable();
      }
      return ct;
    }
  }

  /**
   * Load in the {@link ucar.unidata.idv.ui.ParamInfo}-s defined in the xml from the given root
   * Element. Create a new JTable and add it into the GUI.
   *
   * @param root The xml root
   * @param i Which resource is this
   */
  private void addList(Element root, int i) {
    List infos;
    boolean isWritable = resources.isWritableResource(i);
    if (root != null) {
      infos = createParamInfoList(root);
    } else {
      if (!isWritable) {
        return;
      }
      infos = new ArrayList();
    }

    if (infos.size() == 0) {
      //            infos.add(new ParamInfo("", null, null, null, null));
    }
    ParamDefaultsTable table = new ParamDefaultsTable(infos, isWritable);
    table.setPreferredScrollableViewportSize(new Dimension(500, 70));
    String editableStr = "";
    if (!isWritable) {
      editableStr = " (" + Msg.msg("non-editable") + ") ";
    }
    JLabel label =
        new JLabel(
            "<html>" + Msg.msg("Path: ${param1}", resources.get(i) + editableStr) + "</html>");
    JPanel tablePanel = GuiUtils.topCenter(GuiUtils.inset(label, 4), new JScrollPane(table));

    table.label = resources.getShortName(i);
    tableTabbedPane.add(resources.getShortName(i), tablePanel);
    myTables.add(table);
  }

  /** Intialize me */
  private void init() {
    myTables = new ArrayList();
    tableTabbedPane = new JTabbedPane();
    tableTabbedPane.setPreferredSize(new Dimension(450, 200));
    JMenuBar menuBar = new JMenuBar();
    JMenu fileMenu = new JMenu("File");
    menuBar.add(fileMenu);
    fileMenu.add(GuiUtils.makeMenuItem("New Row", this, "addNewRow"));
    fileMenu.addSeparator();
    fileMenu.add(GuiUtils.makeMenuItem("Open", this, "doOpen"));
    fileMenu.add(GuiUtils.makeMenuItem("Import", this, "doImport"));
    fileMenu.addSeparator();
    fileMenu.add(GuiUtils.makeMenuItem("Export to File", this, "doSaveAs"));
    fileMenu.add(GuiUtils.makeMenuItem("Export to Plugin", this, "exportToPlugin"));
    fileMenu.add(
        GuiUtils.makeMenuItem("Export Selected to Plugin", this, "exportSelectedToPlugin"));
    fileMenu.addSeparator();
    fileMenu.add(GuiUtils.makeMenuItem("Close", this, "doClose"));

    JMenu helpMenu = new JMenu("Help");
    menuBar.add(helpMenu);
    helpMenu.add(GuiUtils.makeMenuItem("Parameter Defaults Help", this, "showHelp"));
    JComponent bottom = GuiUtils.wrap(GuiUtils.makeButton("Close", this, "doClose"));
    contents = GuiUtils.topCenterBottom(menuBar, GuiUtils.inset(tableTabbedPane, 2), bottom);
    setMenuBar(menuBar);
    loadResources(resources);
  }

  /** Export the selected param infos to the plugin manager */
  public void exportSelectedToPlugin() {
    ParamDefaultsTable table = getCurrentTable();
    List selected = table.getSelectedParamInfoList();
    if (selected.size() == 0) {
      LogUtil.userMessage("No rows selected");
      return;
    }
    getIdv().getPluginManager().addObject(selected);
  }

  /** Export allthe param infos to the plugin manager */
  public void exportToPlugin() {
    ParamDefaultsTable table = getCurrentTable();
    List list = table.getParamInfoList();
    if (list.size() == 0) {
      LogUtil.userMessage("No rows selected");
      return;
    }
    getIdv().getPluginManager().addObject(list);
  }

  /** add a new row to users table */
  public void addNewRow() {
    if (myTables.size() > 0) {
      GuiUtils.showComponentInTabs(((ParamDefaultsTable) myTables.get(0)));
      ((ParamDefaultsTable) myTables.get(0)).addNewRow();
    }
  }

  /**
   * Get the param infos
   *
   * @param justFirst if true then just get the first table
   * @return param infos
   */
  public List getParamInfos(boolean justFirst) {
    List infos = new ArrayList();
    for (int i = 0; i < myTables.size(); i++) {
      infos.addAll(((ParamDefaultsTable) myTables.get(i)).getParamInfoList());
      if (justFirst) {
        break;
      }
    }
    return infos;
  }

  /**
   * Get the list of resources
   *
   * @return the list of resources
   */
  public List getResources() {
    List infos = new ArrayList();
    for (int i = 0; i < myTables.size(); i++) {
      ParamDefaultsTable paramDefaultsTable = (ParamDefaultsTable) myTables.get(i);
      for (ParamInfo paramInfo : (List<ParamInfo>) paramDefaultsTable.getParamInfoList()) {
        infos.add(
            new ResourceViewer.ResourceWrapper(
                paramInfo,
                paramInfo.toString(),
                paramDefaultsTable.label,
                paramDefaultsTable.isEditable));
      }
    }
    return infos;
  }

  /**
   * Load in the xml resources
   *
   * @param resources The resources
   */
  public void loadResources(XmlResourceCollection resources) {
    for (int i = 0; i < resources.size(); i++) {
      Element root = resources.getRoot(i);
      addList(root, i);
    }
  }

  /**
   * Return the ParamDefaultsTable which is currently being shown in the tabbed pane
   *
   * @return The current ParamDefaultsTable
   */
  public ParamDefaultsTable getCurrentTable() {
    int index = tableTabbedPane.getSelectedIndex();
    return (ParamDefaultsTable) myTables.get(index);
  }

  /** Import an xml param defaults file */
  public void doImport() {
    try {
      String filename = FileManager.getReadFile(FileManager.FILTER_XML);
      if (filename == null) {
        return;
      }
      Element root = XmlUtil.getRoot(IOUtil.readContents(filename));
      if (root == null) {
        return;
      }
      List infos = createParamInfoList(root);
      ParamDefaultsTable table = getCurrentTable();
      table.getParamInfoList().addAll(infos);
      table.tableChanged();
      saveData();
    } catch (Exception exc) {
      LogUtil.printException(log_, "Error importing file", exc);
    }
  }

  /** Open an xml param defaults file */
  public void doOpen() {
    String filename = FileManager.getReadFile(FileManager.FILTER_XML);
    if (filename == null) {
      return;
    }
    resources.addResource(filename);
    int index = resources.size() - 1;
    addList(resources.getRoot(index), index);
  }

  /**
   * Handle the CLOSEANCEL, OK, HELP, events.
   *
   * @param event The event
   */
  public void actionPerformed(ActionEvent event) {
    String cmd = event.getActionCommand();
    if (cmd.equals(GuiUtils.CMD_CLOSE)) {
      doClose();
    } else if (cmd.equals(GuiUtils.CMD_NEW)) {
      if (myTables.size() > 0) {
        ((ParamDefaultsTable) myTables.get(0)).addNewRow();
      }
    } else if (cmd.equals(GuiUtils.CMD_OK)) {
      saveData();
      doClose();
    } else if (cmd.equals(GuiUtils.CMD_HELP)) {
      showHelp();
    } else if (cmd.equals(GuiUtils.CMD_SAVEAS)) {
      doSaveAs(getCurrentTable().getParamInfoList());
    } else if (cmd.equals(GuiUtils.CMD_OPEN)) {
      doOpen();
    } else if (cmd.equals(GuiUtils.CMD_IMPORT)) {
      doImport();
    }
  }

  /** show help */
  public void showHelp() {
    getIdv().getIdvUIManager().showHelp("idv.tools.parameterdefaultseditor");
  }

  /** Close the window (and the color table editor if it is open) */
  public void doClose() {
    if (colorTableEditor != null) {
      colorTableEditor.setVisible(false);
    }
    super.close();
  }

  /**
   * Get the window title to use
   *
   * @return Window title
   */
  protected String getWindowTitle() {
    return GuiUtils.getApplicationTitle() + "Parameter Defaults Editor";
  }

  /**
   * Save the list of ParamInfo-s into the given file
   *
   * @param infoList List of infos
   * @param filename The filename to write to
   */
  public void doSave(List infoList, String filename) {
    try {
      Element root = createDom(XmlUtil.makeDocument(), infoList);
      IOUtil.writeFile(filename, XmlUtil.toString(root));
    } catch (Exception exc) {
      LogUtil.printException(log_, "Error writing file", exc);
    }
  }

  /** Save the param infos */
  public void doSaveAs() {
    doSaveAs(getCurrentTable().getParamInfoList());
  }

  /**
   * Prompt for a file and write out the ParamInfo-s from the given list.
   *
   * @param infoList List of ParamInfo-s
   */
  public void doSaveAs(List infoList) {
    String filename = FileManager.getWriteFile(FileManager.FILTER_XML, FileManager.SUFFIX_XML);
    if (filename == null) {
      return;
    }
    doSave(infoList, filename);
  }

  /**
   * Copy the given ParamInfo object into the user's editable table
   *
   * @param i the param fino object to copy
   */
  protected void copyToUsers(ParamInfo i) {
    ParamInfo copy = new ParamInfo(i);
    ParamDefaultsTable to = (ParamDefaultsTable) myTables.get(0);
    to.add(copy);
    tableTabbedPane.setSelectedIndex(0);
    to.editRow(copy, true);
  }

  /**
   * Create xml dom from the given list of {@link ucar.unidata.idv.ui.ParamInfo}-s
   *
   * @param doc The document to write to
   * @param paramInfos List of param infos
   * @return Root xml element
   */
  private Element createDom(Document doc, List paramInfos) {
    Element root = doc.createElement(TAG_PARAMS);
    for (int i = 0; i < paramInfos.size(); i++) {
      ParamInfo paramInfo = (ParamInfo) paramInfos.get(i);
      if (paramInfo.getName().trim().length() == 0) {
        continue;
      }
      Element node = doc.createElement(TAG_PARAM);
      node.setAttribute(ATTR_NAME, paramInfo.getName());
      if (paramInfo.hasColorTableName()) {
        node.setAttribute(ATTR_COLORTABLE, paramInfo.getColorTableName());
      }
      if (paramInfo.hasRange()) {
        node.setAttribute(ATTR_RANGE_MIN, "" + paramInfo.getRange().getMin());
        node.setAttribute(ATTR_RANGE_MAX, "" + paramInfo.getRange().getMax());
      }
      if (paramInfo.hasDisplayUnit()) {
        node.setAttribute(ATTR_UNIT, "" + paramInfo.getDisplayUnit());
      }
      if (paramInfo.hasContourInfo()) {
        ContourInfo ci = paramInfo.getContourInfo();
        node.setAttribute(ATTR_CI_INTERVAL, "" + ci.getIntervalString(true));
        node.setAttribute(ATTR_CI_BASE, "" + ci.getBase());
        node.setAttribute(ATTR_CI_MIN, "" + ci.getMin());
        node.setAttribute(ATTR_CI_MAX, "" + ci.getMax());
        if (ci.getDashOn() != DFLT_CI_DASH) {
          node.setAttribute(ATTR_CI_DASH, "" + ci.getDashOn());
        }
        if (ci.getIsLabeled() != DFLT_CI_LABEL) {
          node.setAttribute(ATTR_CI_LABEL, "" + ci.getIsLabeled());
        }
        node.setAttribute(ATTR_CI_WIDTH, "" + ci.getLineWidth());
      }
      root.appendChild(node);
    }
    return root;
  }

  /**
   * Get the color table, range, etc, from the given display control and save them as the param
   * defaults for its data choice
   *
   * @param displayControl the display control to get state from
   */
  public void saveDefaults(DisplayControlImpl displayControl) {
    try {
      List choices = displayControl.getMyDataChoices();
      if (choices.size() != 1) {
        return;
      }
      DataChoice dc = (DataChoice) choices.get(0);
      String name = dc.getName();
      String ctName =
          ((displayControl.getColorTable() != null)
              ? displayControl.getColorTable().getName()
              : null);
      ParamInfo newParamInfo =
          new ParamInfo(
              name,
              ctName,
              displayControl.getRange(),
              displayControl.getContourInfo(),
              displayControl.getDisplayUnit());
      ParamDefaultsTable firstTable = getFirstTable();
      if (!firstTable.editRow(newParamInfo, false)) {
        return;
      }
      ParamInfo origParamInfo = firstTable.findByName(dc.getName());
      if (origParamInfo == null) {
        firstTable.addBeginning(newParamInfo);
        firstTable.getSelectionModel().setSelectionInterval(0, 0);
      } else {
        origParamInfo.initWith(newParamInfo);
        firstTable.tableChanged();
        firstTable.selectParamInfo(origParamInfo);
      }
      saveData();
      show();
      GuiUtils.showComponentInTabs(firstTable);
    } catch (Exception exc) {
      LogUtil.printException(log_, "copying defaults", exc);
    }
  }

  /**
   * Get the first JTable in the list
   *
   * @return First table
   */
  private ParamDefaultsTable getFirstTable() {
    return (ParamDefaultsTable) myTables.get(0);
  }

  /** Write out the user's editable param infos */
  private void saveData() {
    Document usersDoc = XmlUtil.makeDocument();
    Element usersRoot = createDom(usersDoc, getFirstTable().getParamInfoList());
    try {
      resources.setWritableDocument(usersDoc, usersRoot);
      resources.writeWritable();
      // Reinitialize the static state
      paramInfos = new ArrayList();
      paramToInfo = new Hashtable();
      init(resources);
    } catch (Exception exc) {
      LogUtil.printException(log_, "writing aliases xml", exc);
    }
  }

  /**
   * Create the param infos from the given xml root
   *
   * @param root The xml root
   * @param overwriteOk Ok to overwrite an existing one
   */
  private void loadParamDefaults(Element root, boolean overwriteOk) {
    List listOfInfos = createParamInfoList(root);
    for (int i = 0; i < listOfInfos.size(); i++) {
      ParamInfo newParamInfo = (ParamInfo) listOfInfos.get(i);
      String paramName = newParamInfo.getName();
      if (!overwriteOk && (paramToInfo.get(paramName) != null)) {
        continue;
      }

      ParamInfo oldParamInfo = (ParamInfo) paramToInfo.get(paramName);
      if (oldParamInfo == null) {
        paramToInfo.put(paramName, newParamInfo);
        paramInfos.add(newParamInfo);
      } else {
        if (!oldParamInfo.hasColorTableName()) {
          oldParamInfo.setColorTableName(newParamInfo.getColorTableName());
        }
        if (!oldParamInfo.hasRange()) {
          oldParamInfo.setRange(newParamInfo.getRange());
        }
      }
    }
  }

  /**
   * Create the param infos from the given xml root
   *
   * @param root The xml root
   * @return List of param infos
   */
  private List createParamInfoList(Element root) {

    List infos = new ArrayList();

    if (!root.getTagName().equals(TAG_PARAMS)) {
      try {
        Object obj = getIdv().getEncoderForRead().toObject(root);
        if (obj instanceof List) {
          infos.addAll((List) obj);
        } else {
          System.err.println("Unknown object type: " + obj.getClass().getName());
        }
      } catch (Exception exc) {
        System.err.println("Error reading param defaults");
      }
      return infos;
    }

    List nodes = XmlUtil.findChildren(root, TAG_PARAM);

    for (int i = 0; i < nodes.size(); i++) {
      Element child = (Element) nodes.get(i);
      Range range = null;
      Unit displayUnit = null;
      ContourInfo contourInfo = null;

      String paramName = XmlUtil.getAttribute(child, ATTR_NAME);
      String colorTableName = XmlUtil.getAttribute(child, ATTR_COLORTABLE, (String) null);
      String range_min = XmlUtil.getAttribute(child, ATTR_RANGE_MIN, (String) null);
      String range_max = XmlUtil.getAttribute(child, ATTR_RANGE_MAX, (String) null);

      String unitName = XmlUtil.getAttribute(child, ATTR_UNIT, (String) null);

      String ci_interval = XmlUtil.getAttribute(child, ATTR_CI_INTERVAL, (String) null);
      String ci_base = XmlUtil.getAttribute(child, ATTR_CI_BASE, (String) null);
      String ci_min = XmlUtil.getAttribute(child, ATTR_CI_MIN, range_min);
      String ci_max = XmlUtil.getAttribute(child, ATTR_CI_MAX, range_max);
      boolean ci_dash = XmlUtil.getAttribute(child, ATTR_CI_DASH, DFLT_CI_DASH);
      boolean ci_label = XmlUtil.getAttribute(child, ATTR_CI_LABEL, DFLT_CI_LABEL);
      String ci_width = XmlUtil.getAttribute(child, ATTR_CI_WIDTH, String.valueOf(DFLT_CI_WIDTH));

      if (unitName != null) {
        try {
          displayUnit = ucar.visad.Util.parseUnit(unitName);
        } catch (Exception e) {
          LogUtil.printException(log_, "Creating unit: " + unitName, e);
        }
      }

      if ((ci_interval != null) || (ci_base != null)) {
        if (ci_interval == null) {
          ci_interval = "NaN";
        }

        if (ci_base == null) {
          ci_base = "NaN";
        }
        if (ci_min == null) {
          ci_min = "NaN";
        }
        if (ci_max == null) {
          ci_max = "NaN";
        }
        if (ci_width == null) {
          ci_width = "1";
        }
        contourInfo =
            new ContourInfo(
                ci_interval,
                Misc.parseDouble(ci_base),
                Misc.parseDouble(ci_min),
                Misc.parseDouble(ci_max),
                ci_label,
                ci_dash,
                ContourInfo.DEFAULT_FILL,
                Misc.parseDouble(ci_width));
      }

      if ((ci_dash != DFLT_CI_DASH) || (ci_label != DFLT_CI_LABEL)) {
        if (contourInfo == null) {
          contourInfo = new ContourInfo(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
          contourInfo.setIsLabeled(ci_label);
          contourInfo.setDashOn(ci_dash);
        }
      }

      if ((range_min != null) && (range_max != null)) {
        range = new Range(Misc.parseDouble(range_min), Misc.parseDouble(range_max));
      }

      ParamInfo paramInfo =
          new ParamInfo(paramName, colorTableName, range, contourInfo, displayUnit);
      infos.add(paramInfo);
    }
    return infos;
  }

  /**
   * Load in all of the {@link ucar.unidata.idv.ui.ParamInfo}-s pointed to by the given resource
   * collection
   *
   * @param resources The resources (e.g., the paramdefaults.xml)
   */
  private void init(XmlResourceCollection resources) {
    try {
      for (int i = 0; i < resources.size(); i++) {
        Element root = resources.getRoot(i, false);
        if (root != null) {
          loadParamDefaults(root, false);
        }
      }
    } catch (Exception exc) {
      LogUtil.printException(log_, "Loading  parameter to color table properties ", exc);
    }
  }

  /**
   * Find the {@link ucar.unidata.idv.ui.ParamInfo} for the given name
   *
   * @param paramName The name to look for
   * @return The {@link ucar.unidata.idv.ui.ParamInfo} associated with the name
   */
  private ParamInfo getParamInfo(String paramName) {
    if (paramName == null) {
      return null;
    }
    ParamInfo info = (ParamInfo) StringUtil.findMatch(paramName, paramInfos, null);
    if (info == null) {
      info = (ParamInfo) StringUtil.findMatch(paramName.toLowerCase(), paramInfos, null);
    }

    if (info == null) {
      String canonicalName = DataAlias.aliasToCanonical(paramName);
      if (canonicalName != null) {
        info = (ParamInfo) StringUtil.findMatch(canonicalName.toLowerCase(), paramInfos, null);
      }
    }
    return info;
  }

  /**
   * Find the color table name for the given param name
   *
   * @param paramName Name to look for
   * @return Color table name or null if not found
   */
  private ColorTable getColorTable(String paramName) {
    ParamInfo paramInfo = getParamInfo(paramName);
    if (paramInfo != null) {
      return getColorTable(paramInfo);
    }
    return null;
  }

  /**
   * Get the color table for a particular parameter from ParamInfo
   *
   * @param info parameter information
   * @return the associated color table.
   */
  private ColorTable getColorTable(ParamInfo info) {
    if (info.getColorTableName() != null) {
      return getIdv().getColorTableManager().getColorTable(info.getColorTableName());
    }
    return null;
  }

  /**
   * Returns a Range based on the parameter name (e.g., rh, t, etc.)
   *
   * @param paramName Name to look for
   * @return The {@link ucar.unidata.util.Range} found or null
   */
  public Range getParamRange(String paramName) {
    ParamInfo paramInfo = getParamInfo(paramName);
    return ((paramInfo != null) ? paramInfo.getRange() : null);
  }

  /**
   * Returns a ContourInfo based on the parameter name (e.g., rh, t, etc.)
   *
   * @param paramName Name to look for
   * @return The {@link ucar.unidata.util.ContourInfo} found or null
   */
  public ContourInfo getParamContourInfo(String paramName) {
    ParamInfo paramInfo = getParamInfo(paramName);
    return ((paramInfo != null) ? paramInfo.getContourInfo() : null);
  }

  /**
   * Returns a Unit based on the parameter name (e.g., rh, t, etc.)
   *
   * @param paramName Name to look for
   * @return The Unit found or null
   */
  public Unit getParamDisplayUnit(String paramName) {
    ParamInfo paramInfo = getParamInfo(paramName);
    return ((paramInfo != null) ? paramInfo.getDisplayUnit() : null);
  }

  /**
   * Returns a color table based on the parameter name (e.g., rh, t, etc.)
   *
   * @param paramName Name to look for
   * @return The {@link ucar.unidata.util.ColorTable} found or null
   */
  public ColorTable getParamColorTable(String paramName) {
    return getParamColorTable(paramName, true);
  }

  /**
   * Get the color table for the parameters
   *
   * @param paramName parameter name
   * @param useDefault true to use the default color table if not found
   * @return the associated color table
   */
  public ColorTable getParamColorTable(String paramName, boolean useDefault) {
    ColorTable vc = getColorTable(paramName);
    // Try the canonical names.
    if (vc == null) {
      vc = getColorTable(DataAlias.aliasToCanonical(paramName));
    }

    if ((vc == null) && useDefault) {
      vc = getIdv().getColorTableManager().getDefaultColorTable();
    }
    return vc;
  }
}
Beispiel #3
0
/**
 * This is a base class for the different IDV managers (e.g., {@link
 * ucar.unidata.idv.ui.IdvUIManager}. It provides a couple of utilities.
 *
 * @author IDV development team
 */
public abstract class IdvManager extends WindowHolder implements IdvConstants {

  /** Use this member to log messages (through calls to LogUtil) */
  static ucar.unidata.util.LogUtil.LogCategory log_ =
      ucar.unidata.util.LogUtil.getLogInstance(IdvManager.class.getName());

  /** Reference to the IDV */
  private IntegratedDataViewer idv;

  /**
   * Construct this object with the given IDV
   *
   * @param idv The IDV
   */
  public IdvManager(IntegratedDataViewer idv) {
    this.idv = idv;
  }

  /**
   * Get the IDV
   *
   * @return The IDV
   */
  public IntegratedDataViewer getIdv() {
    return idv;
  }

  /**
   * Get the Class of the IDV.
   *
   * @return The Class of the IDV
   */
  protected Class getIdvClass() {
    return getIdv().getClass();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The store from the IDV
   */
  protected IdvObjectStore getStore() {
    return idv.getStore();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The ResourceManager from the IDV
   */
  protected IdvResourceManager getResourceManager() {
    return idv.getResourceManager();
  }

  /**
   * Get the station model manager
   *
   * @return The station model manager
   */
  public StationModelManager getStationModelManager() {
    return getIdv().getStationModelManager();
  }

  /**
   * Get the projection manager
   *
   * @return The projection manager
   */
  public IdvProjectionManager getIdvProjectionManager() {
    return getIdv().getIdvProjectionManager();
  }

  /**
   * Get the persistence manager
   *
   * @return The persistence manager
   */
  public IdvPersistenceManager getPersistenceManager() {
    return idv.getPersistenceManager();
  }

  /**
   * Get the preference manager
   *
   * @return The preference manager
   */
  public IdvPreferenceManager getPreferenceManager() {
    return idv.getPreferenceManager();
  }

  /**
   * Get the {@link ucar.unidata.ui.colortable.ColorTableManager}
   *
   * @return The color table manager
   */
  public ColorTableManager getColorTableManager() {
    return getIdv().getColorTableManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The ui manager from the IDV
   */
  protected IdvUIManager getIdvUIManager() {
    return idv.getIdvUIManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The image generator from the IDV
   */
  protected ImageGenerator getImageGenerator() {
    return idv.getImageGenerator();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The chooser manager from the IDV
   */
  protected IdvChooserManager getIdvChooserManager() {
    return idv.getIdvChooserManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The jython manager from the IDV
   */
  protected JythonManager getJythonManager() {
    return idv.getJythonManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The args manager from the IDV
   */
  protected ArgsManager getArgsManager() {
    return idv.getArgsManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The VM manager from the IDV
   */
  protected VMManager getVMManager() {
    return idv.getVMManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The Publish manager from the IDV
   */
  protected PublishManager getPublishManager() {
    return idv.getPublishManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The state manager from the IDV
   */
  protected StateManager getStateManager() {
    return idv.getStateManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The data manager from the IDV
   */
  protected DataManager getDataManager() {
    return idv.getDataManager();
  }

  protected InstallManager getInstallManager() {
    return idv.getInstallManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @return The collab manager from the IDV
   */
  protected CollabManager getCollabManager() {
    return idv.getCollabManager();
  }

  /**
   * Wrapper method, calling into idv
   *
   * @param msg The message
   * @param excp The exception
   */
  public static void logException(String msg, Throwable excp) {
    LogUtil.printException(log_, msg, excp);
  }

  /** Wrapper method, calling into idv */
  protected void showWaitCursor() {
    idv.showWaitCursor();
  }

  /** Wrapper method, calling into idv */
  protected void showNormalCursor() {
    idv.showNormalCursor();
  }

  /**
   * Utility method to retrieve a boolean property from the idv properties. If the property does not
   * exists return the given default value.
   *
   * @param name The name of the property
   * @param dflt The default value if the property is not found
   * @return The given property or the dflt value
   */
  public boolean getProperty(String name, boolean dflt) {
    return getStateManager().getProperty(name, dflt);
  }

  /**
   * Utility method to retrieve an int property from the idv properties. If the property does not
   * exists return the given default value.
   *
   * @param name The name of the property
   * @param dflt The default value if the property is not found
   * @return The given property or the dflt value
   */
  public int getProperty(String name, int dflt) {
    return getStateManager().getProperty(name, dflt);
  }

  /**
   * Utility method to retrieve a String property from the idv properties. If the property does not
   * exists return the given default value.
   *
   * @param name The name of the property
   * @param dflt The default value if the property is not found
   * @return The given property or the dflt value
   */
  public String getProperty(String name, String dflt) {
    return getStateManager().getProperty(name, dflt);
  }

  /**
   * A utility method that will wait until all displays are finished being created.
   *
   * @param uiManager The ui manager. We use this to access the wait cursor count
   */
  public static void waitUntilDisplaysAreDone(IdvUIManager uiManager) {
    long timeToWait = 100;
    waitUntilDisplaysAreDone(uiManager, timeToWait);
  }

  /**
   * A utility method that will wait until all displays are finished being created. This looks at
   * the DisplayControls, data sources, global wait cursor count, the visad thread pool and looks at
   * any active java3d threads
   *
   * @param uiManager The ui manager. We use this to access the wait cursor count
   * @param timeToWait (milliseconds) elapsed time to wait for nothing to be active
   */
  public static void waitUntilDisplaysAreDone(IdvUIManager uiManager, long timeToWait) {
    Trace.call1("Waiting on displays");
    int successiveTimesWithNoActive = 0;
    int sleepTime = 10;
    long firstTime = System.currentTimeMillis();
    int cnt = 0;
    while (true) {
      boolean cursorCount = (uiManager.getWaitCursorCount() > 0);
      boolean actionCount = ActionImpl.getTaskCount() > 0;
      boolean dataActive = DataSourceImpl.getOutstandingGetDataCalls() > 0;
      boolean anyJ3dActive = anyJava3dThreadsActive();
      boolean allDisplaysInitialized = uiManager.getIdv().getAllDisplaysIntialized();

      //            System.err.println ("\tAll displays init:" + allDisplaysInitialized +" cursor
      // cnt:" + uiManager.getWaitCursorCount() + " action cnt:" +actionCount + " data active: " +
      // dataActive);
      //            if ((cnt++) % 30 == 0) {
      //                System.err.println ("\tcnt:" + uiManager.getWaitCursorCount() + " "
      // +actionCount + " " + dataActive);
      //            }
      boolean anyActive =
          actionCount || cursorCount || dataActive || !allDisplaysInitialized || anyJ3dActive;
      if (dataActive) {
        firstTime = System.currentTimeMillis();
      }

      if (anyActive) {
        successiveTimesWithNoActive = 0;
      } else {
        successiveTimesWithNoActive++;
      }
      if ((timeToWait == 0) && !anyActive) {
        break;
      }
      if (successiveTimesWithNoActive * sleepTime > timeToWait) {
        break;
      }
      Misc.sleep(sleepTime);
      // At most wait 120 seconds
      if (System.currentTimeMillis() - firstTime > 120000) {
        System.err.println("Error waiting for to be done:" + LogUtil.getStackDump(false));
        return;
      }
    }
    Trace.call2("Waiting on displays");
  }

  /**
   * A total hack to see if there are any active Java3D threads running
   *
   * @return any java3d threads running
   */
  private static boolean anyJava3dThreadsActive() {
    ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
    long[] ids = threadBean.getAllThreadIds();
    for (int i = 0; i < ids.length; i++) {
      ThreadInfo info = threadBean.getThreadInfo(ids[i], Integer.MAX_VALUE);
      if (info == null) {
        continue;
      }
      if (info.getThreadState() != Thread.State.RUNNABLE) {
        continue;
      }
      if (info.getThreadName().indexOf("J3D") >= 0) {
        return true;
      }
    }
    return false;
  }
}
Beispiel #4
0
/**
 * A data source for point data
 *
 * @author Don Murray
 * @version $Revision: 1.33 $ $Date: 2007/06/21 14:44:59 $
 */
public abstract class PointDataSource extends FilesDataSource {

  /** logging category */
  static LogUtil.LogCategory log_ = LogUtil.getLogInstance(PointDataSource.class.getName());

  /** property id for the first guess field */
  public static final String PROP_FIRSTGUESS = "prop.firstguess";

  /** dataselection property for grid x spacing */
  public static final String PROP_GRID_X = "prop.grid.x";

  /** dataselection property for grid x spacing */
  public static final String PROP_GRID_Y = "prop.grid.y";

  /** dataselection property for grid y spacing */
  public static final String PROP_GRID_UNIT = "prop.grid.unit";

  /** dataselection property for grid passes */
  public static final String PROP_GRID_NUMPASSES = "prop.grid.numpasses";

  /** dataselection property for grid gain */
  public static final String PROP_GRID_GAIN = "prop.grid.gain";

  /** dataselection property for grid search radius */
  public static final String PROP_GRID_SEARCH_RADIUS = "prop.grid.search.radius";

  /**
   * This gets set on the data choice when we are creating a point ob field intended to be used for
   * making a grid
   */
  public static final String PROP_GRID_PARAM = "prop.grid.param";

  /** station model name property */
  public static final String PROP_STATIONMODELNAME = "prop.stationmodelname";

  /** Identifier for station data */
  public static final String STATION_DATA = "Station Data";

  /** Identifier for point data */
  public static final String POINT_DATA = "Point Data";

  /** Identifier for a station plot */
  public static final String STATION_PLOT = DataCategory.CATEGORY_POINTPLOT;

  /** default categories */
  private List pointCategories = null;

  /** A cached version of the html description of the fields. */
  protected String fieldsDescription;

  /** bind round to factor */
  private double binRoundTo = 0;

  /** time bin width */
  private double binWidth = 0;

  /** for properties dialog */
  private TimeLengthField binWidthField;

  /** for properties dialog */
  private TimeLengthField binRoundToField;

  /** for properties dialog */
  private JComboBox roundToCbx;

  /** for properties dialog */
  private JComboBox widthCbx;

  /** default value for gridding */
  private static final float GRID_DEFAULT = 2;

  /** x for grid */
  private float gridX = GRID_DEFAULT;

  /** y for grid */
  private float gridY = GRID_DEFAULT;

  /** calculate grid spacing */
  private static final String SPACING_COMPUTE = "spacing.compute";

  /** degree grid spacing */
  private static final String SPACING_DEGREES = "spacing.degrees";

  /** points grid spacing */
  private static final String SPACING_POINTS = "spacing.points";

  /** spacing types */
  private static final String[] SPACING_IDS = {SPACING_COMPUTE, SPACING_DEGREES, SPACING_POINTS};

  /** names for spacing ids */
  private static final String[] SPACING_NAMES = {"Automatic", "Degrees", "# Points"};

  /** unit for grid spacing */
  private String gridUnit = SPACING_COMPUTE;

  /** Number of barnes passes */
  private int numGridPasses = 2;

  /** Gain for each pass */
  private float gridGain = 1.0f;

  /** Scale length */
  private static float DEFAULT_RADIUS = 10.0f;

  /** Scale length */
  private float gridSearchRadius = DEFAULT_RADIUS;

  /** Do we make grid fields */
  private boolean makeGridFields = true;

  /** For gui */
  private JCheckBox makeGridFieldsCbx;

  /** For gui */
  private GridParameters gridProperties;

  /**
   * Default constructor
   *
   * @throws VisADException problem creating VisAD data object
   */
  public PointDataSource() throws VisADException {
    init();
  }

  /**
   * Create a PointDataSource
   *
   * @param descriptor descriptor for the DataSource
   * @param source file location or URL
   * @param description description of data
   * @param properties extra properties
   * @throws VisADException
   */
  public PointDataSource(
      DataSourceDescriptor descriptor, String source, String description, Hashtable properties)
      throws VisADException {
    this(descriptor, Misc.toList(new String[] {source}), description, properties);
  }

  /**
   * Create a new PointDataSource
   *
   * @param descriptor data source descriptor
   * @param sources List of sources of data (filename/URL)
   * @param name The name to use
   * @param properties extra properties for initialization
   * @throws VisADException problem creating the data
   */
  public PointDataSource(
      DataSourceDescriptor descriptor, List sources, String name, Hashtable properties)
      throws VisADException {
    super(
        descriptor,
        sources,
        (sources.size() > 1) ? "Point Data" : (String) sources.get(0),
        name,
        properties);
    try {
      init();
    } catch (VisADException exc) {
      setInError(true);
      throw exc;
    }
  }

  /**
   * Initialize this object
   *
   * @throws VisADException problem during initialization
   */
  protected void init() throws VisADException {}

  /**
   * Class PlotModelSelectionComponent holds plot (layout) model. Used for field selector and the
   * properties
   *
   * @author IDV Development Team
   */
  public static class PlotModelSelectionComponent extends DataSelectionComponent {

    /** the data source */
    PointDataSource pointDataSource;

    /** The main component */
    private JComponent comp;

    /** plot model component */
    private PlotModelComponent pmc;

    /**
     * ctor
     *
     * @param pointDataSource the associated source
     */
    public PlotModelSelectionComponent(PointDataSource pointDataSource) {
      super("Layout Model");
      this.pointDataSource = pointDataSource;
      pmc =
          new PlotModelComponent(
              pointDataSource.getDataContext().getIdv(), this, "setPlotModel", null);
    }

    /**
     * Make the gui for the data subset panel
     *
     * @return gui for data subset panel
     */
    protected JComponent doMakeContents() {
      GuiUtils.tmpInsets = GuiUtils.INSETS_5;
      String prop = (String) pointDataSource.getProperty(PROP_STATIONMODELNAME);
      if (dataSelection != null) {
        prop = (String) dataSelection.getProperty(PROP_STATIONMODELNAME);
      }
      if (prop != null) {
        pmc.setPlotModelByName((String) prop);
      }

      return GuiUtils.top(GuiUtils.hflow(Misc.newList(new JLabel("Layout Model: "), pmc), 5, 5));
    }

    /**
     * set properties on dataselection
     *
     * @param dataSelection the dataselection
     */
    public void applyToDataSelection(DataSelection dataSelection) {
      if (dataSelection != null) {
        StationModel plotModel = getPlotModel();
        if (plotModel != null) {
          dataSelection.putProperty(PROP_STATIONMODELNAME, plotModel.getName());
        }
      }
    }

    /**
     * Get the selected plot model
     *
     * @param model plot model
     */
    public void setPlotModel(StationModel model) {
      pmc.setPlotModel(model);
    }

    /**
     * Get the selected plot model
     *
     * @return plot model
     */
    public StationModel getPlotModel() {
      return pmc.getPlotModel();
    }

    /**
     * Should we show in the control properties tabs.
     *
     * @return false;
     */
    public boolean getShowInControlProperties() {
      return false;
    }
  }

  /**
   * Class GridParameters holds the grid spacing/passes gui. Used for the field selector and the
   * properties
   *
   * @author IDV Development Team
   * @version $Revision: 1.3 $
   */
  private static class GridParameters extends DataSelectionComponent {

    /** the data source */
    PointDataSource pointDataSource;

    /** gui component */
    private JCheckBox useDefaultCbx = new JCheckBox("Use Default", true);

    /** gui component */
    private JTextField gridXFld;

    /** gui component */
    private JTextField gridYFld;

    /** gui component */
    private JComboBox gridUnitCmbx;

    /** gui component */
    private JTextField numGridPassesFld;

    /** The list of components */
    private List comps = new ArrayList();

    /** The main component */
    private JComponent comp;

    /** The size component */
    private JComponent sizeComp;

    /** The gain component */
    private ValueSliderComponent gainComp;

    /** The gain component */
    private ValueSliderComponent searchComp;

    /** flag for compute */
    boolean useCompute = true;

    /** The unit two faced objects_ */
    List tfos;

    /**
     * ctor
     *
     * @param pointDataSource the associated data source
     */
    public GridParameters(PointDataSource pointDataSource) {
      super("Grid Parameters");
      this.pointDataSource = pointDataSource;
      gridXFld = new JTextField("" + pointDataSource.gridX, 4);
      gridXFld.setToolTipText("X spacing in spacing units");
      gridYFld = new JTextField("" + pointDataSource.gridY, 4);
      gridYFld.setToolTipText("Y spacing in spacing units");
      gridUnitCmbx = new JComboBox();
      gridUnitCmbx.setToolTipText("Set grid spacing option");
      tfos = TwoFacedObject.createList(SPACING_IDS, SPACING_NAMES);
      GuiUtils.setListData(gridUnitCmbx, tfos);
      gridUnitCmbx.setSelectedItem(TwoFacedObject.findId(pointDataSource.gridUnit, tfos));
      gridUnitCmbx.addActionListener(
          new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
              TwoFacedObject tfo = (TwoFacedObject) ((JComboBox) ae.getSource()).getSelectedItem();
              if (tfo != null) {
                useCompute = tfo.getId().equals(SPACING_IDS[0]);
                enableAutoComps(!useCompute);
              }
            }
          });
      gainComp =
          new ValueSliderComponent(
              pointDataSource,
              0,
              1,
              "gridGain",
              "Gain",
              10,
              false,
              "Factor by which scaleLength is reduced for the second pass");
      searchComp =
          new ValueSliderComponent(
              pointDataSource,
              0,
              20,
              "gridSearchRadius",
              "Search Radius",
              1,
              false,
              "Search radius in grid units for weighting");
      numGridPassesFld = new JTextField("" + pointDataSource.numGridPasses, 4);
      numGridPassesFld.setToolTipText("Set the number of passes for the Barnes analysis");
      comps.add(GuiUtils.rLabel("Spacing:"));
      comps.add(GuiUtils.left(gridUnitCmbx));
      comps.add(GuiUtils.rLabel("Grid Size:"));
      sizeComp =
          GuiUtils.left(GuiUtils.hbox(new JLabel("X: "), gridXFld, new JLabel("  Y: "), gridYFld));
      comps.add(sizeComp);
      comps.add(GuiUtils.rLabel("Passes:"));
      comps.add(GuiUtils.left(numGridPassesFld));
      comps.add(GuiUtils.rLabel("Search Radius:"));
      comps.add(GuiUtils.left(searchComp.getContents(false)));
      comps.add(GuiUtils.rLabel("Gain:"));
      comps.add(GuiUtils.left(gainComp.getContents(false)));
      enableAutoComps(!useCompute);
      useDefaultCbx.addActionListener(
          new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
              checkEnable();
            }
          });
    }

    /** enable/disable the component based on the useDefaultCbx value */
    public void checkEnable() {
      GuiUtils.enableTree(comp, !useDefaultCbx.isSelected());
      if (!useDefaultCbx.isSelected()) {
        enableAutoComps(!useCompute);
      }
    }

    /**
     * enable/disable the components that can't be changed in autocompute
     *
     * @param enable true to enable
     */
    private void enableAutoComps(boolean enable) {
      GuiUtils.enableTree(sizeComp, enable);
      // searchComp.setEnabled(enable);
    }

    /**
     * Make the gui for the field selector
     *
     * @return gui for field selector
     */
    protected JComponent doMakeContents() {
      GuiUtils.tmpInsets = GuiUtils.INSETS_5;
      comp = GuiUtils.doLayout(comps, 2, GuiUtils.WT_N, GuiUtils.WT_N);
      if (dataSelection != null) {
        Object prop;
        prop = dataSelection.getProperty(PROP_GRID_X);
        if (prop != null) {
          gridXFld.setText("" + prop);
          // If we have a data selection property then turn of cbx
          useDefaultCbx.setSelected(false);
        }
        prop = dataSelection.getProperty(PROP_GRID_Y);
        if (prop != null) {
          gridYFld.setText("" + prop);
        }

        prop = dataSelection.getProperty(PROP_GRID_UNIT);
        if (prop != null) {
          gridUnitCmbx.setSelectedItem(TwoFacedObject.findId(prop, tfos));
        }
        prop = dataSelection.getProperty(PROP_GRID_NUMPASSES);
        if (prop != null) {
          numGridPassesFld.setText("" + prop);
        }
        prop = dataSelection.getProperty(PROP_GRID_GAIN);
        if (prop != null) {
          gainComp.setValue(((Number) prop).floatValue());
        }
        prop = dataSelection.getProperty(PROP_GRID_SEARCH_RADIUS);
        if (prop != null) {
          searchComp.setValue(((Number) prop).floatValue());
        }
      }

      checkEnable();
      return GuiUtils.topCenter(GuiUtils.right(useDefaultCbx), GuiUtils.topLeft(comp));
    }

    /**
     * set properties on dataselection
     *
     * @param dataSelection the dataselection
     */
    public void applyToDataSelection(DataSelection dataSelection) {
      if (dataSelection != null) {
        if (!useDefaultCbx.isSelected()) {
          dataSelection.putProperty(PROP_GRID_X, new Float(getGridX()));
          dataSelection.putProperty(PROP_GRID_Y, new Float(getGridY()));
          dataSelection.putProperty(PROP_GRID_UNIT, getGridUnit());
          dataSelection.putProperty(PROP_GRID_NUMPASSES, new Integer(getNumGridPasses()));
          dataSelection.putProperty(PROP_GRID_GAIN, new Float(getGridGain()));
          dataSelection.putProperty(PROP_GRID_SEARCH_RADIUS, new Float(getGridSearchRadius()));
        } else {
          dataSelection.removeProperty(PROP_GRID_X);
          dataSelection.removeProperty(PROP_GRID_Y);
          dataSelection.removeProperty(PROP_GRID_UNIT);
          dataSelection.removeProperty(PROP_GRID_NUMPASSES);
          dataSelection.removeProperty(PROP_GRID_GAIN);
          dataSelection.removeProperty(PROP_GRID_SEARCH_RADIUS);
        }
      }
    }

    /**
     * get grid x
     *
     * @return grid x
     */
    public float getGridX() {
      return (float) GuiUtils.getValue(gridXFld);
    }

    /**
     * get grid y
     *
     * @return grid y
     */
    public float getGridY() {
      return (float) GuiUtils.getValue(gridYFld);
    }

    /**
     * get grid unit
     *
     * @return grid unit
     */
    public String getGridUnit() {
      return (String) TwoFacedObject.getIdString(gridUnitCmbx.getSelectedItem());
    }

    /**
     * get passes
     *
     * @return passes
     */
    public int getNumGridPasses() {
      return GuiUtils.getInt(numGridPassesFld);
    }

    /**
     * get gain
     *
     * @return gain
     */
    public float getGridGain() {
      return gainComp.getValue();
    }

    /**
     * get search radius
     *
     * @return search radius
     */
    public float getGridSearchRadius() {
      return (float) searchComp.getValue();
    }

    /** Remove any components holding refereces to stuff. */
    public void doRemove() {
      if (gainComp != null) {
        gainComp.doRemove();
      }
      if (searchComp != null) {
        searchComp.doRemove();
      }
    }
  }

  /**
   * Add the GridParameters for the field selector
   *
   * @param components comps
   * @param dataChoice for this data
   */
  protected void initDataSelectionComponents(
      List<DataSelectionComponent> components, final DataChoice dataChoice) {

    if ((dataChoice instanceof CompositeDataChoice) || !(dataChoice.getId() instanceof List)) {
      components.add(new PlotModelSelectionComponent(this));
    } else {
      components.add(initGridParameters());
    }
  }

  /**
   * Allow subclasses to initialize a GridParameters
   *
   * @return a GridParameters instance
   */
  protected GridParameters initGridParameters() {
    return new GridParameters(this);
  }

  /**
   * not sure what this does
   *
   * @param dataChoice datachoice
   * @return false
   */
  public boolean canAddCurrentName(DataChoice dataChoice) {
    return false;
  }

  /**
   * add to properties
   *
   * @param comps comps
   */
  public void getPropertiesComponents(List comps) {
    super.getPropertiesComponents(comps);
    binWidthField = new TimeLengthField("Bin Width", true);
    binRoundToField = new TimeLengthField("Bin Round To", true);
    binWidthField.setTime(binWidth);
    binRoundToField.setTime(binRoundTo);
    List roundToItems =
        Misc.toList(
            new Object[] {
              new TwoFacedObject("Change", new Double(0)),
              new TwoFacedObject("On the hour", new Double(60)),
              new TwoFacedObject("5 after", new Double(5)),
              new TwoFacedObject("10 after", new Double(10)),
              new TwoFacedObject("15 after", new Double(15)),
              new TwoFacedObject("20 after", new Double(20)),
              new TwoFacedObject("30 after", new Double(30)),
              new TwoFacedObject("45 after", new Double(45)),
              new TwoFacedObject("10 to", new Double(50)),
              new TwoFacedObject("5 to", new Double(55))
            });

    roundToCbx =
        GuiUtils.makeComboBox(
            roundToItems, roundToItems.get(0), false, this, "setRoundToFromComboBox");

    List widthItems =
        Misc.toList(
            new Object[] {
              new TwoFacedObject("Change", new Double(0)),
              new TwoFacedObject("5 minutes", new Double(5)),
              new TwoFacedObject("10 minutes", new Double(10)),
              new TwoFacedObject("15 minutes", new Double(15)),
              new TwoFacedObject("20 minutes", new Double(20)),
              new TwoFacedObject("30 minutes", new Double(30)),
              new TwoFacedObject("45 minutes", new Double(45)),
              new TwoFacedObject("1 hour", new Double(60)),
              new TwoFacedObject("6 hours", new Double(60 * 6)),
              new TwoFacedObject("12 hours", new Double(60 * 12)),
              new TwoFacedObject("1 day", new Double(60 * 24))
            });

    widthCbx =
        GuiUtils.makeComboBox(widthItems, widthItems.get(0), false, this, "setWidthFromComboBox");

    comps.add(GuiUtils.filler());
    comps.add(getPropertiesHeader("Time Binning"));

    comps.add(GuiUtils.rLabel("Bin Size:"));
    comps.add(GuiUtils.left(GuiUtils.hbox(binWidthField.getContents(), widthCbx, 5)));
    comps.add(GuiUtils.rLabel("Round To:"));
    comps.add(GuiUtils.left(GuiUtils.hbox(binRoundToField.getContents(), roundToCbx, 5)));
  }

  /**
   * Add the Grid Fields component to the properties tab
   *
   * @param tabbedPane properties tab
   */
  public void addPropertiesTabs(JTabbedPane tabbedPane) {
    super.addPropertiesTabs(tabbedPane);
    List comps = new ArrayList();
    gridProperties = new GridParameters(this);
    makeGridFieldsCbx = new JCheckBox("Make Grid Fields", makeGridFields);
    comps.add(GuiUtils.filler());
    comps.add(GuiUtils.left(makeGridFieldsCbx));
    comps.addAll(gridProperties.comps);
    GuiUtils.tmpInsets = GuiUtils.INSETS_5;
    tabbedPane.addTab(
        "Objective Analysis",
        GuiUtils.topLeft(GuiUtils.doLayout(comps, 2, GuiUtils.WT_NN, GuiUtils.WT_N)));
  }

  /**
   * Set the property
   *
   * @param tfo value from combo box_
   */
  public void setRoundToFromComboBox(TwoFacedObject tfo) {
    double value = ((Double) tfo.getId()).doubleValue();
    if (value == 0.0) {
      return;
    }
    binRoundToField.setTime(value);
    roundToCbx.setSelectedIndex(0);
  }

  /**
   * set the property
   *
   * @param tfo value_
   */
  public void setWidthFromComboBox(TwoFacedObject tfo) {
    double value = ((Double) tfo.getId()).doubleValue();
    if (value == 0.0) {
      return;
    }
    binWidthField.setTime(value);
    widthCbx.setSelectedIndex(0);
  }

  /**
   * apply the properties
   *
   * @return success
   */
  public boolean applyProperties() {
    if (!super.applyProperties()) {
      return false;
    }
    boolean changed = false;
    String what = "";
    try {
      what = "Bad bin value";
      changed |= (binRoundToField.getTime() != binRoundTo) || (binWidth != binWidthField.getTime());
      binRoundTo = binRoundToField.getTime();
      binWidth = binWidthField.getTime();

      what = "Bad grid points X value";
      changed |= (gridX != gridProperties.getGridX());
      what = "Bad grid points Y value";
      changed |= (gridY != gridProperties.getGridY());
      what = "Bad grid passes value";
      changed |= (numGridPasses != gridProperties.getNumGridPasses());
      what = "Bad grid unit value";
      changed |= (!gridUnit.equals(gridProperties.getGridUnit()));
      what = "Bad grid search value";
      changed |= (gridSearchRadius != gridProperties.getGridSearchRadius());
      what = "Bad grid gain value";
      changed |= (gridGain != gridProperties.getGridGain());
    } catch (NumberFormatException nfe) {
      LogUtil.userErrorMessage(what);
      return false;
    }

    gridX = gridProperties.getGridX();
    gridY = gridProperties.getGridY();
    gridUnit = gridProperties.getGridUnit();
    numGridPasses = gridProperties.getNumGridPasses();
    gridGain = gridProperties.getGridGain();
    gridSearchRadius = gridProperties.getGridSearchRadius();
    if (makeGridFields != makeGridFieldsCbx.isSelected()) {
      makeGridFields = makeGridFieldsCbx.isSelected();
      dataChoices = null;
      getDataChoices();
      getDataContext().dataSourceChanged(this);
    }

    if (changed) {
      flushCache();
    }

    return true;
  }

  /**
   * Is it ok to create a grid field for the parameter with the given real type
   *
   * @param type the type
   * @return ok to create grid
   */
  protected boolean canCreateGrid(RealType type) {
    return true;
  }

  /**
   * Read a sample of the data. e.g., just the first ob
   *
   * @param dataChoice The data choice
   * @return The first ob
   * @throws Exception On badness
   */
  protected FieldImpl getSample(DataChoice dataChoice) throws Exception {
    return null;
  }

  /** Reload the data */
  public void reloadData() {
    dataChoices = null;
    getDataChoices();
    super.reloadData();
  }

  /** Make the <code>DataChoices</code> for this <code>DataSource</code>. */
  public void doMakeDataChoices() {

    if (sources == null) {
      return;
    }
    String stationModelName = (String) getProperty(PROP_STATIONMODELNAME);
    Hashtable properties =
        Misc.newHashtable(DataChoice.PROP_ICON, "/auxdata/ui/icons/Placemark16.gif");
    if (stationModelName != null) {
      properties.put(PROP_STATIONMODELNAME, stationModelName);
    }
    if (!getDefaultLevels().isEmpty()) {
      properties.put(DataSelection.PROP_DEFAULT_LEVELS, getDefaultLevels());
    }
    DataChoice uberChoice = null;
    /*  Might want to do this someday
    uberChoice = new DirectDataChoice(this,
                                        sources, getName(),
                                        getDataName(),
                                        getPointCategories(),
                                        properties);
    */

    if (sources.size() > 1) {
      uberChoice =
          new CompositeDataChoice(
              this, sources, getName(), getDataName(), getPointCategories(), properties);
      addDataChoice(uberChoice);
    }

    for (int i = 0; i < sources.size(); i++) {
      String dataChoiceDesc = getDescription();
      String dataChoiceName = getDataName();
      if (uberChoice != null) {
        dataChoiceDesc = IOUtil.getFileTail(sources.get(i).toString());
        dataChoiceName = IOUtil.getFileTail(sources.get(i).toString());
      }

      DataChoice choice =
          new DirectDataChoice(
              this,
              new Integer(i),
              dataChoiceDesc,
              dataChoiceName,
              getPointCategories(),
              properties);

      /*
      We'd like to create sub choices for each parameter but we don't really
      know the parameters until we read the data and that can be expensive
                  DirectDataChoice subChoice = new DirectDataChoice(this,
                            (String) sources.get(i),
                            getDescription(), getDataName(),
                            getPointCategories(), properties);
                            choice.addDataChoice(subChoice);*/

      if (uberChoice != null) {
        ((CompositeDataChoice) uberChoice).addDataChoice(choice);
      } else {
        addDataChoice(choice);
      }
      // Only add the grid data choices for the first source
      if (i > 0) {
        continue;
      }
      try {
        FieldImpl sample = (makeGridFields ? getSample(choice) : null);
        if (sample != null) {
          for (int dataChoiceType = 0; dataChoiceType < 2; dataChoiceType++) {
            Hashtable seenFields = new Hashtable();
            if (ucar.unidata.data.grid.GridUtil.isTimeSequence(sample)) {
              sample = (FieldImpl) sample.getSample(0);
            }
            PointOb ob = (PointOb) sample.getSample(0);
            Tuple tuple = (Tuple) ob.getData();
            TupleType tupleType = (TupleType) tuple.getType();
            MathType[] types = tupleType.getComponents();
            CompositeDataChoice compositeDataChoice = null;
            for (int typeIdx = 0; typeIdx < types.length; typeIdx++) {
              if (!(types[typeIdx] instanceof RealType)) {
                continue;
              }
              RealType type = (RealType) types[typeIdx];
              if (!canCreateGrid(type)) {
                continue;
              }
              //                        List gridCategories =
              //                            DataCategory.parseCategories("OA Fields;GRID-2D-TIME;");
              List gridCategories = DataCategory.parseCategories("GRID-2D-TIME;", false);
              if (compositeDataChoice == null) {
                compositeDataChoice =
                    new CompositeDataChoice(
                        this,
                        "",
                        "Grid Fields from Objective Analysis",
                        "Gridded Fields " + ((dataChoiceType == 0) ? "" : "(with first guess)"),
                        Misc.newList(DataCategory.NONE_CATEGORY),
                        null);
                addDataChoice(compositeDataChoice);
              }
              String name = ucar.visad.Util.cleanTypeName(type.toString());
              if (seenFields.get(name) != null) {
                continue;
              }
              seenFields.put(name, name);
              List idList = Misc.newList(new Integer(i), type);
              if (dataChoiceType == 1) {
                idList.add(new Boolean(true));
              }
              DataChoice gridChoice =
                  new DirectDataChoice(this, idList, name, name, gridCategories, (Hashtable) null);
              compositeDataChoice.addDataChoice(gridChoice);
            }
          }
        }
      } catch (Exception exc) {
        throw new WrapperException("Making grid parameters", exc);
      }
      //            if(true) break;
    }
  }

  /**
   * Get the file or url source path from the given data choice. The new version uses an Integer
   * index into the sources list as the id of the data choice. However, this method does handle
   *
   * @param dataChoice The data choice
   * @return The file or url the data choice refers to
   */
  protected String getSource(DataChoice dataChoice) {
    Object id = dataChoice.getId();
    if (id instanceof String) {
      return (String) id;
    } else if (id instanceof Integer) {
      int idx = ((Integer) id).intValue();
      return (String) sources.get(idx);
    }
    if (dataChoice instanceof CompositeDataChoice) {
      return (String) sources.get(0);
    }

    return null;
  }

  /**
   * Get the default categories for data from PointDataSource-s
   *
   * @return list of categories
   */
  protected List getPointCategories() {
    if (pointCategories == null) {
      pointCategories =
          DataCategory.parseCategories(DataCategory.CATEGORY_POINT + ";" + STATION_PLOT, false);
    }
    return pointCategories;
  }

  /**
   * Get the name of this data.
   *
   * @return name of data
   */
  public String getDataName() {
    return POINT_DATA;
  }

  /**
   * See if this DataSource should cache or not
   *
   * @param dataChoice The data choice we got this data from
   * @param data Data to cache
   * @return true
   */
  protected boolean shouldCache(DataChoice dataChoice, Data data) {
    Object id = dataChoice.getId();
    if (!(dataChoice instanceof CompositeDataChoice) && (id instanceof List)) {
      // Note: idList can also
      List idList = (List) id;
      // Check if its a first guess field
      if (idList.size() > 2) {
        boolean doFirstGuessField = ((Boolean) idList.get(2)).booleanValue();
        return !doFirstGuessField;
      }
    }
    return super.shouldCache(dataChoice, data);
  }

  /**
   * Get the data represented by this class. Calls makeObs, real work needs to be implemented there.
   *
   * @param dataChoice choice for data
   * @param category category of data
   * @param dataSelection subselection properties
   * @param requestProperties additional selection properties (not used here)
   * @return Data object representative of the choice
   * @throws RemoteException Java RMI error
   * @throws VisADException VisAD Error
   */
  protected Data getDataInner(
      DataChoice dataChoice,
      DataCategory category,
      DataSelection dataSelection,
      Hashtable requestProperties)
      throws VisADException, RemoteException {

    Object id = dataChoice.getId();
    // CompositeDataChoice

    // If it is a list then we are doing a grid field
    boolean doGriddedData = false;
    if (id instanceof List) {
      if (((List) id).get(0) instanceof Integer) {
        doGriddedData = true;
      }
    }
    if (doGriddedData) {
      List idList = (List) id;
      //            Integer   i          = (Integer) idList.get(0);
      RealType type = (RealType) idList.get(1);
      Hashtable properties = dataChoice.getProperties();
      if (properties == null) {
        properties = new Hashtable();
      }

      Data firstGuessData = null;
      boolean doFirstGuessField = false;
      if (idList.size() > 2) {
        doFirstGuessField = ((Boolean) idList.get(2)).booleanValue();
      }

      if (doFirstGuessField) {
        DataChoice firstGuessDataChoice = (DataChoice) dataChoice.getProperty(PROP_FIRSTGUESS);
        if (firstGuessDataChoice == null) {
          List operands = new ArrayList();
          List categories = DataCategory.parseCategories("GRID-2D-TIME;GRID-3D-TIME", false);
          operands.add(
              new DataOperand("First Guess Field", "First Guess Field", categories, false));
          List userValues = getDataContext().selectDataChoices(operands);
          if (userValues == null) {
            return null;
          }

          firstGuessDataChoice = (DataChoice) userValues.get(0);
          dataChoice.setObjectProperty(PROP_FIRSTGUESS, firstGuessDataChoice);
        }

        if (firstGuessDataChoice != null) {
          firstGuessData = firstGuessDataChoice.getData(null);
          if (firstGuessData == null) {
            return null;
          }
        }
      }

      // Merge the point obs
      properties.put(PROP_GRID_PARAM, type);
      FieldImpl pointObs = null;
      List datas = new ArrayList();
      for (int i = 0; i < sources.size(); i++) {
        DataChoice choice =
            new DirectDataChoice(
                this, new Integer(i), "", "", dataChoice.getCategories(), properties);
        pointObs = (FieldImpl) getDataInner(choice, category, dataSelection, requestProperties);
        if (pointObs != null) {
          datas.add(pointObs);
        }
      }
      if (datas.size() == 0) {
        return null;
      }
      pointObs = PointObFactory.mergeData(datas);
      if (pointObs == null) {
        return null;
      }

      // { minY, minX, maxY, maxX };
      float spacingX = this.gridX;
      float spacingY = this.gridY;
      int passes = this.numGridPasses;
      float gain = this.gridGain;
      float searchRadius = this.gridSearchRadius;
      Number tmp;
      tmp = (Float) dataSelection.getProperty(PROP_GRID_X);
      if (tmp != null) {
        spacingX = tmp.floatValue();
      }
      tmp = (Float) dataSelection.getProperty(PROP_GRID_Y);
      if (tmp != null) {
        spacingY = tmp.floatValue();
      }
      tmp = (Integer) dataSelection.getProperty(PROP_GRID_NUMPASSES);
      if (tmp != null) {
        passes = tmp.intValue();
      }
      String theUnit = (String) dataSelection.getProperty(PROP_GRID_UNIT);
      if (theUnit == null) {
        theUnit = this.gridUnit;
      }
      tmp = (Float) dataSelection.getProperty(PROP_GRID_GAIN);
      if (tmp != null) {
        gain = tmp.floatValue();
      }
      tmp = (Float) dataSelection.getProperty(PROP_GRID_SEARCH_RADIUS);
      if (tmp != null) {
        searchRadius = tmp.floatValue();
      }

      float degreesX = 0, degreesY = 0;
      pointObs = PointObFactory.makeTimeSequenceOfPointObs(pointObs);
      if (theUnit.equals(SPACING_COMPUTE) || (spacingX <= 0) || (spacingY <= 0)) {
        degreesX = PointObFactory.OA_GRID_DEFAULT;
        degreesY = PointObFactory.OA_GRID_DEFAULT;
        if ((searchRadius == DEFAULT_RADIUS) && (firstGuessData == null)) {
          searchRadius = PointObFactory.OA_GRID_DEFAULT;
        }
      } else if (theUnit.equals(SPACING_POINTS)) {
        double[] bbox = PointObFactory.getBoundingBox(pointObs);
        float spanX = (float) Math.abs(bbox[1] - bbox[3]);
        float spanY = (float) Math.abs(bbox[0] - bbox[2]);
        degreesX = spanX / (int) spacingX;
        degreesY = spanY / (int) spacingY;
      } else if (theUnit.equals(SPACING_DEGREES)) {
        degreesX = spacingX;
        degreesY = spacingY;
      }
      Barnes.AnalysisParameters ap =
          new Barnes.AnalysisParameters(degreesX, degreesY, searchRadius, 0.0d);
      log_.debug(
          "X = "
              + degreesX
              + " Y = "
              + degreesY
              + " unit = "
              + theUnit
              + " gain = "
              + gain
              + " search = "
              + searchRadius);

      LogUtil.message("Doing Barnes Analysis");
      FieldImpl fi =
          PointObFactory.barnes(
              pointObs,
              type,
              degreesX,
              degreesY,
              passes,
              gain,
              searchRadius,
              ap,
              (FieldImpl) firstGuessData);
      if (ap.getGridXArray() != null) {
        log_.debug(
            "Analysis params: X = "
                + ap.getGridXArray().length
                + " Y = "
                + ap.getGridYArray().length
                + " search = "
                + ap.getScaleLengthGU()
                + " random = "
                + ap.getRandomDataSpacing());
      }
      return fi;
    }

    GeoSelection geoSelection = ((dataSelection != null) ? dataSelection.getGeoSelection() : null);
    GeoLocationInfo bbox = ((geoSelection == null) ? null : geoSelection.getBoundingBox());

    LatLonRect llr = ((bbox != null) ? bbox.getLatLonRect() : null);

    FieldImpl retField = null;
    try {
      // List choices = (List) dataChoice.getId();
      List choices =
          (dataChoice instanceof CompositeDataChoice)
              ? ((CompositeDataChoice) dataChoice).getDataChoices()
              : Misc.toList(new DataChoice[] {dataChoice});
      List datas = new ArrayList(choices.size());
      for (int i = 0; i < choices.size(); i++) {
        DataChoice subDataChoice = (DataChoice) choices.get(i);
        FieldImpl obs = makeObs(subDataChoice, dataSelection, llr);
        if (obs == null) {
          continue;
        }

        //                if (true) {
        //                    return obs;
        //                }
        datas.add(obs);
        if (fieldsDescription == null) {
          makeFieldDescription(obs);
        }
      }
      if (datas.isEmpty()) {
        return null;
      }
      retField = PointObFactory.mergeData(datas);
    } catch (Exception exc) {
      logException("Creating obs", exc);
    }
    return retField;
  }

  /**
   * Override this method so we don't make any derived data choices from the grid fields
   *
   * @param dataChoices base list of choices
   */
  protected void makeDerivedDataChoices(List dataChoices) {}

  /**
   * Override the base class method to add on the listing of the param names in the point tuple.
   *
   * @return full description of this datasource for help
   */
  public String getFullDescription() {
    String parentDescription = super.getFullDescription();
    if (fieldsDescription == null) {
      try {
        FieldImpl fi = (FieldImpl) getSample(getDescriptionDataChoice());
        makeFieldDescription(fi);
      } catch (Exception exc) {
        logException("getting description", exc);
        return "";
      }
    }
    return parentDescription + "<p>" + ((fieldsDescription != null) ? fieldsDescription : "");
  }

  /**
   * Get the data choice to use for the description
   *
   * @return the data choice
   */
  protected DataChoice getDescriptionDataChoice() {
    return (DataChoice) getDataChoices().get(0);
  }

  /**
   * Create e field description from the field
   *
   * @param fi field to use
   */
  protected void makeFieldDescription(FieldImpl fi) {
    if (fi == null) {
      fieldsDescription = "Bad data: null";
      return;
    }
    try {
      if (ucar.unidata.data.grid.GridUtil.isTimeSequence(fi)) {
        fi = (FieldImpl) fi.getSample(0);
      }
      PointOb ob = (PointOb) fi.getSample(0);
      Tuple tuple = (Tuple) ob.getData();
      MathType[] comps = ((TupleType) tuple.getType()).getComponents();
      Trace.msg("PointDataSource #vars=" + comps.length);
      StringBuffer params = new StringBuffer(comps.length + " Fields:<ul>");
      String dataSourceName = getName();
      DataChoice.addCurrentName(new TwoFacedObject("Point Data>Time", "Time"));
      DataChoice.addCurrentName(new TwoFacedObject("Point Data>Latitude", "Latitude"));
      DataChoice.addCurrentName(new TwoFacedObject("Point Data>Altitude", "Altitude"));

      for (int i = 0; i < comps.length; i++) {
        params.append("<li>");
        String paramName = ucar.visad.Util.cleanTypeName(comps[i].toString());
        DataAlias alias = DataAlias.findAlias(paramName);
        params.append(paramName);
        if (alias != null) {
          params.append(" --  " + alias.getLabel());
          DataChoice.addCurrentName(
              new TwoFacedObject(
                  dataSourceName + ">" + alias.getLabel() + " -- " + paramName, paramName));
        } else {
          DataChoice.addCurrentName(
              new TwoFacedObject(dataSourceName + ">" + paramName, paramName));
        }
        Data data = tuple.getComponent(i);
        if (data instanceof Real) {
          Unit unit = ((Real) data).getUnit();
          if (unit != null) {
            params.append("  [" + unit.toString() + "]");
          }
        }
      }
      fieldsDescription = params.toString();
    } catch (Exception exc) {
      logException("getting description", exc);
    }
  }

  /** Gets called by the {@link DataManager} when this DataSource has been removed. */
  public void doRemove() {
    super.doRemove();
    if (gridProperties != null) {
      gridProperties.doRemove();
    }
  }

  /**
   * Make the observation data
   *
   * @param dataChoice choice describing the data
   * @param subset subselection (not used)
   * @param bbox The bounding box
   * @return FieldImpl of PointObs
   * @throws Exception problem (VisAD or IO)
   */
  protected abstract FieldImpl makeObs(DataChoice dataChoice, DataSelection subset, LatLonRect bbox)
      throws Exception;

  /**
   * Get a list of selected levels. Subclasses should override if they have levels.
   *
   * @return list of levels (may be empty)
   */
  protected List getDefaultLevels() {
    return new ArrayList();
  }

  /**
   * Set the source property (filename or URL). Used by persistence
   *
   * @param value data source
   */
  public void setSource(String value) {
    setSources(Misc.toList(new String[] {value}));
  }

  /**
   * Set the BinWidth property.
   *
   * @param value The new value for BinWidth
   */
  public void setBinWidth(double value) {
    binWidth = value;
  }

  /**
   * Get the BinWidth property.
   *
   * @return The BinWidth
   */
  public double getBinWidth() {
    return binWidth;
  }

  /**
   * Set the BinRoundTo property.
   *
   * @param value The new value for BinRoundTo
   */
  public void setBinRoundTo(double value) {
    binRoundTo = value;
  }

  /**
   * Get the BinRoundTo property.
   *
   * @return The BinRoundTo
   */
  public double getBinRoundTo() {
    return binRoundTo;
  }

  /**
   * Set the GridX property.
   *
   * @param value The new value for GridX
   */
  public void setGridX(float value) {
    gridX = value;
  }

  /**
   * Get the GridX property.
   *
   * @return The GridX
   */
  public float getGridX() {
    return gridX;
  }

  /**
   * Set the GridY property.
   *
   * @param value The new value for GridY
   */
  public void setGridY(float value) {
    gridY = value;
  }

  /**
   * Get the GridY property.
   *
   * @return The GridY
   */
  public float getGridY() {
    return gridY;
  }

  /**
   * Set the GridY property.
   *
   * @param value The new value for GridY
   */
  public void setGridUnit(String value) {
    gridUnit = value;
  }

  /**
   * Get the GridUnit property.
   *
   * @return The GridUnit property
   */
  public String getGridUnit() {
    return gridUnit;
  }

  /**
   * Set the NumGridPasses property.
   *
   * @param value The new value for NumGridPasses
   */
  public void setNumGridPasses(int value) {
    numGridPasses = value;
  }

  /**
   * Get the NumGridPasses property.
   *
   * @return The NumGridPasses
   */
  public int getNumGridPasses() {
    return numGridPasses;
  }

  /**
   * Set the grid gain property.
   *
   * @param value The new value for gain
   */
  public void setGridGain(float value) {
    gridGain = value;
  }

  /**
   * Get the grid gain property.
   *
   * @return The gain
   */
  public float getGridGain() {
    return gridGain;
  }

  /**
   * Set the grid search radius
   *
   * @param value The new value for search radius
   */
  public void setGridSearchRadius(float value) {
    gridSearchRadius = value;
  }

  /**
   * Get the search radius property
   *
   * @return The search radius
   */
  public float getGridSearchRadius() {
    return gridSearchRadius;
  }

  /**
   * Set the MakeGridFields property.
   *
   * @param value The new value for MakeGridFields
   */
  public void setMakeGridFields(boolean value) {
    makeGridFields = value;
  }

  /**
   * Get the MakeGridFields property.
   *
   * @return The MakeGridFields
   */
  public boolean getMakeGridFields() {
    return makeGridFields;
  }
}