Link to home
Start Free TrialLog in
Avatar of cathalmchale
cathalmchale

asked on

Text validation

Hi,

Could someone point me in the right direction here please?
What is the best way to validate a text field which is for filenames? Should I just wait for entry confirmation and then validate or is it nice to create a general class which could be added as a listener to all appropriate text fields?
  Also - in Eclipse when you create a new class it validates the filename on-the-fly -> you must enter a valid file/class name which doesnt already exist. Perhaps I would like to implement this - HOW??

Thanks,
Cathal.
SOLUTION
Avatar of zzynx
zzynx
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of cathalmchale
cathalmchale

ASKER

>> By using your own Document for the textfield

Sorry, what??  you mean extend JTextField or something?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
So in insertString() you do your validation (e.g. checking if the file already exists)
According to the validation you "accept" the entered character(s) or not.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Use JFileChooser
I agree with objects that using JFileChooser is the best. That's what it is made for eventually.
Just don't know how thight you're bound to the use of a text field however.
Well its actually a Wizard similiar to the new class wizard in eclipse.  so i dont think the file-chooser would be appropriate
Then I'd use the document approach
> so i dont think the file-chooser would be appropriate

why not?
>> why not?

perhaps it is?  note that selecting an appropriate file name is step 1 of 5 in the wizard - is it easy to extend and personalise the file-chooser?? Any examples close to what i am looking much appreciated
Don't know if it possible with JFileChooser to avoid that the user chooses a filename that already exists...
>>What is the best way to validate a text field which is for filenames?

Why not create a File and then call the appropriate methods on it?
>> Why not create a File and then call the appropriate methods on it?

I have a new file object with the name the user wants to give it.  is the only way to test if its a valid file(name) to create the file and then check if it exists - this seems a little "expensive", also if it is this way should i use the create tempory file method??

>> if it is this way should i use the create tempory file method??
No.
Use createNewFile():
Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name does not yet exist.
>> is the only way to test if its a valid file(name) to create the file and then check if it exists
Forget the previous. You can simply call exists() on the File object to check if it exist already
(See http://javaalmanac.com/egs/java.io/Exists.html)
>> Forget the previous. You can simply call exists() on the File object to check if it exist already

I check for 2 things:
 Problem 1: file already exists
 Problem 2: invalid file identifier (user has typed a shitty name!!)

so its problem 2 thats the problem!! :)  solution?? cheers
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I now have a class
public class FilenameValidation extends PlainDocument

in another class i do
// the above document class
validation = new FilenameValidation(directory, Configuration.SUFFIX_XML, fileExists, invalidName);
validation.addDocumentListener(this);
// set document of the textfield
titlePage.getNameField().setDocument(validation);

so i have added this class as a document listener - thus i inherit the methods
  public void insertUpdate(DocumentEvent e)
  {
  }
  public void removeUpdate(DocumentEvent e)
  {
  }
  public void changedUpdate(DocumentEvent e)
  {
  }

i just want the changes to occur    ->  like calling a super.insertUpdate .. or whatever!  but obviously i'm not extending the class so cant!

Heres what im doing:
i add the FilenameValidation to the text field  -->  they can type whatever they want, but then i wanted the listener to check if file valid using my class FilenameValidation -> if its not valid then it will disable the next and finish buttons
I doubt if you need a DocumentListener.
Shouldn't you just override insertString() in FilenameValidation
insertString() is triggered for every change that occurs in your textfield
>> Shouldn't you just override insertString() in FilenameValidation
I have - here it is:

public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException
  {
    String fileName = this.getText(0, this.getLength());
    if(extension != null)
      fileName = fileName + extension;
   
    // is this a valid file?
    File file = new File(directory, fileName);
    if(file.exists())
    {
      validFile = false;
      problem = fileExists + ": '" + this.getText(0, this.getLength()) + "'";
    }
    else
    {
      try
      {
        file.createNewFile();
        file.delete();
        validFile = true;
        problem = "";
      }
      catch (IOException ex)
      {
************
        validFile = false;
        problem = invalidName + ": '" + this.getText(0, this.getLength()) + "'";
      }
    }
   
    super.insertString(offset, str, attr);
  }

so i thought a listener which could check each time the value of ******** validFile above -> then report the error / enable / diable Next button etc. ??
Why not passing an instance of whatever object you need in the FilenameValidation constructor and then use it:

Some kind of:

catch (IOException ex) {
        theObjectYouPassed.reportException(problem, ....);
}
...which can also perform additional things (like disabling buttons,...)
Have to go offline now.
See you.
thanks ;-)
Why would you override insertString? Surely what you should do is to ask the user to choose a file name, then use it? The 'use it' stage is when you need to validate, which could be done by a button press, say.
>> Why would you override insertString? Surely what you should do is to ask the user to choose a file name, then use it? The 'use it' stage is when you need to validate, which could be done by a button press, say.

I suggested looking at the Eclipse "new class" type wizard - check it out you'll see what im after
I have a further problem!  When the user types a backspace things go wrong.  all i need to know is
if  String s     is a backspace
how do i do :   s.equals(**backspace**)

Cheers ;-)
>>the Eclipse "new class" type wizard - check it out you'll see what im after

Ah - got a problem there - don't have it and no room on my machine to install it.

My point was centred around the fact that the insertString method is incremental. Therefore, unless you post a fully-formed file name into the text field, the method is going to be called every time you enter a single character, which is not appropriate
I didn't see your last before i posted my last - you can probably see the connection ;-)
(The incrementality works in reverse too)
I want it called everytime they enter a character - the instant the filename becomes invalid (already exists / badly formed name) then the next and finish buttons are disabled and the warning displayed
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>  you need to implement all 3 methods  insertUpdate, removeUpdate, and changedUpdate.

