package eu.omp.irap.vespa.epntapclient.view; import java.awt.Color; import java.awt.Dimension; import java.beans.PropertyChangeEvent; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Locale; import java.util.Objects; import javax.swing.BoxLayout; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import eu.omp.irap.vespa.epntapclient.votable.controller.VOTableConnection; import eu.omp.irap.vespa.epntapclient.votable.controller.VOTableException.BadRequestException; import eu.omp.irap.vespa.epntapclient.votable.controller.VOTableException.HTTPRequestException; public abstract class ParamField extends JPanel { /** The logger for this class. */ private static final Logger logger = LogManager.getLogger(ParamField.class); private static final String RESOLVER_URL = "http://voparis-registry.obspm.fr/ssodnet/1/autocomplete"; private static final String DATE_FORMAT = "dd/MM/yyyy"; private static final String DATE_REGEX = "(^(((0[1-9]|1[0-9]|2[0-8])[\\/](0[1-9]|1[012]))|((29|30|31)[\\/](0[13578]|1[02]))|((29|30)[\\/](0[4,6,9]|11)))[\\/](19|[2-9][0-9])\\d\\d$)|(^29[\\/]02[\\/](19|[2-9][0-9])(00|04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96)$)"; private static final String MIN_SUFFIX = "min"; private static final String MAX_SUFFIX = "max"; protected static RequestView requestView; protected String paramName; public ParamField(RequestView requestView, String paramName) { super(); this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); String strLabel = paramName.replaceAll("_", " ").trim(); JLabel label = new JLabel(strLabel.substring(0, 1).toUpperCase() + strLabel.substring(1)); label.setPreferredSize(new Dimension(140, 15)); this.add(label); // TODO: Add tooltip text based on rr.table_column.column_description this.requestView = requestView; this.paramName = paramName; } public static class StringField extends ParamField { JTextField field; StringField(RequestView requestView, String paramName) { super(requestView, paramName); field = new JTextField(); addChangeListener(field, e -> onUpdate()); this.add(field); } private void onUpdate() { if ("".equals(field.getText())) { requestView.updateParam(paramName, null); } else { requestView.updateParam(paramName, field.getText()); } } } public static class FloatField extends ParamField { JTextField field; FloatField(RequestView requestView, String paramName) { super(requestView, paramName); field = new JTextField(); addChangeListener(field, e -> onUpdate()); this.add(field); } private void onUpdate() { if ("".equals(field.getText())) { field.setBackground(Color.WHITE); requestView.updateParam(paramName, null); } else { try { requestView.updateParam(paramName, Float.parseFloat(field.getText())); field.setBackground(Color.WHITE); } catch (NumberFormatException e) { field.setBackground(Color.PINK); } } } } public static class DateRangeField extends ParamField { JTextField fieldMin; JTextField fieldMax; DateRangeField(RequestView requestView, String paramName) { super(requestView, paramName); fieldMin = new JTextField(); addChangeListener(fieldMin, e -> onUpdate(fieldMin, MIN_SUFFIX)); this.add(fieldMin); fieldMax = new JTextField(); addChangeListener(fieldMax, e -> onUpdate(fieldMax, MAX_SUFFIX)); this.add(fieldMax); } private void onUpdate(JTextField field, String suffix) { DateFormat df = new SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH); if ("".equals(field.getText())) { field.setBackground(Color.WHITE); requestView.updateParam(paramName + suffix, null); } else if (field.getText().matches(DATE_REGEX)) { try { long date = df.parse(field.getText()).getTime(); date = (Math.round((date / 86400000.0) + 2440587.5)); // to JD requestView.updateParam(paramName + suffix, date); field.setBackground(Color.WHITE); } catch (ParseException e) { field.setBackground(Color.PINK); } // TODO: check if date min < date max } else { field.setBackground(Color.PINK); } } } public static class FloatRangeField extends ParamField { JTextField fieldMin; JTextField fieldMax; FloatRangeField(RequestView requestView, String paramName) { super(requestView, paramName); fieldMin = new JTextField(); addChangeListener(fieldMin, e -> onUpdate(fieldMin, MIN_SUFFIX)); this.add(fieldMin); fieldMax = new JTextField(); addChangeListener(fieldMax, e -> onUpdate(fieldMax, MAX_SUFFIX)); this.add(fieldMax); } private void onUpdate(JTextField field, String suffix) { if ("".equals(field.getText())) { field.setBackground(Color.WHITE); requestView.updateParam(paramName + suffix, null); } else { try { requestView.updateParam(paramName + suffix, Float.parseFloat(field.getText())); field.setBackground(Color.WHITE); } catch (NumberFormatException e) { field.setBackground(Color.PINK); } } } } public static class TargetNameField extends ParamField { JComboBox comboBox; JTextField field; String lastContent; TargetNameField(RequestView requestView, String paramName) { super(requestView, paramName); comboBox = new JComboBox(); comboBox.setEditable(true); field = (JTextField) comboBox.getEditor().getEditorComponent(); addChangeListener(field, e -> onUpdate()); this.add(comboBox); } private String[] getTargetNames(String begining) { StringBuilder resolverResult = null; try { resolverResult = VOTableConnection.sendGet(RESOLVER_URL, "q=\"" + begining + "\""); } catch (HTTPRequestException | BadRequestException e) { logger.fatal("Can not send sersolver query: ", e); } JsonObject root = new JsonParser().parse(resolverResult.toString()).getAsJsonObject(); int count = Integer.parseInt(root.get("count").toString()); String[] targetNames = new String[count]; JsonArray hits = root.getAsJsonArray("hits"); for (int i = 0; i < count; i++) { JsonObject elmt = hits.get(i).getAsJsonObject(); targetNames[i] = elmt.get("name").toString().replace("\"", ""); // TODO: Display "[name] ([type])" on the JComboBox, but only "[name]" on the query. } return targetNames; } private void onUpdate() { String content = field.getText(); if (content.length() >= 2 && !content.equals(lastContent)) { lastContent = content; int nbItems = comboBox.getItemCount(); comboBox.removeAllItems(); for (String s : getTargetNames(content)) { comboBox.addItem(s); } field.setText(content); requestView.updateParam(paramName, content); } } } public static class DataProductTypeField extends ParamField { JTextField field; DataProductTypeField(RequestView requestView, String paramName) { super(requestView, paramName); JTextField field = new JTextField(); // TODO: listbox with enumerated values instead of JTextField addChangeListener(field, e -> onUpdate()); this.add(field); } private void onUpdate() { if ("".equals(field.getText())) { requestView.updateParam(paramName, null); } else { requestView.updateParam(paramName, field.getText()); } } } public static class TargetClassField extends ParamField { JTextField field; TargetClassField(RequestView requestView, String paramName) { super(requestView, paramName); JTextField field = new JTextField(); // TODO: listbox with enumerated values instead of JTextField addChangeListener(field, e -> onUpdate()); this.add(field); } private void onUpdate() { if ("".equals(field.getText())) { requestView.updateParam(paramName, null); } else { requestView.updateParam(paramName, field.getText()); } } } // public class EnumParamField extends ParamField { // EnumParamField() { // super(); // } // } public static void addChangeListener(JTextComponent text, ChangeListener changeListener) { Objects.requireNonNull(text); Objects.requireNonNull(changeListener); DocumentListener dl = new DocumentListener() { private int lastChange = 0, lastNotifiedChange = 0; @Override public void insertUpdate(DocumentEvent e) { changedUpdate(e); } @Override public void removeUpdate(DocumentEvent e) { changedUpdate(e); } @Override public void changedUpdate(DocumentEvent e) { lastChange++; SwingUtilities.invokeLater(() -> { if (lastNotifiedChange != lastChange) { lastNotifiedChange = lastChange; changeListener.stateChanged(new ChangeEvent(text)); } }); } }; text.addPropertyChangeListener("document", (PropertyChangeEvent e) -> { Document d1 = (Document) e.getOldValue(); Document d2 = (Document) e.getNewValue(); if (d1 != null) d1.removeDocumentListener(dl); if (d2 != null) d2.addDocumentListener(dl); dl.changedUpdate(null); }); Document d = text.getDocument(); if (d != null) d.addDocumentListener(dl); } }