1 17 package org.apache.commons.cli.avalon; 18 20 import java.text.ParseException ; 21 import java.util.Hashtable ; 22 import java.util.Vector ; 23 24 40 public final class CLArgsParser 41 { 42 private static final int INVALID = Integer.MAX_VALUE; 44 45 private static final int STATE_NORMAL = 0; 46 private static final int STATE_REQUIRE_2ARGS = 1; 47 private static final int STATE_REQUIRE_ARG = 2; 48 private static final int STATE_OPTIONAL_ARG = 3; 49 private static final int STATE_NO_OPTIONS = 4; 50 private static final int STATE_OPTION_MODE = 5; 51 52 private static final int TOKEN_SEPARATOR = 0; 54 private static final int TOKEN_STRING = 1; 55 56 private static final char[] ARG_SEPARATORS = 57 new char[]{(char)0, '='}; 58 59 private static final char[] NULL_SEPARATORS = 60 new char[]{(char)0}; 61 62 private final CLOptionDescriptor[] m_optionDescriptors; 63 private final Vector m_options; 64 private Hashtable m_optionIndex; 65 private final ParserControl m_control; 66 67 private String m_errorMessage; 68 private String [] m_unparsedArgs = new String []{}; 69 70 private char m_ch; 72 private String [] m_args; 73 private boolean m_isLong; 74 private int m_argIndex; 75 private int m_stringIndex; 76 private int m_stringLength; 77 78 private int m_lastChar = INVALID; 79 80 private int m_lastOptionId; 81 private CLOption m_option; 82 private int m_state = STATE_NORMAL; 83 84 90 public final String [] getUnparsedArgs() 91 { 92 return m_unparsedArgs; 93 } 94 95 100 public final Vector getArguments() 101 { 102 return m_options; 104 } 105 106 115 public final CLOption getArgumentById( final int id ) 116 { 117 return (CLOption)m_optionIndex.get( new Integer ( id ) ); 118 } 119 120 129 public final CLOption getArgumentByName( final String name ) 130 { 131 return (CLOption)m_optionIndex.get( name ); 132 } 133 134 140 private final CLOptionDescriptor getDescriptorFor( final int id ) 141 { 142 for( int i = 0; i < m_optionDescriptors.length; i++ ) 143 { 144 if( m_optionDescriptors[i].getId() == id ) 145 { 146 return m_optionDescriptors[i]; 147 } 148 } 149 150 return null; 151 } 152 153 159 private final CLOptionDescriptor getDescriptorFor( final String name ) 160 { 161 for( int i = 0; i < m_optionDescriptors.length; i++ ) 162 { 163 if( m_optionDescriptors[i].getName().equals( name ) ) 164 { 165 return m_optionDescriptors[i]; 166 } 167 } 168 169 return null; 170 } 171 172 177 public final String getErrorString() 178 { 179 return m_errorMessage; 181 } 182 183 189 private final int getStateFor( final CLOptionDescriptor descriptor ) 190 { 191 final int flags = descriptor.getFlags(); 192 if( (flags & CLOptionDescriptor.ARGUMENTS_REQUIRED_2) == 193 CLOptionDescriptor.ARGUMENTS_REQUIRED_2 ) 194 { 195 return STATE_REQUIRE_2ARGS; 196 } 197 else if( (flags & CLOptionDescriptor.ARGUMENT_REQUIRED) == 198 CLOptionDescriptor.ARGUMENT_REQUIRED ) 199 { 200 return STATE_REQUIRE_ARG; 201 } 202 else if( (flags & CLOptionDescriptor.ARGUMENT_OPTIONAL) == 203 CLOptionDescriptor.ARGUMENT_OPTIONAL ) 204 { 205 return STATE_OPTIONAL_ARG; 206 } 207 else 208 { 209 return STATE_NORMAL; 210 } 211 } 212 213 221 public CLArgsParser( final String [] args, 222 final CLOptionDescriptor[] optionDescriptors, 223 final ParserControl control ) 224 { 225 m_optionDescriptors = optionDescriptors; 226 m_control = control; 227 m_options = new Vector (); 228 m_args = args; 229 230 try 231 { 232 parse(); 233 checkIncompatibilities( m_options ); 234 buildOptionIndex(); 235 } 236 catch( final ParseException pe ) 237 { 238 m_errorMessage = pe.getMessage(); 239 } 240 241 } 244 245 251 private final void checkIncompatibilities( final Vector arguments ) 252 throws ParseException 253 { 254 final int size = arguments.size(); 255 256 for( int i = 0; i < size; i++ ) 257 { 258 final CLOption option = (CLOption)arguments.elementAt( i ); 259 final int id = option.getDescriptor().getId(); 260 final CLOptionDescriptor descriptor = getDescriptorFor( id ); 261 262 if( null == descriptor ) 265 { 266 continue; 267 } 268 269 final int[] incompatible = descriptor.getIncompatible(); 270 271 checkIncompatible( arguments, incompatible, i ); 272 } 273 } 274 275 private final void checkIncompatible( final Vector arguments, 276 final int[] incompatible, 277 final int original ) 278 throws ParseException 279 { 280 final int size = arguments.size(); 281 282 for( int i = 0; i < size; i++ ) 283 { 284 if( original == i ) 285 { 286 continue; 287 } 288 289 final CLOption option = (CLOption)arguments.elementAt( i ); 290 final int id = option.getDescriptor().getId(); 291 292 for( int j = 0; j < incompatible.length; j++ ) 293 { 294 if( id == incompatible[j] ) 295 { 296 final CLOption originalOption = (CLOption)arguments.elementAt( original ); 297 final int originalId = originalOption.getDescriptor().getId(); 298 299 String message = null; 300 301 if( id == originalId ) 302 { 303 message = 304 "Duplicate options for " + describeDualOption( originalId ) + 305 " found."; 306 } 307 else 308 { 309 message = "Incompatible options -" + 310 describeDualOption( id ) + " and " + 311 describeDualOption( originalId ) + " found."; 312 } 313 throw new ParseException ( message, 0 ); 314 } 315 } 316 } 317 } 318 319 private final String describeDualOption( final int id ) 320 { 321 final CLOptionDescriptor descriptor = getDescriptorFor( id ); 322 if( null == descriptor ) 323 { 324 return "<parameter>"; 325 } 326 else 327 { 328 final StringBuffer sb = new StringBuffer (); 329 boolean hasCharOption = false; 330 331 if( Character.isLetter( (char)id ) ) 332 { 333 sb.append( '-' ); 334 sb.append( (char)id ); 335 hasCharOption = true; 336 } 337 338 final String longOption = descriptor.getName(); 339 if( null != longOption ) 340 { 341 if( hasCharOption ) 342 { 343 sb.append( '/' ); 344 } 345 sb.append( "--" ); 346 sb.append( longOption ); 347 } 348 349 return sb.toString(); 350 } 351 } 352 353 359 public CLArgsParser( final String [] args, 360 final CLOptionDescriptor[] optionDescriptors ) 361 { 362 this( args, optionDescriptors, null ); 363 } 364 365 375 private final String [] subArray( final String [] array, 376 final int index, 377 final int charIndex ) 378 { 379 final int remaining = array.length - index; 380 final String [] result = new String [remaining]; 381 382 if( remaining > 1 ) 383 { 384 System.arraycopy( array, index + 1, result, 1, remaining - 1 ); 385 } 386 387 result[0] = array[index].substring( charIndex - 1 ); 388 389 return result; 390 } 391 392 395 private final void parse() 396 throws ParseException 397 { 398 if( 0 == m_args.length ) 399 { 400 return; 401 } 402 403 m_stringLength = m_args[m_argIndex].length(); 404 405 407 while( true ) 408 { 409 m_ch = peekAtChar(); 410 411 414 if( m_argIndex >= m_args.length ) 415 { 416 break; 417 } 418 419 if( null != m_control && m_control.isFinished( m_lastOptionId ) ) 420 { 421 m_unparsedArgs = subArray( m_args, m_argIndex, m_stringIndex ); 423 return; 424 } 425 426 429 if( STATE_OPTION_MODE == m_state ) 430 { 431 if( 0 == m_ch ) 434 { 435 getChar(); m_state = STATE_NORMAL; 437 } 438 else 439 { 440 parseShortOption(); 441 } 442 } 443 else if( STATE_NORMAL == m_state ) 444 { 445 parseNormal(); 446 } 447 else if( STATE_NO_OPTIONS == m_state ) 448 { 449 addOption( new CLOption( m_args[m_argIndex++] ) ); 451 } 452 else if( STATE_OPTIONAL_ARG == m_state && m_isLong && m_ch != 0) 453 { 454 m_state = STATE_NORMAL; 455 addOption( m_option ); 456 } 457 else 458 { 459 parseArguments(); 460 } 461 } 462 463 if( m_option != null ) 464 { 465 if( STATE_OPTIONAL_ARG == m_state ) 466 { 467 m_options.addElement( m_option ); 468 } 469 else if( STATE_REQUIRE_ARG == m_state ) 470 { 471 final CLOptionDescriptor descriptor = getDescriptorFor( 472 m_option.getDescriptor().getId() ); 473 final String message = 474 "Missing argument to option " + getOptionDescription( descriptor ); 475 throw new ParseException ( message, 0 ); 476 } 477 else if( STATE_REQUIRE_2ARGS == m_state ) 478 { 479 if( 1 == m_option.getArgumentCount() ) 480 { 481 m_option.addArgument( "" ); 482 m_options.addElement( m_option ); 483 } 484 else 485 { 486 final CLOptionDescriptor descriptor = getDescriptorFor( 487 m_option.getDescriptor().getId() ); 488 final String message = 489 "Missing argument to option " + getOptionDescription( descriptor ); 490 throw new ParseException ( message, 0 ); 491 } 492 } 493 else 494 { 495 throw new ParseException ( "IllegalState " + m_state + ": " + m_option, 0 ); 496 } 497 } 498 } 499 500 private final String getOptionDescription( final CLOptionDescriptor descriptor ) 501 { 502 if( m_isLong ) 503 { 504 return "--" + descriptor.getName(); 505 } 506 else 507 { 508 return "-" + (char)descriptor.getId(); 509 } 510 } 511 512 private final char peekAtChar() 513 { 514 if( INVALID == m_lastChar ) 515 { 516 m_lastChar = readChar(); 517 } 518 return (char)m_lastChar; 519 } 520 521 private final char getChar() 522 { 523 if( INVALID != m_lastChar ) 524 { 525 final char result = (char)m_lastChar; 526 m_lastChar = INVALID; 527 return result; 528 } 529 else 530 { 531 return readChar(); 532 } 533 } 534 535 private final char readChar() 536 { 537 if( m_stringIndex >= m_stringLength ) 538 { 539 m_argIndex++; 540 m_stringIndex = 0; 541 542 if( m_argIndex < m_args.length ) 543 { 544 m_stringLength = m_args[m_argIndex].length(); 545 } 546 else 547 { 548 m_stringLength = 0; 549 } 550 551 return 0; 552 } 553 554 if( m_argIndex >= m_args.length ) 555 { 556 return 0; 557 } 558 559 return m_args[m_argIndex].charAt( m_stringIndex++ ); 560 } 561 562 private final Token nextToken( final char[] separators ) 563 { 564 m_ch = getChar(); 565 566 if( isSeparator( m_ch, separators ) ) 567 { 568 m_ch = getChar(); 569 return new Token( TOKEN_SEPARATOR, null ); 570 } 571 572 final StringBuffer sb = new StringBuffer (); 573 574 do 575 { 576 sb.append( m_ch ); 577 m_ch = getChar(); 578 } 579 while( !isSeparator( m_ch, separators ) ); 580 581 return new Token( TOKEN_STRING, sb.toString() ); 582 } 583 584 private final boolean isSeparator( final char ch, final char[] separators ) 585 { 586 for( int i = 0; i < separators.length; i++ ) 587 { 588 if( ch == separators[i] ) 589 { 590 return true; 591 } 592 } 593 594 return false; 595 } 596 597 private final void addOption( final CLOption option ) 598 { 599 m_options.addElement( option ); 600 m_lastOptionId = option.getDescriptor().getId(); 601 m_option = null; 602 } 603 604 private final void parseOption( final CLOptionDescriptor descriptor, 605 final String optionString ) 606 throws ParseException 607 { 608 if( null == descriptor ) 609 { 610 throw new ParseException ( "Unknown option " + optionString, 0 ); 611 } 612 613 m_state = getStateFor( descriptor ); 614 m_option = new CLOption( descriptor ); 615 616 if( STATE_NORMAL == m_state ) 617 { 618 addOption( m_option ); 619 } 620 } 621 622 private final void parseShortOption() 623 throws ParseException 624 { 625 m_ch = getChar(); 626 final CLOptionDescriptor descriptor = getDescriptorFor( m_ch ); 627 m_isLong = false; 628 parseOption( descriptor, "-" + m_ch ); 629 630 if( STATE_NORMAL == m_state ) 631 { 632 m_state = STATE_OPTION_MODE; 633 } 634 } 635 636 private final void parseArguments() 637 throws ParseException 638 { 639 if( STATE_REQUIRE_ARG == m_state ) 640 { 641 if( '=' == m_ch || 0 == m_ch ) 642 { 643 getChar(); 644 } 645 646 final Token token = nextToken( NULL_SEPARATORS ); 647 m_option.addArgument( token.getValue() ); 648 649 addOption( m_option ); 650 m_state = STATE_NORMAL; 651 } 652 else if( STATE_OPTIONAL_ARG == m_state ) 653 { 654 if( '-' == m_ch || 0 == m_ch ) 655 { 656 getChar(); addOption( m_option ); 658 m_state = STATE_NORMAL; 659 return; 660 } 661 662 if( '=' == m_ch ) 663 { 664 getChar(); 665 } 666 667 final Token token = nextToken( NULL_SEPARATORS ); 668 m_option.addArgument( token.getValue() ); 669 670 addOption( m_option ); 671 m_state = STATE_NORMAL; 672 } 673 else if( STATE_REQUIRE_2ARGS == m_state ) 674 { 675 if( 0 == m_option.getArgumentCount() ) 676 { 677 682 if (!m_isLong){ 683 if (0 == peekAtChar()){ 684 getChar(); 685 } 686 } 687 final Token token = nextToken( ARG_SEPARATORS ); 688 689 if( TOKEN_SEPARATOR == token.getType() ) 690 { 691 final CLOptionDescriptor descriptor = getDescriptorFor( 692 m_option.getDescriptor().getId() ); 693 final String message = 694 "Unable to parse first argument for option " + 695 getOptionDescription( descriptor ); 696 throw new ParseException ( message, 0 ); 697 } 698 else 699 { 700 m_option.addArgument( token.getValue() ); 701 } 702 if (0 == m_ch && '-' == peekAtChar()){ 704 m_option.addArgument( "" ); 706 m_options.addElement( m_option ); 707 m_state = STATE_NORMAL; 708 } 709 } 710 else { 712 final StringBuffer sb = new StringBuffer (); 713 714 m_ch = getChar(); 715 while( !isSeparator( m_ch, NULL_SEPARATORS ) ) 716 { 717 sb.append( m_ch ); 718 m_ch = getChar(); 719 } 720 721 final String argument = sb.toString(); 722 723 725 m_option.addArgument( argument ); 726 addOption( m_option ); 727 m_option = null; 728 m_state = STATE_NORMAL; 729 } 730 } 731 } 732 733 736 private final void parseNormal() 737 throws ParseException 738 { 739 if( '-' != m_ch ) 740 { 741 final String argument = nextToken( NULL_SEPARATORS ).getValue(); 743 addOption( new CLOption( argument ) ); 744 m_state = STATE_NORMAL; 745 } 746 else 747 { 748 getChar(); 750 if( 0 == peekAtChar() ) 751 { 752 throw new ParseException ( "Malformed option -", 0 ); 753 } 754 else 755 { 756 m_ch = peekAtChar(); 757 758 if( '-' != m_ch ) 760 { 761 parseShortOption(); 762 } 763 else 764 { 765 getChar(); 769 if( 0 == peekAtChar() ) 770 { 771 getChar(); 772 m_state = STATE_NO_OPTIONS; 773 } 774 else 775 { 776 final String optionName = nextToken( ARG_SEPARATORS ).getValue(); 778 final CLOptionDescriptor descriptor = getDescriptorFor( optionName ); 779 m_isLong = true; 780 parseOption( descriptor, "--" + optionName ); 781 } 782 } 783 } 784 } 785 } 786 787 790 private final void buildOptionIndex() 791 { 792 final int size = m_options.size(); 793 m_optionIndex = new Hashtable ( size * 2 ); 794 795 for( int i = 0; i < size; i++ ) 796 { 797 final CLOption option = (CLOption)m_options.get( i ); 798 final CLOptionDescriptor optionDescriptor = 799 getDescriptorFor( option.getDescriptor().getId() ); 800 801 m_optionIndex.put( new Integer ( option.getDescriptor().getId() ), option ); 802 803 if( null != optionDescriptor && 804 null != optionDescriptor.getName() ) 805 { 806 m_optionIndex.put( optionDescriptor.getName(), option ); 807 } 808 } 809 } 810 } 811 | Popular Tags |