1 package io.jawk.jrt;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import java.io.Closeable;
26 import java.io.FileInputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30 import java.nio.charset.StandardCharsets;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Objects;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class StreamInputSource implements InputSource, Closeable {
57
58 private final InputStream defaultInput;
59 private final VariableManager vm;
60 private final JRT jrt;
61
62
63 private Map<Object, Object> arglistMap;
64 private int arglistIdx;
65 private int arglistMaxKey;
66 private boolean hasFilenames;
67
68
69 private PartitioningReader partitioningReader;
70 private boolean currentFromFilenameList;
71 private String currentRecord;
72 private boolean currentReaderExhausted;
73
74
75
76
77
78
79
80
81
82
83
84 public StreamInputSource(InputStream defaultInput, VariableManager vm, JRT jrt) {
85 this.defaultInput = Objects.requireNonNull(defaultInput, "defaultInput");
86 this.vm = Objects.requireNonNull(vm, "vm");
87 this.jrt = Objects.requireNonNull(jrt, "jrt");
88 }
89
90
91 @Override
92 public boolean nextRecord() throws IOException {
93 initializeArgList();
94
95 while (true) {
96 if (partitioningReader == null || currentReaderExhausted) {
97 if (!prepareNextReader()) {
98 return false;
99 }
100 currentReaderExhausted = false;
101 }
102
103 String nextRecord = partitioningReader.readRecord();
104 if (nextRecord != null) {
105 currentRecord = nextRecord;
106 currentFromFilenameList = partitioningReader.fromFilenameList();
107 return true;
108 }
109 if (!partitioningReader.fromFilenameList()) {
110 return false;
111 }
112 currentReaderExhausted = true;
113 }
114 }
115
116
117 @Override
118 public String getRecordText() {
119 return currentRecord;
120 }
121
122
123
124
125
126
127
128 @Override
129 public List<String> getFields() {
130 return null;
131 }
132
133
134 @Override
135 public boolean isFromFilenameList() {
136 return currentFromFilenameList;
137 }
138
139
140
141
142
143
144
145 public void setRecordSeparator(String rs) {
146 if (partitioningReader != null) {
147 partitioningReader.setRecordSeparator(rs);
148 }
149 }
150
151
152
153
154
155
156
157 PartitioningReader getPartitioningReader() {
158 return partitioningReader;
159 }
160
161
162
163
164
165
166
167
168 private void initializeArgList() {
169 if (arglistMap != null) {
170 return;
171 }
172 arglistMap = toArgvMap(vm.getARGV());
173 arglistMaxKey = computeMaxArgvKey();
174 arglistIdx = 1;
175 hasFilenames = detectFilenames();
176 }
177
178 private Map<Object, Object> toArgvMap(Object argv) {
179 if (!(argv instanceof Map)) {
180 throw new IllegalArgumentException("ARGV must be a Map.");
181 }
182 @SuppressWarnings("unchecked")
183 Map<Object, Object> argvMap = (Map<Object, Object>) argv;
184 return argvMap;
185 }
186
187
188
189
190
191
192 private int computeMaxArgvKey() {
193 int max = 0;
194 for (Object key : arglistMap.keySet()) {
195 int idx = (int) JRT.toLong(key);
196 if (idx > max) {
197 max = idx;
198 }
199 }
200 return max;
201 }
202
203
204
205
206
207
208
209 private boolean detectFilenames() {
210 int traversalArgCount = getTraversalArgCount();
211 boolean found = false;
212 for (int i = 1; i < traversalArgCount && !found; i++) {
213 Object argValue = getArgvValue(i);
214 if (argValue == MISSING_ARGV_VALUE) {
215 continue;
216 }
217 String arg = jrt.toAwkString(argValue);
218 if (arg.isEmpty() || arg.indexOf('=') > 0) {
219 continue;
220 }
221 found = true;
222 }
223 return found;
224 }
225
226
227
228
229
230
231 private int getArgCount() {
232 long raw = JRT.toLong(vm.getARGC());
233 if (raw <= 0) {
234 return 0;
235 }
236 if (raw > Integer.MAX_VALUE) {
237 return Integer.MAX_VALUE;
238 }
239 return (int) raw;
240 }
241
242
243
244
245
246
247
248
249 private int getTraversalArgCount() {
250 int argCount = getArgCount();
251 if (argCount <= 0) {
252 return 0;
253 }
254 return Math.min(argCount, arglistMaxKey + 1);
255 }
256
257
258
259
260
261
262
263
264 private String nextArgument() {
265 int traversalArgCount = getTraversalArgCount();
266 while (arglistIdx < traversalArgCount) {
267 int idx = arglistIdx++;
268 Object argValue = getArgvValue(idx);
269 if (argValue == MISSING_ARGV_VALUE) {
270 continue;
271 }
272 String arg = jrt.toAwkString(argValue);
273 if (!arg.isEmpty()) {
274 return arg;
275 }
276 }
277 return null;
278 }
279
280 private static final Object MISSING_ARGV_VALUE = new Object();
281
282 private Object getArgvValue(int index) {
283 Long longIndex = Long.valueOf(index);
284 if (arglistMap instanceof AssocArray) {
285 return JRT.containsAwkKey(arglistMap, longIndex) ? JRT.getAwkValue(arglistMap, longIndex) : MISSING_ARGV_VALUE;
286 }
287 if (arglistMap.containsKey(longIndex)) {
288 return arglistMap.get(longIndex);
289 }
290 Integer intIndex = Integer.valueOf(index);
291 if (arglistMap.containsKey(intIndex)) {
292 return arglistMap.get(intIndex);
293 }
294 for (Map.Entry<Object, Object> entry : arglistMap.entrySet()) {
295 Object key = entry.getKey();
296 if (!(key instanceof Number)) {
297 continue;
298 }
299 double numericKey = ((Number) key).doubleValue();
300 if (JRT.isActuallyLong(numericKey) && ((long) Math.rint(numericKey)) == index) {
301 return entry.getValue();
302 }
303 }
304 return MISSING_ARGV_VALUE;
305 }
306
307
308
309
310
311
312
313
314
315
316 private boolean prepareNextReader() throws IOException {
317 boolean ready = false;
318 arglistMaxKey = computeMaxArgvKey();
319 hasFilenames = detectFilenames();
320 while (!ready) {
321 String arg = nextArgument();
322 if (arg == null) {
323
324 hasFilenames = detectFilenames();
325 if (partitioningReader == null && !hasFilenames) {
326 partitioningReader = new PartitioningReader(
327 new InputStreamReader(defaultInput, StandardCharsets.UTF_8),
328 jrt.getRSString());
329 jrt.setFILENAMEViaJrt("");
330 return true;
331 }
332 closeCurrentReaderIfFileStream();
333 return false;
334 }
335 if (arg.indexOf('=') > 0) {
336 setFilelistVariable(arg);
337
338 arglistMaxKey = computeMaxArgvKey();
339 hasFilenames = detectFilenames();
340 if (partitioningReader == null && !hasFilenames) {
341 partitioningReader = new PartitioningReader(
342 new InputStreamReader(defaultInput, StandardCharsets.UTF_8),
343 jrt.getRSString());
344 jrt.setFILENAMEViaJrt("");
345 return true;
346 }
347 if (partitioningReader != null) {
348 jrt.setNR(jrt.getNR() + 1);
349 }
350 } else {
351 closeCurrentReaderIfFileStream();
352 partitioningReader = new PartitioningReader(
353 new InputStreamReader(new FileInputStream(arg), StandardCharsets.UTF_8),
354 jrt.getRSString(),
355 true);
356 jrt.setFILENAMEViaJrt(arg);
357 jrt.setFNR(0L);
358 ready = true;
359 }
360 }
361 return true;
362 }
363
364
365
366
367
368
369 private void closeCurrentReaderIfFileStream() {
370 if (partitioningReader != null && partitioningReader.fromFilenameList()) {
371 try {
372 partitioningReader.close();
373 } catch (IOException ignored) {
374
375 }
376 }
377 }
378
379
380
381
382
383
384
385
386
387
388 @Override
389 public void close() throws IOException {
390 closeCurrentReaderIfFileStream();
391 }
392
393
394
395
396
397
398
399 private void setFilelistVariable(String nameValue) {
400 int eqIdx = nameValue.indexOf('=');
401 if (eqIdx == 0) {
402 throw new IllegalArgumentException(
403 "Must have a non-blank variable name in a name=value variable assignment argument.");
404 }
405 String name = nameValue.substring(0, eqIdx);
406 String value = nameValue.substring(eqIdx + 1);
407 Object obj;
408 try {
409 obj = Integer.parseInt(value);
410 } catch (NumberFormatException nfe) {
411 try {
412 obj = Double.parseDouble(value);
413 } catch (NumberFormatException nfe2) {
414 obj = value;
415 }
416 }
417 vm.assignVariable(name, obj);
418 }
419 }