/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/*
 * Das2ServerDataSourceEditorPanel.java
 *
 * Created on Oct 16, 2009, 12:59:27 PM
 */
package org.das2.datasource;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import javax.swing.text.DefaultEditorKit;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.das2.DasException;
import org.das2.client.DasServer;
import org.das2.datum.Datum;
import org.das2.datum.DatumRange;
import org.das2.datum.DatumRangeUtil;
import org.das2.datum.TimeUtil;
import org.das2.system.RequestProcessor;
import org.das2.util.LoggerManager;
import org.das2.util.monitor.ProgressMonitor;
import org.autoplot.datasource.AutoplotSettings;
import org.autoplot.datasource.DataSetURI;
import org.autoplot.datasource.DataSourceEditorPanel;
import org.autoplot.datasource.RecentComboBox;
import org.autoplot.datasource.TimeRangeTool;
import org.autoplot.datasource.URISplit;
import org.autoplot.datasource.WindowManager;
import org.das2.client.Das2ServerGUI;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
 *
 * @author jbf
 */
public class Das2ServerDataSourceEditorPanel extends javax.swing.JPanel implements DataSourceEditorPanel {
    private static final Logger logger= LoggerManager.getLogger("apdss.das2server");
    private static final char EXAMPLE_TIMERANGE_LABEL_DELIM = '|';
    //DANGER: NB gui code doesn't use this...
    private static final String EXAMPLE_TIME_RANGES = "Example Time Ranges";
    private final String DEFAULT_TIMERANGE="2001-01-01";
    private String dsdfContent;
    /** Creates new form Das2ServerDataSourceEditorPanel */
    public Das2ServerDataSourceEditorPanel() {
        initComponents();
        recentComboBox1.setPreferenceNode(RecentComboBox.PREF_NODE_TIMERANGE);
    }
    /**
     * list of servers we know about
     */
    private List d2ss;
	 
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // //GEN-BEGIN:initComponents
    private void initComponents() {
        das2ServerComboBox = new javax.swing.JComboBox();
        jLabel1 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTree1 = new javax.swing.JTree();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        readerParamsTextArea = new javax.swing.JTextArea();
        jLabel5 = new javax.swing.JLabel();
        tcaTextField = new javax.swing.JTextField();
        jLabel6 = new javax.swing.JLabel();
        viewDsdfButton = new javax.swing.JButton();
        validRangeLabel = new javax.swing.JLabel();
        discoveryCb = new javax.swing.JCheckBox();
        examplesComboBox = new javax.swing.JComboBox();
        jLabel7 = new javax.swing.JLabel();
        descriptionLabel = new javax.swing.JLabel();
        timeRangeTool = new javax.swing.JButton();
        intrinsicCb = new javax.swing.JCheckBox();
        recentComboBox1 = new org.autoplot.datasource.RecentComboBox();
        itemsComboBox = new javax.swing.JComboBox<>();
        editParamsButton = new javax.swing.JButton();
        setName("das2serverDataSourceEditorPanel"); // NOI18N
        das2ServerComboBox.setEditable(true);
        das2ServerComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "https://planet.physics.uiowa.edu/das/das2Server", "https://jupiter.physics.uiowa.edu/das/server" }));
        das2ServerComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                das2ServerComboBoxActionPerformed(evt);
            }
        });
        jLabel1.setText("Das2 Server URL:");
        javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("Loading DataSets List...");
        jTree1.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
        jTree1.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
                jTree1ValueChanged(evt);
            }
        });
        jScrollPane1.setViewportView(jTree1);
        jLabel2.setText("Data Set Id:");
        jLabel3.setText("Time Range:");
        jLabel4.setText("Reader Parameters:");
        jLabel4.setToolTipText("Special parameters for the reader that implements the data source.  ");
        readerParamsTextArea.setColumns(20);
        readerParamsTextArea.setRows(5);
        jScrollPane2.setViewportView(readerParamsTextArea);
        jLabel5.setText("Sampling Interval (sec):");
        jLabel5.setToolTipText(" Interval (in seconds) to use for arbitrary resolution data.
Typically used for spacecraft positions or models, leave blank for most datasets.
  ");
        tcaTextField.setText(" ");
        tcaTextField.setToolTipText(" Interval (in seconds) to use for TCA (ephemeris) data.
 Leave blank for most datasets.
  ");
        jLabel6.setText("TCA Item:");
        jLabel6.setToolTipText("The optional item number for TCAs.");
        viewDsdfButton.setText("View DSDF");
        viewDsdfButton.setToolTipText("View the DSDF configuration file on the server");
        viewDsdfButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                viewDsdfButtonActionPerformed(evt);
            }
        });
        validRangeLabel.setFont(new java.awt.Font("DejaVu LGC Sans", 0, 10)); // NOI18N
        validRangeLabel.setText("no valid range for dataset provided");
        discoveryCb.setText("require example time");
        discoveryCb.setToolTipText("Show only datasets that have identified example times.  These should be a higher quality, and can be tested by a machine.");
        discoveryCb.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                discoveryCbActionPerformed(evt);
            }
        });
        examplesComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Example Time Ranges", " " }));
        examplesComboBox.setToolTipText("Example times specified in the data set descriptor file");
        examplesComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                examplesComboBoxActionPerformed(evt);
            }
        });
        jLabel7.setText("Description:");
        jLabel7.setToolTipText("Description provided by the server (in its dsdf file)");
        descriptionLabel.setText(" ");
        timeRangeTool.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/das2/datasource/calendar.png"))); // NOI18N
        timeRangeTool.setToolTipText("Time Range Tool");
        timeRangeTool.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                timeRangeToolActionPerformed(evt);
            }
        });
        intrinsicCb.setText("Force Intrinsic Resolution");
        intrinsicCb.setToolTipText("Force the load of data at its intrinsic resolution, or for modelled quantities, always load at the sampling interval.");
        intrinsicCb.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                intrinsicCbActionPerformed(evt);
            }
        });
        recentComboBox1.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                recentComboBox1ItemStateChanged(evt);
            }
        });
        itemsComboBox.setEditable(true);
        itemsComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { " ", " " }));
        editParamsButton.setText("Edit Params");
        editParamsButton.setToolTipText("The DSDF can be used to describe the parameters, and this experimental GUI will be generated.");
        editParamsButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                editParamsButtonActionPerformed(evt);
            }
        });
        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(das2ServerComboBox, 0, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(layout.createSequentialGroup()
                        .add(jLabel2)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .add(discoveryCb))
                    .add(jScrollPane1)
                    .add(layout.createSequentialGroup()
                        .add(jLabel7)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(descriptionLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                            .add(layout.createSequentialGroup()
                                .add(21, 21, 21)
                                .add(validRangeLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 267, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                                .add(examplesComboBox, 0, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                                .add(jLabel3)
                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                                .add(recentComboBox1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                                .add(timeRangeTool))
                            .add(org.jdesktop.layout.GroupLayout.TRAILING, jScrollPane2))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                            .add(viewDsdfButton)
                            .add(editParamsButton)))
                    .add(layout.createSequentialGroup()
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                            .add(jLabel1)
                            .add(jLabel4)
                            .add(layout.createSequentialGroup()
                                .add(intrinsicCb)
                                .add(28, 28, 28)
                                .add(jLabel5)
                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                                .add(tcaTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 70, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                                .add(26, 26, 26)
                                .add(jLabel6)
                                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                                .add(itemsComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
                        .add(0, 27, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(jLabel1)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(das2ServerComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .add(5, 5, 5)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jLabel2)
                    .add(discoveryCb))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 240, Short.MAX_VALUE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jLabel7)
                    .add(descriptionLabel))
                .add(7, 7, 7)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(viewDsdfButton)
                    .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                        .add(jLabel3)
                        .add(recentComboBox1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, timeRangeTool))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(examplesComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(validRangeLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jLabel4)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(layout.createSequentialGroup()
                        .add(jScrollPane2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 53, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(jLabel5)
                            .add(tcaTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                            .add(jLabel6)
                            .add(intrinsicCb)
                            .add(itemsComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
                    .add(editParamsButton))
                .addContainerGap())
        );
    }// //GEN-END:initComponents
    private DatumRange validTimeRange= null; // some timerange believed to be valid.
    
    private final Map readerParams= new HashMap(); // from dataset url to params.
            
    private final Map otherParams= new HashMap(); // other params which are not supported by this dialog, like qubeSubset
    
    private String userTimeRange= null;
    
    private final Map tcaItem= new HashMap(); // from ID to selected TCA item.
        
    private static class Example {
        private String timeRange;
        private String label="";
        private String params="";
    }
    
    private final LinkedHashMap theExamples= new LinkedHashMap<>();
    
    /**
     * populate the timeRange and label parts of the Example.
     * @param s the string 
     * @param e null or the existing Example
     */
    private static Example parseExample( String s, Example e ) {
        if ( e==null ) e= new Example();
        int j= s.indexOf( EXAMPLE_TIMERANGE_LABEL_DELIM );
        if ( j>-1 ) {
            e.timeRange= s.substring(0,j);
            e.label= s.substring(j+1).trim();
        } else {
            e.timeRange= s;
        }
        return e;
    }
    
    private static Example addParamsToExample( String s, Example e ) {
        if ( e==null ) e= new Example();
        e.params= s;
        return e;
    }
    
    /**
     * open a connection to retrieve the URL, possibly handling one redirect.
     * @param url the URL, such as http://jupiter.physics.uiowa.edu/das/server?server=logo
     * @return the input stream.
     * @throws IOException 
     */
    private InputStream openConnection( URL url ) throws IOException {
        HttpURLConnection httpConn= (HttpURLConnection)url.openConnection();
        int nStatus= httpConn.getResponseCode();
        if ( nStatus==301 ) {
            String newUrl= httpConn.getHeaderField("Location");
            httpConn.disconnect(); //TODO: this is sloppy.  The buffer needs to be emptied.
            if ( newUrl==null ) {
                throw new IllegalArgumentException("301 response but no new location");
            }
            httpConn=  (HttpURLConnection) new URL(newUrl).openConnection();
            nStatus= ((HttpURLConnection) httpConn).getResponseCode();
            //if ( conn.getHeaderField("Strict-Transport-Security"))
        }
        return httpConn.getInputStream();        
    }
    
    /**
     * this is called off the event thread for the web transaction, then hop back on it to populate the GUI.
     * @param url
     */
    private void updateDataSetSelected( final URL url ) {
        InputStream in= null;
        try {
            in= openConnection(url);
            StringBuilder sb = new StringBuilder();
            int by = in.read();
            while (by != -1) {
                sb.append((char) by);
                by = in.read();
            }
            in.close();
            String s = sb.toString();
            final int packetTagLength=10;
            int contentLength = Integer.parseInt(s.substring(4, packetTagLength )); // "[00]000192  "
            String sxml = s.substring( packetTagLength, packetTagLength + contentLength);
            Reader xin = new BufferedReader(new StringReader(sxml));
            DocumentBuilder builder;
            builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            InputSource source = new InputSource(xin);
            final Document document = builder.parse(source);
            Runnable run = new Runnable() {
                @Override
                public void run() {
                    try {
                    validTimeRange= null;
                    boolean isTca= false;
                    XPathFactory factory = XPathFactory.newInstance();
                    XPath xpath = (XPath) factory.newXPath();
                    String curr= Das2ServerDataSourceEditorPanel.this.recentComboBox1.getText();
                    Node description= (Node) xpath.evaluate( "/stream/properties/@description", document, XPathConstants.NODE );
                    descriptionLabel.setText( description==null ? "" : description.getNodeValue() );
                    NodeList exs=  (NodeList) xpath.evaluate( "/stream/properties/@*", document, XPathConstants.NODESET );
                    Example example= null;
                    //String exampleParams= null;
                    Map items= new HashMap<>();
                    Pattern itemPattern= Pattern.compile("item_(\\d\\d)");
                    for ( int i=0; i-1 ) {
                                    s= s.substring(0,ipipe);
                                }
                                s= s.trim();
                                items.put( name, s );
                            }
                        }
                    }
                    
                    String selectedItem= tcaItem.get( url.toString() ); 
                    if ( items.size()>0 && isTca ) {
                        DefaultComboBoxModel aModel= new DefaultComboBoxModel();
                        aModel.addElement(""); // all items
                        String e;
                        int index=0;
                        do {
                            e= items.get( String.format("item_%02d", index ) );
                            if ( e!=null ) {
                                if ( index==0 ) {
                                    aModel.addElement("0 ("+e+")");
                                } else {
                                    aModel.addElement(e);
                                }
                            }
                            index++;
                        } while ( e!=null );
                        itemsComboBox.setModel(aModel);
                    } else {
                        DefaultComboBoxModel aModel= new DefaultComboBoxModel();
                        aModel.addElement(""); // all items
                        itemsComboBox.setModel(aModel);
                    }
                    if ( selectedItem!=null ) {
                        itemsComboBox.setSelectedItem(selectedItem);
                    }
                    
                    if ( example!=null && curr.equals(DEFAULT_TIMERANGE) ) { // DANGER: what if they are the same?
                        Das2ServerDataSourceEditorPanel.this.recentComboBox1.setText( example.timeRange );
                    }
                    if ( example!=null ) {
                        try {
                            validTimeRange= DatumRangeUtil.parseTimeRange(example.timeRange);
                        } catch (ParseException ex) {
                            logger.info("default timerange doesn't parse!");
                        }                        
                    }
                    
                    if ( theExamples.size()>0 ) {
                        List keys= new ArrayList<>(theExamples.size());
                        keys.add("LABEL");
                        
                        if ( theExamples.containsKey("example") ) {
                            keys.add( "example" );
                        }
                        for ( String k: theExamples.keySet() ) {
                            if ( !k.equals("example") ) keys.add(k);
                        }
                        
                        if ( theExamples.size()>0 ) {
                            String anExample;
                            anExample= theExamples.entrySet().iterator().next().getValue().timeRange;
                            if ( Das2ServerDataSourceEditorPanel.this.userTimeRange!=null ) {
                                anExample=  Das2ServerDataSourceEditorPanel.this.userTimeRange;
                            }
                            Das2ServerDataSourceEditorPanel.this.recentComboBox1.setText( anExample );
                        }
                                                
                        DefaultComboBoxModel model= new DefaultComboBoxModel( keys.toArray() );
                        Das2ServerDataSourceEditorPanel.this.examplesComboBox.setModel( model );
                        Das2ServerDataSourceEditorPanel.this.examplesComboBox.setEnabled(true);
                    } else {
                        DefaultComboBoxModel model= new DefaultComboBoxModel( new String[] { "NONEFOUND" } );
                        Das2ServerDataSourceEditorPanel.this.examplesComboBox.setModel( model );
                        Das2ServerDataSourceEditorPanel.this.examplesComboBox.setEnabled(false);
                    }
                    Das2ServerDataSourceEditorPanel.this.examplesComboBox.setRenderer( new ListCellRenderer() {
                        @Override
                        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                            JLabel def= new JLabel();
                            if ( value.equals("LABEL") ) {
                                def.setText(String.format( Locale.US, "Example Time Ranges (%d)", theExamples.size() ));
                            } else if ( value.equals("NONEFOUND") ) {
                                def.setText(String.format( Locale.US, "No example time ranges found..." ) );
                            } else {
                                Example e= theExamples.get((String)value);
                                def.setText( "" + e.timeRange + " "+ e.label + "" );
                            }
                            return def;
                        }
                    });
                    if ( example==null ) { // legacy
                        Node exampleRange= (Node) xpath.evaluate( "/stream/properties/@x_range", document, XPathConstants.NODE );
                        if ( exampleRange!=null && curr.equals(DEFAULT_TIMERANGE) ) {
                            Das2ServerDataSourceEditorPanel.this.recentComboBox1.setText( exampleRange.getNodeValue() );
                        }
                        if ( exampleRange!=null ) {
                            try {
                               validTimeRange= DatumRangeUtil.parseTimeRange(exampleRange.getNodeValue());
                            } catch (ParseException ex) {
                               logger.info("example timerange doesn't parse!");
                            }
                        }
                    }
                    
                    String rp= readerParams.get( getDataSetId(url) );
                    readerParamsTextArea.setText( rp==null ? "" : rp );
                    
                    Node validRange= (Node)  xpath.evaluate( "/stream/properties/@validRange", document, XPathConstants.NODE );
                    if ( validRange!=null ) {
                        Das2ServerDataSourceEditorPanel.this.validRangeLabel.setText( "valid range: " + validRange.getNodeValue() );
                    } else {
                        Das2ServerDataSourceEditorPanel.this.validRangeLabel.setText("no valid range for dataset provided");
                    }
                    if ( isTca ) {
                        tcaTextField.setText("60");
                    } else {
                        tcaTextField.setText("");
                    }
                    } catch ( XPathExpressionException ex ) {
                        logger.log(Level.SEVERE, ex.getMessage(), ex);
                    }
                }
            };
            SwingUtilities.invokeLater(run);
        } catch (SAXException ex) {
            JOptionPane.showMessageDialog(examplesComboBox, "Unable to parse dsdf: "+ ex.getMessage() );
            logger.log(Level.SEVERE, ex.getMessage(), ex);
        } catch (ParserConfigurationException | IOException ex) {
            JOptionPane.showMessageDialog(examplesComboBox, "Unable to parse dsdf: "+ ex.getMessage() );
            logger.log(Level.SEVERE, ex.getMessage(), ex);
        } finally {
            try {
                if ( in!=null ) in.close();
            } catch (IOException ex) {
                logger.log(Level.SEVERE, ex.getMessage(), ex);
            }
        }
    }
    /**
     * return the dataset id (Juno/JED/ElectronSpectra) for the tree path.
     * @param p the tree path.
     * @return the dataset id like "http://jupiter.physics.uiowa.edu/das/server?Juno/JED/ElectronSpectra"
     */
    private String getDataSetId( TreePath p ) {
        Object[] oo= p.getPath(); 
        StringBuilder ds= new StringBuilder( String.valueOf( oo[0] ) );
        ds.append("?").append(oo[1]);
        for ( int i=2; i?
     * @param url
     * @return "http://jupiter.physics.uiowa.edu/das/server?Juno/JED/ElectronSpectra"
     */
    private String getDataSetId( URL url ) {
        Map p= URISplit.parseParams(url.getQuery());
        return url.getProtocol()+"://"+url.getHost() + url.getPath() + "?" + p.get("dataset");
    }
    
    private void jTree1ValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_jTree1ValueChanged
        TreePath p= evt.getPath();
        TreePath old= evt.getOldLeadSelectionPath();
        
        TreeModel m= ((JTree)evt.getSource()).getModel();
        if ( old!=null && m.isLeaf(old.getLastPathComponent()) ) {
            String osurl = getDataSetId( old );
            readerParams.put( osurl, readerParamsTextArea.getText() );
        }
        
        this.dsdfContent=null;
        if ( ! m.isLeaf( p.getLastPathComponent() ) ) {
            descriptionLabel.setText( "" );
            this.validRangeLabel.setText("no dataset selected");
            this.viewDsdfButton.setEnabled(false);
        } else {
            this.viewDsdfButton.setEnabled(true);
            this.validRangeLabel.setText("retrieving dataset info...");
            String ds= getDataSetId( p );
            int i= ds.indexOf('?');
            String surl = p.getPath()[0] + "?server=dsdf&dataset=" + ds.substring(i+1);
            try {
                final URL url = new URL(surl);
                RequestProcessor.invokeLater( new Runnable() {
                    @Override
                    public void run() {
                        updateDataSetSelected( url );
                    }
                });
            } catch ( MalformedURLException ex ) {
                logger.log(Level.SEVERE, ex.getMessage(), ex);
                JOptionPane.showConfirmDialog( this, "Internal Error: "+ex.toString() ); // give a message
            }
        }
    }//GEN-LAST:event_jTree1ValueChanged
    private void updateDas2Servers() {
        Runnable runOffEvt= new Runnable() {
            public void run() {
                updateDas2ServersImmediately();
            }
        };
        new Thread(runOffEvt).start();
    }
    private void updateDas2ServersImmediately() {
        d2ss= listDas2Servers();
        Runnable run= new Runnable() {
            @Override
            public void run() {
                das2ServerComboBox.setModel( new DefaultComboBoxModel(d2ss.toArray()) );
            
                if ( serverURL.length()==0 ) {
                    serverURL= d2ss.get(0);
                    RequestProcessor.invokeLater( getDataSetsRunnable() );
                }
                das2ServerComboBox.setSelectedItem(serverURL);
                das2ServerComboBox.setRenderer(myListCellRenderer);
            }
        };
        SwingUtilities.invokeLater(run);
    }
    
    private static final Map icons= Collections.synchronizedMap( new HashMap() );
    
    private static Icon iconFor( Object o, boolean wait ) {
        //return new javax.swing.ImageIcon(Das2ServerDataSourceEditorPanel.class.getResource("/org/autoplot/datasource/fileMag.png"));
        //icons.clear(); // for debugging
        ImageIcon result= icons.get(o.toString());
        if (result==null && wait ) {
            try {
                long t1= System.currentTimeMillis();
                result= new ImageIcon( new URL( "" + o +"?server=logo" ) );
                Image im= result.getImage();
                int h= im.getWidth(null);
                int w= im.getHeight(null);
                int s= 20;
                int h1= Math.min(24,s*w/h);
                BufferedImage bi=  new BufferedImage( s, h1, BufferedImage.TYPE_INT_ARGB);
                Graphics g= bi.createGraphics();
                g.drawImage( im, 0, 0, s, h1, null, null );
                result= new ImageIcon(bi);
                logger.log(Level.FINE, "time to load icon for {0}: {1} ms", new Object[]{ o, System.currentTimeMillis()-t1});
                icons.put( o.toString(), result );
            } catch (MalformedURLException ex) {
                Logger.getLogger(Das2ServerDataSourceEditorPanel.class.getName()).log(Level.SEVERE, null, ex);
            }
        } 
        return result;
    }
    
    private static class IconCellRenderer implements ListCellRenderer {
        DefaultListCellRenderer r= new DefaultListCellRenderer();
        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            Component c= r.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            Icon icon= iconFor( value, false );
            ((DefaultListCellRenderer)c).setIcon(icon);
            return c;
        }
    }
    
    private transient final ListCellRenderer myListCellRenderer= new IconCellRenderer();
    
    private List listPeers( String suri ) {
        String uri= suri+"?server=peers";
        List result= new ArrayList();
        InputStream in=null;
        
        try {
            in = new URL(uri).openStream();
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            InputSource source = new InputSource(in);
            Document initialDocument = builder.parse(source);
            XPathFactory factory = XPathFactory.newInstance();
            XPath xpath = factory.newXPath();
            NodeList urls = (NodeList) xpath.evaluate("//das2server/peers/server/url/text()", initialDocument, XPathConstants.NODESET );
            for ( int i=0; i listDas2Servers() {
        List d2ss1= new ArrayList( );
        d2ss1.add( "https://planet.physics.uiowa.edu/das/das2Server" );
        if ( serverURL.length()==0 ) {
            d2ss1.addAll( listPeers("https://planet.physics.uiowa.edu/das/das2Server") );
        } else {
            d2ss1.addAll( listPeers(serverURL) );
        }
        File home = new File(AutoplotSettings.settings().resolveProperty(AutoplotSettings.PROP_AUTOPLOTDATA));
        File book = new File(home, "bookmarks");
        File hist = new File(book, "history.txt");
        long t0= System.currentTimeMillis();
        logger.log( Level.FINE, "reading recent datasources from {0}", hist.toString());
        if ( hist.exists() ) {
            BufferedReader r=null;
            try {
                String seek="das2server:";
                int ttaglen= 25;
                r = new BufferedReader(new FileReader(hist));
                String s = r.readLine();
                LinkedHashSet dss = new LinkedHashSet();
                while (s != null) {
                    if ( s.length()>ttaglen+15 && s.substring(ttaglen+4,ttaglen+15).equalsIgnoreCase(seek)) {
                        int i= s.indexOf('?');
                        if ( i==-1 ) i= s.length();
                        String key= s.substring(ttaglen+4+seek.length(),i);
                        if ( dss.contains(key) ) dss.remove( key ); // move to the end
                        dss.add( key );
                    }
                    s = r.readLine();
                }
                d2ss1.removeAll(dss);  // remove whatever we have already
                List d2ssDiscoveryList= new ArrayList(dss);
                Collections.reverse( d2ssDiscoveryList );
                d2ssDiscoveryList.addAll(d2ss1);
                d2ss1= d2ssDiscoveryList; // put the most recently used ones at the front of the list
                
                logger.log( Level.FINE, "read extra das2servers in {0} millis\n", (System.currentTimeMillis()-t0) );
            } catch ( IOException ex ) {
                logger.log( Level.FINE, "IOException when reading in {0}", hist );
                JOptionPane.showConfirmDialog(examplesComboBox,"IOException when reading in "+hist );
            } finally {
                try {
                    if ( r!=null ) r.close();
                } catch (IOException ex) {
                    logger.log( Level.SEVERE, ex.getMessage(), ex );
                }
            }
        } else {
            logger.log( Level.FINE, "no history file found: {0}", hist );
        }
        
        final List fd2ss1= d2ss1;
        Runnable run= new Runnable() {
            @Override
            public void run() {
                for ( String s: fd2ss1 ) {
                    Icon i= iconFor( s, true ); // force load of icon off the event thread.
                    logger.log(Level.FINER, "iconHeight={0}", i.getIconHeight());
                }
            };
        };
        new Thread(run,"loadDas2ServerIcons").start();
        
        return d2ss1;
    }
    private String readDsdf( URL url ) throws IOException {
        try ( InputStream in = openConnection(url) ) {
            StringBuilder sb= new StringBuilder();
            int by= in.read();
            while ( by!=-1 ) {
                sb.append( (char)by );
                by= in.read();
            }
            String s= sb.toString();
            int contentLength= Integer.parseInt( s.substring(4,10) );
            String sxml= s.substring(10,10+contentLength);
            return sxml;
        }
    }
    private void showDsdf( URL url ) {
      
        try {
            
            String sxml= readDsdf( url );
            Reader xin = new BufferedReader(new StringReader(sxml));
            DocumentBuilder builder;
            builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            InputSource source = new InputSource(xin);
            Document document = builder.parse(source);
            XPathFactory factory= XPathFactory.newInstance();
            XPath xpath= (XPath) factory.newXPath();
            NodeList o= (NodeList)xpath.evaluate( "/stream/properties/@*", document, XPathConstants.NODESET );
            StringBuilder result= new StringBuilder("");
            for ( int ii=0; ii0 ) {
            try {
                URL url= new URL(String.valueOf(o));
                setServerURL( url.toString() );
            } catch (MalformedURLException ex) {
                if ( !String.valueOf(o).contains(":") ) {
                    JOptionPane.showMessageDialog(this,"Invalid URL (no http):
"+o);
                } else {
                    JOptionPane.showMessageDialog(this,"Invalid URL:
"+o);
                }
            }
        }
    }//GEN-LAST:event_das2ServerComboBoxActionPerformed
    private void discoveryCbActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_discoveryCbActionPerformed
        org.das2.util.LoggerManager.logGuiEvent(evt);
        jTree1.setModel( waitTreeModel() );
        RequestProcessor.invokeLater( getDataSetsRunnable() );
    }//GEN-LAST:event_discoveryCbActionPerformed
    private void examplesComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_examplesComboBoxActionPerformed
        org.das2.util.LoggerManager.logGuiEvent(evt);
        String item= (String) examplesComboBox.getSelectedItem();
        if ( item.startsWith("example") ) {
            logger.log(Level.FINE, "example item: {0}", item);
            Example e= theExamples.get(item);
            if ( e==null ) {
                logger.warning("whoops, where is the label");
            } else {
                recentComboBox1.setText( e.timeRange );
                readerParamsTextArea.setText( e.params );
            }
        }
    }//GEN-LAST:event_examplesComboBoxActionPerformed
    private void timeRangeToolActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_timeRangeToolActionPerformed
        org.das2.util.LoggerManager.logGuiEvent(evt);
        TimeRangeTool tt= new TimeRangeTool();
        //JTextField tf= timeRangeTextField;
        RecentComboBox tf= recentComboBox1;
        tt.setSelectedRange(tf.getText());
        int r= JOptionPane.showConfirmDialog( this, tt, "Select Time Range", JOptionPane.OK_CANCEL_OPTION );
        if ( r==JOptionPane.OK_OPTION) {
            tf.setText(tt.getSelectedRange());
        }
    }//GEN-LAST:event_timeRangeToolActionPerformed
   private void intrinsicCbActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intrinsicCbActionPerformed
      // TODO add your handling code here:
   }//GEN-LAST:event_intrinsicCbActionPerformed
    private void recentComboBox1ItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_recentComboBox1ItemStateChanged
        logger.log(Level.FINEST, "changed {0}", evt.getItem());
    }//GEN-LAST:event_recentComboBox1ItemStateChanged
    private void editParamsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editParamsButtonActionPerformed
        org.das2.util.LoggerManager.logGuiEvent(evt);
        TreePath p= jTree1.getSelectionPath();
        TreeModel m= jTree1.getModel();
        if ( p==null ) {
            JOptionPane.showConfirmDialog( this, "No dataset selected" );
            return;
        }
        if ( m.isLeaf( p.getLastPathComponent() ) ) {
            {
                try {
                    Object[] oo = p.getPath();
                    StringBuilder ds = new StringBuilder( String.valueOf(oo[1]) );
                    for (int i = 2; i < oo.length; i++) {
                        ds.append( "/" ).append( oo[i] );
                    }
                    String surl = oo[0] + "?server=dsdf&dataset=" + ds.toString();
                    final URL url = new URL(surl);
                    RequestProcessor.invokeLater( new Runnable() {
                        @Override
                        public void run() {
                            if ( dsdfContent==null ) {
                                try {
                                    dsdfContent= readDsdf( url );
                                } catch (IOException ex) {
                                    logger.log(Level.SEVERE, null, ex);
                                }
                            }
                            if ( dsdfContent!=null ) {
                                editDsdfContent();
                            }
                            
                        }
                    } );
                    
                } catch ( MalformedURLException ex ) {
                    logger.log(Level.SEVERE, ex.getMessage(), ex);
                    JOptionPane.showConfirmDialog( this, "Internal Error: "+ex.toString() ); // give a message
                }
            }
        }
        
    }//GEN-LAST:event_editParamsButtonActionPerformed
    public void editDsdfContent() {
        Das2ServerGUI x = new Das2ServerGUI();
        x.setSpecification( dsdfContent );
        x.setParameters( readerParamsTextArea.getText() );
        JScrollPane sp= new JScrollPane(x.getPanel());
        sp.setMaximumSize( new Dimension(500,800) );
        sp.setPreferredSize( new Dimension(300,500) );
        sp.getVerticalScrollBar().setUnitIncrement(sp.getFont().getSize());
        int response= WindowManager.showConfirmDialog( this, sp, "Edit reader params", JOptionPane.OK_CANCEL_OPTION );
        if ( response==JOptionPane.OK_OPTION ) {
            readerParamsTextArea.setText( x.getParameters() );
        }
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    public javax.swing.JComboBox das2ServerComboBox;
    public javax.swing.JLabel descriptionLabel;
    public javax.swing.JCheckBox discoveryCb;
    public javax.swing.JButton editParamsButton;
    public javax.swing.JComboBox examplesComboBox;
    public javax.swing.JCheckBox intrinsicCb;
    public javax.swing.JComboBox itemsComboBox;
    public javax.swing.JLabel jLabel1;
    public javax.swing.JLabel jLabel2;
    public javax.swing.JLabel jLabel3;
    public javax.swing.JLabel jLabel4;
    public javax.swing.JLabel jLabel5;
    public javax.swing.JLabel jLabel6;
    public javax.swing.JLabel jLabel7;
    public javax.swing.JScrollPane jScrollPane1;
    public javax.swing.JScrollPane jScrollPane2;
    public javax.swing.JTree jTree1;
    public javax.swing.JTextArea readerParamsTextArea;
    public org.autoplot.datasource.RecentComboBox recentComboBox1;
    public javax.swing.JTextField tcaTextField;
    public javax.swing.JButton timeRangeTool;
    public javax.swing.JLabel validRangeLabel;
    public javax.swing.JButton viewDsdfButton;
    // End of variables declaration//GEN-END:variables
    protected String serverURL = "";
    public static final String PROP_SERVERURL = "serverURL";
    public String getServerURL() {
        return serverURL;
    }
    public void setServerURL(String serverURL) {
        String oldServerURL = this.serverURL;
        this.serverURL = serverURL;
        if ( !this.serverURL.equals(oldServerURL) ) {
            //timeRangeTextField.setText( DEFAULT_TIMERANGE );
            descriptionLabel.setText("");
            jTree1.setModel( waitTreeModel() );
            RequestProcessor.invokeLater( getDataSetsRunnable() );
        }
        firePropertyChange(PROP_SERVERURL, oldServerURL, serverURL);
    }
    protected String dataSetId = null;
    public static final String PROP_DATASETID = "dataSetId";
    public String getDataSetId() {
        return dataSetId;
    }
    public void setDataSetId(String dataSetId) {
        String oldDataSetId = this.dataSetId;
        this.dataSetId = dataSetId;
        firePropertyChange(PROP_DATASETID, oldDataSetId, dataSetId);
    }
    @Override
    public JPanel getPanel() {
        return this;
    }
    @Override
    public boolean reject(String uri) throws Exception {
        URISplit split = URISplit.parse(uri);
        if ( split.file==null || split.file.equals("file:///") ) { // use UIOWA's main one by default.
            split.file= "https://planet.physics.uiowa.edu/das/das2Server";
        }
        String s= split.file;
        if ( s.equals("https://planet.physics.uiowa.edu/das/das2Server") ) {
            return false;
        }
        // there's really no way to tell if it really is a Das2Server on the event thread, so accept all URIs. 
        // and graphically tell them if it is not a valid server.
        return false;
    }
    @Override
    public boolean prepare( String uri, java.awt.Window parent, ProgressMonitor mon) {
        return true;
    }
    @Override
    public void setURI(String uri) {
        URISplit split= URISplit.parse(uri);
        if ( split.resourceUri==null ) {
            serverURL= "";
        } else {
            String uriServerUrl= DataSetURI.fromUri( split.resourceUri );
            if ( uriServerUrl.length()>0 && !uriServerUrl.startsWith("file:/") ) {
                serverURL= uriServerUrl;
            } else {
                serverURL= "";
            }
        }
        Map params= URISplit.parseParams(split.params);
        dataSetId= params.remove("dataset");
        if ( dataSetId==null ) {
            dataSetId= params.remove("arg_0");
        }
        if ( dataSetId!=null && dataSetId.startsWith("/") ) {
            dataSetId= dataSetId.substring(1);
        }
        String startTime= params.remove("start_time");
        String endTime= params.remove("end_time");
        String str= params.remove("timerange");
        if ( str!=null ) {
            userTimeRange= str;
            DatumRange tr;
            try {
                tr = DatumRangeUtil.parseTimeRange( str );
                startTime= tr.min().toString();
                endTime= tr.max().toString();
            } catch (ParseException ex) {
                logger.log(Level.SEVERE, ex.getMessage(), ex);
            }
        }
        if ( startTime!=null && endTime!=null ) {
            try {
                Datum t1= TimeUtil.create( startTime );
                Datum t2= TimeUtil.create( endTime );
                DatumRange dr = new DatumRange( t1, t2 );
                if ( userTimeRange==null ) userTimeRange= dr.toString();
                recentComboBox1.setText(dr.toString());
            } catch ( ParseException ex ) {
                recentComboBox1.setText( DEFAULT_TIMERANGE );
            }
        } else {
            recentComboBox1.setText( DEFAULT_TIMERANGE );
        }
		  
        String intrinsic = params.remove("intrinsic");
        intrinsicCb.setSelected("true".equals(intrinsic));
		  
        String interval= params.remove("interval");
        if ( interval!=null ) {
            tcaTextField.setText(interval);
        }
        String item= params.remove("item");
        if ( item!=null ) {
            itemsComboBox.setSelectedItem(item);
            tcaItem.put( "" + serverURL + "?server=dsdf&dataset=" + dataSetId, item );
        }
        
        otherParams.put( "qubeSubset", params.remove("qubeSubset") );
                
        StringBuilder paramsStr= new StringBuilder();
        for ( Entry e: params.entrySet() ) {
            if ( e.getKey().startsWith("arg_") ) {
                paramsStr.append(e.getValue()).append("\n");
            } else {
                paramsStr.append(e.getKey()).append("=").append(e.getValue()).append("\n");
            }
        }
        readerParamsTextArea.setText(paramsStr.toString());
        String key= serverURL + "?" + dataSetId;
        readerParams.put( key, paramsStr.toString() );
        das2ServerComboBox.setSelectedItem(serverURL);
        
        updateDas2Servers(); // this will set serverUrl to the last used server if nothing is specified.
        
        if ( serverURL.length()>0 ) {
            RequestProcessor.invokeLater( getDataSetsRunnable() );
        }
    }
    TreeModel waitTreeModel() {
        DefaultMutableTreeNode treeNode1 = new DefaultMutableTreeNode("updating, please wait..." );
        return new javax.swing.tree.DefaultTreeModel(treeNode1);
    }
	 
    /**
     * Class to handle custom rendering of the dataset list.  The DefaultTreeCellRenderer
     * is just a JLable derived object.  So those are the functions being used to get the
     * display properly rendered
     */
    private static class DataSetItemRenderer extends DefaultTreeCellRenderer {
        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value,
                boolean bSelected, boolean bExpanded, boolean bIsLeaf, int iRow, boolean bHasFocus) {
            super.getTreeCellRendererComponent(tree, value, bSelected, bExpanded, bIsLeaf, iRow,
                    bHasFocus);
            DefaultMutableTreeNode tn = (DefaultMutableTreeNode) value;
            Object obj = tn.getUserObject();
            if (obj instanceof DasServer.DataSrcListItem) {
                DasServer.DataSrcListItem li = (DasServer.DataSrcListItem) obj;
                if (li.description() == null) {
                    setText(String.format("%s", li.name()));
                } else {
                    setText(String.format("%s  %s", li.name(), li.description()));
                }                 
            }
            return this;
        }
    }
    private void updateTree( TreeModel model ) {
        jTree1.setModel(model);
		jTree1.setCellRenderer(new DataSetItemRenderer());
        if ( dataSetId!=null ) selectDataSetId();
    }
    Runnable getDataSetsRunnable() {
        Runnable run= new Runnable() {
            @Override
            public void run() {
                String ss1= serverURL;
                try {
                    DasServer server= DasServer.create( new URL( ss1 ) );
                    final TreeModel model;
                    if ( discoveryCb.isSelected() ) {
                        model= server.getDataSetListWithDiscovery();
                    } else {
                        model= server.getDataSetList();
                    }
                    updateDas2ServersImmediately();
                    SwingUtilities.invokeLater( new Runnable() {
                        @Override
                        public void run() {
                            updateTree(model);
                        }
                    });
                    
                } catch (DasException ex) {
                    logger.log(Level.SEVERE, ex.getMessage(), ex);
                    javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("Error connecting to " + ss1 + ", \n" + ex );
                    jTree1.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
                } catch (MalformedURLException ex) {
                    javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("Error connecting to " + ss1 + ", \n" + ex );
                    jTree1.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
                    
                }
            }
        };
        return run;
    }
    private void selectDataSetId() {
        String[] ss= dataSetId.split("/");
        TreeNode[] oo= new TreeNode[ss.length+1];
        oo[0]= (TreeNode) jTree1.getModel().getRoot();
        for ( int i=1; i 1) {
            DefaultMutableTreeNode tn = (DefaultMutableTreeNode) tp0[1];
            DasServer.DataSrcListItem li = (DasServer.DataSrcListItem) tn.getUserObject();
            ldataSetId = new StringBuilder(li.name());
            for (int i = 2; i < tp0.length; i++) {
                tn = (DefaultMutableTreeNode) tp0[i];
                li = (DasServer.DataSrcListItem) tn.getUserObject();
                ldataSetId.append("/").append(li.name());
            }
        }
        if ( folder ) ldataSetId.append("/");
        StringBuilder params= new StringBuilder();
        String lreaderParams= readerParamsTextArea.getText();
        String[] ss= lreaderParams.split("\n");
        for ( int i=0; i0 ) result.append("&").append(params.toString());
        
        if ( otherParams.get("qubeSubset")!=null ) {
            result.append("&qubeSubset=").append(otherParams.get("qubeSubset"));
        }
        
        return result.toString();
    }
    @Override
    public void markProblems(List problems) {
    }
    //private boolean expert;
    
    /**
     * call this before prepare.
     * @param expert
     */
    public void setExpertMode( boolean expert ) {
        //this.expert= expert;
        this.discoveryCb.setSelected(!expert);
    }
}