001/* 002 * ============================================================================ 003 * Copyright © 2002-2021 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.config.cli; 019 020import static org.apiguardian.api.API.Status.STABLE; 021import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 022 023import java.util.Collection; 024import java.util.Optional; 025import java.util.function.BiConsumer; 026 027import org.apiguardian.api.API; 028import org.tquadrat.foundation.annotation.ClassVersion; 029import org.tquadrat.foundation.annotation.MountPoint; 030import org.tquadrat.foundation.config.CmdLineException; 031import org.tquadrat.foundation.config.spi.CLIDefinition; 032import org.tquadrat.foundation.config.spi.Parameters; 033import org.tquadrat.foundation.i18n.Message; 034import org.tquadrat.foundation.i18n.Translation; 035 036/** 037 * <p>{@summary The abstract base class for the value handler that takes a 038 * String value from the command line, translates it to the target type and 039 * sets the value to the property.}</p> 040 * <p>To circumvent possible problems with reflection in a modularised 041 * context, the handler uses an instance of 042 * {@link BiConsumer} 043 * to set the property; the 044 * {@link BiConsumer#accept(Object, Object)} 045 * method of that instance is called with two arguments:</p> 046 * <ol> 047 * <li>the name of the property to set</li> 048 * <li>the value for that property</li> 049 * </ol> 050 * <p>Basically, the implementation of such a value setter function may be 051 * implemented in any way, but the annotation processor creates them as simple 052 * as possible. To set a value to the attribute {@code m_Value}, it would look 053 * like</p> 054 * <pre><code>(p,v) -> m_Value = v;</code></pre> 055 * <p>and to add a value to the list attribute {@code m_List}, it would be</p> 056 * <pre><code>(p,v) -> m_List.add( v );</code></pre> 057 * <p>That means that the name of the property is ignored.</p> 058 * <p>But customer implementations may use other implementations as well.</p> 059 * 060 * @param <T> The target type. 061 * 062 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 063 * @version $Id: CmdLineValueHandler.java 1061 2023-09-25 16:32:43Z tquadrat $ 064 * @since 0.1.0 065 * 066 * @UMLGraph.link 067 */ 068@ClassVersion( sourceVersion = "$Id: CmdLineValueHandler.java 1061 2023-09-25 16:32:43Z tquadrat $" ) 069@API( status = STABLE, since = "0.1.0" ) 070public abstract class CmdLineValueHandler<T> 071{ 072 /*-----------*\ 073 ====** Constants **======================================================== 074 \*-----------*/ 075 /** 076 * The default property name that is used if no context is given: {@value}. 077 */ 078 public static final String DEFAULT_PROPERTY_NAME = "dummyProperty"; 079 080 /** 081 * The error message for an invalid entry on the command line: {@value}. 082 */ 083 public static final String MSG_InvalidParameter = "'%1$s' cannot be parsed as a valid command line parameter"; 084 085 /** 086 * The resource bundle key for the message about an invalid entry on the 087 * command line. 088 */ 089 @SuppressWarnings( "StaticMethodOnlyUsedInOneClass" ) 090 @Message 091 ( 092 description = "The generic message about an invalid entry on the command line", 093 translations = 094 { 095 @Translation( language = "en", text = MSG_InvalidParameter ), 096 @Translation( language = "de", text = "'%1$s' kann nicht zu einem gültigen Kommandozeilenparameter umgewandelt werden" ) 097 } 098 ) 099 public static final int MSGKEY_InvalidParameter = 1; 100 101 /*------------*\ 102 ====** Attributes **======================================================= 103 \*------------*/ 104 /** 105 * The CLI definition that provides the context for this value handler. 106 */ 107 private CLIDefinition m_CLIDefinition; 108 109 /** 110 * <p>{@summary The value setter.}</p> 111 * <p>The arguments of the lambda are:</p> 112 * <ol> 113 * <li>the name of the property to set</li> 114 * <li>the value for that property</li> 115 * </ol> 116 * <p>In most usages, the first argument is ignored.</p> 117 */ 118 private final BiConsumer<String,T> m_ValueSetter; 119 120 /*--------------*\ 121 ====** Constructors **===================================================== 122 \*--------------*/ 123 /** 124 * Creates a new {@code CmdLineValueHandler} instance. 125 * 126 * @param valueSetter The 127 * {@link BiConsumer Consumer} 128 * that places the translated value to the property. 129 */ 130 protected CmdLineValueHandler( final BiConsumer<String,T> valueSetter ) 131 { 132 m_ValueSetter = requireNonNullArgument( valueSetter, "valueSetter" ); 133 } // CmdLineValueHandler() 134 135 /** 136 * Creates a new {@code CmdLineValueHandler} instance. 137 * 138 * @param context The CLI definition that provides the context for this 139 * value handler. 140 * @param valueSetter The 141 * {@link BiConsumer Consumer} 142 * that places the translated value to the property. 143 */ 144 protected CmdLineValueHandler( final CLIDefinition context, final BiConsumer<String,T> valueSetter ) 145 { 146 this( valueSetter ); 147 m_CLIDefinition = requireNonNullArgument( context, "context" ); 148 } // CmdLineValueHandler() 149 150 /*---------*\ 151 ====** Methods **========================================================== 152 \*---------*/ 153 /** 154 * Returns a reference to the context. 155 * 156 * @return An instance of 157 * {@link Optional} 158 * that holds the CLI definition. 159 */ 160 protected final Optional<CLIDefinition> getCLIDefinition() { return Optional.ofNullable( m_CLIDefinition ); } 161 162 /** 163 * Returns the name of the property that is the target for the value. 164 * 165 * @return The property name. 166 */ 167 protected String getPropertyName() 168 { 169 final var retValue = getCLIDefinition().map( CLIDefinition::propertyName ).orElse( DEFAULT_PROPERTY_NAME ); 170 171 //---* Done *---------------------------------------------------------- 172 return retValue; 173 } // getPropertyName() 174 175 /** 176 * Returns a reference for the value setter. 177 * 178 * @return The value setter. 179 */ 180 protected final BiConsumer<String,T> getValueSetter() { return m_ValueSetter; } 181 182 /** 183 * Parses the given command line snippet and stores the result to the 184 * property. 185 * 186 * @note This method can be overridden, but in most cases, the 187 * implementation provided here should be sufficient. 188 * 189 * @param params The command line values to parse. 190 * @return The number of values that the method took from the command 191 * line. 192 * @throws CmdLineException An error occurred when parsing the 193 * parameters. 194 */ 195 @MountPoint 196 public int parseCmdLine( final Parameters params ) 197 { 198 var retValue = -1; 199 try 200 { 201 final var result = translate( requireNonNullArgument( params, "params" ) ); 202 retValue = result.size(); 203 result.forEach( r -> m_ValueSetter.accept( m_CLIDefinition.propertyName(), r ) ); 204 } 205 catch( final CmdLineException e ) { throw e; } 206 catch( @SuppressWarnings( "OverlyBroadCatchBlock" ) final Exception e ) 207 { 208 throw new CmdLineException( m_CLIDefinition, e ); 209 } 210 211 //---* Done *---------------------------------------------------------- 212 return retValue; 213 } // parseCmdLine() 214 215 /** 216 * Sets the CLI definition that provides the context for this value 217 * handler. 218 * 219 * @param context The CLI definition. 220 */ 221 public final void setContext( final CLIDefinition context ) 222 { 223 m_CLIDefinition = requireNonNullArgument( context, "context" ); 224 } // setContext() 225 226 /** 227 * Translates the command line values that can be referenced via the 228 * {@code params} argument to the target type. 229 * 230 * @param params The command line values to translate. 231 * @return A collection with the result; each entry in the collection 232 * corresponds to one value from the command line. 233 * @throws CmdLineException The given parameters cannot be parsed to 234 * the target type. 235 */ 236 protected abstract Collection<T> translate( final Parameters params ) throws CmdLineException; 237} 238// class CmdLineValueHandler 239 240/* 241 * End of File 242 */