/* Copyright 2013 Jonatan Jönsson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package se.softhouse.jargo;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.and;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Maps.newLinkedHashMap;
import static se.softhouse.jargo.Argument.IS_REQUIRED;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import se.softhouse.jargo.internal.Texts.ProgrammaticErrors;
import com.google.common.collect.Sets;
/**
* Holds parsed arguments for a {@link CommandLineParser#parse(String...)} invocation.
* Use {@link #get(Argument)} to fetch a parsed command line value.
*/
@Immutable
public final class ParsedArguments
{
/**
* Stores results from {@link StringParser#parse(String, Locale)}
*/
@Nonnull private final Map<Argument<?>, Object> parsedArguments = newLinkedHashMap();
@Nonnull private final Set<Argument<?>> allArguments;
/**
* Keeps a running total of how many indexed arguments that have been parsed
*/
private int indexedArgumentsParsed = 0;
ParsedArguments(Set<Argument<?>> arguments)
{
allArguments = arguments;
}
/**
* Returns the parsed value for the given {@code argumentToFetch},
* if no value was given on the command line the default value is
* returned.
*
* @see StringParser#defaultValue()
*/
@Nullable
@CheckReturnValue
public <T> T get(final Argument<T> argumentToFetch)
{
if(!wasGiven(argumentToFetch))
{
checkArgument(allArguments.contains(argumentToFetch), ProgrammaticErrors.ILLEGAL_ARGUMENT, argumentToFetch);
return argumentToFetch.defaultValue();
}
return getValue(argumentToFetch);
}
/**
* Checks if an {@link Argument} was given in the command line arguments
*
* @param argument the {@link Argument} to check
* @return true if {@code argument} was given, that is, the default value will not be used
*/
public boolean wasGiven(Argument<?> argument)
{
return parsedArguments.containsKey(checkNotNull(argument));
}
@Override
public String toString()
{
return parsedArguments.toString();
}
@Override
public boolean equals(@Nullable Object other)
{
if(this == other)
return true;
if(!(other instanceof ParsedArguments))
return false;
return parsedArguments.equals(((ParsedArguments) other).parsedArguments);
}
@Override
public int hashCode()
{
return parsedArguments.hashCode();
}
// Publicly this class is Immutable, CommandLineParserInstance is only allowed to modify it
// during parsing
<T> void put(final Argument<T> definition, @Nullable final T value)
{
if(definition.isIndexed())
{
indexedArgumentsParsed++;
}
parsedArguments.put(definition, value);
}
/**
* Makes the parsed value for {@code definition} unmodifiable.
*/
<T> void finalize(final Argument<T> definition)
{
T value = getValue(definition);
T finalizedValue = definition.finalizer().apply(value);
put(definition, finalizedValue);
}
<T> T getValue(final Argument<T> definition)
{
// Safe because put guarantees that the map is heterogeneous
@SuppressWarnings("unchecked")
T value = (T) parsedArguments.get(definition);
return value;
}
Collection<Argument<?>> requiredArgumentsLeft()
{
return filter(allArguments, and(not(in(parsedArguments.keySet())), IS_REQUIRED));
}
int indexedArgumentsParsed()
{
return indexedArgumentsParsed;
}
Set<Argument<?>> parsedArguments()
{
return parsedArguments.keySet();
}
Set<String> nonParsedArguments()
{
Set<String> validArguments = Sets.newHashSetWithExpectedSize(allArguments.size());
for(Argument<?> argument : allArguments)
{
boolean wasGiven = wasGiven(argument);
if(!wasGiven || argument.isAllowedToRepeat())
{
for(String name : argument.names())
{
if(argument.separator().equals(ArgumentBuilder.DEFAULT_SEPARATOR) || argument.isPropertyMap())
{
validArguments.add(name);
}
else
{
validArguments.add(name + argument.separator());
}
}
}
}
return validArguments;
}
}