001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.cli; 019 020import java.io.Serializable; 021import java.util.ArrayList; 022import java.util.List; 023 024/** 025 * Describes a single command-line option. It maintains 026 * information regarding the short-name of the option, the long-name, 027 * if any exists, a flag indicating if an argument is required for 028 * this option, and a self-documenting description of the option. 029 * <p> 030 * An Option is not created independently, but is created through 031 * an instance of {@link Options}. An Option is required to have 032 * at least a short or a long-name. 033 * <p> 034 * <b>Note:</b> once an {@link Option} has been added to an instance 035 * of {@link Options}, it's required flag may not be changed anymore. 036 * 037 * @see org.apache.commons.cli.Options 038 * @see org.apache.commons.cli.CommandLine 039 * 040 * @version $Id: Option.java 1677406 2015-05-03 14:27:31Z britter $ 041 */ 042public class Option implements Cloneable, Serializable 043{ 044 /** constant that specifies the number of argument values has not been specified */ 045 public static final int UNINITIALIZED = -1; 046 047 /** constant that specifies the number of argument values is infinite */ 048 public static final int UNLIMITED_VALUES = -2; 049 050 /** The serial version UID. */ 051 private static final long serialVersionUID = 1L; 052 053 /** the name of the option */ 054 private final String opt; 055 056 /** the long representation of the option */ 057 private String longOpt; 058 059 /** the name of the argument for this option */ 060 private String argName; 061 062 /** description of the option */ 063 private String description; 064 065 /** specifies whether this option is required to be present */ 066 private boolean required; 067 068 /** specifies whether the argument value of this Option is optional */ 069 private boolean optionalArg; 070 071 /** the number of argument values this option can have */ 072 private int numberOfArgs = UNINITIALIZED; 073 074 /** the type of this Option */ 075 private Class<?> type = String.class; 076 077 /** the list of argument values **/ 078 private List<String> values = new ArrayList<String>(); 079 080 /** the character that is the value separator */ 081 private char valuesep; 082 083 /** 084 * Private constructor used by the nested Builder class. 085 * 086 * @param builder builder used to create this option 087 */ 088 private Option(final Builder builder) 089 { 090 this.argName = builder.argName; 091 this.description = builder.description; 092 this.longOpt = builder.longOpt; 093 this.numberOfArgs = builder.numberOfArgs; 094 this.opt = builder.opt; 095 this.optionalArg = builder.optionalArg; 096 this.required = builder.required; 097 this.type = builder.type; 098 this.valuesep = builder.valuesep; 099 } 100 101 /** 102 * Creates an Option using the specified parameters. 103 * The option does not take an argument. 104 * 105 * @param opt short representation of the option 106 * @param description describes the function of the option 107 * 108 * @throws IllegalArgumentException if there are any non valid 109 * Option characters in <code>opt</code>. 110 */ 111 public Option(String opt, String description) throws IllegalArgumentException 112 { 113 this(opt, null, false, description); 114 } 115 116 /** 117 * Creates an Option using the specified parameters. 118 * 119 * @param opt short representation of the option 120 * @param hasArg specifies whether the Option takes an argument or not 121 * @param description describes the function of the option 122 * 123 * @throws IllegalArgumentException if there are any non valid 124 * Option characters in <code>opt</code>. 125 */ 126 public Option(String opt, boolean hasArg, String description) throws IllegalArgumentException 127 { 128 this(opt, null, hasArg, description); 129 } 130 131 /** 132 * Creates an Option using the specified parameters. 133 * 134 * @param opt short representation of the option 135 * @param longOpt the long representation of the option 136 * @param hasArg specifies whether the Option takes an argument or not 137 * @param description describes the function of the option 138 * 139 * @throws IllegalArgumentException if there are any non valid 140 * Option characters in <code>opt</code>. 141 */ 142 public Option(String opt, String longOpt, boolean hasArg, String description) 143 throws IllegalArgumentException 144 { 145 // ensure that the option is valid 146 OptionValidator.validateOption(opt); 147 148 this.opt = opt; 149 this.longOpt = longOpt; 150 151 // if hasArg is set then the number of arguments is 1 152 if (hasArg) 153 { 154 this.numberOfArgs = 1; 155 } 156 157 this.description = description; 158 } 159 160 /** 161 * Returns the id of this Option. This is only set when the 162 * Option shortOpt is a single character. This is used for switch 163 * statements. 164 * 165 * @return the id of this Option 166 */ 167 public int getId() 168 { 169 return getKey().charAt(0); 170 } 171 172 /** 173 * Returns the 'unique' Option identifier. 174 * 175 * @return the 'unique' Option identifier 176 */ 177 String getKey() 178 { 179 // if 'opt' is null, then it is a 'long' option 180 return (opt == null) ? longOpt : opt; 181 } 182 183 /** 184 * Retrieve the name of this Option. 185 * 186 * It is this String which can be used with 187 * {@link CommandLine#hasOption(String opt)} and 188 * {@link CommandLine#getOptionValue(String opt)} to check 189 * for existence and argument. 190 * 191 * @return The name of this option 192 */ 193 public String getOpt() 194 { 195 return opt; 196 } 197 198 /** 199 * Retrieve the type of this Option. 200 * 201 * @return The type of this option 202 */ 203 public Object getType() 204 { 205 return type; 206 } 207 208 /** 209 * Sets the type of this Option. 210 * <p> 211 * <b>Note:</b> this method is kept for binary compatibility and the 212 * input type is supposed to be a {@link Class} object. 213 * 214 * @param type the type of this Option 215 * @deprecated since 1.3, use {@link #setType(Class)} instead 216 */ 217 @Deprecated 218 public void setType(Object type) 219 { 220 setType((Class<?>) type); 221 } 222 223 /** 224 * Sets the type of this Option. 225 * 226 * @param type the type of this Option 227 * @since 1.3 228 */ 229 public void setType(Class<?> type) 230 { 231 this.type = type; 232 } 233 234 /** 235 * Retrieve the long name of this Option. 236 * 237 * @return Long name of this option, or null, if there is no long name 238 */ 239 public String getLongOpt() 240 { 241 return longOpt; 242 } 243 244 /** 245 * Sets the long name of this Option. 246 * 247 * @param longOpt the long name of this Option 248 */ 249 public void setLongOpt(String longOpt) 250 { 251 this.longOpt = longOpt; 252 } 253 254 /** 255 * Sets whether this Option can have an optional argument. 256 * 257 * @param optionalArg specifies whether the Option can have 258 * an optional argument. 259 */ 260 public void setOptionalArg(boolean optionalArg) 261 { 262 this.optionalArg = optionalArg; 263 } 264 265 /** 266 * @return whether this Option can have an optional argument 267 */ 268 public boolean hasOptionalArg() 269 { 270 return optionalArg; 271 } 272 273 /** 274 * Query to see if this Option has a long name 275 * 276 * @return boolean flag indicating existence of a long name 277 */ 278 public boolean hasLongOpt() 279 { 280 return longOpt != null; 281 } 282 283 /** 284 * Query to see if this Option requires an argument 285 * 286 * @return boolean flag indicating if an argument is required 287 */ 288 public boolean hasArg() 289 { 290 return numberOfArgs > 0 || numberOfArgs == UNLIMITED_VALUES; 291 } 292 293 /** 294 * Retrieve the self-documenting description of this Option 295 * 296 * @return The string description of this option 297 */ 298 public String getDescription() 299 { 300 return description; 301 } 302 303 /** 304 * Sets the self-documenting description of this Option 305 * 306 * @param description The description of this option 307 * @since 1.1 308 */ 309 public void setDescription(String description) 310 { 311 this.description = description; 312 } 313 314 /** 315 * Query to see if this Option is mandatory 316 * 317 * @return boolean flag indicating whether this Option is mandatory 318 */ 319 public boolean isRequired() 320 { 321 return required; 322 } 323 324 /** 325 * Sets whether this Option is mandatory. 326 * 327 * @param required specifies whether this Option is mandatory 328 */ 329 public void setRequired(boolean required) 330 { 331 this.required = required; 332 } 333 334 /** 335 * Sets the display name for the argument value. 336 * 337 * @param argName the display name for the argument value. 338 */ 339 public void setArgName(String argName) 340 { 341 this.argName = argName; 342 } 343 344 /** 345 * Gets the display name for the argument value. 346 * 347 * @return the display name for the argument value. 348 */ 349 public String getArgName() 350 { 351 return argName; 352 } 353 354 /** 355 * Returns whether the display name for the argument value has been set. 356 * 357 * @return if the display name for the argument value has been set. 358 */ 359 public boolean hasArgName() 360 { 361 return argName != null && argName.length() > 0; 362 } 363 364 /** 365 * Query to see if this Option can take many values. 366 * 367 * @return boolean flag indicating if multiple values are allowed 368 */ 369 public boolean hasArgs() 370 { 371 return numberOfArgs > 1 || numberOfArgs == UNLIMITED_VALUES; 372 } 373 374 /** 375 * Sets the number of argument values this Option can take. 376 * 377 * @param num the number of argument values 378 */ 379 public void setArgs(int num) 380 { 381 this.numberOfArgs = num; 382 } 383 384 /** 385 * Sets the value separator. For example if the argument value 386 * was a Java property, the value separator would be '='. 387 * 388 * @param sep The value separator. 389 */ 390 public void setValueSeparator(char sep) 391 { 392 this.valuesep = sep; 393 } 394 395 /** 396 * Returns the value separator character. 397 * 398 * @return the value separator character. 399 */ 400 public char getValueSeparator() 401 { 402 return valuesep; 403 } 404 405 /** 406 * Return whether this Option has specified a value separator. 407 * 408 * @return whether this Option has specified a value separator. 409 * @since 1.1 410 */ 411 public boolean hasValueSeparator() 412 { 413 return valuesep > 0; 414 } 415 416 /** 417 * Returns the number of argument values this Option can take. 418 * 419 * @return num the number of argument values 420 */ 421 public int getArgs() 422 { 423 return numberOfArgs; 424 } 425 426 /** 427 * Adds the specified value to this Option. 428 * 429 * @param value is a/the value of this Option 430 */ 431 void addValueForProcessing(String value) 432 { 433 if (numberOfArgs == UNINITIALIZED) 434 { 435 throw new RuntimeException("NO_ARGS_ALLOWED"); 436 } 437 processValue(value); 438 } 439 440 /** 441 * Processes the value. If this Option has a value separator 442 * the value will have to be parsed into individual tokens. When 443 * n-1 tokens have been processed and there are more value separators 444 * in the value, parsing is ceased and the remaining characters are 445 * added as a single token. 446 * 447 * @param value The String to be processed. 448 * 449 * @since 1.0.1 450 */ 451 private void processValue(String value) 452 { 453 // this Option has a separator character 454 if (hasValueSeparator()) 455 { 456 // get the separator character 457 char sep = getValueSeparator(); 458 459 // store the index for the value separator 460 int index = value.indexOf(sep); 461 462 // while there are more value separators 463 while (index != -1) 464 { 465 // next value to be added 466 if (values.size() == numberOfArgs - 1) 467 { 468 break; 469 } 470 471 // store 472 add(value.substring(0, index)); 473 474 // parse 475 value = value.substring(index + 1); 476 477 // get new index 478 index = value.indexOf(sep); 479 } 480 } 481 482 // store the actual value or the last value that has been parsed 483 add(value); 484 } 485 486 /** 487 * Add the value to this Option. If the number of arguments 488 * is greater than zero and there is enough space in the list then 489 * add the value. Otherwise, throw a runtime exception. 490 * 491 * @param value The value to be added to this Option 492 * 493 * @since 1.0.1 494 */ 495 private void add(String value) 496 { 497 if (!acceptsArg()) 498 { 499 throw new RuntimeException("Cannot add value, list full."); 500 } 501 502 // store value 503 values.add(value); 504 } 505 506 /** 507 * Returns the specified value of this Option or 508 * <code>null</code> if there is no value. 509 * 510 * @return the value/first value of this Option or 511 * <code>null</code> if there is no value. 512 */ 513 public String getValue() 514 { 515 return hasNoValues() ? null : values.get(0); 516 } 517 518 /** 519 * Returns the specified value of this Option or 520 * <code>null</code> if there is no value. 521 * 522 * @param index The index of the value to be returned. 523 * 524 * @return the specified value of this Option or 525 * <code>null</code> if there is no value. 526 * 527 * @throws IndexOutOfBoundsException if index is less than 1 528 * or greater than the number of the values for this Option. 529 */ 530 public String getValue(int index) throws IndexOutOfBoundsException 531 { 532 return hasNoValues() ? null : values.get(index); 533 } 534 535 /** 536 * Returns the value/first value of this Option or the 537 * <code>defaultValue</code> if there is no value. 538 * 539 * @param defaultValue The value to be returned if there 540 * is no value. 541 * 542 * @return the value/first value of this Option or the 543 * <code>defaultValue</code> if there are no values. 544 */ 545 public String getValue(String defaultValue) 546 { 547 String value = getValue(); 548 549 return (value != null) ? value : defaultValue; 550 } 551 552 /** 553 * Return the values of this Option as a String array 554 * or null if there are no values 555 * 556 * @return the values of this Option as a String array 557 * or null if there are no values 558 */ 559 public String[] getValues() 560 { 561 return hasNoValues() ? null : values.toArray(new String[values.size()]); 562 } 563 564 /** 565 * @return the values of this Option as a List 566 * or null if there are no values 567 */ 568 public List<String> getValuesList() 569 { 570 return values; 571 } 572 573 /** 574 * Dump state, suitable for debugging. 575 * 576 * @return Stringified form of this object 577 */ 578 @Override 579 public String toString() 580 { 581 StringBuilder buf = new StringBuilder().append("[ option: "); 582 583 buf.append(opt); 584 585 if (longOpt != null) 586 { 587 buf.append(" ").append(longOpt); 588 } 589 590 buf.append(" "); 591 592 if (hasArgs()) 593 { 594 buf.append("[ARG...]"); 595 } 596 else if (hasArg()) 597 { 598 buf.append(" [ARG]"); 599 } 600 601 buf.append(" :: ").append(description); 602 603 if (type != null) 604 { 605 buf.append(" :: ").append(type); 606 } 607 608 buf.append(" ]"); 609 610 return buf.toString(); 611 } 612 613 /** 614 * Returns whether this Option has any values. 615 * 616 * @return whether this Option has any values. 617 */ 618 private boolean hasNoValues() 619 { 620 return values.isEmpty(); 621 } 622 623 @Override 624 public boolean equals(Object o) 625 { 626 if (this == o) 627 { 628 return true; 629 } 630 if (o == null || getClass() != o.getClass()) 631 { 632 return false; 633 } 634 635 Option option = (Option) o; 636 637 638 if (opt != null ? !opt.equals(option.opt) : option.opt != null) 639 { 640 return false; 641 } 642 if (longOpt != null ? !longOpt.equals(option.longOpt) : option.longOpt != null) 643 { 644 return false; 645 } 646 647 return true; 648 } 649 650 @Override 651 public int hashCode() 652 { 653 int result; 654 result = opt != null ? opt.hashCode() : 0; 655 result = 31 * result + (longOpt != null ? longOpt.hashCode() : 0); 656 return result; 657 } 658 659 /** 660 * A rather odd clone method - due to incorrect code in 1.0 it is public 661 * and in 1.1 rather than throwing a CloneNotSupportedException it throws 662 * a RuntimeException so as to maintain backwards compat at the API level. 663 * 664 * After calling this method, it is very likely you will want to call 665 * clearValues(). 666 * 667 * @return a clone of this Option instance 668 * @throws RuntimeException if a {@link CloneNotSupportedException} has been thrown 669 * by {@code super.clone()} 670 */ 671 @Override 672 public Object clone() 673 { 674 try 675 { 676 Option option = (Option) super.clone(); 677 option.values = new ArrayList<String>(values); 678 return option; 679 } 680 catch (CloneNotSupportedException cnse) 681 { 682 throw new RuntimeException("A CloneNotSupportedException was thrown: " + cnse.getMessage()); 683 } 684 } 685 686 /** 687 * Clear the Option values. After a parse is complete, these are left with 688 * data in them and they need clearing if another parse is done. 689 * 690 * See: <a href="https://issues.apache.org/jira/browse/CLI-71">CLI-71</a> 691 */ 692 void clearValues() 693 { 694 values.clear(); 695 } 696 697 /** 698 * This method is not intended to be used. It was a piece of internal 699 * API that was made public in 1.0. It currently throws an UnsupportedOperationException. 700 * 701 * @param value the value to add 702 * @return always throws an {@link UnsupportedOperationException} 703 * @throws UnsupportedOperationException always 704 * @deprecated 705 */ 706 @Deprecated 707 public boolean addValue(String value) 708 { 709 throw new UnsupportedOperationException("The addValue method is not intended for client use. " 710 + "Subclasses should use the addValueForProcessing method instead. "); 711 } 712 713 /** 714 * Tells if the option can accept more arguments. 715 * 716 * @return false if the maximum number of arguments is reached 717 * @since 1.3 718 */ 719 boolean acceptsArg() 720 { 721 return (hasArg() || hasArgs() || hasOptionalArg()) && (numberOfArgs <= 0 || values.size() < numberOfArgs); 722 } 723 724 /** 725 * Tells if the option requires more arguments to be valid. 726 * 727 * @return false if the option doesn't require more arguments 728 * @since 1.3 729 */ 730 boolean requiresArg() 731 { 732 if (optionalArg) 733 { 734 return false; 735 } 736 if (numberOfArgs == UNLIMITED_VALUES) 737 { 738 return values.isEmpty(); 739 } 740 return acceptsArg(); 741 } 742 743 /** 744 * Returns a {@link Builder} to create an {@link Option} using descriptive 745 * methods. 746 * 747 * @return a new {@link Builder} instance 748 * @since 1.3 749 */ 750 public static Builder builder() 751 { 752 return builder(null); 753 } 754 755 /** 756 * Returns a {@link Builder} to create an {@link Option} using descriptive 757 * methods. 758 * 759 * @param opt short representation of the option 760 * @return a new {@link Builder} instance 761 * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt} 762 * @since 1.3 763 */ 764 public static Builder builder(final String opt) 765 { 766 return new Builder(opt); 767 } 768 769 /** 770 * A nested builder class to create <code>Option</code> instances 771 * using descriptive methods. 772 * <p> 773 * Example usage: 774 * <pre> 775 * Option option = Option.builder("a") 776 * .required(true) 777 * .longOpt("arg-name") 778 * .build(); 779 * </pre> 780 * 781 * @since 1.3 782 */ 783 public static final class Builder 784 { 785 /** the name of the option */ 786 private final String opt; 787 788 /** description of the option */ 789 private String description; 790 791 /** the long representation of the option */ 792 private String longOpt; 793 794 /** the name of the argument for this option */ 795 private String argName; 796 797 /** specifies whether this option is required to be present */ 798 private boolean required; 799 800 /** specifies whether the argument value of this Option is optional */ 801 private boolean optionalArg; 802 803 /** the number of argument values this option can have */ 804 private int numberOfArgs = UNINITIALIZED; 805 806 /** the type of this Option */ 807 private Class<?> type = String.class; 808 809 /** the character that is the value separator */ 810 private char valuesep; 811 812 /** 813 * Constructs a new <code>Builder</code> with the minimum 814 * required parameters for an <code>Option</code> instance. 815 * 816 * @param opt short representation of the option 817 * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt} 818 */ 819 private Builder(final String opt) throws IllegalArgumentException 820 { 821 OptionValidator.validateOption(opt); 822 this.opt = opt; 823 } 824 825 /** 826 * Sets the display name for the argument value. 827 * 828 * @param argName the display name for the argument value. 829 * @return this builder, to allow method chaining 830 */ 831 public Builder argName(final String argName) 832 { 833 this.argName = argName; 834 return this; 835 } 836 837 /** 838 * Sets the description for this option. 839 * 840 * @param description the description of the option. 841 * @return this builder, to allow method chaining 842 */ 843 public Builder desc(final String description) 844 { 845 this.description = description; 846 return this; 847 } 848 849 /** 850 * Sets the long name of the Option. 851 * 852 * @param longOpt the long name of the Option 853 * @return this builder, to allow method chaining 854 */ 855 public Builder longOpt(final String longOpt) 856 { 857 this.longOpt = longOpt; 858 return this; 859 } 860 861 /** 862 * Sets the number of argument values the Option can take. 863 * 864 * @param numberOfArgs the number of argument values 865 * @return this builder, to allow method chaining 866 */ 867 public Builder numberOfArgs(final int numberOfArgs) 868 { 869 this.numberOfArgs = numberOfArgs; 870 return this; 871 } 872 873 /** 874 * Sets whether the Option can have an optional argument. 875 * 876 * @param isOptional specifies whether the Option can have 877 * an optional argument. 878 * @return this builder, to allow method chaining 879 */ 880 public Builder optionalArg(final boolean isOptional) 881 { 882 this.optionalArg = isOptional; 883 return this; 884 } 885 886 /** 887 * Marks this Option as required. 888 * 889 * @return this builder, to allow method chaining 890 */ 891 public Builder required() 892 { 893 return required(true); 894 } 895 896 /** 897 * Sets whether the Option is mandatory. 898 * 899 * @param required specifies whether the Option is mandatory 900 * @return this builder, to allow method chaining 901 */ 902 public Builder required(final boolean required) 903 { 904 this.required = required; 905 return this; 906 } 907 908 /** 909 * Sets the type of the Option. 910 * 911 * @param type the type of the Option 912 * @return this builder, to allow method chaining 913 */ 914 public Builder type(final Class<?> type) 915 { 916 this.type = type; 917 return this; 918 } 919 920 /** 921 * The Option will use '=' as a means to separate argument value. 922 * 923 * @return this builder, to allow method chaining 924 */ 925 public Builder valueSeparator() 926 { 927 return valueSeparator('='); 928 } 929 930 /** 931 * The Option will use <code>sep</code> as a means to 932 * separate argument values. 933 * <p> 934 * <b>Example:</b> 935 * <pre> 936 * Option opt = Option.builder("D").hasArgs() 937 * .valueSeparator('=') 938 * .build(); 939 * Options options = new Options(); 940 * options.addOption(opt); 941 * String[] args = {"-Dkey=value"}; 942 * CommandLineParser parser = new DefaultParser(); 943 * CommandLine line = parser.parse(options, args); 944 * String propertyName = line.getOptionValues("D")[0]; // will be "key" 945 * String propertyValue = line.getOptionValues("D")[1]; // will be "value" 946 * </pre> 947 * 948 * @param sep The value separator. 949 * @return this builder, to allow method chaining 950 */ 951 public Builder valueSeparator(final char sep) 952 { 953 valuesep = sep; 954 return this; 955 } 956 957 /** 958 * Indicates that the Option will require an argument. 959 * 960 * @return this builder, to allow method chaining 961 */ 962 public Builder hasArg() 963 { 964 return hasArg(true); 965 } 966 967 /** 968 * Indicates if the Option has an argument or not. 969 * 970 * @param hasArg specifies whether the Option takes an argument or not 971 * @return this builder, to allow method chaining 972 */ 973 public Builder hasArg(final boolean hasArg) 974 { 975 // set to UNINITIALIZED when no arg is specified to be compatible with OptionBuilder 976 numberOfArgs = hasArg ? 1 : Option.UNINITIALIZED; 977 return this; 978 } 979 980 /** 981 * Indicates that the Option can have unlimited argument values. 982 * 983 * @return this builder, to allow method chaining 984 */ 985 public Builder hasArgs() 986 { 987 numberOfArgs = Option.UNLIMITED_VALUES; 988 return this; 989 } 990 991 /** 992 * Constructs an Option with the values declared by this {@link Builder}. 993 * 994 * @return the new {@link Option} 995 * @throws IllegalArgumentException if neither {@code opt} or {@code longOpt} has been set 996 */ 997 public Option build() 998 { 999 if (opt == null && longOpt == null) 1000 { 1001 throw new IllegalArgumentException("Either opt or longOpt must be specified"); 1002 } 1003 return new Option(this); 1004 } 1005 } 1006}