blob: d18a2a28b4aa449860976e34bae5f3f7a09850cd [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001package aQute.lib.osgi;
2
3import java.io.*;
4import java.lang.reflect.*;
5import java.net.*;
6import java.text.*;
7import java.util.*;
8import java.util.regex.*;
9
10import aQute.lib.io.*;
11import aQute.libg.sed.*;
12import aQute.libg.version.*;
13
14/**
15 * Provide a macro processor. This processor can replace variables in strings
16 * based on a properties and a domain. The domain can implement functions that
17 * start with a "_" and take args[], the names of these functions are available
18 * as functions in the macro processor (without the _). Macros can nest to any
19 * depth but may not contain loops.
20 *
21 * Add POSIX macros:
22 * ${#parameter}
23 String length.
24
25${parameter%word}
26 Remove smallest suffix pattern.
27
28${parameter%%word}
29 Remove largest suffix pattern.
30
31${parameter#word}
32 Remove smallest prefix pattern.
33
34${parameter##word}
35 Remove largest prefix pattern.
36 */
37public class Macro implements Replacer {
38 Processor domain;
39 Object targets[];
40 boolean flattening;
41
42 public Macro(Processor domain, Object... targets) {
43 this.domain = domain;
44 this.targets = targets;
45 if (targets != null) {
46 for (Object o : targets) {
47 assert o != null;
48 }
49 }
50 }
51
52 public String process(String line, Processor source) {
53 return process(line, new Link(source,null,line));
54 }
55
56 String process(String line, Link link) {
57 StringBuffer sb = new StringBuffer();
58 process(line, 0, '\u0000', '\u0000', sb, link);
59 return sb.toString();
60 }
61
62 int process(CharSequence org, int index, char begin, char end, StringBuffer result, Link link) {
63 StringBuilder line = new StringBuilder(org);
64 int nesting = 1;
65
66 StringBuffer variable = new StringBuffer();
67 outer: while (index < line.length()) {
68 char c1 = line.charAt(index++);
69 if (c1 == end) {
70 if (--nesting == 0) {
71 result.append(replace(variable.toString(), link));
72 return index;
73 }
74 } else if (c1 == begin)
75 nesting++;
76 else if (c1 == '\\' && index < line.length() - 1 && line.charAt(index) == '$') {
77 // remove the escape backslash and interpret the dollar as a
78 // literal
79 index++;
80 variable.append('$');
81 continue outer;
82 } else if (c1 == '$' && index < line.length() - 2) {
83 char c2 = line.charAt(index);
84 char terminator = getTerminator(c2);
85 if (terminator != 0) {
86 index = process(line, index + 1, c2, terminator, variable, link);
87 continue outer;
88 }
89 }
90 variable.append(c1);
91 }
92 result.append(variable);
93 return index;
94 }
95
96 public static char getTerminator(char c) {
97 switch (c) {
98 case '(':
99 return ')';
100 case '[':
101 return ']';
102 case '{':
103 return '}';
104 case '<':
105 return '>';
106 case '\u00ab': // Guillemet double << >>
107 return '\u00bb';
108 case '\u2039': // Guillemet single
109 return '\u203a';
110 }
111 return 0;
112 }
113
114 protected String replace(String key, Link link) {
115 if (link != null && link.contains(key))
116 return "${infinite:" + link.toString() + "}";
117
118 if (key != null) {
119 key = key.trim();
120 if (key.length() > 0) {
121 Processor source = domain;
122 String value = null;
123 while( source != null) {
124 value = source.getProperties().getProperty(key);
125 if ( value != null )
126 break;
127
128 source = source.getParent();
129 }
130
131 if (value != null)
132 return process(value, new Link(source,link, key));
133
134 value = doCommands(key, link);
135 if (value != null)
136 return process(value, new Link(source, link, key));
137
138 if (key != null && key.trim().length() > 0) {
139 value = System.getProperty(key);
140 if (value != null)
141 return value;
142 }
143 if (!flattening)
144 domain.warning("No translation found for macro: " + key);
145 } else {
146 domain.warning("Found empty macro key");
147 }
148 } else {
149 domain.warning("Found null macro key");
150 }
151 return "${" + key + "}";
152 }
153
154 /**
155 * Parse the key as a command. A command consist of parameters separated by
156 * ':'.
157 *
158 * @param key
159 * @return
160 */
161 static Pattern commands = Pattern.compile("(?<!\\\\);");
162
163 private String doCommands(String key, Link source) {
164 String[] args = commands.split(key);
165 if (args == null || args.length == 0)
166 return null;
167
168 for (int i = 0; i < args.length; i++)
169 if (args[i].indexOf('\\') >= 0)
170 args[i] = args[i].replaceAll("\\\\;", ";");
171
172
173 if ( args[0].startsWith("^")) {
174 String varname = args[0].substring(1).trim();
175
176 Processor parent = source.start.getParent();
177 if ( parent != null)
178 return parent.getProperty(varname);
179 else
180 return null;
181 }
182
183
184 Processor rover = domain;
185 while (rover != null) {
186 String result = doCommand(rover, args[0], args);
187 if (result != null)
188 return result;
189
190 rover = rover.getParent();
191 }
192
193 for (int i = 0; targets != null && i < targets.length; i++) {
194 String result = doCommand(targets[i], args[0], args);
195 if (result != null)
196 return result;
197 }
198
199 return doCommand(this, args[0], args);
200 }
201
202 private String doCommand(Object target, String method, String[] args) {
203 if (target == null)
204 ; // System.out.println("Huh? Target should never be null " +
205 // domain);
206 else {
207 String cname = "_" + method.replaceAll("-", "_");
208 try {
209 Method m = target.getClass().getMethod(cname, new Class[] { String[].class });
210 return (String) m.invoke(target, new Object[] { args });
211 } catch (NoSuchMethodException e) {
212 // Ignore
213 } catch (InvocationTargetException e) {
214 if ( e.getCause() instanceof IllegalArgumentException ) {
215 domain.error("%s, for cmd: %s, arguments; %s", e.getMessage(), method, Arrays.toString(args));
216 } else {
217 domain.warning("Exception in replace: " + e.getCause());
218 e.getCause().printStackTrace();
219 }
220 } catch (Exception e) {
221 domain.warning("Exception in replace: " + e + " method=" + method);
222 e.printStackTrace();
223 }
224 }
225 return null;
226 }
227
228 /**
229 * Return a unique list where the duplicates are removed.
230 *
231 * @param args
232 * @return
233 */
234 static String _uniqHelp = "${uniq;<list> ...}";
235
236 public String _uniq(String args[]) {
237 verifyCommand(args, _uniqHelp, null, 1, Integer.MAX_VALUE);
238 Set<String> set = new LinkedHashSet<String>();
239 for (int i = 1; i < args.length; i++) {
240 Processor.split(args[i], set);
241 }
242 return Processor.join(set, ",");
243 }
244
245 public String _pathseparator(String args[]) {
246 return File.pathSeparator;
247 }
248
249 public String _separator(String args[]) {
250 return File.separator;
251 }
252
253 public String _filter(String args[]) {
254 return filter(args, false);
255 }
256
257 public String _filterout(String args[]) {
258 return filter(args, true);
259
260 }
261
262 static String _filterHelp = "${%s;<list>;<regex>}";
263
264 String filter(String[] args, boolean include) {
265 verifyCommand(args, String.format(_filterHelp, args[0]), null, 3, 3);
266
267 Collection<String> list = new ArrayList<String>(Processor.split(args[1]));
268 Pattern pattern = Pattern.compile(args[2]);
269
270 for (Iterator<String> i = list.iterator(); i.hasNext();) {
271 if (pattern.matcher(i.next()).matches() == include)
272 i.remove();
273 }
274 return Processor.join(list);
275 }
276
277 static String _sortHelp = "${sort;<list>...}";
278
279 public String _sort(String args[]) {
280 verifyCommand(args, _sortHelp, null, 2, Integer.MAX_VALUE);
281
282 List<String> result = new ArrayList<String>();
283 for (int i = 1; i < args.length; i++) {
284 Processor.split(args[i], result);
285 }
286 Collections.sort(result);
287 return Processor.join(result);
288 }
289
290 static String _joinHelp = "${join;<list>...}";
291
292 public String _join(String args[]) {
293
294 verifyCommand(args, _joinHelp, null, 1, Integer.MAX_VALUE);
295
296 List<String> result = new ArrayList<String>();
297 for (int i = 1; i < args.length; i++) {
298 Processor.split(args[i], result);
299 }
300 return Processor.join(result);
301 }
302
303 static String _ifHelp = "${if;<condition>;<iftrue> [;<iffalse>] }";
304
305 public String _if(String args[]) {
306 verifyCommand(args, _ifHelp, null, 3, 4);
307 String condition = args[1].trim();
308 if (condition.length() != 0)
309 return args[2];
310 if (args.length > 3)
311 return args[3];
312 else
313 return "";
314 }
315
316 public String _now(String args[]) {
317 return new Date().toString();
318 }
319
320 public static String _fmodifiedHelp = "${fmodified;<list of filenames>...}, return latest modification date";
321
322 public String _fmodified(String args[]) throws Exception {
323 verifyCommand(args, _fmodifiedHelp, null, 2, Integer.MAX_VALUE);
324
325 long time = 0;
326 Collection<String> names = new ArrayList<String>();
327 for (int i = 1; i < args.length; i++) {
328 Processor.split(args[i], names);
329 }
330 for (String name : names) {
331 File f = new File(name);
332 if (f.exists() && f.lastModified() > time)
333 time = f.lastModified();
334 }
335 return "" + time;
336 }
337
338 public String _long2date(String args[]) {
339 try {
340 return new Date(Long.parseLong(args[1])).toString();
341 } catch (Exception e) {
342 e.printStackTrace();
343 }
344 return "not a valid long";
345 }
346
347 public String _literal(String args[]) {
348 if (args.length != 2)
349 throw new RuntimeException("Need a value for the ${literal;<value>} macro");
350 return "${" + args[1] + "}";
351 }
352
353 public String _def(String args[]) {
354 if (args.length != 2)
355 throw new RuntimeException("Need a value for the ${def;<value>} macro");
356
357 return domain.getProperty(args[1], "");
358 }
359
360 /**
361 *
362 * replace ; <list> ; regex ; replace
363 *
364 * @param args
365 * @return
366 */
367 public String _replace(String args[]) {
368 if (args.length != 4) {
369 domain.warning("Invalid nr of arguments to replace " + Arrays.asList(args));
370 return null;
371 }
372
373 String list[] = args[1].split("\\s*,\\s*");
374 StringBuffer sb = new StringBuffer();
375 String del = "";
376 for (int i = 0; i < list.length; i++) {
377 String element = list[i].trim();
378 if (!element.equals("")) {
379 sb.append(del);
380 sb.append(element.replaceAll(args[2], args[3]));
381 del = ", ";
382 }
383 }
384
385 return sb.toString();
386 }
387
388 public String _warning(String args[]) {
389 for (int i = 1; i < args.length; i++) {
390 domain.warning(process(args[i]));
391 }
392 return "";
393 }
394
395 public String _error(String args[]) {
396 for (int i = 1; i < args.length; i++) {
397 domain.error(process(args[i]));
398 }
399 return "";
400 }
401
402 /**
403 * toclassname ; <path>.class ( , <path>.class ) *
404 *
405 * @param args
406 * @return
407 */
408 static String _toclassnameHelp = "${classname;<list of class names>}, convert class paths to FQN class names ";
409
410 public String _toclassname(String args[]) {
411 verifyCommand(args, _toclassnameHelp, null, 2, 2);
412 Collection<String> paths = Processor.split(args[1]);
413
414 List<String> names = new ArrayList<String>(paths.size());
415 for (String path : paths) {
416 if (path.endsWith(".class")) {
417 String name = path.substring(0, path.length() - 6).replace('/', '.');
418 names.add(name);
419 } else if (path.endsWith(".java")) {
420 String name = path.substring(0, path.length() - 5).replace('/', '.');
421 names.add(name);
422 } else {
423 domain.warning("in toclassname, " + args[1]
424 + " is not a class path because it does not end in .class");
425 }
426 }
427 return Processor.join(names, ",");
428 }
429
430 /**
431 * toclassname ; <path>.class ( , <path>.class ) *
432 *
433 * @param args
434 * @return
435 */
436
437 static String _toclasspathHelp = "${toclasspath;<list>[;boolean]}, convert a list of class names to paths";
438
439 public String _toclasspath(String args[]) {
440 verifyCommand(args, _toclasspathHelp, null, 2, 3);
441 boolean cl = true;
442 if (args.length > 2)
443 cl = new Boolean(args[2]);
444
445 Collection<String> names = Processor.split(args[1]);
446 Collection<String> paths = new ArrayList<String>(names.size());
447 for (String name : names) {
448 String path = name.replace('.', '/') + (cl ? ".class" : "");
449 paths.add(path);
450 }
451 return Processor.join(paths, ",");
452 }
453
454 public String _dir(String args[]) {
455 if (args.length < 2) {
456 domain.warning("Need at least one file name for ${dir;...}");
457 return null;
458 } else {
459 String del = "";
460 StringBuffer sb = new StringBuffer();
461 for (int i = 1; i < args.length; i++) {
462 File f = domain.getFile(args[i]);
463 if (f.exists() && f.getParentFile().exists()) {
464 sb.append(del);
465 sb.append(f.getParentFile().getAbsolutePath());
466 del = ",";
467 }
468 }
469 return sb.toString();
470 }
471
472 }
473
474 public String _basename(String args[]) {
475 if (args.length < 2) {
476 domain.warning("Need at least one file name for ${basename;...}");
477 return null;
478 } else {
479 String del = "";
480 StringBuffer sb = new StringBuffer();
481 for (int i = 1; i < args.length; i++) {
482 File f = domain.getFile(args[i]);
483 if (f.exists() && f.getParentFile().exists()) {
484 sb.append(del);
485 sb.append(f.getName());
486 del = ",";
487 }
488 }
489 return sb.toString();
490 }
491
492 }
493
494 public String _isfile(String args[]) {
495 if (args.length < 2) {
496 domain.warning("Need at least one file name for ${isfile;...}");
497 return null;
498 } else {
499 boolean isfile = true;
500 for (int i = 1; i < args.length; i++) {
501 File f = new File(args[i]).getAbsoluteFile();
502 isfile &= f.isFile();
503 }
504 return isfile ? "true" : "false";
505 }
506
507 }
508
509 public String _isdir(String args[]) {
510 if (args.length < 2) {
511 domain.warning("Need at least one file name for ${isdir;...}");
512 return null;
513 } else {
514 boolean isdir = true;
515 for (int i = 1; i < args.length; i++) {
516 File f = new File(args[i]).getAbsoluteFile();
517 isdir &= f.isDirectory();
518 }
519 return isdir ? "true" : "false";
520 }
521
522 }
523
524 public String _tstamp(String args[]) {
525 String format = "yyyyMMddHHmm";
526 long now = System.currentTimeMillis();
527
528 if (args.length > 1) {
529 format = args[1];
530 if (args.length > 2) {
531 now = Long.parseLong(args[2]);
532 if (args.length > 3) {
533 domain.warning("Too many arguments for tstamp: " + Arrays.toString(args));
534 }
535 }
536 }
537 SimpleDateFormat sdf = new SimpleDateFormat(format);
538 return sdf.format(new Date(now));
539 }
540
541 /**
542 * Wildcard a directory. The lists can contain Instruction that are matched
543 * against the given directory
544 *
545 * ${lsr;<dir>;<list>(;<list>)*} ${lsa;<dir>;<list>(;<list>)*}
546 *
547 * @author aqute
548 *
549 */
550
551 public String _lsr(String args[]) {
552 return ls(args, true);
553 }
554
555 public String _lsa(String args[]) {
556 return ls(args, false);
557 }
558
559 String ls(String args[], boolean relative) {
560 if (args.length < 2)
561 throw new IllegalArgumentException(
562 "the ${ls} macro must at least have a directory as parameter");
563
564 File dir = domain.getFile(args[1]);
565 if (!dir.isAbsolute())
566 throw new IllegalArgumentException(
567 "the ${ls} macro directory parameter is not absolute: " + dir);
568
569 if (!dir.exists())
570 throw new IllegalArgumentException(
571 "the ${ls} macro directory parameter does not exist: " + dir);
572
573 if (!dir.isDirectory())
574 throw new IllegalArgumentException(
575 "the ${ls} macro directory parameter points to a file instead of a directory: "
576 + dir);
577
578 String[] files = dir.list();
579 List<String> result;
580
581 if (args.length < 3) {
582 result = Arrays.asList(files);
583 } else
584 result = new ArrayList<String>();
585
586 for (int i = 2; i < args.length; i++) {
587 String parts[] = args[i].split("\\s*,\\s*");
588 for (String pattern : parts) {
589 // So make it in to an instruction
590 Instruction instr = Instruction.getPattern(pattern);
591
592 // For each project, match it against the instruction
593 for (int f = 0; f < files.length; f++) {
594 if (files[f] != null) {
595 if (instr.matches(files[f])) {
596 if (!instr.isNegated()) {
597 if (relative)
598 result.add(files[f]);
599 else
600 result.add(new File(dir, files[f]).getAbsolutePath());
601 }
602 files[f] = null;
603 }
604 }
605 }
606 }
607 }
608 return Processor.join(result, ",");
609 }
610
611 public String _currenttime(String args[]) {
612 return Long.toString(System.currentTimeMillis());
613 }
614
615 /**
616 * Modify a version to set a version policy. Thed policy is a mask that is
617 * mapped to a version.
618 *
619 * <pre>
620 * + increment
621 * - decrement
622 * = maintain
623 * &tilde; discard
624 *
625 * ==+ = maintain major, minor, increment micro, discard qualifier
626 * &tilde;&tilde;&tilde;= = just get the qualifier
627 * version=&quot;[${version;==;${@}},${version;=+;${@}})&quot;
628 * </pre>
629 *
630 *
631 *
632 *
633 * @param args
634 * @return
635 */
636 final static String MASK_STRING = "[\\-+=~0123456789]{0,3}[=~]?";
637 final static Pattern MASK = Pattern.compile(MASK_STRING);
638 final static String _versionHelp = "${version;<mask>;<version>}, modify a version\n"
639 + "<mask> ::= [ M [ M [ M [ MQ ]]]\n"
640 + "M ::= '+' | '-' | MQ\n"
641 + "MQ ::= '~' | '='";
642 final static Pattern _versionPattern[] = new Pattern[] { null, null, MASK,
643 Verifier.VERSION };
644
645 public String _version(String args[]) {
646 verifyCommand(args, _versionHelp, null, 2, 3);
647
648 String mask = args[1];
649
650 Version version = null;
651 if (args.length >= 3)
652 version = new Version(args[2]);
653
654 return version(version, mask);
655 }
656
657 String version(Version version, String mask) {
658 if (version == null) {
659 String v = domain.getProperty("@");
660 if (v == null) {
661 domain
662 .error(
663 "No version specified for ${version} or ${range} and no implicit version ${@} either, mask=%s",
664 mask);
665 v = "0";
666 }
667 version = new Version(v);
668 }
669
670 StringBuilder sb = new StringBuilder();
671 String del = "";
672
673 for (int i = 0; i < mask.length(); i++) {
674 char c = mask.charAt(i);
675 String result = null;
676 if (c != '~') {
677 if (i == 3) {
678 result = version.getQualifier();
679 } else if (Character.isDigit(c)) {
680 // Handle masks like +00, =+0
681 result = String.valueOf(c);
682 } else {
683 int x = version.get(i);
684 switch (c) {
685 case '+':
686 x++;
687 break;
688 case '-':
689 x--;
690 break;
691 case '=':
692 break;
693 }
694 result = Integer.toString(x);
695 }
696 if (result != null) {
697 sb.append(del);
698 del = ".";
699 sb.append(result);
700 }
701 }
702 }
703 return sb.toString();
704 }
705
706 /**
707 * Schortcut for version policy
708 *
709 * <pre>
710 * -provide-policy : ${policy;[==,=+)}
711 * -consume-policy : ${policy;[==,+)}
712 * </pre>
713 *
714 * @param args
715 * @return
716 */
717
718 static Pattern RANGE_MASK = Pattern.compile("(\\[|\\()(" + MASK_STRING + "),(" + MASK_STRING +")(\\]|\\))");
719 static String _rangeHelp = "${range;<mask>[;<version>]}, range for version, if version not specified lookyp ${@}\n"
720 + "<mask> ::= [ M [ M [ M [ MQ ]]]\n"
721 + "M ::= '+' | '-' | MQ\n" + "MQ ::= '~' | '='";
722 static Pattern _rangePattern[] = new Pattern[] { null, RANGE_MASK };
723
724 public String _range(String args[]) {
725 verifyCommand(args, _rangeHelp, _rangePattern, 2, 3);
726 Version version = null;
727 if (args.length >= 3)
728 version = new Version(args[2]);
729
730 String spec = args[1];
731
732 Matcher m = RANGE_MASK.matcher(spec);
733 m.matches();
734 String floor = m.group(1);
735 String floorMask = m.group(2);
736 String ceilingMask = m.group(3);
737 String ceiling = m.group(4);
738
739 StringBuilder sb = new StringBuilder();
740 sb.append(floor);
741 sb.append(version(version, floorMask));
742 sb.append(",");
743 sb.append(version(version, ceilingMask));
744 sb.append(ceiling);
745
746 String s = sb.toString();
747 VersionRange vr = new VersionRange(s);
748 if (!(vr.includes(vr.getHigh()) || vr.includes(vr.getLow()))) {
749 domain.error("${range} macro created an invalid range %s from %s and mask %s", s,
750 version, spec);
751 }
752 return sb.toString();
753 }
754
755 /**
756 * System command. Execute a command and insert the result.
757 *
758 * @param args
759 * @param help
760 * @param patterns
761 * @param low
762 * @param high
763 */
764 public String _system(String args[]) throws Exception {
765 verifyCommand(args, "${system;<command>[;<in>]}, execute a system command", null, 2, 3);
766 String command = args[1];
767 String input = null;
768
769 if (args.length > 2) {
770 input = args[2];
771 }
772
773 Process process = Runtime.getRuntime().exec(command, null, domain.getBase());
774 if (input != null) {
775 process.getOutputStream().write(input.getBytes("UTF-8"));
776 }
777 process.getOutputStream().close();
778
779 String s = IO.collect(process.getInputStream(), "UTF-8");
780 int exitValue = process.waitFor();
781 if (exitValue != 0) {
782 domain.error("System command " + command + " failed with " + exitValue);
783 }
784 return s.trim();
785 }
786
787 public String _env(String args[]) {
788 verifyCommand(args, "${env;<name>}, get the environmet variable", null, 2, 2);
789
790 try {
791 return System.getenv(args[1]);
792 } catch (Throwable t) {
793 return null;
794 }
795 }
796
797 /**
798 * Get the contents of a file.
799 *
800 * @param in
801 * @return
802 * @throws IOException
803 */
804
805 public String _cat(String args[]) throws IOException {
806 verifyCommand(args, "${cat;<in>}, get the content of a file", null, 2, 2);
807 File f = domain.getFile(args[1]);
808 if (f.isFile()) {
809 return IO.collect(f);
810 } else if (f.isDirectory()) {
811 return Arrays.toString(f.list());
812 } else {
813 try {
814 URL url = new URL(args[1]);
815 return IO.collect(url, "UTF-8");
816 } catch (MalformedURLException mfue) {
817 // Ignore here
818 }
819 return null;
820 }
821 }
822
823 public static void verifyCommand(String args[], String help, Pattern[] patterns, int low,
824 int high) {
825 String message = "";
826 if (args.length > high) {
827 message = "too many arguments";
828 } else if (args.length < low) {
829 message = "too few arguments";
830 } else {
831 for (int i = 0; patterns != null && i < patterns.length && i < args.length; i++) {
832 if (patterns[i] != null) {
833 Matcher m = patterns[i].matcher(args[i]);
834 if (!m.matches())
835 message += String.format("Argument %s (%s) does not match %s\n", i,
836 args[i], patterns[i].pattern());
837 }
838 }
839 }
840 if (message.length() != 0) {
841 StringBuilder sb = new StringBuilder();
842 String del = "${";
843 for (String arg : args) {
844 sb.append(del);
845 sb.append(arg);
846 del = ";";
847 }
848 sb.append("}, is not understood. ");
849 sb.append(message);
850 throw new IllegalArgumentException(sb.toString());
851 }
852 }
853
854 // Helper class to track expansion of variables
855 // on the stack.
856 static class Link {
857 Link previous;
858 String key;
859 Processor start;
860
861 public Link(Processor start, Link previous, String key) {
862 this.previous = previous;
863 this.key = key;
864 this.start = start;
865 }
866
867 public boolean contains(String key) {
868 if (this.key.equals(key))
869 return true;
870
871 if (previous == null)
872 return false;
873
874 return previous.contains(key);
875 }
876
877 public String toString() {
878 StringBuffer sb = new StringBuffer();
879 String del = "[";
880 for (Link r = this; r != null; r = r.previous) {
881 sb.append(del);
882 sb.append(r.key);
883 del = ",";
884 }
885 sb.append("]");
886 return sb.toString();
887 }
888 }
889
890 /**
891 * Take all the properties and translate them to actual values. This method
892 * takes the set properties and traverse them over all entries, including
893 * the default properties for that properties. The values no longer contain
894 * macros.
895 *
896 * @return A new Properties with the flattened values
897 */
898 public Properties getFlattenedProperties() {
899 // Some macros only work in a lower processor, so we
900 // do not report unknown macros while flattening
901 flattening = true;
902 try {
903 Properties flattened = new Properties();
904 Properties source = domain.getProperties();
905 for (Enumeration<?> e = source.propertyNames(); e.hasMoreElements();) {
906 String key = (String) e.nextElement();
907 if (!key.startsWith("_"))
908 if (key.startsWith("-"))
909 flattened.put(key, source.getProperty(key));
910 else
911 flattened.put(key, process(source.getProperty(key)));
912 }
913 return flattened;
914 } finally {
915 flattening = false;
916 }
917 };
918
919 public static String _fileHelp = "${file;<base>;<paths>...}, create correct OS dependent path";
920
921 public String _osfile(String args[]) {
922 verifyCommand(args, _fileHelp, null, 3, 3);
923 File base = new File(args[1]);
924 File f = Processor.getFile(base, args[2]);
925 return f.getAbsolutePath();
926 }
927
928 public String _path(String args[]) {
929 List<String> list = new ArrayList<String>();
930 for (int i = 1; i < args.length; i++) {
931 list.addAll(Processor.split(args[i]));
932 }
933 return Processor.join(list, File.pathSeparator);
934 }
935
936 public static Properties getParent(Properties p) {
937 try {
938 Field f = Properties.class.getDeclaredField("defaults");
939 f.setAccessible(true);
940 return (Properties) f.get(p);
941 } catch (Exception e) {
942 Field[] fields = Properties.class.getFields();
943 System.out.println(Arrays.toString(fields));
944 return null;
945 }
946 }
947
948 public String process(String line) {
949 return process(line,domain);
950 }
951
952}