/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * 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; } }