/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Portions copyright 2014-2016 ForgeRock AS */ package com.forgerock.opendj.cli; import static com.forgerock.opendj.cli.CliMessages.*; import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; import org.forgerock.i18n.LocalizableMessageBuilder; /** * This class defines an argument whose value will be read from a file rather * than actually specified on the command-line. When a value is specified on the * command line, it will be treated as the path to the file containing the * actual value rather than the value itself.
*
* Note that if no filename is provided on the command line but a default * value is specified programmatically or if the default value is read from a * specified property, then that default value will be taken as the actual value * rather than a filename.
*
* Also note that this argument type assumes that the entire value for the * argument is on a single line in the specified file. If the file contains * multiple lines, then only the first line will be read. */ public final class FileBasedArgument extends Argument { /** * Returns a builder which can be used for incrementally constructing a new * {@link FileBasedArgument}. * * @param longIdentifier * The generic long identifier that will be used to refer to this argument. * @return A builder to continue building the {@link FileBasedArgument}. */ public static Builder builder(final String longIdentifier) { return new Builder(longIdentifier); } /** A fluent API for incrementally constructing {@link FileBasedArgument}. */ public static final class Builder extends ArgumentBuilder { private Builder(final String longIdentifier) { super(longIdentifier); } @Override Builder getThis() { return this; } @Override public FileBasedArgument buildArgument() throws ArgumentException { return new FileBasedArgument(this); } } /** The mapping between filenames specified and the first lines read from those files. */ private final Map namesToValues = new LinkedHashMap<>(); private FileBasedArgument(final Builder builder) throws ArgumentException { super(builder); } /** * Adds a value to the set of values for this argument. This should only be * called if the value is allowed by the valueIsAcceptable * method. Note that in this case, correct behavior depends on a previous * successful call to valueIsAcceptable so that the value read * from the file may be stored in the name-to-value hash and used in place * of the filename here. * * @param valueString * The string representation of the value to add to this * argument. */ @Override public void addValue(final String valueString) { final String actualValue = namesToValues.get(valueString); if (actualValue != null) { super.addValue(actualValue); } } /** * Retrieves a map between the filenames specified on the command line and * the first lines read from those files. * * @return A map between the filenames specified on the command line and the * first lines read from those files. */ public Map getNameToValueMap() { return namesToValues; } /** * Indicates whether the provided value is acceptable for use in this * argument. * * @param valueString * The value for which to make the determination. * @param invalidReason * A buffer into which the invalid reason may be written if the * value is not acceptable. * @return true if the value is acceptable, or * false if it is not. */ @Override public boolean valueIsAcceptable(final String valueString, final LocalizableMessageBuilder invalidReason) { // First, make sure that the specified file exists. File valueFile; try { valueFile = new File(valueString); if (!valueFile.exists()) { invalidReason.append(ERR_FILEARG_NO_SUCH_FILE.get(valueString, longIdentifier)); return false; } } catch (final Exception e) { invalidReason.append(ERR_FILEARG_CANNOT_VERIFY_FILE_EXISTENCE.get( valueString, longIdentifier, getExceptionMessage(e))); return false; } // Open the file, read the first line and close the file. String line; try (BufferedReader reader = new BufferedReader(new FileReader(valueFile))) { line = reader.readLine(); } catch (final FileNotFoundException e) { invalidReason.append(ERR_FILEARG_CANNOT_OPEN_FILE.get(valueString, longIdentifier, getExceptionMessage(e))); return false; } catch (final IOException e) { invalidReason.append(ERR_FILEARG_CANNOT_READ_FILE.get(valueString, longIdentifier, getExceptionMessage(e))); return false; } // If the line read is null, then that means the file was empty. if (line == null) { invalidReason.append(ERR_FILEARG_EMPTY_FILE.get(valueString, longIdentifier)); return false; } // Store the value in the hash so it will be available for addValue. // We won't do any validation on the value itself, so anything that we // read will be considered acceptable. namesToValues.put(valueString, line); return true; } }