View Javadoc
1   package org.sentrysoftware.jawk.util;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2023 Sentry Software
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import java.io.InputStream;
26  import java.io.PrintStream;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.Map;
32  
33  /**
34   * A simple container for the parameters of a single AWK invocation.
35   * These values have defaults.
36   * These defaults may be changed through command line arguments,
37   * or when invoking Jawk programmatically, from within Java code.
38   *
39   * @author Danny Daglas
40   */
41  public class AwkSettings {
42  
43  	/**
44  	 * Where input is read from.
45  	 * By default, this is {@link System#in}.
46  	 */
47  	private InputStream input = System.in;
48  
49  	/**
50  	 * Contains variable assignments which are applied prior to
51  	 * executing the script (-v assignments).
52  	 * The values may be of type <code>Integer</code>,
53  	 * <code>Double</code> or <code>String</code>.
54  	 */
55  	private Map<String, Object> variables = new HashMap<String, Object>();
56  
57  	/**
58  	 * Contains name=value or filename entries.
59  	 * Order is important, which is why name=value and filenames
60  	 * are listed in the same List container.
61  	 */
62  	private List<String> nameValueOrFileNames = new ArrayList<String>();
63  
64  	/**
65  	 * Script sources meta info.
66  	 * This will usually be either one String container,
67  	 * made up of the script given on the command line directly,
68  	 * with the first non-"-" parameter,
69  	 * or one or multiple script file names (if provided with -f switches).
70  	 */
71  	private List<ScriptSource> scriptSources = new ArrayList<ScriptSource>();
72  
73  	/**
74  	 * Initial Field Separator (FS) value.
75  	 * <code>null</code> means the default FS value.
76  	 */
77  	private String fieldSeparator = null;
78  
79  	/**
80  	 * Whether to dump the syntax tree;
81  	 * <code>false</code> by default.
82  	 */
83  	private boolean dumpSyntaxTree = false;
84  
85  	/**
86  	 * Whether to dump the intermediate code;
87  	 * <code>false</code> by default.
88  	 */
89  	private boolean dumpIntermediateCode = false;
90  
91  	/**
92  	 * Whether to enable additional functions (_sleep/_dump);
93  	 * <code>false</code> by default.
94  	 */
95  	private boolean additionalFunctions = false;
96  
97  	/**
98  	 * Whether to enable additional type functions (_INTEGER/_DOUBLE/_STRING);
99  	 * <code>false</code> by default.
100 	 */
101 
102 	private boolean additionalTypeFunctions = false;
103 
104 	/**
105 	 * Whether to maintain array keys in sorted order;
106 	 * <code>false</code> by default.
107 	 */
108 	private boolean useSortedArrayKeys = false;
109 
110 	/**
111 	 * Whether to trap <code>IllegalFormatExceptions</code>
112 	 * for <code>[s]printf</code>;
113 	 * <code>true</code> by default.
114 	 */
115 	private boolean catchIllegalFormatExceptions = true;
116 
117 	/**
118 	 * Whether user extensions are enabled;
119 	 * <code>false</code> by default.
120 	 */
121 	private boolean userExtensions = false;
122 
123 	/**
124 	 * Write to intermediate file;
125 	 * <code>false</code> by default.
126 	 */
127 	private boolean writeIntermediateFile = false;
128 
129 	/**
130 	 * Output filename;
131 	 * <code>null</code> by default,
132 	 * which means the appropriate default file-name will get used.
133 	 */
134 	private String outputFilename = null;
135 
136 	/**
137 	 * Output stream;
138 	 * <code>System.out</code> by default,
139 	 * which means we will print to stdout by default
140 	 */
141 	private PrintStream outputStream = System.out;
142 
143 	/**
144 	 * Compiled destination directory (if provided);
145 	 * <code>"."</code> by default.
146 	 */
147 	private String destinationDirectory = ".";
148 
149 	/**
150 	 * Locale for the output of numbers
151 	 * <code>US-English</code> by default.
152 	 */
153 	private Locale locale = Locale.US;
154 
155 	/**
156 	 * Default value for RS, when not set specifically by the AWK script
157 	 */
158 	private String defaultRS = System.getProperty("line.separator", "\n");
159 
160 	/**
161 	 * Default value for ORS, when not set specifically by the AWK script
162 	 */
163 	private String defaultORS = System.getProperty("line.separator", "\n");
164 
165 	/**
166 	 * <p>toDescriptionString.</p>
167 	 *
168 	 * @return a human readable representation of the parameters values.
169 	 */
170 	public String toDescriptionString() {
171 
172 		StringBuilder desc = new StringBuilder();
173 
174 		final char newLine = '\n';
175 
176 		desc.append("variables = ")
177 				.append(getVariables()).append(newLine);
178 		desc.append("nameValueOrFileNames = ")
179 				.append(getNameValueOrFileNames()).append(newLine);
180 		desc.append("scriptSources = ")
181 				.append(scriptSources).append(newLine);
182 		desc.append("fieldSeparator = ")
183 				.append(getFieldSeparator()).append(newLine);
184 		desc.append("dumpSyntaxTree = ")
185 				.append(isDumpSyntaxTree()).append(newLine);
186 		desc.append("dumpIntermediateCode = ")
187 				.append(isDumpIntermediateCode()).append(newLine);
188 		desc.append("additionalFunctions = ")
189 				.append(isAdditionalFunctions()).append(newLine);
190 		desc.append("additionalTypeFunctions = ")
191 				.append(isAdditionalTypeFunctions()).append(newLine);
192 		desc.append("useSortedArrayKeys = ")
193 				.append(isUseSortedArrayKeys()).append(newLine);
194 		desc.append("catchIllegalFormatExceptions = ")
195 				.append(isCatchIllegalFormatExceptions()).append(newLine);
196 		desc.append("writeIntermediateFile = ")
197 				.append(isWriteIntermediateFile()).append(newLine);
198 		desc.append("outputFilename = ")
199 				.append(getOutputFilename()).append(newLine);
200 		desc.append("destinationDirectory = ")
201 				.append(getDestinationDirectory()).append(newLine);
202 
203 		return desc.toString();
204 	}
205 
206 	/**
207 	 * Provides a description of extensions that are enabled/disabled.
208 	 * The default compiler implementation uses this method
209 	 * to describe extensions which are compiled into the script.
210 	 * The description is then provided to the user within the usage.
211 	 *
212 	 * @return A description of the extensions which are enabled/disabled.
213 	 */
214 	public String toExtensionDescription() {
215 
216 		StringBuilder extensions = new StringBuilder();
217 
218 		if (isAdditionalFunctions()) {
219 			extensions.append(", _sleep & _dump enabled");
220 		}
221 		if (isAdditionalTypeFunctions()) {
222 			extensions.append(", _INTEGER, _DOUBLE, _STRING enabled");
223 		}
224 		if (isUseSortedArrayKeys()) {
225 			extensions.append(", associative array keys are sorted");
226 		}
227 		if (isCatchIllegalFormatExceptions()) {
228 			extensions.append(", IllegalFormatExceptions NOT trapped");
229 		}
230 
231 		if (extensions.length() > 0) {
232 			return "{extensions: " + extensions.substring(2) + "}";
233 		} else {
234 			return "{no compiled extensions utilized}";
235 		}
236 	}
237 
238 
239 	@SuppressWarnings("unused")
240 	private void addInitialVariable(String keyValue) {
241 		int equalsIdx = keyValue.indexOf('=');
242 		assert equalsIdx >= 0;
243 		String name = keyValue.substring(0, equalsIdx);
244 		String valueString = keyValue.substring(equalsIdx + 1);
245 		Object value;
246 		// deduce type
247 		try {
248 			value = Integer.parseInt(valueString);
249 		} catch (NumberFormatException nfe) {
250 			try {
251 				value = Double.parseDouble(valueString);
252 			} catch (NumberFormatException nfe2) {
253 				value = valueString;
254 			}
255 		}
256 		// note: can overwrite previously defined variables
257 		getVariables().put(name, value);
258 	}
259 
260 	/**
261 	 * <p>Getter for the field <code>outputFilename</code>.</p>
262 	 *
263 	 * @param defaultFileName The filename to return if -o argument
264 	 *   is not used.
265 	 * @return the optarg for the -o parameter, or the defaultFileName
266 	 *   parameter if -o is not utilized.
267 	 */
268 	public String getOutputFilename(String defaultFileName) {
269 		if (getOutputFilename() == null) {
270 			return defaultFileName;
271 		} else {
272 			return getOutputFilename();
273 		}
274 	}
275 
276 	/**
277 	 * <p>Getter for the field <code>scriptSources</code>.</p>
278 	 *
279 	 * @return the script sources meta info.
280 	 * This will usually be either one String container,
281 	 * made up of the script given on the command line directly,
282 	 * with the first non-"-" parameter,
283 	 * or one or multiple script file names (if provided with -f switches).
284 	 */
285 	public List<ScriptSource> getScriptSources() {
286 		return scriptSources;
287 	}
288 
289 	/**
290 	 * Add the specified ScriptSource
291 	 *
292 	 * @param scriptSource ScriptSource instance to add
293 	 */
294 	public void addScriptSource(ScriptSource scriptSource) {
295 		scriptSources.add(scriptSource);
296 	}
297 
298 	/**
299 	 * Where input is read from.
300 	 * By default, this is {@link java.lang.System#in}.
301 	 *
302 	 * @return the input
303 	 */
304 	public InputStream getInput() {
305 		return input;
306 	}
307 
308 	/**
309 	 * Where input is read from.
310 	 * By default, this is {@link java.lang.System#in}.
311 	 *
312 	 * @param input the input to set
313 	 */
314 	public void setInput(InputStream input) {
315 		this.input = input;
316 	}
317 
318 	/**
319 	 * Contains variable assignments which are applied prior to
320 	 * executing the script (-v assignments).
321 	 * The values may be of type <code>Integer</code>,
322 	 * <code>Double</code> or <code>String</code>.
323 	 *
324 	 * @return the variables
325 	 */
326 	public Map<String, Object> getVariables() {
327 		return variables;
328 	}
329 
330 	/**
331 	 * Contains variable assignments which are applied prior to
332 	 * executing the script (-v assignments).
333 	 * The values may be of type <code>Integer</code>,
334 	 * <code>Double</code> or <code>String</code>.
335 	 *
336 	 * @param variables the variables to set
337 	 */
338 	public void setVariables(Map<String, Object> variables) {
339 		this.variables = variables;
340 	}
341 
342 	/**
343 	 * Contains name=value or filename entries.
344 	 * Order is important, which is why name=value and filenames
345 	 * are listed in the same List container.
346 	 *
347 	 * @return the nameValueOrFileNames
348 	 */
349 	public List<String> getNameValueOrFileNames() {
350 		return nameValueOrFileNames;
351 	}
352 
353 	/**
354 	 * Contains name=value or filename entries.
355 	 * Order is important, which is why name=value and filenames
356 	 * are listed in the same List container.
357 	 *
358 	 * @param nameValueOrFileNames the nameValueOrFileNames to set
359 	 */
360 	public void setNameValueOrFileNames(List<String> nameValueOrFileNames) {
361 		this.nameValueOrFileNames = nameValueOrFileNames;
362 	}
363 
364 	/**
365 	 * Script sources meta info.
366 	 * This will usually be either one String container,
367 	 * made up of the script given on the command line directly,
368 	 * with the first non-"-" parameter,
369 	 * or one or multiple script file names (if provided with -f switches).
370 	 *
371 	 * @param scriptSources the scriptSources to set
372 	 */
373 	public void setScriptSources(List<ScriptSource> scriptSources) {
374 		this.scriptSources = scriptSources;
375 	}
376 
377 	/**
378 	 * Initial Field Separator (FS) value.
379 	 * <code>null</code> means the default FS value.
380 	 *
381 	 * @return the fieldSeparator
382 	 */
383 	public String getFieldSeparator() {
384 		return fieldSeparator;
385 	}
386 
387 	/**
388 	 * Initial Field Separator (FS) value.
389 	 * <code>null</code> means the default FS value.
390 	 *
391 	 * @param fieldSeparator the fieldSeparator to set
392 	 */
393 	public void setFieldSeparator(String fieldSeparator) {
394 		this.fieldSeparator = fieldSeparator;
395 	}
396 
397 	/**
398 	 * Whether to dump the syntax tree;
399 	 * <code>false</code> by default.
400 	 *
401 	 * @return the dumpSyntaxTree
402 	 */
403 	public boolean isDumpSyntaxTree() {
404 		return dumpSyntaxTree;
405 	}
406 
407 	/**
408 	 * Whether to dump the syntax tree;
409 	 * <code>false</code> by default.
410 	 *
411 	 * @param dumpSyntaxTree the dumpSyntaxTree to set
412 	 */
413 	public void setDumpSyntaxTree(boolean dumpSyntaxTree) {
414 		this.dumpSyntaxTree = dumpSyntaxTree;
415 	}
416 
417 	/**
418 	 * Whether to dump the intermediate code;
419 	 * <code>false</code> by default.
420 	 *
421 	 * @return the dumpIntermediateCode
422 	 */
423 	public boolean isDumpIntermediateCode() {
424 		return dumpIntermediateCode;
425 	}
426 
427 	/**
428 	 * Whether to dump the intermediate code;
429 	 * <code>false</code> by default.
430 	 *
431 	 * @param dumpIntermediateCode the dumpIntermediateCode to set
432 	 */
433 	public void setDumpIntermediateCode(boolean dumpIntermediateCode) {
434 		this.dumpIntermediateCode = dumpIntermediateCode;
435 	}
436 
437 	/**
438 	 * Whether to enable additional functions (_sleep/_dump);
439 	 * <code>false</code> by default.
440 	 *
441 	 * @return the additionalFunctions
442 	 */
443 	public boolean isAdditionalFunctions() {
444 		return additionalFunctions;
445 	}
446 
447 	/**
448 	 * Whether to enable additional functions (_sleep/_dump);
449 	 * <code>false</code> by default.
450 	 *
451 	 * @param additionalFunctions the additionalFunctions to set
452 	 */
453 	public void setAdditionalFunctions(boolean additionalFunctions) {
454 		this.additionalFunctions = additionalFunctions;
455 	}
456 
457 	/**
458 	 * Whether to enable additional type functions (_INTEGER/_DOUBLE/_STRING);
459 	 * <code>false</code> by default.
460 	 *
461 	 * @return the additionalTypeFunctions
462 	 */
463 	public boolean isAdditionalTypeFunctions() {
464 		return additionalTypeFunctions;
465 	}
466 
467 	/**
468 	 * Whether to enable additional type functions (_INTEGER/_DOUBLE/_STRING);
469 	 * <code>false</code> by default.
470 	 *
471 	 * @param additionalTypeFunctions the additionalTypeFunctions to set
472 	 */
473 	public void setAdditionalTypeFunctions(boolean additionalTypeFunctions) {
474 		this.additionalTypeFunctions = additionalTypeFunctions;
475 	}
476 
477 	/**
478 	 * Whether to maintain array keys in sorted order;
479 	 * <code>false</code> by default.
480 	 *
481 	 * @return the useSortedArrayKeys
482 	 */
483 	public boolean isUseSortedArrayKeys() {
484 		return useSortedArrayKeys;
485 	}
486 
487 	/**
488 	 * Whether to maintain array keys in sorted order;
489 	 * <code>false</code> by default.
490 	 *
491 	 * @param useSortedArrayKeys the useSortedArrayKeys to set
492 	 */
493 	public void setUseSortedArrayKeys(boolean useSortedArrayKeys) {
494 		this.useSortedArrayKeys = useSortedArrayKeys;
495 	}
496 
497 	/**
498 	 * Whether user extensions are enabled;
499 	 * <code>false</code> by default.
500 	 *
501 	 * @return the userExtensions
502 	 */
503 	public boolean isUserExtensions() {
504 		return userExtensions;
505 	}
506 
507 	/**
508 	 * Whether user extensions are enabled;
509 	 * <code>false</code> by default.
510 	 *
511 	 * @param userExtensions the userExtensions to set
512 	 */
513 	public void setUserExtensions(boolean userExtensions) {
514 		this.userExtensions = userExtensions;
515 	}
516 
517 	/**
518 	 * Write to intermediate file;
519 	 * <code>false</code> by default.
520 	 *
521 	 * @return the writeIntermediateFile
522 	 */
523 	public boolean isWriteIntermediateFile() {
524 		return writeIntermediateFile;
525 	}
526 
527 	/**
528 	 * Write to intermediate file;
529 	 * <code>false</code> by default.
530 	 *
531 	 * @param writeIntermediateFile the writeIntermediateFile to set
532 	 */
533 	public void setWriteIntermediateFile(boolean writeIntermediateFile) {
534 		this.writeIntermediateFile = writeIntermediateFile;
535 	}
536 
537 	/**
538 	 * Output filename;
539 	 * <code>null</code> by default,
540 	 * which means the appropriate default file-name will get used.
541 	 *
542 	 * @return the outputFilename
543 	 */
544 	public String getOutputFilename() {
545 		return outputFilename;
546 	}
547 
548 	/**
549 	 * Output filename;
550 	 * <code>null</code> by default,
551 	 * which means the appropriate default file-name will get used.
552 	 *
553 	 * @param outputFilename the outputFilename to set
554 	 */
555 	public void setOutputFilename(String outputFilename) {
556 		this.outputFilename = outputFilename;
557 	}
558 
559 	/**
560 	 * Output stream;
561 	 * <code>System.out</code> by default,
562 	 * which means we will print to stdout by default
563 	 *
564 	 * @return the output stream
565 	 */
566 	public PrintStream getOutputStream() {
567 		return outputStream;
568 	}
569 
570 	/**
571 	 * Sets the OutputStream to print to (instead of System.out by default)
572 	 *
573 	 * @param pOutputStream OutputStream to use for print statements
574 	 */
575 	public void setOutputStream(PrintStream pOutputStream) {
576 		outputStream = pOutputStream;
577 	}
578 
579 	/**
580 	 * Compiled destination directory (if provided);
581 	 * <code>"."</code> by default.
582 	 *
583 	 * @return the destinationDirectory
584 	 */
585 	public String getDestinationDirectory() {
586 		return destinationDirectory;
587 	}
588 
589 	/**
590 	 * Compiled destination directory (if provided).
591 	 *
592 	 * @param destinationDirectory the destinationDirectory to set,
593 	 *   <code>"."</code> by default.
594 	 */
595 	public void setDestinationDirectory(String destinationDirectory) {
596 
597 		if (destinationDirectory == null) {
598 			throw new IllegalArgumentException("The destination directory might never be null (you might want to use \".\")");
599 		}
600 
601 		this.destinationDirectory = destinationDirectory;
602 	}
603 
604 	/**
605 	 * Whether to trap <code>IllegalFormatExceptions</code>
606 	 * for <code>[s]printf</code>;
607 	 * <code>true</code> by default.
608 	 *
609 	 * @return the catchIllegalFormatExceptions
610 	 */
611 	public boolean isCatchIllegalFormatExceptions() {
612 		return catchIllegalFormatExceptions;
613 	}
614 
615 	/**
616 	 * Whether to trap <code>IllegalFormatExceptions</code>
617 	 * for <code>[s]printf</code>;
618 	 * <code>true</code> by default.
619 	 *
620 	 * @param catchIllegalFormatExceptions the catchIllegalFormatExceptions to set
621 	 */
622 	public void setCatchIllegalFormatExceptions(boolean catchIllegalFormatExceptions) {
623 		this.catchIllegalFormatExceptions = catchIllegalFormatExceptions;
624 	}
625 
626 	/**
627 	 * <p>Getter for the field <code>locale</code>.</p>
628 	 *
629 	 * @return the Locale that will be used for outputting numbers
630 	 */
631 	public Locale getLocale() {
632 		return locale;
633 	}
634 
635 	/**
636 	 * Sets the Locale for outputting numbers
637 	 *
638 	 * @param pLocale The locale to be used (e.g.: <code>Locale.US</code>)
639 	 */
640 	public void setLocale(Locale pLocale) {
641 		locale = pLocale;
642 	}
643 
644 	/**
645 	 * <p>Getter for the field <code>defaultRS</code>.</p>
646 	 *
647 	 * @return the default RS, when not set by the AWK script
648 	 */
649 	public String getDefaultRS() {
650 		return defaultRS;
651 	}
652 
653 	/**
654 	 * Sets the default RS, when not set by the AWK script
655 	 *
656 	 * @param rs The regular expression that separates records
657 	 */
658 	public void setDefaultRS(String rs) {
659 		defaultRS = rs;
660 	}
661 
662 	/**
663 	 * <p>Getter for the field <code>defaultORS</code>.</p>
664 	 *
665 	 * @return the default ORS, when not set by the AWK script
666 	 */
667 	public String getDefaultORS() {
668 		return defaultORS;
669 	}
670 
671 	/**
672 	 * Sets the default ORS, when not set by the AWK script
673 	 *
674 	 * @param ors The string that separates output records (with the print statement)
675 	 */
676 	public void setDefaultORS(String ors) {
677 		defaultORS = ors;
678 	}
679 }