Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
IdAllocator |
|
| 1.9;1.9 |
1 | // Copyright 2004, 2005 The Apache Software Foundation |
|
2 | // |
|
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
|
4 | // you may not use this file except in compliance with the License. |
|
5 | // You may obtain a copy of the License at |
|
6 | // |
|
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
|
8 | // |
|
9 | // Unless required by applicable law or agreed to in writing, software |
|
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
|
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 | // See the License for the specific language governing permissions and |
|
13 | // limitations under the License. |
|
14 | ||
15 | package org.apache.tapestry.util; |
|
16 | ||
17 | import org.apache.hivemind.ApplicationRuntimeException; |
|
18 | import org.apache.hivemind.util.Defense; |
|
19 | ||
20 | import java.util.HashMap; |
|
21 | import java.util.Map; |
|
22 | ||
23 | /** |
|
24 | * Used to "uniquify" names within a given context. A base name is passed in, |
|
25 | * and the return value is the base name, or the base name extended with a |
|
26 | * suffix to make it unique. |
|
27 | * |
|
28 | * @author Howard Lewis Ship |
|
29 | * @since 3.0 |
|
30 | */ |
|
31 | ||
32 | public class IdAllocator |
|
33 | { |
|
34 | ||
35 | private static final String SEPARATOR = "_"; |
|
36 | ||
37 | 225 | private final Map _generatorMap = new HashMap(); |
38 | ||
39 | private final String _namespace; |
|
40 | ||
41 | /** Class used only by IdAllocator. */ |
|
42 | private class NameGenerator implements Cloneable |
|
43 | { |
|
44 | ||
45 | private final String _baseId; |
|
46 | ||
47 | private int _index; |
|
48 | ||
49 | NameGenerator(String baseId) |
|
50 | 336 | { |
51 | 336 | _baseId = baseId + SEPARATOR; |
52 | 336 | } |
53 | ||
54 | public String nextId() |
|
55 | { |
|
56 | 53 | return _baseId + _index++; |
57 | } |
|
58 | ||
59 | public String peekId() |
|
60 | { |
|
61 | 6 | return _baseId + _index; |
62 | } |
|
63 | ||
64 | /** |
|
65 | * {@inheritDoc} |
|
66 | */ |
|
67 | protected Object clone() |
|
68 | throws CloneNotSupportedException |
|
69 | { |
|
70 | 0 | return super.clone(); |
71 | } |
|
72 | } |
|
73 | ||
74 | public IdAllocator() |
|
75 | { |
|
76 | 213 | this(""); |
77 | 213 | } |
78 | ||
79 | public IdAllocator(String namespace) |
|
80 | 225 | { |
81 | 225 | Defense.notNull(namespace, "namespace"); |
82 | ||
83 | 225 | _namespace = namespace; |
84 | 225 | } |
85 | ||
86 | /** |
|
87 | * Utility for stripping out the standard allocator generated portion of a component id string |
|
88 | * in order to get what the most likely original component id was. |
|
89 | * |
|
90 | * @param input |
|
91 | * The generated component id. |
|
92 | * @return The id stripped of any allocated id meta, if any was found. |
|
93 | */ |
|
94 | public static String convertAllocatedComponentId(String input) |
|
95 | { |
|
96 | 4 | if (input == null) |
97 | 0 | return null; |
98 | ||
99 | 4 | int index = input.indexOf(SEPARATOR); |
100 | ||
101 | 4 | return index > -1 ? input.substring(0, index) : input; |
102 | } |
|
103 | ||
104 | /** |
|
105 | * Allocates the id. Repeated calls for the same name will return "name", |
|
106 | * "name_0", "name_1", etc. |
|
107 | */ |
|
108 | ||
109 | public String allocateId(String name) |
|
110 | { |
|
111 | 387 | String key = name + _namespace; |
112 | ||
113 | 387 | NameGenerator g = (NameGenerator) _generatorMap.get(key.toLowerCase()); |
114 | 387 | String result = null; |
115 | ||
116 | 387 | if (g == null) |
117 | { |
|
118 | 335 | g = new NameGenerator(key); |
119 | 335 | result = key; |
120 | } |
|
121 | else |
|
122 | 52 | result = g.nextId(); |
123 | ||
124 | // Handle the degenerate case, where a base name of the form "foo$0" has |
|
125 | // been |
|
126 | // requested. Skip over any duplicates thus formed. |
|
127 | ||
128 | 388 | while(_generatorMap.containsKey(result.toLowerCase())) |
129 | 1 | result = g.nextId(); |
130 | ||
131 | 387 | _generatorMap.put(result.toLowerCase(), g); |
132 | ||
133 | 387 | return result; |
134 | } |
|
135 | ||
136 | /** |
|
137 | * Should return the exact same thing as {@link #allocateId(String)}, with the difference |
|
138 | * that the calculated id is not allocated and stored so multiple calls will always return the |
|
139 | * same thing. |
|
140 | * |
|
141 | * @param name The name to peek at. |
|
142 | * @return The next id that will be allocated for the given name. |
|
143 | */ |
|
144 | public String peekNextId(String name) |
|
145 | { |
|
146 | 7 | String key = name + _namespace; |
147 | ||
148 | 7 | NameGenerator g = (NameGenerator) _generatorMap.get(key.toLowerCase()); |
149 | 7 | String result = null; |
150 | ||
151 | 7 | if (g == null) |
152 | { |
|
153 | 1 | g = new NameGenerator(key); |
154 | 1 | result = key; |
155 | } else |
|
156 | 6 | result = g.peekId(); |
157 | ||
158 | // Handle the degenerate case, where a base name of the form "foo_0" has |
|
159 | // been |
|
160 | // requested. Skip over any duplicates thus formed. |
|
161 | ||
162 | // in a peek we don't want to actually increment any id state so we must |
|
163 | // clone |
|
164 | ||
165 | 7 | if (_generatorMap.containsKey(result.toLowerCase())) { |
166 | ||
167 | try { |
|
168 | 0 | NameGenerator cg = (NameGenerator)g.clone(); |
169 | ||
170 | 0 | while (_generatorMap.containsKey(result.toLowerCase())) |
171 | 0 | result = cg.nextId(); |
172 | ||
173 | 0 | } catch (CloneNotSupportedException e) { |
174 | 0 | throw new ApplicationRuntimeException(e); |
175 | 0 | } |
176 | } |
|
177 | ||
178 | 7 | return result; |
179 | } |
|
180 | ||
181 | /** |
|
182 | * Clears the allocator, resetting it to freshly allocated state. |
|
183 | */ |
|
184 | ||
185 | public void clear() |
|
186 | { |
|
187 | 51 | _generatorMap.clear(); |
188 | 51 | } |
189 | } |