package org.das2.client;
import java.awt.Dimension;
import java.awt.Component;
import java.awt.FlowLayout;
import java.io.IOException;
import javax.swing.JPanel;
import javax.swing.BoxLayout;
import javax.swing.JOptionPane;
import javax.swing.JCheckBox;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JList;
import java.util.LinkedHashMap;
import java.util.ArrayList;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathConstants;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Box;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
/**
* Code for editing a Das2Server parameters argument. This is a somewhat
* arbitary control, but the DSDF may try to describe a GUI, using the
* spec at https://github.com/das-developers/das2docs/wiki/Structured-Sub%E2%80%90values-for-Params
*
*
* Das2ServerGUI x = new Das2ServerGUI();
* String dsdf = "param_01 = '1.5V_REF | Simulate +1.8 monitor'\n"
* + "param_02 = '1.5V_WvFE'\n"
* + "param_03 = '1.5V_Y180'\n"
* + "param_04 = '1.8U | Power Supply'\n"
* + "param_05 = '1.8V_MEM'";
* x.setSpecification(dsdf);
* x.setParameters("1.5V_REF 1.5V_REF 1.8V_MEM");
* if (JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(null, x.panel)) {
* System.err.println(x.getParameters());
* }
*
* @author jbf
*/
public class Das2ServerGUI {
/**
* Read the Das2Server container for DSDF
*
* @param xmlsrc
* @return string array
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
* @throws XPathExpressionException
*/
private static Control[] readXML(String xmlsrc) throws SAXException, IOException, ParserConfigurationException, XPathExpressionException {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
InputSource inputsrc = new InputSource(new StringReader(xmlsrc));
Document doc = builder.parse(inputsrc);
XPath xp = XPathFactory.newInstance().newXPath();
Node nn = (Node) xp.evaluate("//properties", doc, XPathConstants.NODE);
ArrayList ll = new ArrayList(MAX_CONTROLS);
for (int i = 0; i < MAX_CONTROLS; i++) {
ll.add(i, "");
}
NamedNodeMap aa = nn.getAttributes();
for (int i = 0; i < aa.getLength(); i++) {
Node itm = aa.item(i);
if (itm.getNodeName().startsWith("param_")) {
int idx = Integer.parseInt(itm.getNodeName().substring(6));
ll.set(idx, itm.getNodeValue());
}
}
Control[] controls= new Control[MAX_CONTROLS];
for ( int i=0; i ll = new ArrayList<>(MAX_CONTROLS-1);
for (int i = 0; i < MAX_CONTROLS-1; i++) {
ll.add(i, "");
}
for (String line : dsdfsrc.split("\n")) {
if (line.startsWith("param_")) {
int ieq = line.indexOf("=");
int idx = parseInt(line.substring(0, ieq).substring(6));
String vv = line.substring(ieq + 1).trim();
if ((vv.charAt(0) == '\'') && (vv.charAt(vv.length() - 1) == '\'')) {
vv = vv.substring(1, vv.length() - 1);
}
ll.set(idx, vv);
}
}
Control[] controls= new Control[MAX_CONTROLS];
for ( int i=0; i -1) {
tp.setSelected(true);
}
} catch (Exception e) {
continue;
}
}
}
private List getSelectedListItems(Component jcomponent) {
if (jcomponent instanceof JList) {
return ((JList) jcomponent).getSelectedValuesList();
} else {
ArrayList resultList = new ArrayList();
JPanel jList = (JPanel) jcomponent;
for (Component c : jList.getComponents()) {
if ( c instanceof JCheckBox ) {
JCheckBox b = (JCheckBox) c;
if (b.isSelected()) {
resultList.add(b.getText());
}
}
}
return resultList;
}
}
private String getRegex(String t) {
// "Return the regular expression for the template. The first field will be the @ symbol value";
return t.replaceAll("@", "(\\S+)");
}
private static class Control {
private Control(String line) {
this.ll= line;
}
/**
* the line
*/
String ll;
/**
* the type
*/
String tt;
/**
* formats (e.g. --dftlen @)
*/
String ff;
/**
* special arguments for the GUI type, like sep
*/
String ss;
/**
* the component implementing the control
*/
JComponent cc;
}
private static final int MAX_CONTROLS=101;
private Control[] controls;
private JPanel panel;
/**
* set the DSDF specification for the parameters. This can be an XML document
* or a list of IDL name/value pairs.
* @param sss
*/
public void setSpecification(String sss) {
if ( sss.length()<4 ) {
throw new IllegalArgumentException("control string is too short");
}
if ( sss.substring(0, 4).equals("[00]") ){
sss = sss.substring(10);
}
if ( sss.trim().startsWith("<") ) {
try {
controls = readXML(sss);
} catch (SAXException | IOException | ParserConfigurationException | XPathExpressionException ex) {
Logger.getLogger("das2").log(Level.SEVERE, null, ex);
}
} else {
controls = readDsdf(sss);
}
controls[MAX_CONTROLS-1]= new Control(""); // last control is used to capture everything not recognized.
}
public void setParameters(String paramz) {
String delim= "\\s+"; //TODO: delim
String delimRaw=" ";
String[] paramsArr = paramz.split(delim);
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setAlignmentX(0.0f);
boolean addedFirst= false;
boolean extra = false;
for (int i = 0; i < 100; i++) {
Control contrl= controls[i];
String itm = contrl.ll;
if (itm.length() > 0) {
if ( addedFirst ) {
panel.add( Box.createVerticalStrut( panel.getFont().getSize() ) );
}
itm = itm.trim();
String[] ss = itm.split("\\s*\\|\\s*", -2);
int narg = ss.length;
JComponent c2 = null;
JComponent row= null;
String title= narg>1 ? ss[1] : "";
if ( title.contains("\\n") ) {
title= ""+title.replaceAll("\\\\n","
");
}
if (narg == 1) {
JCheckBox c = new JCheckBox(ss[0]);
String vv = findParamValue(paramsArr, ss[0], null);
if (vv.length() > 0) {
c.setSelected(true);
}
contrl.tt = "JCheckBox";
contrl.ss = ss[0];
panel.add(c);
c2 = c;
} else if (narg == 2) {
JCheckBox c = new JCheckBox(ss[0] + ": " + title);
String vv = findParamValue(paramsArr, ss[0], null);
if (vv.length() > 0) {
c.setSelected(true);
}
contrl.tt = "JCheckBox";
contrl.ss = ss[0];
panel.add(c);
c2 = c;
} else if (narg == 3) {
if ( isFlagOrSwitch( ss[2]) ) {
JCheckBox cb= new JCheckBox(title + " (" + ss[2] + ")" );
panel.add( cb );
String vv = findParamValue(paramsArr, ss[2], null);
cb.setSelected(vv.length()>0);
contrl.tt = "JCheckBox";
contrl.ss = ss[2];
c2= cb;
} else {
panel.add( new JLabel(title) );
JTextField c = new JTextField("");
c.setMaximumSize(new Dimension(8000, c.getPreferredSize().height));
String vv = findParamValue(paramsArr, ss[0], null);
contrl.ff = ss[2];
vv = findParamValue(paramsArr, ss[0], ss[2]);
if (vv.length() > 0) {
c.setText(vv);
}
contrl.tt = "JTextField";
panel.add(c);
row = getRow( new JLabel(ss[0]+": "), c );
c2= c;
}
} else if (narg == 4) {
if (ss[3].startsWith("set:")) {
panel.add(new JLabel(title));
String[] sepAllItems = ss[3].substring(4).trim().split("\\s+");
String[] allItems = Arrays.copyOfRange(sepAllItems, 1, sepAllItems.length);
String sep = sepAllItems[0];
contrl.ss = sep;
JPanel panel1 = new JPanel();
panel1.setLayout(new BoxLayout(panel1, BoxLayout.X_AXIS));
panel1.add( new JLabel(ss[0]+ ":" ));
for (String item : allItems) {
JCheckBox b1 = new JCheckBox(item);
b1.setSelected(false);
panel1.add(b1);
}
c2 = panel1;
String itemMatch = String.join("|", allItems);
String regex = String.format("^(%s)(?:%s(%s))*$", itemMatch, sep, itemMatch);
String vv = findParamValue(paramsArr, ss[0], regex);
if (vv.length() > 0) {
String[] items = vv.split(sep);
setSelectedListItems(c2, allItems, items);
}
contrl.tt = "JList";
} else if ( ss[3].startsWith("option:") ) {
panel.add(new JLabel( title ));
String[] options= ss[3].substring(8).trim().split("\\s+");
String[] options1= new String[options.length+1];
options1[0]= "";
System.arraycopy( options, 0, options1, 1, options.length);
options= options1;
JComboBox c = new JComboBox(options);
row = getRow( new JLabel(ss[0]+": "), c );
c2= c;
c.setMaximumSize(new Dimension(8000, c.getPreferredSize().height));
String vv = findParamValue(paramsArr, ss[0], null);
contrl.ff = ss[2];
vv = findParamValue(paramsArr, ss[0], ss[2]);
if (vv.length() > 0) {
c.setSelectedItem(vv);
}
contrl.tt = "JComboBox";
} else {
panel.add( new JLabel(title) );
JTextField c = new JTextField("");
row = getRow( new JLabel(ss[0]+": "), c );
c2= c;
c.setMaximumSize(new Dimension(8000, c.getPreferredSize().height));
String vv = findParamValue(paramsArr, ss[0], null);
contrl.ff = ss[2];
vv = findParamValue(paramsArr, ss[0], ss[2]);
if (vv.length() > 0) {
c.setText(vv);
}
contrl.tt = "JTextField";
}
if ( row==null ) {
panel.add(c2);
} else {
panel.add(row);
}
} else {
extra = true;
}
if (c2 != null) {
if ( row==null ) {
panel.add(c2);
} else {
panel.add(row);
}
contrl.cc = c2;
c2.setAlignmentX(Component.LEFT_ALIGNMENT);
}
addedFirst= true;
}
}
StringBuilder extraArgs= new StringBuilder();
for ( int i=0; i0 ) {
extraArgs.append(paramsArr[i]);
}
if ( i>0 ) extraArgs.append(delimRaw);
}
if ( extra || extraArgs.length()>0 ) {
if ( addedFirst ) {
panel.add( Box.createVerticalStrut( panel.getFont().getSize() ) );
}
panel.add(new JLabel("Text not described in DSDF"));
JTextArea c = new JTextArea();
c.setAlignmentX(Component.LEFT_ALIGNMENT);
c.setText(extraArgs.toString());
c.setRows(4);
panel.add(c);
controls[100].cc = c;
}
}
public String getParameters() {
String delim = " ";
StringBuilder parametersBuilder = new StringBuilder();
for (int i = 0; i < 100; i++) {
if (controls[i].cc == null) {
} else if (controls[i].tt.equals("JCheckBox")) {
if (((JCheckBox) controls[i].cc).isSelected()) {
String txt = controls[i].ss;
parametersBuilder.append(txt);
parametersBuilder.append(delim);
}
} else if (controls[i].tt.equals("JTextField")) {
String s = ((JTextField) controls[i].cc).getText();
if (s.trim().length() > 0) {
s = controls[i].ff.replace("@", s);
parametersBuilder.append(s);
parametersBuilder.append(delim);
}
} else if (controls[i].tt.equals("JList")) {
List selectedItems = getSelectedListItems(controls[i].cc);
String sep = controls[i].ss;
parametersBuilder.append(String.join(sep, selectedItems));
parametersBuilder.append(delim);
} else if (controls[i].tt.equals("JComboBox")) {
String s= String.valueOf(((JComboBox)controls[i].cc).getSelectedItem());
if ( s.trim().length()>0 ) {
s = controls[i].ff.replace("@", s);
parametersBuilder.append(s);
parametersBuilder.append(delim);
}
}
}
if (controls[100].cc != null) {
parametersBuilder.append(delim);
String txt = ((JTextArea) controls[100].cc).getText().replace("\n", delim);
txt = txt.trim();
parametersBuilder.append(txt);
}
return parametersBuilder.toString();
}
/**
* return the panel. See the javadoc for how this is to be called.
* Note this will typically need to be put into a scrollpane.
* @return
*/
public JPanel getPanel() {
return panel;
}
private JComponent getRow(JComponent jLabel, JComponent c) {
JPanel result= new JPanel();
result.setLayout( new BoxLayout(result,BoxLayout.X_AXIS) );
result.add(jLabel);
result.add(c);
result.setAlignmentX(0.0f);
return result;
}
private boolean isFlagOrSwitch(String ss3) {
if (ss3.startsWith("set:")) {
return false;
} else if ( ss3.startsWith("option:") ) {
return false;
} else {
return true;
}
}
}