/****************************************************************************** * Copyright (C) 2015 Yevgeny Krasik * * * * 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 com.github.ykrasik.jaci.cli.param; import com.github.ykrasik.jaci.Identifier; import com.github.ykrasik.jaci.cli.CliConstants; import com.github.ykrasik.jaci.cli.assist.CliValueType; import com.github.ykrasik.jaci.cli.exception.ParseError; import com.github.ykrasik.jaci.cli.exception.ParseException; import com.github.ykrasik.jaci.util.function.Spplr; import com.github.ykrasik.jaci.util.opt.Opt; import java.util.Objects; /** * An abstract implementation of a {@link CliParam}. * Handles most common cases like optional values. * Specialized for a value type. * * @param <T> The type of values parsed by this parameter. * * @author Yevgeny Krasik */ public abstract class AbstractCliParam<T> implements CliParam { protected static final CliValueType NULL_REF = CliValueType.COMMAND_PARAM_VALUE; /** This parameter's identifier. */ private final Identifier identifier; /** If this value is present, this parameter is considered optional. */ private final Opt<Spplr<T>> defaultValueSupplier; /** Whether this parameter is nullable. */ protected final boolean nullable; protected AbstractCliParam(Identifier identifier, Opt<Spplr<T>> defaultValueSupplier, boolean nullable) { this.identifier = Objects.requireNonNull(identifier, "identifier"); this.defaultValueSupplier = Objects.requireNonNull(defaultValueSupplier, "defaultValueSupplier"); this.nullable = nullable; } @Override public Identifier getIdentifier() { return identifier; } @Override public String toExternalForm() { return new StringBuilder() .append(isOptional() ? '[' : '{') .append(getName()) .append(" : ") .append(getValueTypeName()) .append(isOptional() ? ']' : '}') .toString(); } // Type specialization - subclasses must parse a value of type T. @Override public T parse(String arg) throws ParseException { if (isNull(arg)) { if (nullable) { return null; } else { throw new ParseException(ParseError.INVALID_PARAM, "Parameter doesn't take 'null' values: " + getName()); } } return parseNonNull(arg); } protected abstract T parseNonNull(String arg) throws ParseException; @Override public T unbound() throws ParseException { if (!isOptional()) { throw missingParamValue(); } return defaultValueSupplier.get().get(); } @Override public T noValue() throws ParseException { throw missingParamValue(); } @Override public boolean isNullable() { return nullable; } private boolean isOptional() { // A parameter is considered optional if it has a defaultValueSupplier. return defaultValueSupplier.isPresent(); } private ParseException missingParamValue() throws ParseException { throw new ParseException(ParseError.PARAM_NOT_BOUND, "Parameter value missing: '"+getName()+'\''); } protected ParseException invalidParamValue(String value) throws ParseException { throw new ParseException( ParseError.INVALID_PARAM_VALUE, "Invalid value for "+getValueTypeName()+" parameter '"+getName()+"': '"+value+'\'' ); } protected boolean isNull(String arg) { return CliConstants.NULL.equals(arg.toLowerCase()); } /** * @return The name of the value type parsed by this parameter. */ protected abstract String getValueTypeName(); private String getName() { return identifier.getName(); } @Override public String toString() { return toExternalForm(); } }