1 | package org.dcache.util; |
2 | |
3 | import java.io.File; |
4 | import java.io.FileReader; |
5 | import java.io.Reader; |
6 | import java.io.IOException; |
7 | import java.io.InputStream; |
8 | import java.io.PrintStream; |
9 | |
10 | import java.util.Properties; |
11 | import java.util.Stack; |
12 | import java.util.Set; |
13 | import java.util.HashSet; |
14 | import java.util.NoSuchElementException; |
15 | import java.util.Arrays; |
16 | import java.util.Collection; |
17 | import java.util.InvalidPropertiesFormatException; |
18 | import java.util.regex.Pattern; |
19 | |
20 | import dmg.util.Replaceable; |
21 | import dmg.util.Formats; |
22 | |
23 | /** |
24 | * ReplaceableProperties extends the regular Properties class with |
25 | * support for ${...} placeholders. |
26 | * |
27 | * Besides implementing Replaceable, the class extends the load |
28 | * methods in such a way that repeated definitions of the same |
29 | * properties is reported as an error. |
30 | */ |
31 | public class ReplaceableProperties |
32 | extends Properties |
33 | implements Replaceable |
34 | { |
35 | private boolean _loading = false; |
36 | private Stack<String> _replacementStack = new Stack<String>(); |
37 | |
38 | public ReplaceableProperties(Properties properties) |
39 | { |
40 | super(properties); |
41 | } |
42 | |
43 | /** |
44 | * @throws IllegalArgumentException during loading if a property |
45 | * is defined multiple times. |
46 | */ |
47 | public synchronized void load(Reader reader) throws IOException |
48 | { |
49 | _loading = true; |
50 | try { |
51 | super.load(reader); |
52 | } finally { |
53 | _loading = false; |
54 | } |
55 | } |
56 | |
57 | /** |
58 | * @throws IllegalArgumentException during loading if a property |
59 | * is defined multiple times. |
60 | */ |
61 | public synchronized void load(InputStream in) throws IOException |
62 | { |
63 | _loading = true; |
64 | try { |
65 | super.load(in); |
66 | } finally { |
67 | _loading = false; |
68 | } |
69 | } |
70 | |
71 | /** |
72 | * @throws IllegalArgumentException during loading if a property |
73 | * is defined multiple times. |
74 | */ |
75 | public synchronized void loadFromXML(InputStream in) |
76 | throws IOException, InvalidPropertiesFormatException |
77 | { |
78 | _loading = true; |
79 | try { |
80 | super.loadFromXML(in); |
81 | } finally { |
82 | _loading = false; |
83 | } |
84 | } |
85 | |
86 | /** |
87 | * Loads a Java properties file. |
88 | */ |
89 | public void loadFile(File file) |
90 | throws IOException |
91 | { |
92 | Reader in = new FileReader(file); |
93 | try { |
94 | load(in); |
95 | } finally { |
96 | in.close(); |
97 | } |
98 | } |
99 | |
100 | /** |
101 | * @throws IllegalArgumentException during loading if key is |
102 | * already defined. |
103 | */ |
104 | public Object put(Object key, Object value) |
105 | { |
106 | if (_loading && containsKey(key)) { |
107 | throw new IllegalArgumentException(String.format("%s is already defined", key)); |
108 | } |
109 | return super.put(key, value); |
110 | } |
111 | |
112 | /** |
113 | * Substitutes all placeholders in a string. |
114 | */ |
115 | public String replaceKeywords(String s) |
116 | { |
117 | return Formats.replaceKeywords(s, this); |
118 | } |
119 | |
120 | /** |
121 | * Returns the value of a property with all placeholders in the |
122 | * value substituted recursively. |
123 | */ |
124 | @Override |
125 | public synchronized String getReplacement(String name) |
126 | throws NoSuchElementException |
127 | { |
128 | String value = getProperty(name); |
129 | if (value != null) { |
130 | if (_replacementStack.search(name) == -1) { |
131 | _replacementStack.push(name); |
132 | try { |
133 | value = replaceKeywords(value); |
134 | } finally { |
135 | _replacementStack.pop(); |
136 | } |
137 | } |
138 | } |
139 | return value; |
140 | } |
141 | |
142 | public synchronized Set<String> |
143 | matchingStringPropertyNames(Collection<Pattern> patterns) |
144 | { |
145 | Set<String> matchingNames = new HashSet<String>(); |
146 | for (String key: stringPropertyNames()) { |
147 | for (Pattern pattern: patterns) { |
148 | if (pattern.matcher(key).matches()) { |
149 | matchingNames.add(key); |
150 | break; |
151 | } |
152 | } |
153 | } |
154 | return matchingNames; |
155 | } |
156 | } |