Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DefinitionKeyHelper |
|
|
3.0;3 |
1 | /* |
|
2 | * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH |
|
3 | * |
|
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 | * you may not use this file except in compliance with the License. |
|
6 | * You may obtain a copy of the License at |
|
7 | * |
|
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 | * |
|
10 | * Unless required by applicable law or agreed to in writing, software |
|
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 | * See the License for the specific language governing permissions and |
|
14 | * limitations under the License. |
|
15 | */ |
|
16 | package de.smartics.properties.spi.config.definition; |
|
17 | ||
18 | import static de.smartics.properties.api.config.domain.key.ApplicationId.ANY_APP; |
|
19 | import static de.smartics.properties.api.config.domain.key.EnvironmentId.ANY_ENV; |
|
20 | ||
21 | import java.io.Serializable; |
|
22 | import java.util.StringTokenizer; |
|
23 | ||
24 | import javax.annotation.concurrent.ThreadSafe; |
|
25 | ||
26 | import de.smartics.properties.api.config.domain.key.ApplicationId; |
|
27 | import de.smartics.properties.api.config.domain.key.ConfigurationKey; |
|
28 | import de.smartics.properties.api.config.domain.key.EnvironmentId; |
|
29 | import de.smartics.util.lang.Arguments; |
|
30 | ||
31 | /** |
|
32 | * Derives a {@link ConfigurationKey} from a path found in a definition file. |
|
33 | */ |
|
34 | @ThreadSafe |
|
35 | public final class DefinitionKeyHelper implements Serializable |
|
36 | { |
|
37 | // ********************************* Fields ********************************* |
|
38 | ||
39 | // --- constants ------------------------------------------------------------ |
|
40 | ||
41 | /** |
|
42 | * The class version identifier. |
|
43 | */ |
|
44 | private static final long serialVersionUID = 1L; |
|
45 | ||
46 | // --- members -------------------------------------------------------------- |
|
47 | ||
48 | /** |
|
49 | * The context to evaluate the configuration keys from properties file paths. |
|
50 | * |
|
51 | * @serial |
|
52 | */ |
|
53 | private final PropertiesDefinitionContext context; |
|
54 | ||
55 | // ****************************** Initializer ******************************* |
|
56 | ||
57 | // ****************************** Constructors ****************************** |
|
58 | ||
59 | /** |
|
60 | * Convenience constructor using the default TLDs and not registering any |
|
61 | * environments, nodes or groups. |
|
62 | */ |
|
63 | public DefinitionKeyHelper() |
|
64 | { |
|
65 | 0 | this(new PropertiesDefinitionContext()); |
66 | 0 | } |
67 | ||
68 | /** |
|
69 | * Default constructor. |
|
70 | * |
|
71 | * @param context the context to evaluate the configuration keys from |
|
72 | * properties file paths. |
|
73 | * @throws NullPointerException if {@code context} is <code>null</code>. |
|
74 | */ |
|
75 | public DefinitionKeyHelper(final PropertiesDefinitionContext context) |
|
76 | throws NullPointerException |
|
77 | 0 | { |
78 | 0 | Arguments.checkNotNull("context", context); |
79 | ||
80 | 0 | this.context = context; |
81 | 0 | } |
82 | ||
83 | // ****************************** Inner Classes ***************************** |
|
84 | ||
85 | // ********************************* Methods ******************************** |
|
86 | ||
87 | // --- init ----------------------------------------------------------------- |
|
88 | ||
89 | // --- get&set -------------------------------------------------------------- |
|
90 | ||
91 | // --- business ------------------------------------------------------------- |
|
92 | ||
93 | /** |
|
94 | * Parses the given path to create a configuration key. |
|
95 | * <p> |
|
96 | * The expected syntax is as follows: |
|
97 | * </p> |
|
98 | * <ol> |
|
99 | * <li><code>/environment</code></li> |
|
100 | * <li><code>/environment/node</code></li> |
|
101 | * <li><code>/environment/node/group</code></li> |
|
102 | * <li><code>/environment/node/group/application</code></li> |
|
103 | * <li><code>/environment/node/group/application/version</code></li> |
|
104 | * <li><code>/environment/group</code></li> |
|
105 | * <li><code>/environment/group/application</code></li> |
|
106 | * <li><code>/environment/group/application/version</code></li> |
|
107 | * <li><code>/group</code></li> |
|
108 | * <li><code>/group/application</code></li> |
|
109 | * <li><code>/group/application/version</code></li> |
|
110 | * </ol> |
|
111 | * <p> |
|
112 | * A file ending with properties following the path will be chopped. |
|
113 | * </p> |
|
114 | * <p> |
|
115 | * The parser has to determine whether a part of the path is a |
|
116 | * <code>environment</code>, a <code>node</code> or a <code>group</code>. |
|
117 | * Since a <code>node</code> is always prefixed by an <code>environment</code> |
|
118 | * only the following two cases have to be dealt with: |
|
119 | * </p> |
|
120 | * <ol> |
|
121 | * <li><code>environment</code> vs. <code>group</code></li> |
|
122 | * <li><code>node</code> vs. <code>group</code></li> |
|
123 | * </ol> |
|
124 | * <p> |
|
125 | * <code>group</code>s start with |
|
126 | * </p> |
|
127 | * <ol> |
|
128 | * <li>A TLD as registered by default by |
|
129 | * {@link PropertiesDefinitionContext#DEFAULT_TLDS} or explicitly |
|
130 | * registered with {@link PropertiesDefinitionContext}</li> |
|
131 | * <li>Two letters followed by a dot (<code>.</code>)</li> |
|
132 | * <li>Any sequence of characters that is explicitly registered as a group in |
|
133 | * the <code>definition.xml</code> file</li> |
|
134 | * </ol> |
|
135 | * <p> |
|
136 | * <code>environment</code>s and <code>node</code>s do not start as |
|
137 | * <code>groups</code> except they are explicitly registered in the |
|
138 | * <code>definition.xml</code> file. |
|
139 | * </p> |
|
140 | * |
|
141 | * @param pathWithFile the path to parse. |
|
142 | * @return the configuration key. |
|
143 | * @throws IllegalArgumentException if the given {@code path} is not valid |
|
144 | * according to the rules given above. |
|
145 | */ |
|
146 | public ConfigurationKey parse(final String pathWithFile) |
|
147 | throws IllegalArgumentException |
|
148 | { |
|
149 | 0 | final ConfigurationKey explicitKey = fetchExplicitKey(pathWithFile); |
150 | 0 | if (explicitKey != null) |
151 | { |
|
152 | 0 | return explicitKey; |
153 | } |
|
154 | ||
155 | 0 | final String path = chopFile(pathWithFile); |
156 | ||
157 | 0 | final StringTokenizer tokenizer = new StringTokenizer(path, "/"); |
158 | 0 | if (tokenizer.hasMoreTokens()) |
159 | { |
|
160 | final ConfigurationKey key; |
|
161 | ||
162 | 0 | final String token = tokenizer.nextToken(); |
163 | 0 | if (isGroup(token)) |
164 | { |
|
165 | 0 | key = parseApplicationKey(token, tokenizer); |
166 | } |
|
167 | else |
|
168 | { |
|
169 | 0 | key = parseEnvironmentKey(token, tokenizer); |
170 | } |
|
171 | ||
172 | 0 | return key; |
173 | } |
|
174 | ||
175 | 0 | return new ConfigurationKey(EnvironmentId.ANY_ENV, ApplicationId.ANY_APP); |
176 | // throw new IllegalArgumentException( |
|
177 | // "Path '" + path + "' does not contain an environment or group."); |
|
178 | } |
|
179 | ||
180 | private static String chopFile(final String pathWithFile) |
|
181 | { |
|
182 | 0 | if (pathWithFile.endsWith(".properties")) |
183 | { |
|
184 | 0 | final int lastSlash = pathWithFile.lastIndexOf('/'); |
185 | 0 | if (lastSlash != -1) |
186 | { |
|
187 | 0 | final String path = pathWithFile.substring(0, lastSlash); |
188 | 0 | return path; |
189 | } |
|
190 | ||
191 | 0 | return ""; |
192 | } |
|
193 | 0 | return pathWithFile; |
194 | } |
|
195 | ||
196 | private ConfigurationKey fetchExplicitKey(final String path) |
|
197 | { |
|
198 | 0 | ConfigurationKey key = context.getKey(path); |
199 | 0 | if (key == null) |
200 | { |
|
201 | 0 | key = context.getKey(null); |
202 | } |
|
203 | 0 | return key; |
204 | } |
|
205 | ||
206 | private boolean isGroup(final String token) |
|
207 | { |
|
208 | 0 | if (context.isRegisteredEnvironment(token) |
209 | || context.isRegisteredNode(token)) |
|
210 | { |
|
211 | 0 | return false; |
212 | } |
|
213 | ||
214 | 0 | return context.isGroup(token); |
215 | } |
|
216 | ||
217 | private ConfigurationKey parseEnvironmentKey(final String environment, |
|
218 | final StringTokenizer tokenizer) |
|
219 | { |
|
220 | final EnvironmentId envId; |
|
221 | final ApplicationId appId; |
|
222 | 0 | if (tokenizer.hasMoreTokens()) |
223 | { |
|
224 | 0 | final String token = tokenizer.nextToken(); |
225 | 0 | if (isGroup(token)) |
226 | { |
|
227 | 0 | envId = new EnvironmentId(environment); |
228 | 0 | appId = parseAppId(token, tokenizer); |
229 | } |
|
230 | else |
|
231 | { |
|
232 | 0 | envId = new EnvironmentId(environment, token); |
233 | 0 | if (tokenizer.hasMoreTokens()) |
234 | { |
|
235 | 0 | appId = parseAppId(tokenizer.nextToken(), tokenizer); |
236 | } |
|
237 | else |
|
238 | { |
|
239 | 0 | appId = ANY_APP; |
240 | } |
|
241 | } |
|
242 | 0 | } |
243 | else |
|
244 | { |
|
245 | 0 | envId = new EnvironmentId(environment); |
246 | 0 | appId = ANY_APP; |
247 | } |
|
248 | ||
249 | 0 | final ConfigurationKey key = new ConfigurationKey(envId, appId); |
250 | ||
251 | 0 | return key; |
252 | } |
|
253 | ||
254 | private ApplicationId parseAppId(final String group, |
|
255 | final StringTokenizer tokenizer) |
|
256 | { |
|
257 | 0 | String artifact = null; |
258 | 0 | String version = null; |
259 | 0 | if (tokenizer.hasMoreTokens()) |
260 | { |
|
261 | 0 | artifact = tokenizer.nextToken(); |
262 | 0 | if (tokenizer.hasMoreTokens()) |
263 | { |
|
264 | 0 | version = tokenizer.nextToken(); |
265 | } |
|
266 | } |
|
267 | ||
268 | 0 | return new ApplicationId(group, artifact, version); |
269 | } |
|
270 | ||
271 | private ConfigurationKey parseApplicationKey(final String group, |
|
272 | final StringTokenizer tokenizer) |
|
273 | { |
|
274 | 0 | final EnvironmentId envId = ANY_ENV; |
275 | 0 | final ApplicationId appId = parseAppId(group, tokenizer); |
276 | ||
277 | 0 | return new ConfigurationKey(envId, appId); |
278 | } |
|
279 | ||
280 | // --- object basics -------------------------------------------------------- |
|
281 | ||
282 | } |