001/* 002 * Copyright 2008-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2008-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.util.args; 037 038 039 040import java.io.BufferedReader; 041import java.io.File; 042import java.io.FileInputStream; 043import java.io.IOException; 044import java.io.InputStream; 045import java.io.InputStreamReader; 046import java.security.GeneralSecurityException; 047import java.util.ArrayList; 048import java.util.Collections; 049import java.util.Iterator; 050import java.util.List; 051 052import com.unboundid.ldap.sdk.unboundidds.tools.ToolUtils; 053import com.unboundid.util.ByteStringBuffer; 054import com.unboundid.util.Mutable; 055import com.unboundid.util.NotNull; 056import com.unboundid.util.Nullable; 057import com.unboundid.util.ObjectPair; 058import com.unboundid.util.StaticUtils; 059import com.unboundid.util.ThreadSafety; 060import com.unboundid.util.ThreadSafetyLevel; 061 062import static com.unboundid.util.args.ArgsMessages.*; 063 064 065 066/** 067 * This class defines an argument that is intended to hold values which refer to 068 * files on the local filesystem. File arguments must take values, and it is 069 * possible to restrict the values to files that exist, or whose parent exists. 070 */ 071@Mutable() 072@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 073public final class FileArgument 074 extends Argument 075{ 076 /** 077 * A pre-allocated list of potential passphrases that may be used when trying 078 * to read from an encrypted file. The passphrase is not expected to be 079 * correct, but this list is used to ensure that the LDAP SDK does not 080 * unexpectedly prompt for an encryption passphrase if a file is encrypted and 081 * the key is not found in a Ping Identity server's encryption settings 082 * database. 083 */ 084 @NotNull private static final List<char[]> 085 POTENTIAL_PASSPHRASES_TO_AVOID_PROMPTING = 086 Collections.singletonList(new char[0]); 087 088 089 090 /** 091 * The serial version UID for this serializable class. 092 */ 093 private static final long serialVersionUID = 741228505566416489L; 094 095 096 097 // Indicates whether values must represent files that exist. 098 private final boolean fileMustExist; 099 100 // Indicates whether the provided value must be a directory if it exists. 101 private final boolean mustBeDirectory; 102 103 // Indicates whether the provided value must be a regular file if it exists. 104 private final boolean mustBeFile; 105 106 // Indicates whether values must represent files with parent directories that 107 // exist. 108 private final boolean parentMustExist; 109 110 // The set of values assigned to this argument. 111 @NotNull private final ArrayList<File> values; 112 113 // The path to the directory that will serve as the base directory for 114 // relative paths. 115 @Nullable private File relativeBaseDirectory; 116 117 // The argument value validators that have been registered for this argument. 118 @NotNull private final List<ArgumentValueValidator> validators; 119 120 // The list of default values for this argument. 121 @Nullable private final List<File> defaultValues; 122 123 124 125 /** 126 * Creates a new file argument with the provided information. It will not 127 * be required, will permit at most one occurrence, will use a default 128 * placeholder, will not have any default values, and will not impose any 129 * constraints on the kinds of values it can have. 130 * 131 * @param shortIdentifier The short identifier for this argument. It may 132 * not be {@code null} if the long identifier is 133 * {@code null}. 134 * @param longIdentifier The long identifier for this argument. It may 135 * not be {@code null} if the short identifier is 136 * {@code null}. 137 * @param description A human-readable description for this argument. 138 * It must not be {@code null}. 139 * 140 * @throws ArgumentException If there is a problem with the definition of 141 * this argument. 142 */ 143 public FileArgument(@Nullable final Character shortIdentifier, 144 @Nullable final String longIdentifier, 145 @NotNull final String description) 146 throws ArgumentException 147 { 148 this(shortIdentifier, longIdentifier, false, 1, null, description); 149 } 150 151 152 153 /** 154 * Creates a new file argument with the provided information. There will not 155 * be any default values or constraints on the kinds of values it can have. 156 * 157 * @param shortIdentifier The short identifier for this argument. It may 158 * not be {@code null} if the long identifier is 159 * {@code null}. 160 * @param longIdentifier The long identifier for this argument. It may 161 * not be {@code null} if the short identifier is 162 * {@code null}. 163 * @param isRequired Indicates whether this argument is required to 164 * be provided. 165 * @param maxOccurrences The maximum number of times this argument may be 166 * provided on the command line. A value less than 167 * or equal to zero indicates that it may be present 168 * any number of times. 169 * @param valuePlaceholder A placeholder to display in usage information to 170 * indicate that a value must be provided. It may 171 * be {@code null} if a default placeholder should 172 * be used. 173 * @param description A human-readable description for this argument. 174 * It must not be {@code null}. 175 * 176 * @throws ArgumentException If there is a problem with the definition of 177 * this argument. 178 */ 179 public FileArgument(@Nullable final Character shortIdentifier, 180 @Nullable final String longIdentifier, 181 final boolean isRequired, final int maxOccurrences, 182 @Nullable final String valuePlaceholder, 183 @NotNull final String description) 184 throws ArgumentException 185 { 186 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 187 valuePlaceholder, description, false, false, false, false, null); 188 } 189 190 191 192 /** 193 * Creates a new file argument with the provided information. It will not 194 * have any default values. 195 * 196 * @param shortIdentifier The short identifier for this argument. It may 197 * not be {@code null} if the long identifier is 198 * {@code null}. 199 * @param longIdentifier The long identifier for this argument. It may 200 * not be {@code null} if the short identifier is 201 * {@code null}. 202 * @param isRequired Indicates whether this argument is required to 203 * be provided. 204 * @param maxOccurrences The maximum number of times this argument may be 205 * provided on the command line. A value less than 206 * or equal to zero indicates that it may be present 207 * any number of times. 208 * @param valuePlaceholder A placeholder to display in usage information to 209 * indicate that a value must be provided. It may 210 * be {@code null} if a default placeholder should 211 * be used. 212 * @param description A human-readable description for this argument. 213 * It must not be {@code null}. 214 * @param fileMustExist Indicates whether each value must refer to a file 215 * that exists. 216 * @param parentMustExist Indicates whether each value must refer to a file 217 * whose parent directory exists. 218 * @param mustBeFile Indicates whether each value must refer to a 219 * regular file, if it exists. 220 * @param mustBeDirectory Indicates whether each value must refer to a 221 * directory, if it exists. 222 * 223 * @throws ArgumentException If there is a problem with the definition of 224 * this argument. 225 */ 226 public FileArgument(@Nullable final Character shortIdentifier, 227 @Nullable final String longIdentifier, 228 final boolean isRequired, final int maxOccurrences, 229 @Nullable final String valuePlaceholder, 230 @NotNull final String description, 231 final boolean fileMustExist, 232 final boolean parentMustExist, final boolean mustBeFile, 233 final boolean mustBeDirectory) 234 throws ArgumentException 235 { 236 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 237 valuePlaceholder, description, fileMustExist, parentMustExist, 238 mustBeFile, mustBeDirectory, null); 239 } 240 241 242 243 /** 244 * Creates a new file argument with the provided information. 245 * 246 * @param shortIdentifier The short identifier for this argument. It may 247 * not be {@code null} if the long identifier is 248 * {@code null}. 249 * @param longIdentifier The long identifier for this argument. It may 250 * not be {@code null} if the short identifier is 251 * {@code null}. 252 * @param isRequired Indicates whether this argument is required to 253 * be provided. 254 * @param maxOccurrences The maximum number of times this argument may be 255 * provided on the command line. A value less than 256 * or equal to zero indicates that it may be present 257 * any number of times. 258 * @param valuePlaceholder A placeholder to display in usage information to 259 * indicate that a value must be provided. It may 260 * be {@code null} if a default placeholder should 261 * be used. 262 * @param description A human-readable description for this argument. 263 * It must not be {@code null}. 264 * @param fileMustExist Indicates whether each value must refer to a file 265 * that exists. 266 * @param parentMustExist Indicates whether each value must refer to a file 267 * whose parent directory exists. 268 * @param mustBeFile Indicates whether each value must refer to a 269 * regular file, if it exists. 270 * @param mustBeDirectory Indicates whether each value must refer to a 271 * directory, if it exists. 272 * @param defaultValues The set of default values to use for this 273 * argument if no values were provided. 274 * 275 * @throws ArgumentException If there is a problem with the definition of 276 * this argument. 277 */ 278 public FileArgument(@Nullable final Character shortIdentifier, 279 @Nullable final String longIdentifier, 280 final boolean isRequired, final int maxOccurrences, 281 @Nullable final String valuePlaceholder, 282 @NotNull final String description, 283 final boolean fileMustExist, 284 final boolean parentMustExist, final boolean mustBeFile, 285 final boolean mustBeDirectory, 286 @Nullable final List<File> defaultValues) 287 throws ArgumentException 288 { 289 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 290 (valuePlaceholder == null) 291 ? INFO_PLACEHOLDER_PATH.get() 292 : valuePlaceholder, 293 description); 294 295 if (mustBeFile && mustBeDirectory) 296 { 297 throw new ArgumentException(ERR_FILE_CANNOT_BE_FILE_AND_DIRECTORY.get( 298 getIdentifierString())); 299 } 300 301 this.fileMustExist = fileMustExist; 302 this.parentMustExist = parentMustExist; 303 this.mustBeFile = mustBeFile; 304 this.mustBeDirectory = mustBeDirectory; 305 306 if ((defaultValues == null) || defaultValues.isEmpty()) 307 { 308 this.defaultValues = null; 309 } 310 else 311 { 312 this.defaultValues = Collections.unmodifiableList(defaultValues); 313 } 314 315 values = new ArrayList<>(5); 316 validators = new ArrayList<>(5); 317 relativeBaseDirectory = null; 318 } 319 320 321 322 /** 323 * Creates a new file argument that is a "clean" copy of the provided source 324 * argument. 325 * 326 * @param source The source argument to use for this argument. 327 */ 328 private FileArgument(@NotNull final FileArgument source) 329 { 330 super(source); 331 332 fileMustExist = source.fileMustExist; 333 mustBeDirectory = source.mustBeDirectory; 334 mustBeFile = source.mustBeFile; 335 parentMustExist = source.parentMustExist; 336 defaultValues = source.defaultValues; 337 relativeBaseDirectory = source.relativeBaseDirectory; 338 validators = new ArrayList<>(source.validators); 339 values = new ArrayList<>(5); 340 } 341 342 343 344 /** 345 * Indicates whether each value must refer to a file that exists. 346 * 347 * @return {@code true} if the target files must exist, or {@code false} if 348 * it is acceptable for values to refer to files that do not exist. 349 */ 350 public boolean fileMustExist() 351 { 352 return fileMustExist; 353 } 354 355 356 357 /** 358 * Indicates whether each value must refer to a file whose parent directory 359 * exists. 360 * 361 * @return {@code true} if the parent directory for target files must exist, 362 * or {@code false} if it is acceptable for values to refer to files 363 * whose parent directories do not exist. 364 */ 365 public boolean parentMustExist() 366 { 367 return parentMustExist; 368 } 369 370 371 372 /** 373 * Indicates whether each value must refer to a regular file (if it exists). 374 * 375 * @return {@code true} if each value must refer to a regular file (if it 376 * exists), or {@code false} if it may refer to a directory. 377 */ 378 public boolean mustBeFile() 379 { 380 return mustBeFile; 381 } 382 383 384 385 /** 386 * Indicates whether each value must refer to a directory (if it exists). 387 * 388 * @return {@code true} if each value must refer to a directory (if it 389 * exists), or {@code false} if it may refer to a regular file. 390 */ 391 public boolean mustBeDirectory() 392 { 393 return mustBeDirectory; 394 } 395 396 397 398 /** 399 * Retrieves the list of default values for this argument, which will be used 400 * if no values were provided. 401 * 402 * @return The list of default values for this argument, or {@code null} if 403 * there are no default values. 404 */ 405 @Nullable() 406 public List<File> getDefaultValues() 407 { 408 return defaultValues; 409 } 410 411 412 413 /** 414 * Retrieves the directory that will serve as the base directory for relative 415 * paths, if one has been defined. 416 * 417 * @return The directory that will serve as the base directory for relative 418 * paths, or {@code null} if relative paths will be relative to the 419 * current working directory. 420 */ 421 @Nullable() 422 public File getRelativeBaseDirectory() 423 { 424 return relativeBaseDirectory; 425 } 426 427 428 429 /** 430 * Specifies the directory that will serve as the base directory for relative 431 * paths. 432 * 433 * @param relativeBaseDirectory The directory that will serve as the base 434 * directory for relative paths. It may be 435 * {@code null} if relative paths should be 436 * relative to the current working directory. 437 */ 438 public void setRelativeBaseDirectory( 439 @Nullable final File relativeBaseDirectory) 440 { 441 this.relativeBaseDirectory = relativeBaseDirectory; 442 } 443 444 445 446 /** 447 * Updates this argument to ensure that the provided validator will be invoked 448 * for any values provided to this argument. This validator will be invoked 449 * after all other validation has been performed for this argument. 450 * 451 * @param validator The argument value validator to be invoked. It must not 452 * be {@code null}. 453 */ 454 public void addValueValidator(@NotNull final ArgumentValueValidator validator) 455 { 456 validators.add(validator); 457 } 458 459 460 461 /** 462 * {@inheritDoc} 463 */ 464 @Override() 465 protected void addValue(@NotNull final String valueString) 466 throws ArgumentException 467 { 468 // NOTE: java.io.File has an extremely weird behavior. When a File object 469 // is created from a relative path and that path contains only the filename, 470 // then calling getParent or getParentFile will return null even though it 471 // obviously has a parent. Therefore, you must always create a File using 472 // the absolute path if you might want to get the parent. Also, if the path 473 // is relative, then we might want to control the base to which it is 474 // relative. 475 File f = new File(valueString); 476 if (! f.isAbsolute()) 477 { 478 if (relativeBaseDirectory == null) 479 { 480 f = new File(f.getAbsolutePath()); 481 } 482 else 483 { 484 f = new File(new File(relativeBaseDirectory, 485 valueString).getAbsolutePath()); 486 } 487 } 488 489 if (f.exists()) 490 { 491 if (mustBeFile && (! f.isFile())) 492 { 493 throw new ArgumentException(ERR_FILE_VALUE_NOT_FILE.get( 494 getIdentifierString(), 495 f.getAbsolutePath())); 496 } 497 else if (mustBeDirectory && (! f.isDirectory())) 498 { 499 throw new ArgumentException(ERR_FILE_VALUE_NOT_DIRECTORY.get( 500 getIdentifierString(), 501 f.getAbsolutePath())); 502 } 503 } 504 else 505 { 506 if (fileMustExist) 507 { 508 throw new ArgumentException(ERR_FILE_DOESNT_EXIST.get( 509 f.getAbsolutePath(), 510 getIdentifierString())); 511 } 512 else if (parentMustExist) 513 { 514 final File parentFile = f.getAbsoluteFile().getParentFile(); 515 if ((parentFile == null) || 516 (! parentFile.exists()) || 517 (! parentFile.isDirectory())) 518 { 519 throw new ArgumentException(ERR_FILE_PARENT_DOESNT_EXIST.get( 520 f.getAbsolutePath(), 521 getIdentifierString())); 522 } 523 } 524 } 525 526 if (values.size() >= getMaxOccurrences()) 527 { 528 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 529 getIdentifierString())); 530 } 531 532 for (final ArgumentValueValidator v : validators) 533 { 534 v.validateArgumentValue(this, valueString); 535 } 536 537 values.add(f); 538 } 539 540 541 542 /** 543 * Retrieves the value for this argument, or the default value if none was 544 * provided. If there are multiple values, then the first will be returned. 545 * 546 * @return The value for this argument, or the default value if none was 547 * provided, or {@code null} if there is no value and no default 548 * value. 549 */ 550 @Nullable() 551 public File getValue() 552 { 553 if (values.isEmpty()) 554 { 555 if ((defaultValues == null) || defaultValues.isEmpty()) 556 { 557 return null; 558 } 559 else 560 { 561 return defaultValues.get(0); 562 } 563 } 564 else 565 { 566 return values.get(0); 567 } 568 } 569 570 571 572 /** 573 * Retrieves the set of values for this argument. 574 * 575 * @return The set of values for this argument. 576 */ 577 @NotNull() 578 public List<File> getValues() 579 { 580 if (values.isEmpty() && (defaultValues != null)) 581 { 582 return defaultValues; 583 } 584 585 return Collections.unmodifiableList(values); 586 } 587 588 589 590 /** 591 * Retrieves an input stream that may be used to read the contents of the file 592 * specified as the value to this argument. If there are multiple values for 593 * this argument, then the file specified as the first value will be used. 594 * 595 * @return An input stream that may be used to read the data from the file, 596 * or {@code null} if no values were provided. 597 * 598 * @throws IOException If the specified file does not exist or if a problem 599 * occurs while obtaining the input stream. 600 */ 601 @Nullable() 602 public InputStream getFileInputStream() 603 throws IOException 604 { 605 final File f = getValue(); 606 if (f == null) 607 { 608 return null; 609 } 610 611 InputStream inputStream = new FileInputStream(f); 612 613 boolean closeStream = true; 614 try 615 { 616 final List<char[]> potentialPassphrases = new ArrayList<>(); 617 618 619 try 620 { 621 final ObjectPair<InputStream,char[]> streamPair = 622 ToolUtils.getPossiblyPassphraseEncryptedInputStream(inputStream, 623 POTENTIAL_PASSPHRASES_TO_AVOID_PROMPTING, false, 624 INFO_FILE_ENTER_ENC_PW.get(f.getAbsolutePath()), 625 ERR_FILE_WRONG_ENC_PW.get(f.getAbsolutePath()), System.out, 626 System.err); 627 inputStream = streamPair.getFirst(); 628 } 629 catch (final GeneralSecurityException e) 630 { 631 throw new IOException( 632 ERR_FILE_CANNOT_DECRYPT.get(f.getAbsolutePath(), 633 StaticUtils.getExceptionMessage(e)), 634 e); 635 } 636 637 inputStream = ToolUtils.getPossiblyGZIPCompressedInputStream(inputStream); 638 closeStream = false; 639 return inputStream; 640 } 641 finally 642 { 643 if (closeStream) 644 { 645 inputStream.close(); 646 } 647 } 648 } 649 650 651 652 /** 653 * Reads the contents of the file specified as the value to this argument and 654 * retrieves a list of the lines contained in it. If there are multiple 655 * values for this argument, then the file specified as the first value will 656 * be used. 657 * 658 * @return A list containing the lines of the target file, or {@code null} if 659 * no values were provided. 660 * 661 * @throws IOException If the specified file does not exist or a problem 662 * occurs while reading the contents of the file. 663 */ 664 @Nullable() 665 public List<String> getFileLines() 666 throws IOException 667 { 668 return getFileLines(true); 669 } 670 671 672 673 /** 674 * Reads the contents of the file specified as the value to this argument and 675 * retrieves a list of the non-blank lines contained in it. If there are 676 * multiple values for this argument, then the file specified as the first 677 * value will be used. 678 * 679 * @return A list containing the non-blank lines of the target file, or 680 * {@code null} if no values were provided. 681 * 682 * @throws IOException If the specified file does not exist or a problem 683 * occurs while reading the contents of the file. 684 */ 685 @Nullable() 686 public List<String> getNonBlankFileLines() 687 throws IOException 688 { 689 return getFileLines(false); 690 } 691 692 693 694 /** 695 * Reads the contents of the file specified as the value to this argument and 696 * retrieves a list of the lines contained in it, optionally omitting blank 697 * lines. If there are multiple values for this argument, then the file 698 * specified as the first value will be used. 699 * 700 * @param includeBlank Indicates whether to include blank lines in the 701 * list that is returned. 702 * 703 * @return A list containing the lines of the target file, or {@code null} if 704 * no values were provided. 705 * 706 * @throws IOException If the specified file does not exist or a problem 707 * occurs while reading the contents of the file. 708 */ 709 @Nullable() 710 private List<String> getFileLines(final boolean includeBlank) 711 throws IOException 712 { 713 final InputStream inputStream = getFileInputStream(); 714 if (inputStream == null) 715 { 716 return null; 717 } 718 719 try (InputStreamReader inputStreamReader = 720 new InputStreamReader(inputStream); 721 BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) 722 { 723 final List<String> lines = new ArrayList<>(); 724 while (true) 725 { 726 final String line = bufferedReader.readLine(); 727 if (line == null) 728 { 729 return lines; 730 } 731 732 if (includeBlank || (! line.isEmpty())) 733 { 734 lines.add(line); 735 } 736 } 737 } 738 finally 739 { 740 inputStream.close(); 741 } 742 } 743 744 745 746 /** 747 * Reads the contents of the file specified as the value to this argument. If 748 * there are multiple values for this argument, then the file specified as the 749 * first value will be used. 750 * 751 * @return A byte array containing the contents of the target file, or 752 * {@code null} if no values were provided. 753 * 754 * @throws IOException If the specified file does not exist or a problem 755 * occurs while reading the contents of the file. 756 */ 757 @Nullable() 758 public byte[] getFileBytes() 759 throws IOException 760 { 761 final InputStream inputStream = getFileInputStream(); 762 if (inputStream == null) 763 { 764 return null; 765 } 766 767 try 768 { 769 final ByteStringBuffer buffer = new ByteStringBuffer(); 770 buffer.readFrom(inputStream); 771 return buffer.toByteArray(); 772 } 773 finally 774 { 775 inputStream.close(); 776 } 777 } 778 779 780 781 /** 782 * {@inheritDoc} 783 */ 784 @Override() 785 @NotNull() 786 public List<String> getValueStringRepresentations(final boolean useDefault) 787 { 788 final List<File> files; 789 if (values.isEmpty()) 790 { 791 if (useDefault) 792 { 793 files = defaultValues; 794 } 795 else 796 { 797 return Collections.emptyList(); 798 } 799 } 800 else 801 { 802 files = values; 803 } 804 805 if ((files == null) || files.isEmpty()) 806 { 807 return Collections.emptyList(); 808 } 809 810 final ArrayList<String> valueStrings = new ArrayList<>(files.size()); 811 for (final File f : files) 812 { 813 valueStrings.add(f.getAbsolutePath()); 814 } 815 return Collections.unmodifiableList(valueStrings); 816 } 817 818 819 820 /** 821 * {@inheritDoc} 822 */ 823 @Override() 824 protected boolean hasDefaultValue() 825 { 826 return ((defaultValues != null) && (! defaultValues.isEmpty())); 827 } 828 829 830 831 /** 832 * {@inheritDoc} 833 */ 834 @Override() 835 @NotNull() 836 public String getDataTypeName() 837 { 838 if (mustBeDirectory) 839 { 840 return INFO_FILE_TYPE_PATH_DIRECTORY.get(); 841 } 842 else 843 { 844 return INFO_FILE_TYPE_PATH_FILE.get(); 845 } 846 } 847 848 849 850 /** 851 * {@inheritDoc} 852 */ 853 @Override() 854 @NotNull() 855 public String getValueConstraints() 856 { 857 final StringBuilder buffer = new StringBuilder(); 858 859 if (mustBeDirectory) 860 { 861 if (fileMustExist) 862 { 863 buffer.append(INFO_FILE_CONSTRAINTS_DIR_MUST_EXIST.get()); 864 } 865 else if (parentMustExist) 866 { 867 buffer.append(INFO_FILE_CONSTRAINTS_DIR_PARENT_MUST_EXIST.get()); 868 } 869 else 870 { 871 buffer.append(INFO_FILE_CONSTRAINTS_DIR_MAY_EXIST.get()); 872 } 873 } 874 else 875 { 876 if (fileMustExist) 877 { 878 buffer.append(INFO_FILE_CONSTRAINTS_FILE_MUST_EXIST.get()); 879 } 880 else if (parentMustExist) 881 { 882 buffer.append(INFO_FILE_CONSTRAINTS_FILE_PARENT_MUST_EXIST.get()); 883 } 884 else 885 { 886 buffer.append(INFO_FILE_CONSTRAINTS_FILE_MAY_EXIST.get()); 887 } 888 } 889 890 if (relativeBaseDirectory != null) 891 { 892 buffer.append(" "); 893 buffer.append(INFO_FILE_CONSTRAINTS_RELATIVE_PATH_SPECIFIED_ROOT.get( 894 relativeBaseDirectory.getAbsolutePath())); 895 } 896 897 return buffer.toString(); 898 } 899 900 901 902 /** 903 * {@inheritDoc} 904 */ 905 @Override() 906 protected void reset() 907 { 908 super.reset(); 909 values.clear(); 910 } 911 912 913 914 /** 915 * {@inheritDoc} 916 */ 917 @Override() 918 @NotNull() 919 public FileArgument getCleanCopy() 920 { 921 return new FileArgument(this); 922 } 923 924 925 926 /** 927 * {@inheritDoc} 928 */ 929 @Override() 930 protected void addToCommandLine(@NotNull final List<String> argStrings) 931 { 932 for (final File f : values) 933 { 934 argStrings.add(getIdentifierString()); 935 if (isSensitive()) 936 { 937 argStrings.add("***REDACTED***"); 938 } 939 else 940 { 941 argStrings.add(f.getAbsolutePath()); 942 } 943 } 944 } 945 946 947 948 /** 949 * {@inheritDoc} 950 */ 951 @Override() 952 public void toString(@NotNull final StringBuilder buffer) 953 { 954 buffer.append("FileArgument("); 955 appendBasicToStringInfo(buffer); 956 957 buffer.append(", fileMustExist="); 958 buffer.append(fileMustExist); 959 buffer.append(", parentMustExist="); 960 buffer.append(parentMustExist); 961 buffer.append(", mustBeFile="); 962 buffer.append(mustBeFile); 963 buffer.append(", mustBeDirectory="); 964 buffer.append(mustBeDirectory); 965 966 if (relativeBaseDirectory != null) 967 { 968 buffer.append(", relativeBaseDirectory='"); 969 buffer.append(relativeBaseDirectory.getAbsolutePath()); 970 buffer.append('\''); 971 } 972 973 if ((defaultValues != null) && (! defaultValues.isEmpty())) 974 { 975 if (defaultValues.size() == 1) 976 { 977 buffer.append(", defaultValue='"); 978 buffer.append(defaultValues.get(0).toString()); 979 } 980 else 981 { 982 buffer.append(", defaultValues={"); 983 984 final Iterator<File> iterator = defaultValues.iterator(); 985 while (iterator.hasNext()) 986 { 987 buffer.append('\''); 988 buffer.append(iterator.next().toString()); 989 buffer.append('\''); 990 991 if (iterator.hasNext()) 992 { 993 buffer.append(", "); 994 } 995 } 996 997 buffer.append('}'); 998 } 999 } 1000 1001 buffer.append(')'); 1002 } 1003}