so i'm back to using the listener again now!!
>>/ badly formed name)

Wouldn't this, by virtue of the way you intend to do this, be the normal state?
>> so i'm back to using the listener again now!!
Not at all.

>> I have a further problem!  When the user types a backspace things go wrong
Sure your insertString() is triggered then? For me it isn't...
OK i use a validation class which extends PlainDocument, and you can see the insertString method posted above.
Perhaps instead i should just attach a default document to the textfield, then add a document listener and then do this insert string type validation locally??

does this sound more correct?????
Adapt your insertString() code as follows:

Replace

>>    String fileName = this.getText(0, this.getLength());
>>    if(extension != null)
>>        fileName = fileName + extension;

by

        if (s == null || s.length() == 0)
             return;
        StringBuffer t = new StringBuffer(getLength() + s.length());
        t.append(getText(0, index));
        t.append(s);
        t.append(getText(index, getLength() - index));
        String fileName = t.toString();
        if(extension != null)
           fileName = fileName + extension;
Sorry replace "s" by "str" (2nd parameter)
>> Sure your insertString() is triggered then? For me it isn't...

actually no it isnt, thats the problem! so they type an invalid name - warning shown - they hit backspace to make it valid again, but because insertString not called i do not recognise this
This code makes it even work when you copy/paste some characters in the middle of the name you already typed:

e.g.
1) You have:  MyName
2) You copy "File" to the clipboard
3) You put the cursor before the 'N'
4) You paste
5) You become "MyFileName"
>> actually no it isnt, thats the problem!
I see.  (Nevertheless I think the above changes are needed)

Thinking...
I dont fully understand your code - what is the variable "index" ?? and then of course this backspace problem, perhaps i do need to follow the listener idea?
>> what is the variable "index" ??
Oh, another one to replace, that's your offset (the 1st argument)

>> perhaps i do need to follow the listener idea?
Maybe yes indeed.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Typo:
>> public DocumentListener()
should have been
      public MyDocListener()
Yup the document listener was certainly the way to go!! (and then i even had access to the textField.getText() !!!)
thanks EVERYONES help.

1 last question - i add a label (with icon) to display any problems
If no problem then icon and label = "" and null
This makes the overall content pane "jump around" - whats the best way to solve this? just fix size?- inwhich case i just want to fix the height and not width!
How should i do this?

i shall have to deliberate as to how to distribut points,
cheers ;-)
>> This makes the overall content pane "jump around" - whats the best way to solve this?
Chosing such a layout in which you don't have that phenomenon ;°)

e.g. Place the label in the south of a (wide enough) panel having the borderlayout

>>i shall have to deliberate as to how to distribut points
Award all comments that *really* helped you and you're done.
>> Chosing such a layout in which you don't have that phenomenon ;°)

I wish!!
not so easy in this case - the label can either be  16*16 icon and some text, or no icon no text (invisible!)
i could place it north but doesnt help!

JPanel content = new JPanel(new BorderLayout());
      // file already exists / invalid name label
      JPanel labelPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
      fileProblem = new JLabel();

      labelPanel.add(fileProblem);
      content.add(labelPanel, BorderLayout.NORTH);
      // the title page content
      content.add(titlePage, BorderLayout.CENTER);
      //JideSwingUtilities.setOpaqueRecursively(titlePage, false);
      return content;
Don't understand:

If your titlePage which is in the center has the preferred width of let's say 400,
your panel in the north will have 400 pixels available too. (That's how a BorderLayout works)

Then, the label placed in the north (no matter if it has icon/text or not) always has 400 pixels to use.
Then why should there be jumping around?
>> always has 400 pixels to use.
always has 400 pixels available to use.
>> Then why should there be jumping around?

the width aint the problem its the height
And this doesn't help?

fileProblem.setPreferredSize( new Dimension(100, 17) );
fileProblem.setMinimumSize( new Dimension(10, 17) );
fileProblem.setMaximumSize( new Dimension(Integer.MAX_VALUE, 17) );
Maybe you'll have to repeat those lines after each occurrence of

fileProblem.setIcon(...);
fileProblem.setText(...);
yeah for sure it does but a number of times in the past i would have liked to have been able to specify 1 and allow the other to be chosen by the component itself:
i tried this but it didnt work:

JPanel content = new JPanel(new BorderLayout());
      // file already exists / invalid name label
      JPanel labelPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
 {
        public Dimension getMinimumSize()
        {
          return new Dimension(fileProblem.getWidth(), 17);
        }
      };
      fileProblem = new JLabel();
     
      labelPanel.add(fileProblem);
      content.add(labelPanel, BorderLayout.NORTH);
      // the title page content
      content.add(titlePage, BorderLayout.CENTER);
      //JideSwingUtilities.setOpaqueRecursively(titlePage, false);
      return content;
Try

public class MyLabel extends JLabel {

   public Dimension getMinimumSize() {
       return new Dimension(super.getMinimumSize().width, 17);
   }
   public Dimension getMaximumSize() {
       return new Dimension(super.getMaximumSize().width, 17);
   }
   public Dimension getPreferredSize() {
       return new Dimension(super.getPreferredSize().width, 17);
   }

}

Have to go ofline again. Sorry.
Success
actually this works, thanks:

JPanel labelPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
      {
        public Dimension getMinimumSize()
        {
          return new Dimension(fileProblem.getWidth(), 25);
        }
        public Dimension getPreferredSize()
        {
          return getMinimumSize();
        }
      };
Thanks for accepting