001/* 002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 003 * 004 * http://www.izforge.com/izpack/ 005 * http://developer.berlios.de/projects/izpack/ 006 * 007 * Copyright 2002 Elmar Grom 008 * 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 */ 021 022package com.izforge.izpack.panels; 023 024import java.awt.Toolkit; 025import java.awt.event.FocusEvent; 026import java.awt.event.FocusListener; 027import java.awt.event.KeyEvent; 028import java.awt.event.KeyListener; 029import java.util.Map; 030import java.util.StringTokenizer; 031import java.util.Vector; 032 033import javax.swing.JComponent; 034import javax.swing.JLabel; 035import javax.swing.JTextField; 036import javax.swing.event.CaretEvent; 037import javax.swing.event.CaretListener; 038 039import org.apache.regexp.RE; 040 041import com.izforge.izpack.installer.InstallData; 042import com.izforge.izpack.util.VariableSubstitutor; 043 044/*---------------------------------------------------------------------------*/ 045/** 046 * This class assists the user in entering serial numbers. <BR> 047 * <BR> 048 * Serial numbers, license number, CD keys and the like are often lenghty alpha-numerical numbers. 049 * In many cases they are devided into multiple parts by dash or point separators. Entering these in 050 * a single text field can be a frustrating experience for the user. This class provides a way of 051 * presenting the user with an assembly of input fields that are arranged in the same way as the 052 * key, with the separators already in place. Immideate testing for format compliance if performed 053 * ans soon as each field is completed. In addition, the cursor is automatically advanced to make 054 * entering numbers as painless as possible. <br> 055 * <br> 056 * <b>Formatting:</b> 057 * 058 * <ul> 059 * <li><code>N:X:Y </code>- numeric field, accepts digits only 060 * <li><code>H:X:Y </code>- hex field, accepts only hexadecimal digits 061 * <li><code>A:X:Y </code>- alpha field, accepts only letters, no digits 062 * <li><code>AN:X:Y</code>- alpha-numeric field, accepts digits and letters 063 * </ul> 064 * <b>Example:</b> <br> 065 * <br> 066 * <code>"N:4:4 - H:6:6 - AN:3:3 x A:5:5"</code><br> 067 * <br> 068 * This formatting string will produce a serial number field consisting of four separate input 069 * fields. The fisrt input field will accept four numeric digits, the second six hexa-decimal 070 * digits, the third three alpha-numeric digits and the fourth five letters. The first three input 071 * fields will be separated by '-' and the third and fourth by 'x'. The following snapshot was 072 * obtained with this setting: <br> 073 * <br> 074 * <img src="doc-files/RuleInputField-1.gif"/> 075 * 076 * @version 0.0.1 / 10/19/02 077 * @author Elmar Grom 078 */ 079/*---------------------------------------------------------------------------*/ 080public class RuleInputField extends JComponent implements KeyListener, FocusListener, 081 CaretListener, ProcessingClient 082{ 083 084 /** 085 * 086 */ 087 private static final long serialVersionUID = 3832616275124958257L; 088 089 /** 090 * Used to specify the retsult format. This constant specifies to return the contents of all 091 * fields concatenated into one long string, with separation between each component. 092 */ 093 public static final int PLAIN_STRING = 1; 094 095 /** 096 * Used to specify the retsult format. This constant specifies to return the contents of all 097 * fields together with all separators as specified in the field format concatenated into one 098 * long string. In this case the resulting string looks just like the user saw it during data 099 * entry 100 */ 101 public static final int DISPLAY_FORMAT = 2; 102 103 /** 104 * Used to specify the retsult format. This constant specifies to return the contents of all 105 * fields concatenated into one long string, with a special separator string inserted in between 106 * the individual components. 107 */ 108 public static final int SPECIAL_SEPARATOR = 3; 109 110 /** 111 * Used to specify the retsult format. This constant specifies to return the contents of all 112 * fields in a somehow modified way. How the content is modified depends on the operation 113 * performed by a encryption service class. The class must be provided at object instatiation. 114 */ 115 public static final int ENCRYPTED = 4; 116 117 /** Used internally to identify the default setting for the return format. */ 118 private static int DEFAULT = DISPLAY_FORMAT; 119 120 private Vector items = new Vector(); 121 122 /** 123 * This <code>Vector</code> holds a reference to each input field, in the order in which they 124 * appear on the screen. 125 */ 126 private Vector inputFields = new Vector(); 127 128 private boolean hasParams = false; 129 130 private Map validatorParams; 131 132 private RuleTextField activeField; 133 134 private boolean backstep = false; 135 136 private Toolkit toolkit; 137 138 private String separator; 139 140 private int resultFormat = DEFAULT; 141 142 private InstallData idata = null; 143 /** 144 * Holds an instance of the <code>Validator</code> if one was specified and available 145 */ 146 private Validator validationService; 147 148 /** 149 * Holds an instance of the <code>Processor</code> if one was specified and available 150 */ 151 private Processor encryptionService; 152 153 /** 154 * @return true if this instance has any parameters to pass to the Validator instance. 155 */ 156 public boolean hasParams() 157 { 158 return hasParams; 159 } 160 161 /*--------------------------------------------------------------------------*/ 162 /** 163 * Constructs a rule input field. 164 * 165 * @param format a string that specifies the formatting and to a limited degree the behavior of 166 * this field. 167 * @param preset a string that specifies preset values for specific sub-fields. 168 * @param separator a string to be used for separating the contents of individual fields in the 169 * string returned by <code>getText()</code> 170 * @param validator A string that specifies a class to perform validation services. The string 171 * must completely identify the class, so that it can be instantiated. The class must implement 172 * the <code>RuleValidator</code> interface. If an attempt to instantiate this class fails, no 173 * validation will be performed. 174 * @param validatorParams A <code>java.util.Map</code> containing name/ value pairs, which 175 * will be forwarded to the validator. 176 * @param processor A string that specifies a class to perform processing services. The string 177 * must completely identify the class, so that it can be instantiated. The class must implement 178 * the <code>Processor</code> interface. If an attempt to instantiate this class fails, no 179 * processing will be performed. Instead, the text is returned in the default formatting. 180 * @param resultFormat specifies in which format the resulting text should be returned, wehn 181 * <code>getText()</code> is called. The following values are legal:<br> 182 * <ul> 183 * <li>PLAIN_STRING 184 * <li>DISPLAY_FORMAT 185 * <li>SPECIAL_SEPARATOR 186 * <li>ENCRYPTED 187 * </ul> 188 * @param toolkit needed to gain access to <code>beep()</code> 189 */ 190 /*--------------------------------------------------------------------------*/ 191 public RuleInputField(String format, String preset, String separator, String validator, 192 Map validatorParams, String processor, int resultFormat, Toolkit toolkit, 193 InstallData idata) 194 { 195 this(format, preset, separator, validator, processor, resultFormat, toolkit, idata); 196 this.validatorParams = validatorParams; 197 this.hasParams = true; 198 } 199 200 /*--------------------------------------------------------------------------*/ 201 /** 202 * Constructs a rule input field. 203 * 204 * @param format a string that specifies the formatting and to a limited degree the behavior of 205 * this field. 206 * @param preset a string that specifies preset values for specific sub-fields. 207 * @param separator a string to be used for separating the contents of individual fields in the 208 * string returned by <code>getText()</code> 209 * @param validator A string that specifies a class to perform validation services. The string 210 * must completely identify the class, so that it can be instantiated. The class must implement 211 * the <code>RuleValidator</code> interface. If an attempt to instantiate this class fails, no 212 * validation will be performed. 213 * @param processor A string that specifies a class to perform processing services. The string 214 * must completely identify the class, so that it can be instantiated. The class must implement 215 * the <code>Processor</code> interface. If an attempt to instantiate this class fails, no 216 * processing will be performed. Instead, the text is returned in the default formatting. 217 * @param resultFormat specifies in which format the resulting text should be returned, wehn 218 * <code>getText()</code> is called. The following values are legal:<br> 219 * <ul> 220 * <li>PLAIN_STRING 221 * <li>DISPLAY_FORMAT 222 * <li>SPECIAL_SEPARATOR 223 * <li>ENCRYPTED 224 * </ul> 225 * @param toolkit needed to gain access to <code>beep()</code> 226 */ 227 /*--------------------------------------------------------------------------*/ 228 public RuleInputField(String format, String preset, String separator, String validator, 229 String processor, int resultFormat, Toolkit toolkit, InstallData idata) 230 { 231 this.toolkit = toolkit; 232 this.separator = separator; 233 this.resultFormat = resultFormat; 234 this.idata = idata; 235 236 com.izforge.izpack.gui.FlowLayout layout = new com.izforge.izpack.gui.FlowLayout(); 237 layout.setAlignment(com.izforge.izpack.gui.FlowLayout.LEFT); 238 setLayout(layout); 239 240 // ---------------------------------------------------- 241 // attempt to create an instance of the Validator 242 // ---------------------------------------------------- 243 try 244 { 245 validationService = (Validator) Class.forName(validator).newInstance(); 246 } 247 catch (Throwable exception) 248 { 249 validationService = null; 250 } 251 252 // ---------------------------------------------------- 253 // attempt to create an instance of the Processor 254 // ---------------------------------------------------- 255 try 256 { 257 encryptionService = (Processor) Class.forName(processor).newInstance(); 258 } 259 catch (Throwable exception) 260 { 261 encryptionService = null; 262 } 263 264 // ---------------------------------------------------- 265 // create the fields and field separators 266 // ---------------------------------------------------- 267 createItems(format); 268 269 if ((preset != null) && (preset.length() > 0)) 270 { 271 setFields(preset); 272 } 273 274 // ---------------------------------------------------- 275 // set the focus to the first field 276 // ---------------------------------------------------- 277 activeField = (RuleTextField) inputFields.elementAt(0); 278 activeField.grabFocus(); 279 } 280 281 /*--------------------------------------------------------------------------*/ 282 /** 283 * Returns the number of sub-fields composing this <code>RuleInputField</code>. 284 * 285 * @return the number of sub-fields 286 */ 287 /*--------------------------------------------------------------------------*/ 288 public int getNumFields() 289 { 290 return (inputFields.size()); 291 } 292 293 /*--------------------------------------------------------------------------*/ 294 /** 295 * Returns the contents of the field indicated by <code>index</code>. 296 * 297 * @param index the index of the sub-field from which the contents is requested. 298 * 299 * @return the contents of the indicated sub-field. 300 * 301 * @exception IndexOutOfBoundsException if the index is out of bounds. 302 */ 303 /*--------------------------------------------------------------------------*/ 304 public String getFieldContents(int index) throws IndexOutOfBoundsException 305 { 306 if ((index < 0) || (index > (inputFields.size() - 1))) { throw (new IndexOutOfBoundsException()); } 307 308 return (((JTextField) inputFields.elementAt(index)).getText()); 309 } 310 311 /*--------------------------------------------------------------------------*/ 312 /** 313 * Returns the validator parameters, if any. The caller should check for the existence of 314 * validator parameters via the <code>hasParams()</code> method prior to invoking this method. 315 * 316 * @return a java.util.Map containing the validator parameters. 317 */ 318 public Map getValidatorParams() 319 { 320 return validatorParams; 321 } 322 323 /*---------------------------------------------------------------------------*/ 324 /** 325 * Returns the field contents, assembled acording to the encryption and separator rules. 326 * 327 * @return the field contents 328 */ 329 /*--------------------------------------------------------------------------*/ 330 public String getText() 331 { 332 Object item; 333 StringBuffer buffer = new StringBuffer(); 334 int size = inputFields.size(); 335 336 // ---------------------------------------------------- 337 // have the encryption service class perfrom the task 338 // of assembling an output string. If we have no instance 339 // of this class available set the formatting 340 // instruction to the default setting. 341 // ---------------------------------------------------- 342 if (resultFormat == ENCRYPTED) 343 { 344 if (encryptionService != null) 345 { 346 buffer.append(encryptionService.process(this)); 347 } 348 else 349 { 350 resultFormat = DEFAULT; 351 } 352 } 353 354 // ---------------------------------------------------- 355 // concatentate the field contents, with no separators 356 // in between. 357 // ---------------------------------------------------- 358 else if (resultFormat == PLAIN_STRING) 359 { 360 for (int i = 0; i < inputFields.size(); i++) 361 { 362 buffer.append(((JTextField) inputFields.elementAt(i)).getText()); 363 } 364 } 365 366 // ---------------------------------------------------- 367 // concatenate the field contents and setarators, as 368 // specified for the display of the field. 369 // ---------------------------------------------------- 370 else if (resultFormat == DISPLAY_FORMAT) 371 { 372 for (int i = 0; i < items.size(); i++) 373 { 374 item = items.elementAt(i); 375 if (item instanceof JTextField) 376 { 377 buffer.append(((JTextField) item).getText()); 378 } 379 else 380 { 381 buffer.append((String) item); 382 } 383 } 384 } 385 386 // ---------------------------------------------------- 387 // concatenate the field contents and insert the 388 // separator string in between. 389 // ---------------------------------------------------- 390 else if (resultFormat == SPECIAL_SEPARATOR) 391 { 392 for (int i = 0; i < size; i++) 393 { 394 buffer.append(((JTextField) inputFields.elementAt(i)).getText()); 395 396 if (i < (size - 1)) 397 { 398 buffer.append(separator); 399 } 400 } 401 } 402 403 return (buffer.toString()); 404 } 405 406 /*--------------------------------------------------------------------------*/ 407 /** 408 * Creates the items that make up this field. Both separators and input fields are considered 409 * items. The items created are stored in <code>items</code>. In addition, all fields are 410 * stored in <code>inputFields</code>. 411 * 412 * @param format a string that specifies the layout of the input fields and separators. 413 */ 414 /*--------------------------------------------------------------------------*/ 415 /* 416 * $ @design 417 * 418 * I used a simple StringTokenizer to break the format string into individual tokens. The 419 * approach in building up the field is to consider each token a potential definition for an 420 * input field. Therefore I attempt to create an instance of FieldSpec from each token. 421 * FieldSpec analyzes the token and if it does not represent a valid specification for an input 422 * field throws an exception. If I catch an exception, I know the token does not represent a 423 * valid field specification. In this case I treat the token as a separator, even though this 424 * might not be what the user had intended. However, this approach allows me to implement robust 425 * behavior (no exception thrown) even though the user might have made a mistake in the 426 * definition. The mistake should become immediately obvious when testing the code, since a 427 * input field definition would show up as separator between two fields. 428 * -------------------------------------------------------------------------- 429 */ 430 private void createItems(String format) 431 { 432 Object item; 433 JTextField field; 434 String token; 435 FieldSpec spec; 436 StringTokenizer tokenizer = new StringTokenizer(format); 437 438 while (tokenizer.hasMoreTokens()) 439 { 440 token = tokenizer.nextToken(); 441 try 442 { 443 spec = new FieldSpec(token); 444 field = new RuleTextField(spec.getColumns(), spec.getEditLength(), spec.getType(), 445 spec.getUnlimitedEdit(), toolkit); 446 447 // ------------------------------------------------ 448 // if the previous item is also a field, insert a 449 // space as separator 450 // ------------------------------------------------ 451 if (items.size() > 0) 452 { 453 item = items.lastElement(); 454 455 if (item instanceof JTextField) 456 { 457 items.add(" "); 458 } 459 } 460 461 items.add(field); 462 inputFields.add(field); 463 field.addFocusListener(this); 464 field.addKeyListener(this); 465 field.addCaretListener(this); 466 } 467 // -------------------------------------------------- 468 // if we were not successful creating an input field, 469 // the token must be a separator or the definition 470 // has an error. Simply insert it as separator, 471 // when testing the installer the error should become 472 // obvious to the developer. 473 // -------------------------------------------------- 474 catch (Throwable exception) 475 { 476 if (items.size() == 0) 477 { 478 items.add(token); 479 } 480 else 481 { 482 item = items.lastElement(); 483 484 // ---------------------------------------------- 485 // if the previous item is also a separator, 486 // simply concatenate the token with a space 487 // inserted in between, don't add it as new 488 // separator. 489 // ---------------------------------------------- 490 if (item instanceof String) 491 { 492 items.setElementAt(item + " " + token, (items.size() - 1)); 493 } 494 else 495 { 496 items.add(token); 497 } 498 } 499 } 500 } 501 502 // ---------------------------------------------------- 503 // add the fields and separators to the component 504 // ---------------------------------------------------- 505 for (int i = 0; i < items.size(); i++) 506 { 507 item = items.elementAt(i); 508 509 if (item instanceof String) 510 { 511 add(new JLabel((String) item)); 512 } 513 else 514 { 515 add((JTextField) item); 516 } 517 } 518 } 519 520 /*--------------------------------------------------------------------------*/ 521 /** 522 * Sets each field to a pre-defined value. 523 * 524 * @param data a <code>String</code> containing the preset values for each field. The format 525 * of the string is as follows: The content for the individuals fields must be separated by 526 * whitespace. Each data block is preceeded by the index of the field to set (counting starts at 527 * 0) followed by a colon ':'and after that the actual data for the field. 528 */ 529 /*--------------------------------------------------------------------------*/ 530 private void setFields(String data) 531 { 532 StringTokenizer tokenizer = new StringTokenizer(data); 533 String token; 534 String indexString; 535 int index; 536 boolean process = false; 537 String[] vals = null; 538 int i = 0; 539 540 vals = new String[tokenizer.countTokens()]; 541 while (tokenizer.hasMoreTokens()) 542 { 543 token = tokenizer.nextToken(); 544 indexString = token.substring(0, token.indexOf(':')); 545 546 try 547 { 548 index = Integer.parseInt(indexString); 549 if (index < inputFields.size()) 550 { 551 String val = token.substring((token.indexOf(':') + 1), token.length()); 552 String className = ""; 553 if (val.indexOf(":") > -1) 554 { 555 className = val.substring(val.indexOf(":") + 1); 556 val = val.substring(0, val.indexOf(":")); 557 } 558 559 if (!className.equals("") && !process) 560 { 561 process = true; 562 } 563 VariableSubstitutor vs = new VariableSubstitutor(idata.getVariables()); 564 val = vs.substitute(val, null); 565 vals[i] = val; 566 i++; 567 ((JTextField) inputFields.elementAt(index)).setText(val); 568 } 569 } 570 catch (Throwable exception) 571 { 572 exception.printStackTrace(); 573 } 574 } 575 576 if (process) 577 { 578 tokenizer = new StringTokenizer(data); 579 while (tokenizer.hasMoreTokens()) 580 { 581 token = tokenizer.nextToken(); 582 indexString = token.substring(0, token.indexOf(':')); 583 584 try 585 { 586 index = Integer.parseInt(indexString); 587 if (index < inputFields.size()) 588 { 589 String val = token.substring((token.indexOf(':') + 1), token.length()); 590 String className = ""; 591 String presult = ""; 592 if (val.indexOf(":") > -1) 593 { 594 className = val.substring(val.indexOf(":") + 1); 595 val = val.substring(0, val.indexOf(":")); 596 } 597 598 if (!className.equals("")) 599 { 600 Processor p = (Processor) Class.forName(className).newInstance(); 601 presult = p.process(this); 602 } 603 String[] td = new RE("\\*").split(presult); 604 ((JTextField) inputFields.elementAt(index)).setText(td[index]); 605 } 606 } 607 catch (Throwable exception) 608 { 609 ; 610 } 611 } 612 } 613 } 614 615 /*--------------------------------------------------------------------------*/ 616 /** 617 * This method validates the field content. Validating is performed through a user supplied 618 * service class that provides the validation rules. 619 * 620 * @return <code>true</code> if the validation passes or no implementation of a validation 621 * rule exists. Otherwise <code>false</code> is returned. 622 */ 623 /*--------------------------------------------------------------------------*/ 624 public boolean validateContents() 625 { 626 if (validationService != null) 627 { 628 return (validationService.validate(this)); 629 } 630 else 631 { 632 return (true); 633 } 634 } 635 636 /*---------------------------------------------------------------------------* 637 Implementation for KeyListener 638 *---------------------------------------------------------------------------*/ 639 640 /*--------------------------------------------------------------------------*/ 641 /** 642 * This method is invoked when a key has been typed. The event occurs when a key press is 643 * followed by a key release. 644 * 645 * @param event the key event forwarded by the system. 646 */ 647 /*--------------------------------------------------------------------------*/ 648 public void keyTyped(KeyEvent event) 649 { 650 } 651 652 /*--------------------------------------------------------------------------*/ 653 /** 654 * This method is invoked when a key has been pressed. This method verifies the condition of the 655 * input field in focus. Once the column count in the field has reached the specified maximum, 656 * the rule specified for the field in question is invoked. In case the test result is positive, 657 * focus is set to the next field. If hte test result is negative, the field content is marked 658 * and the caret set to the start of the field. 659 * 660 * @param event the key event forwarded by the system. 661 */ 662 /*--------------------------------------------------------------------------*/ 663 public void keyPressed(KeyEvent event) 664 { 665 if (event.getKeyCode() == KeyEvent.VK_BACK_SPACE) 666 { 667 int caretPosition = activeField.getCaretPosition(); 668 669 if (caretPosition == 0) 670 { 671 int activeIndex = inputFields.indexOf(activeField); 672 673 if (activeIndex > 0) 674 { 675 activeIndex--; 676 backstep = true; 677 activeField = (RuleTextField) inputFields.elementAt(activeIndex); 678 activeField.grabFocus(); 679 } 680 } 681 } 682 } 683 684 /*--------------------------------------------------------------------------*/ 685 /** 686 * This method is invoked when a key has been released. 687 * 688 * @param event the key event forwarded by the system. 689 */ 690 /*--------------------------------------------------------------------------*/ 691 public void keyReleased(KeyEvent event) 692 { 693 } 694 695 /*---------------------------------------------------------------------------* 696 Implementation for FocusListener 697 *---------------------------------------------------------------------------*/ 698 699 /*--------------------------------------------------------------------------*/ 700 /** 701 * Invoked when a component gains the keyboard focus. 702 * 703 * @param event the focus event forwardes by the sytem. 704 */ 705 /*--------------------------------------------------------------------------*/ 706 /* 707 * $ @design <- keep this tag in place and don't write on this line! 708 * 709 * Enter design related documentation here. 710 * -------------------------------------------------------------------------- 711 */ 712 public void focusGained(FocusEvent event) 713 { 714 activeField = (RuleTextField) event.getSource(); 715 716 if (backstep) 717 { 718 activeField.setCaretPosition(activeField.getText().length()); 719 backstep = false; 720 } 721 else 722 { 723 activeField.selectAll(); 724 } 725 } 726 727 /*--------------------------------------------------------------------------*/ 728 /** 729 * Invoked when a component loses the keyboard focus. This method does nothing, we are only 730 * interested in 'focus gained' events. 731 * 732 * @param event the focus event forwardes by the sytem. 733 */ 734 /*--------------------------------------------------------------------------*/ 735 public void focusLost(FocusEvent event) 736 { 737 } 738 739 /*---------------------------------------------------------------------------* 740 Implementation for CaretListener 741 *---------------------------------------------------------------------------*/ 742 743 /*--------------------------------------------------------------------------*/ 744 /** 745 * Called when the caret position is updated. 746 * 747 * @param event the caret event received from the text field 748 */ 749 /*--------------------------------------------------------------------------*/ 750 public void caretUpdate(CaretEvent event) 751 { 752 String text = activeField.getText(); 753 int fieldSize = activeField.getEditLength(); 754 int caretPosition = activeField.getCaretPosition(); 755 int selection = activeField.getSelectionEnd() - activeField.getSelectionStart(); 756 757 if ((!inputFields.lastElement().equals(activeField)) && (!activeField.unlimitedEdit())) 758 { 759 if ((text.length() == fieldSize) && (selection == 0) && (caretPosition == fieldSize) 760 && !backstep) 761 { 762 activeField.transferFocus(); 763 } 764 } 765 } 766 767 // ---------------------------------------------------------------------------- 768 // 769 // ---------------------------------------------------------------------------- 770 private static class FieldSpec 771 { 772 773 private int MIN_TOKENS = 2; 774 775 private int MAX_TOKENS = 3; 776 777 private int type; 778 779 private int columns; 780 781 private int editLength; 782 783 private boolean unlimitedEdit = false; 784 785 public FieldSpec(String spec) throws Exception 786 { 787 StringTokenizer tokenizer = new StringTokenizer(spec, ":"); 788 789 if ((tokenizer.countTokens() >= MIN_TOKENS) && (tokenizer.countTokens() <= MAX_TOKENS)) 790 { 791 String token = tokenizer.nextToken().toUpperCase(); 792 // ------------------------------------------------ 793 // test the first token for a valid type identifier 794 // if it's valid assign the token to the type. 795 // ------------------------------------------------ 796 if (token.equals("N")) 797 { 798 type = RuleTextField.N; 799 } 800 else if (token.equals("H")) 801 { 802 type = RuleTextField.H; 803 } 804 else if (token.equals("A")) 805 { 806 type = RuleTextField.A; 807 } 808 else if (token.equals("O")) 809 { 810 type = RuleTextField.O; 811 } 812 else if (token.equals("AN")) 813 { 814 type = RuleTextField.AN; 815 } 816 else 817 { 818 throw (new Exception()); 819 } 820 821 // ------------------------------------------------ 822 // test for a valid integer to define the size 823 // of the field and assing to columns. 824 // ------------------------------------------------ 825 try 826 { 827 token = tokenizer.nextToken(); 828 columns = Integer.parseInt(token); 829 } 830 catch (Throwable exception) 831 { 832 throw (new Exception()); 833 } 834 835 // ------------------------------------------------ 836 // test for a valid integer to define the edit 837 // length and assign to editLength. If this fails 838 // test for identifier for unlimited edit length. 839 // If this works, set unlimitedEdit to true. 840 // ------------------------------------------------ 841 try 842 { 843 token = tokenizer.nextToken().toUpperCase(); 844 editLength = Integer.parseInt(token); 845 } 846 catch (Throwable exception) 847 { 848 if (token.equals("U")) 849 { 850 unlimitedEdit = true; 851 } 852 else 853 { 854 throw (new Exception()); 855 } 856 } 857 858 } 859 else 860 { 861 throw (new Exception()); 862 } 863 } 864 865 public int getColumns() 866 { 867 return (columns); 868 } 869 870 public int getEditLength() 871 { 872 return (editLength); 873 } 874 875 public int getType() 876 { 877 return (type); 878 } 879 880 public boolean getUnlimitedEdit() 881 { 882 return (unlimitedEdit); 883 } 884 885 } 886 // ---------------------------------------------------------------------------- 887 888} 889/*---------------------------------------------------------------------------*/