package validationdemo;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.Document;
import org.netbeans.validation.api.Problem;
import org.netbeans.validation.api.Problems;
import org.netbeans.validation.api.Severity;
import org.netbeans.validation.api.Validator;
import org.netbeans.validation.api.ui.ValidationPanel;
import org.netbeans.validation.api.builtin.Validators;
import org.netbeans.validation.api.ui.ValidationListener;

public class Main {

  public static void xmain(String[] args) throws Exception {
    //Set the system look and feel
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

    final JFrame jf = new JFrame("Validators Demo");
    jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    //Here we create our Validation Panel.  It has a built-in
    //ValidationGroup we can use - we will just call
    //pnl.getValidationGroup() and add validators to it tied to
    //components
    final ValidationPanel pnl = new ValidationPanel();
    jf.setContentPane(pnl);

    //A panel to hold most of our components that we will be
    //validating
    JPanel inner = new JPanel();
    inner.setLayout(new BoxLayout(inner, BoxLayout.Y_AXIS));
    inner.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    pnl.setInnerComponent(inner);

    JLabel lbl = new JLabel("IP Address or Host Name");
    inner.add (lbl);
    JTextField field = new JTextField ("127.0.0.1");
    field.setName ("Address");
    inner.add (field);
    Validator<Document> dd = Validators.forDocument(true, Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.HOST_NAME_OR_IP_ADDRESS);
    pnl.getValidationGroup().add (field, dd);

    //Now let's add some dialog buttons we want to control.  If there is
    //a fatal error, the OK button should be disabled
    final JButton okButton = new JButton("OK");
    okButton.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
        System.exit(0);
      }
    });

    JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
    inner.add(buttonPanel);

    buttonPanel.add(okButton);

    //Add a cancel button that's always enabled
    JButton cancelButton = new JButton("Cancel");
    buttonPanel.add(cancelButton);
    cancelButton.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
        System.exit(1);
      }
    });

    pnl.addChangeListener(new ChangeListener() {

      public void stateChanged(ChangeEvent e) {
        Problem p = pnl.getProblem();
        boolean enable = p == null ? true : p.severity() != Severity.FATAL;

        okButton.setEnabled(enable);
        jf.setDefaultCloseOperation(!enable ?
          WindowConstants.DO_NOTHING_ON_CLOSE :
          WindowConstants.EXIT_ON_CLOSE);
      }
    });
    jf.getRootPane().setDefaultButton(okButton);

    jf.pack();
    jf.setVisible(true);
  }

  public static void main(String[] args) throws Exception {
    //Set the system look and feel
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    
    final JFrame jf = new JFrame("Validators Demo");
    jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    //Here we create our Validation Panel.  It has a built-in
    //ValidationGroup we can use - we will just call
    //pnl.getValidationGroup() and add validators to it tied to
    //components
    final ValidationPanel pnl = new ValidationPanel();
    jf.setContentPane(pnl);

    //A panel to hold most of our components that we will be
    //validating
    JPanel inner = new JPanel();
    inner.setLayout(new BoxLayout(inner, BoxLayout.Y_AXIS));
    inner.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    pnl.setInnerComponent(inner);
    JLabel lbl;
    JTextField field;

    //Okay, here's our first thing to validate
    lbl = new JLabel("Not a java keyword:");
    inner.add(lbl);
    field = new JTextField("listener");
    field.setName("Non Identifier");
    inner.add(field);

    //So, we'll get a validator that works against a Document, which does
    //trim strings (that's the true argument), which will not like
    //empty strings or java keywords
    Validator<Document> d = Validators.forDocument(true,
            Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.REQUIRE_JAVA_IDENTIFIER);

    //Now we add it to the validation group
    pnl.getValidationGroup().add(field, d);

    //This one is similar to the example above, but it will split the string
    //into component parts divided by '.' characters first
    lbl = new JLabel("Legal java package name:");
    inner.add(lbl);
    field = new JTextField("com.foo.bar.baz");
    field.setName("package name");
    inner.add(field);
    Validator<Document> ddd = Validators.forDocument(true,
            Validators.REQUIRE_NON_EMPTY_STRING, Validators.JAVA_PACKAGE_NAME);
    pnl.getValidationGroup().add (field, ddd);

    lbl = new JLabel("IP Address or Host Name");
    inner.add (lbl);
    field = new JTextField ("127.0.0.1");
    field.setName ("Address");
    inner.add (field);
    Validator<Document> dd = Validators.forDocument(false,
            Validators.HOST_NAME_OR_IP_ADDRESS);
    pnl.getValidationGroup().add (field, dd);

    lbl = new JLabel("Must be a non-negative integer");
    inner.add(lbl);
    field = new JTextField("42");
    field.setName("the number");
    inner.add(field);

    //Note that we're very picky here - require non-negative number and
    //require valid number don't care that we want an Integer - we also
    //need to use require valid integer
    pnl.getValidationGroup().add(field,
            Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.REQUIRE_VALID_NUMBER,
            Validators.REQUIRE_VALID_INTEGER,
            Validators.REQUIRE_NON_NEGATIVE_NUMBER);

    lbl = new JLabel("Email address");
    inner.add(lbl);
    field = new JTextField("Foo Bar <foo@bar.com>");
    field.setName("Email address");
    inner.add(field);

    //Note that we're very picky here - require non-negative number and
    //require valid number don't care that we want an Integer - we also
    //need to use require valid integer
    pnl.getValidationGroup().add(field,
            Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.EMAIL_ADDRESS);

    lbl = new JLabel("Hexadecimal number ");
    inner.add(lbl);
    field = new JTextField("CAFEBABE");
    field.setName("hex number");
    inner.add(field);

    pnl.getValidationGroup().add(field,
            Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.VALID_HEXADECIMAL_NUMBER);

    lbl = new JLabel("No spaces: ");
    field = new JTextField("ThisTextHasNoSpaces");
    field.setName("No spaces");
    pnl.getValidationGroup().add(field,
            Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.NO_WHITESPACE);
    inner.add(lbl);
    inner.add(field);

    lbl = new JLabel("Enter a URL");
    field = new JTextField("http://netbeans.org/");
    field.setName("url");
    pnl.getValidationGroup().add(field, Validators.URL_MUST_BE_VALID);
    inner.add(lbl);
    inner.add(field);

    lbl = new JLabel("file that exists");
    //Find a random file so we can populate the field with a valid initial
    //value, if possible
    File userdir = new File(System.getProperty("user.dir"));
    File aFile = null;
    for (File f : userdir.listFiles()) {
      if (f.isFile()) {
        aFile = f;
        break;
      }
    }
    field = new JTextField(aFile == null ? "" : aFile.getAbsolutePath());

    //Note there is an alternative to field.setName() if we are using that
    //for some other purpose
    field.putClientProperty(ValidationListener.CLIENT_PROP_NAME, "File");
    pnl.getValidationGroup().add(field,
            Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.FILE_MUST_BE_FILE);
    inner.add(lbl);
    inner.add(field);

    lbl = new JLabel("Folder that exists");
    field = new JTextField(System.getProperty("user.dir"));
    field.setName("folder");
    pnl.getValidationGroup().add(field,
            Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.FILE_MUST_BE_DIRECTORY);
    inner.add(lbl);
    inner.add(field);

    lbl = new JLabel("Valid file name");
    field = new JTextField("Validators.java");
    field.setName("File Name");

    //Here we're requiring a valid file name
    //(no file or path separator chars)
    pnl.getValidationGroup().add(field,
            Validators.REQUIRE_NON_EMPTY_STRING,
            Validators.REQUIRE_VALID_FILENAME);
    inner.add(lbl);
    inner.add(field);

    //Here we will do custom validation of a JColorChooser

    final JColorChooser chooser = new JColorChooser();
    //Use an intermediary panel to keep the layout from jumping when
    //the problem is shown/hidden
    JPanel ccPanel = new JPanel();
    ccPanel.add (chooser);
    //Add it to the main panel because GridLayout will make it too small
    //ValidationPanel panel uses BorderLayout (and will throw an exception
    //if you try to change it)
    pnl.add(ccPanel, BorderLayout.EAST);

    //Set a default value that won't show an error
    chooser.setColor(new Color(191, 86, 86));

    //ColorValidator is defined below
    final ColorValidator colorValidator = new ColorValidator();

    //Note if we could also implement Validator directly on this class;
    //however it's more reusable if we don't
    class ColorListener extends ValidationListener implements ChangeListener {

      @Override
      protected boolean validate(Problems problems) {
        return colorValidator.validate(problems, null,
                chooser.getColor());
      }

      public void stateChanged(ChangeEvent ce) {
        validate();
      }
    }
    ColorListener cl = new ColorListener();
    chooser.getSelectionModel().addChangeListener(cl);

    //Add our custom validation code to the validation group
    pnl.getValidationGroup().add(cl);

    //Now let's add some dialog buttons we want to control.  If there is
    //a fatal error, the OK button should be disabled
    final JButton okButton = new JButton("OK");
    okButton.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
        System.exit(0);
      }
    });

    JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
    inner.add(buttonPanel);

    buttonPanel.add(okButton);

    //Add a cancel button that's always enabled
    JButton cancelButton = new JButton("Cancel");
    buttonPanel.add(cancelButton);
    cancelButton.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
        System.exit(1);
      }
    });

    pnl.addChangeListener(new ChangeListener() {

      public void stateChanged(ChangeEvent e) {
        Problem p = pnl.getProblem();
        boolean enable = p == null ? true : p.severity() != Severity.FATAL;

        okButton.setEnabled(enable);
        jf.setDefaultCloseOperation(!enable ?
          WindowConstants.DO_NOTHING_ON_CLOSE :
          WindowConstants.EXIT_ON_CLOSE);
      }
    });
    jf.getRootPane().setDefaultButton(okButton);

    jf.pack();
    jf.setVisible(true);
  }

  private static final class ColorValidator implements Validator<Color> {

    public boolean validate(Problems problems, String compName, Color model) {
      //Convert the color to Hue/Saturation/Brightness
      //scaled from 0F to 1.0F
      float[] hsb = Color.RGBtoHSB(model.getRed(), model.getGreen(),
              model.getBlue(), null);
      boolean result = true;
      if (hsb[2] < 0.25) {
        //Dark colors cause a fatal error
        problems.add("Color is too dark");
        result = false;
      }
      if (hsb[1] > 0.8) {
        //highly saturated colors get a warning
        problems.add("Color is very saturated", Severity.WARNING);
        result = false;
      }
      if (hsb[2] > 0.9) {
        //Very bright colors get an information message
        problems.add("Color is very bright", Severity.INFO);
        result = false;
      }
      return result;
    }
  }
}
