001package org.unix4j.unix.from; 002 003 004import org.unix4j.command.Arguments; 005import org.unix4j.context.ExecutionContext; 006import org.unix4j.convert.ValueConverter; 007import org.unix4j.util.ArrayUtil; 008import org.unix4j.variable.Arg; 009import org.unix4j.variable.VariableContext; 010 011import org.unix4j.unix.From; 012 013/** 014 * Arguments and options for the {@link From from} command. 015 */ 016public final class FromArguments implements Arguments<FromArguments> { 017 018 019 020 // operand: <path> 021 private String path; 022 private boolean pathIsSet = false; 023 024 // operand: <base> 025 private Class<?> base; 026 private boolean baseIsSet = false; 027 028 // operand: <resource> 029 private String resource; 030 private boolean resourceIsSet = false; 031 032 // operand: <input> 033 private org.unix4j.io.Input input; 034 private boolean inputIsSet = false; 035 036 /** 037 * Constructor to use if no options are specified. 038 */ 039 public FromArguments() { 040 super(); 041 } 042 043 private Object resolveVariable(VariableContext context, String variable) { 044 final Object value = context.getValue(variable); 045 if (value != null) { 046 return value; 047 } 048 throw new IllegalArgumentException("cannot resolve variable " + variable + 049 " in command: from " + this); 050 } 051 private <V> V convert(ExecutionContext context, String operandName, Class<V> operandType, Object value) { 052 final ValueConverter<V> converter = context.getValueConverterFor(operandType); 053 final V convertedValue; 054 if (converter != null) { 055 convertedValue = converter.convert(value); 056 } else { 057 convertedValue = null; 058 } 059 if (convertedValue != null) { 060 return convertedValue; 061 } 062 throw new IllegalArgumentException("cannot convert --" + operandName + 063 " value '" + value + "' into the type " + operandType.getName() + 064 " for from command"); 065 } 066 067 @Override 068 public FromArguments getForContext(ExecutionContext context) { 069 if (context == null) { 070 throw new NullPointerException("context cannot be null"); 071 } 072 //no String... args for this command, hence nothing to resolve 073 return this; 074 } 075 076 /** 077 * Returns the {@code <path>} operand value (variables are NOT resolved): the file to use as input; wildcards * and ? are supported; relative 078 paths are resolved on the basis of the current working directory. 079 * 080 * @return the {@code <path>} operand value (variables are not resolved) 081 * @throws IllegalStateException if this operand has never been set 082 * @see #getPath(ExecutionContext) 083 */ 084 public String getPath() { 085 if (pathIsSet) { 086 return path; 087 } 088 throw new IllegalStateException("operand has not been set: " + path); 089 } 090 /** 091 * Returns the {@code <path>} (variables are resolved): the file to use as input; wildcards * and ? are supported; relative 092 paths are resolved on the basis of the current working directory. 093 * 094 * @param context the execution context used to resolve variables 095 * @return the {@code <path>} operand value after resolving variables 096 * @throws IllegalStateException if this operand has never been set 097 * @see #getPath() 098 */ 099 public String getPath(ExecutionContext context) { 100 final String value = getPath(); 101 if (Arg.isVariable(value)) { 102 final Object resolved = resolveVariable(context.getVariableContext(), value); 103 final String converted = convert(context, "path", String.class, resolved); 104 return converted; 105 } 106 return value; 107 } 108 109 /** 110 * Returns true if the {@code <path>} operand has been set. 111 * <p> 112 * Note that this method returns true even if {@code null} was passed to the 113 * {@link #setPath(String)} method. 114 * 115 * @return true if the setter for the {@code <path>} operand has 116 * been called at least once 117 */ 118 public boolean isPathSet() { 119 return pathIsSet; 120 } 121 /** 122 * Sets {@code <path>}: the file to use as input; wildcards * and ? are supported; relative 123 paths are resolved on the basis of the current working directory. 124 * 125 * @param path the value for the {@code <path>} operand 126 */ 127 public void setPath(String path) { 128 this.path = path; 129 this.pathIsSet = true; 130 } 131 /** 132 * Returns the {@code <base>} operand value: base class for subsequent {@code resource} operand if relative paths are used. 133 * 134 * @return the {@code <base>} operand value (variables are not resolved) 135 * @throws IllegalStateException if this operand has never been set 136 * 137 */ 138 public Class<?> getBase() { 139 if (baseIsSet) { 140 return base; 141 } 142 throw new IllegalStateException("operand has not been set: " + base); 143 } 144 145 /** 146 * Returns true if the {@code <base>} operand has been set. 147 * <p> 148 * Note that this method returns true even if {@code null} was passed to the 149 * {@link #setBase(Class)} method. 150 * 151 * @return true if the setter for the {@code <base>} operand has 152 * been called at least once 153 */ 154 public boolean isBaseSet() { 155 return baseIsSet; 156 } 157 /** 158 * Sets {@code <base>}: base class for subsequent {@code resource} operand if relative paths are used. 159 * 160 * @param base the value for the {@code <base>} operand 161 */ 162 public void setBase(Class<?> base) { 163 this.base = base; 164 this.baseIsSet = true; 165 } 166 /** 167 * Returns the {@code <resource>} operand value (variables are NOT resolved): a path to the file to redirect to the next command. The resource needs 168 to be on the classpath. If the file is in the root directory, the 169 filename should be prefixed with a forward slash. e.g.: 170 {@code "/test-file.txt"}. 171 <p> 172 If the file is in a package, then the package should be specified 173 prefixed with a forward slash, and with each dot "." replaced with a 174 forward slash. e.g.: 175 {@code "/org/company/mypackage/test-file.txt"}. 176 A {@code base} class operand can be provided for relative paths; see 177 {@link java.lang.Class#getResourceAsStream(String)} for details about 178 resource loading. 179 * 180 * @return the {@code <resource>} operand value (variables are not resolved) 181 * @throws IllegalStateException if this operand has never been set 182 * @see #getResource(ExecutionContext) 183 */ 184 public String getResource() { 185 if (resourceIsSet) { 186 return resource; 187 } 188 throw new IllegalStateException("operand has not been set: " + resource); 189 } 190 /** 191 * Returns the {@code <resource>} (variables are resolved): a path to the file to redirect to the next command. The resource needs 192 to be on the classpath. If the file is in the root directory, the 193 filename should be prefixed with a forward slash. e.g.: 194 {@code "/test-file.txt"}. 195 <p> 196 If the file is in a package, then the package should be specified 197 prefixed with a forward slash, and with each dot "." replaced with a 198 forward slash. e.g.: 199 {@code "/org/company/mypackage/test-file.txt"}. 200 A {@code base} class operand can be provided for relative paths; see 201 {@link java.lang.Class#getResourceAsStream(String)} for details about 202 resource loading. 203 * 204 * @param context the execution context used to resolve variables 205 * @return the {@code <resource>} operand value after resolving variables 206 * @throws IllegalStateException if this operand has never been set 207 * @see #getResource() 208 */ 209 public String getResource(ExecutionContext context) { 210 final String value = getResource(); 211 if (Arg.isVariable(value)) { 212 final Object resolved = resolveVariable(context.getVariableContext(), value); 213 final String converted = convert(context, "resource", String.class, resolved); 214 return converted; 215 } 216 return value; 217 } 218 219 /** 220 * Returns true if the {@code <resource>} operand has been set. 221 * <p> 222 * Note that this method returns true even if {@code null} was passed to the 223 * {@link #setResource(String)} method. 224 * 225 * @return true if the setter for the {@code <resource>} operand has 226 * been called at least once 227 */ 228 public boolean isResourceSet() { 229 return resourceIsSet; 230 } 231 /** 232 * Sets {@code <resource>}: a path to the file to redirect to the next command. The resource needs 233 to be on the classpath. If the file is in the root directory, the 234 filename should be prefixed with a forward slash. e.g.: 235 {@code "/test-file.txt"}. 236 <p> 237 If the file is in a package, then the package should be specified 238 prefixed with a forward slash, and with each dot "." replaced with a 239 forward slash. e.g.: 240 {@code "/org/company/mypackage/test-file.txt"}. 241 A {@code base} class operand can be provided for relative paths; see 242 {@link java.lang.Class#getResourceAsStream(String)} for details about 243 resource loading. 244 * 245 * @param resource the value for the {@code <resource>} operand 246 */ 247 public void setResource(String resource) { 248 this.resource = resource; 249 this.resourceIsSet = true; 250 } 251 /** 252 * Returns the {@code <input>} operand value: the input object to read from 253 * 254 * @return the {@code <input>} operand value (variables are not resolved) 255 * @throws IllegalStateException if this operand has never been set 256 * 257 */ 258 public org.unix4j.io.Input getInput() { 259 if (inputIsSet) { 260 return input; 261 } 262 throw new IllegalStateException("operand has not been set: " + input); 263 } 264 265 /** 266 * Returns true if the {@code <input>} operand has been set. 267 * <p> 268 * Note that this method returns true even if {@code null} was passed to the 269 * {@link #setInput(org.unix4j.io.Input)} method. 270 * 271 * @return true if the setter for the {@code <input>} operand has 272 * been called at least once 273 */ 274 public boolean isInputSet() { 275 return inputIsSet; 276 } 277 /** 278 * Sets {@code <input>}: the input object to read from 279 * 280 * @param input the value for the {@code <input>} operand 281 */ 282 public void setInput(org.unix4j.io.Input input) { 283 this.input = input; 284 this.inputIsSet = true; 285 } 286 287 288 @Override 289 public String toString() { 290 // ok, we have options or arguments or both 291 final StringBuilder sb = new StringBuilder(); 292 293{ 294 // operand: <path> 295 if (pathIsSet) { 296 if (sb.length() > 0) sb.append(' '); 297 sb.append("--").append("path"); 298 sb.append(" ").append(toString(getPath())); 299 } 300 // operand: <base> 301 if (baseIsSet) { 302 if (sb.length() > 0) sb.append(' '); 303 sb.append("--").append("base"); 304 sb.append(" ").append(toString(getBase())); 305 } 306 // operand: <resource> 307 if (resourceIsSet) { 308 if (sb.length() > 0) sb.append(' '); 309 sb.append("--").append("resource"); 310 sb.append(" ").append(toString(getResource())); 311 } 312 // operand: <input> 313 if (inputIsSet) { 314 if (sb.length() > 0) sb.append(' '); 315 sb.append("--").append("input"); 316 sb.append(" ").append(toString(getInput())); 317 } 318 } 319 320 return sb.toString(); 321 } 322 private static String toString(Object value) { 323 if (value != null && value.getClass().isArray()) { 324 return ArrayUtil.toString(value); 325 } 326 return String.valueOf(value); 327 } 328}