1
2 package org.sentrysoftware.jawk.ext;
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import java.io.File;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.Map;
34
35 import org.sentrysoftware.jawk.NotImplementedError;
36 import org.sentrysoftware.jawk.jrt.AssocArray;
37 import org.sentrysoftware.jawk.jrt.AwkRuntimeException;
38 import org.sentrysoftware.jawk.jrt.BlockObject;
39 import org.sentrysoftware.jawk.jrt.IllegalAwkArgumentException;
40 import org.sentrysoftware.jawk.jrt.JRT;
41 import org.sentrysoftware.jawk.jrt.VariableManager;
42 import org.sentrysoftware.jawk.util.AwkLogger;
43 import org.slf4j.Logger;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194 public class CoreExtension extends AbstractExtension implements JawkExtension {
195
196 private static CoreExtension instance = null;
197 private static final Object INSTANCE_LOCK = new Object();
198 private static final Logger LOG = AwkLogger.getLogger(CoreExtension.class);
199
200 private int refMapIdx = 0;
201 private Map<String, Object> referenceMap = new HashMap<String, Object>();
202 private Map<AssocArray, Iterator<?>> iterators = new HashMap<AssocArray, Iterator<?>>();
203 private static final Integer ZERO = Integer.valueOf(0);
204 private static final Integer ONE = Integer.valueOf(1);
205 private int waitInt = 0;
206
207
208 private final Date dateObj = new Date();
209 private final SimpleDateFormat dateFormat = new SimpleDateFormat();
210
211 private final BlockObject timeoutBlocker = new BlockObject() {
212
213 @Override
214 public String getNotifierTag() {
215 return "Timeout";
216 }
217
218 @Override
219 public final void block()
220 throws InterruptedException
221 {
222 synchronized (timeoutBlocker) {
223 timeoutBlocker.wait(waitInt);
224 }
225 }
226 };
227
228
229
230
231 public CoreExtension() {
232 synchronized (INSTANCE_LOCK) {
233 if (instance == null) {
234 instance = this;
235 } else {
236 LOG.warn("Multiple CoreExtension instances in this VM. Using original instance.");
237 }
238 }
239 }
240
241
242 @Override
243 public String getExtensionName() {
244 return "Core Extension";
245 }
246
247
248 @Override
249 public String[] extensionKeywords() {
250 return new String[] {
251 "Array",
252 "Map",
253 "HashMap",
254 "TreeMap",
255 "LinkedMap",
256 "MapUnion",
257 "MapCopy",
258 "TypeOf",
259 "String",
260 "Double",
261 "Halt",
262 "Dereference",
263 "DeRef",
264 "NewReference",
265 "NewRef",
266 "Unreference",
267 "UnRef",
268 "InRef",
269 "IsInRef",
270 "DumpRefs",
271 "Timeout",
272 "Throw",
273 "Version",
274
275 "Date",
276 "FileExists",
277 };
278 }
279
280
281 @Override
282 public int[] getAssocArrayParameterPositions(String extensionKeyword, int numArgs) {
283 if (( extensionKeyword.equals("Map")
284 || extensionKeyword.equals("HashMap")
285 || extensionKeyword.equals("LinkedMap")
286 || extensionKeyword.equals("TreeMap")) && ((numArgs % 2) == 1))
287 {
288
289
290 return new int[] {0};
291 } else if (extensionKeyword.equals("Array")) {
292
293
294 return new int[] {0};
295 } else if (extensionKeyword.equals("NewReference")
296 || extensionKeyword.equals("NewRef"))
297 {
298 if (numArgs == 1) {
299 return new int[] {0};
300 } else {
301 return super.getAssocArrayParameterPositions(extensionKeyword, numArgs);
302 }
303 } else {
304 return super.getAssocArrayParameterPositions(extensionKeyword, numArgs);
305 }
306 }
307
308
309 @Override
310 public Object invoke(String keyword, Object[] args) {
311 if (keyword.equals("Map") || keyword.equals("HashMap")) {
312 return map(args, getVm(), AssocArray.MT_HASH);
313 } else if (keyword.equals("LinkedMap")) {
314 return map(args, getVm(), AssocArray.MT_LINKED);
315 } else if (keyword.equals("TreeMap")) {
316 return map(args, getVm(), AssocArray.MT_TREE);
317 } else if (keyword.equals("MapUnion")) {
318 return mapUnion(args, getVm(), AssocArray.MT_LINKED);
319 } else if (keyword.equals("MapCopy")) {
320 checkNumArgs(args, 2);
321 return mapCopy(args);
322 } else if (keyword.equals("Array")) {
323 return array(args, getVm());
324 } else if (keyword.equals("TypeOf")) {
325 checkNumArgs(args, 1);
326 return typeOf(args[0], getVm());
327 } else if (keyword.equals("String")) {
328 checkNumArgs(args, 1);
329 return toString(args[0], getVm());
330 } else if (keyword.equals("Double")) {
331 checkNumArgs(args, 1);
332 return toDouble(args[0], getVm());
333 } else if (keyword.equals("Halt")) {
334 if (args.length == 0) {
335 Runtime.getRuntime().halt(0);
336 } else if (args.length == 1) {
337 Runtime.getRuntime().halt((int) JRT.toDouble(args[0]));
338 } else {
339 throw new IllegalAwkArgumentException(keyword + " requires 0 or 1 argument, not " + args.length);
340 }
341 } else if (keyword.equals("NewReference") || keyword.equals("NewRef")) {
342 if (args.length == 1) {
343 return newReference(args[0]);
344 } else if (args.length == 3) {
345 return newReference(toAwkString(args[0]), args[1], args[2]);
346 } else {
347 throw new IllegalAwkArgumentException(keyword + " requires 1 or 3 arguments, not " + args.length);
348 }
349 } else if (keyword.equals("Dereference") || keyword.equals("DeRef")) {
350 if (args.length == 1) {
351 return resolve(dereference(args[0], getVm()), getVm());
352 } else if (args.length == 2) {
353 return resolve(dereference(toAwkString(args[0]), args[1], getVm()), getVm());
354 } else {
355 throw new IllegalAwkArgumentException(keyword + " requires 1 or 2 arguments, not " + args.length);
356 }
357 } else if (keyword.equals("Unreference") || keyword.equals("UnRef")) {
358 checkNumArgs(args, 1);
359 return unreference(args[0], getVm());
360 } else if (keyword.equals("InRef")) {
361 checkNumArgs(args, 1);
362 return inref(args[0], getVm());
363 } else if (keyword.equals("IsInRef")) {
364 checkNumArgs(args, 2);
365 return isInRef(args[0], args[1], getVm());
366 } else if (keyword.equals("DumpRefs")) {
367 checkNumArgs(args, 0);
368 dumpRefs();
369 } else if (keyword.equals("Timeout")) {
370 checkNumArgs(args, 1);
371 return timeout((int) JRT.toDouble(args[0]));
372 } else if (keyword.equals("Throw")) {
373 throw new AwkRuntimeException(Arrays.toString(args));
374 } else if (keyword.equals("Version")) {
375 checkNumArgs(args, 1);
376 return version(args[0]);
377 } else if (keyword.equals("Date")) {
378 if (args.length == 0) {
379 return date();
380 } else if (args.length == 1) {
381 return date(toAwkString(args[0]));
382 } else {
383 throw new IllegalAwkArgumentException(keyword + " expects 0 or 1 argument, not " + args.length);
384 }
385 } else if (keyword.equals("FileExists")) {
386 checkNumArgs(args, 1);
387 return fileExists(toAwkString(args[0]));
388 } else {
389 throw new NotImplementedError(keyword);
390 }
391
392 return null;
393 }
394
395 private Object resolve(Object arg, VariableManager vm) {
396
397 Object obj = arg;
398 while (true) {
399 if (obj instanceof AssocArray) {
400 return obj;
401 }
402 String argCheck = toAwkString(obj);
403 if (referenceMap.get(argCheck) != null) {
404 obj = referenceMap.get(argCheck);
405 } else {
406 return obj;
407 }
408 }
409 }
410
411 static String newReference(Object arg) {
412 if (!(arg instanceof AssocArray)) {
413 throw new IllegalAwkArgumentException("NewRef[erence] requires an assoc array, not " + arg.getClass().getName());
414 }
415
416
417
418
419 int refIdx = instance.refMapIdx++;
420
421 String argStr;
422 if (arg instanceof AssocArray) {
423 argStr = arg.getClass().getName();
424 } else {
425 argStr = arg.toString();
426 }
427 if (argStr.length() > 63) {
428 argStr = argStr.substring(0, 60) + "...";
429 }
430
431 String retval = "@REFERENCE@ " + refIdx + " <" + argStr + ">";
432 instance.referenceMap.put(retval, arg);
433 return retval;
434 }
435
436
437 static Object newReference(String refstring, Object key, Object value) {
438 AssocArray aa = (AssocArray) instance.referenceMap.get(refstring);
439 if (aa == null) {
440 throw new IllegalAwkArgumentException("AssocArray reference doesn't exist.");
441 }
442 return aa.put(key, value);
443 }
444
445
446 private Object dereference(Object arg, VariableManager vm) {
447
448 if (arg instanceof AssocArray) {
449 throw new IllegalAwkArgumentException("an assoc array cannot be a reference handle");
450 } else {
451 String argCheck = toAwkString(arg);
452 return dereference(argCheck);
453 }
454 }
455
456
457 static Object dereference(String argCheck) {
458 if (instance.referenceMap.get(argCheck) != null) {
459 return instance.referenceMap.get(argCheck);
460 } else {
461 throw new IllegalAwkArgumentException(argCheck + " not a valid reference");
462 }
463 }
464
465
466
467 static Object dereference(String refstring, Object key, VariableManager vm) {
468 AssocArray aa = (AssocArray) instance.referenceMap.get(refstring);
469 if (aa == null) {
470 throw new IllegalAwkArgumentException("AssocArray reference doesn't exist.");
471 }
472 if (!(key instanceof AssocArray)) {
473
474 String keyCheck = instance.toAwkString(key);
475 if (instance.referenceMap.get(keyCheck) != null) {
476
477 key = instance.referenceMap.get(keyCheck);
478 }
479 }
480 return aa.get(key);
481 }
482
483 static int unreference(Object arg, VariableManager vm) {
484 String argCheck = instance.toAwkString(arg);
485 if (instance.referenceMap.get(argCheck) == null) {
486 throw new IllegalAwkArgumentException("Not a reference : " + argCheck);
487 }
488
489 instance.referenceMap.remove(argCheck);
490 assert instance.referenceMap.get(argCheck) == null;
491 return 1;
492 }
493
494 private Object inref(Object arg, VariableManager vm) {
495 if (arg instanceof AssocArray) {
496 throw new IllegalAwkArgumentException("InRef requires a Reference (string) argument, not an assoc array");
497 }
498 String argCheck = toAwkString(arg);
499 if (referenceMap.get(argCheck) == null) {
500 throw new IllegalAwkArgumentException("Not a reference : " + argCheck);
501 }
502 Object o = referenceMap.get(argCheck);
503 if (!(o instanceof AssocArray)) {
504 throw new IllegalAwkArgumentException("Reference not an assoc array. ref.class = " + o.getClass().getName());
505 }
506
507 AssocArray aa = (AssocArray) o;
508
509
510
511
512 Iterator<?> iter = iterators.get(aa);
513 if (iter == null)
514
515
516 {
517 iter = new ArrayList<Object>(aa.keySet()).iterator();
518 iterators.put(aa, iter);
519 }
520
521 Object retval = null;
522
523 while (iter.hasNext()) {
524 retval = iter.next();
525 if (retval instanceof String && retval.toString().equals("")) {
526 throw new AwkRuntimeException("Assoc array key contains a blank string ?!");
527 }
528 break;
529 }
530
531 if (retval == null) {
532 iterators.remove(aa);
533 retval = "";
534 }
535
536 if (retval instanceof AssocArray) {
537
538 for (String ref : referenceMap.keySet()) {
539 if (referenceMap.get(ref) == retval) {
540 return ref;
541 }
542 }
543
544
545 return newReference(retval);
546 } else {
547 return retval;
548 }
549 }
550
551 private int isInRef(Object ref, Object key, VariableManager vm) {
552 if (ref instanceof AssocArray) {
553 throw new IllegalAwkArgumentException("Expecting a reference string for the 1st argument, not an assoc array.");
554 }
555 String refstring = toAwkString(ref);
556 return isInRef(refstring, key);
557 }
558
559 static int isInRef(String refstring, Object key) {
560 Object o = instance.referenceMap.get(refstring);
561 if (o == null) {
562 throw new IllegalAwkArgumentException("Invalid refstring : " + refstring);
563 }
564 AssocArray aa = (AssocArray) o;
565 return aa.isIn(key) ? ONE : ZERO;
566 }
567
568 private void dumpRefs() {
569 for (Map.Entry<String, Object> entry : referenceMap.entrySet()) {
570 Object value = entry.getValue();
571 if (value instanceof AssocArray) {
572 value = ((AssocArray) value).mapString();
573 }
574 LOG.info("REF : {} = {}", new Object[] {entry.getKey(), value});
575 }
576 }
577
578 static String typeOf(Object arg, VariableManager vm) {
579 if (arg instanceof AssocArray) {
580 return "AssocArray";
581 } else if (arg instanceof Integer) {
582 return "Integer";
583 } else if (arg instanceof Double) {
584 return "Double";
585 } else {
586 String stringRep = instance.toAwkString(arg);
587 if (instance.referenceMap.get(stringRep) != null) {
588 return "Reference";
589 } else {
590 return "String";
591 }
592 }
593 }
594
595 @SuppressWarnings("unused")
596 private int get(AssocArray retval, AssocArray map, Object key) {
597 retval.clear();
598 retval.put(0, map.get(key));
599 return 1;
600 }
601
602 @SuppressWarnings("unused")
603 private Object toScalar(AssocArray aa) {
604 return aa.get(0);
605 }
606
607 private Object map(Object[] args, VariableManager vm, int mapType) {
608 if (args.length % 2 == 0) {
609 return subMap(args, vm, mapType);
610 } else {
611 return topLevelMap(args, vm, mapType, false);
612 }
613 }
614
615 private Object mapUnion(Object[] args, VariableManager vm, int mapType) {
616 return topLevelMap(args, vm, mapType, true);
617 }
618
619 private int topLevelMap(Object[] args, VariableManager vm, int mapType, boolean mapUnion) {
620 AssocArray aa = (AssocArray) args[0];
621 if (!mapUnion) {
622 aa.clear();
623 aa.useMapType(mapType);
624 }
625 int cnt = 0;
626 for (int i = 1; i < args.length; i += 2) {
627 if (args[i] instanceof AssocArray) {
628 args[i] = newReference(args[i]);
629 }
630 if (args[i + 1] instanceof AssocArray) {
631 args[i + 1] = newReference(args[i + 1]);
632 }
633
634 aa.put(args[i], args[i + 1]);
635
636 ++cnt;
637 }
638 return cnt;
639 }
640
641 private AssocArray subMap(Object[] args, VariableManager vm, int mapType) {
642 AssocArray aa = new AssocArray(false);
643 aa.useMapType(mapType);
644 for (int i = 0; i < args.length; i += 2) {
645 if (args[i] instanceof AssocArray) {
646 args[i] = newReference(args[i]);
647 }
648 if (args[i + 1] instanceof AssocArray) {
649 args[i + 1] = newReference(args[i + 1]);
650 }
651
652 aa.put(args[i], args[i + 1]);
653 }
654 return aa;
655 }
656
657 private int array(Object[] args, VariableManager vm) {
658 AssocArray aa = (AssocArray) args[0];
659 aa.clear();
660 aa.useMapType(AssocArray.MT_TREE);
661 String subsep = toAwkString(vm.getSUBSEP());
662 int cnt = 0;
663 for (int i = 1; i < args.length; ++i) {
664 Object o = args[i];
665 if (o instanceof AssocArray) {
666 AssocArray arr = (AssocArray) o;
667 for (Object key : arr.keySet()) {
668 aa.put("" + i + subsep + key, arr.get(key));
669 }
670 } else {
671 aa.put("" + i, o);
672 }
673
674 ++cnt;
675 }
676 return cnt;
677 }
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702 private int mapCopy(Object[] args) {
703 AssocArray aaTarget = (AssocArray) args[0];
704 AssocArray aaSource = (AssocArray) args[1];
705 aaTarget.clear();
706 int cnt = 0;
707 for (Object o : aaSource.keySet()) {
708 aaTarget.put(o, aaSource.get(o));
709 ++cnt;
710 }
711 return cnt;
712 }
713
714 private Object toDouble(Object arg, VariableManager vm) {
715 if (arg instanceof AssocArray) {
716 throw new IllegalArgumentException("Cannot deduce double value from an associative array.");
717 }
718 if (arg instanceof Number) {
719 return ((Number) arg).doubleValue();
720 }
721
722
723
724 try {
725 String str = toAwkString(arg);
726 double d = Double.parseDouble(str);
727 return d;
728 } catch (NumberFormatException nfe) {
729 return "";
730 }
731 }
732
733 private static String toString(Object arg, VariableManager vm) {
734 if (arg instanceof AssocArray) {
735 return ((AssocArray) arg).mapString();
736 } else {
737 return instance.toAwkString(arg);
738 }
739 }
740
741 private Object timeout(int ms) {
742 if (ms <= 0) {
743 throw new IllegalAwkArgumentException("Timeout requires a positive # argument, not " + ms + ".");
744 }
745 waitInt = ms;
746 return timeoutBlocker;
747 }
748
749 private String version(Object obj) {
750 if (obj instanceof AssocArray) {
751 return ((AssocArray) obj).getMapVersion();
752 } else {
753 Class<?> cls = (Class<?>) obj.getClass();
754 return cls.getPackage().getSpecificationVersion();
755 }
756 }
757
758 private String date() {
759 dateObj.setTime(System.currentTimeMillis());
760 return dateObj.toString();
761 }
762
763 private String date(String formatString) {
764 dateObj.setTime(System.currentTimeMillis());
765 dateFormat.applyPattern(formatString);
766 return dateFormat.format(dateObj);
767 }
768
769 private int fileExists(String path) {
770 if (new File(path).exists()) {
771 return ONE;
772 } else {
773 return ZERO;
774 }
775 }
776 }