Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
IdAllocator |
|
| 1.7777777777777777;1.778 |
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 | 224 | 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 | 337 | { |
51 | 337 | _baseId = baseId + SEPARATOR; |
52 | 337 | } |
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 | 212 | this(""); |
77 | 212 | } |
78 | ||
79 | public IdAllocator(String namespace) |
|
80 | 224 | { |
81 | 224 | Defense.notNull(namespace, "namespace"); |
82 | ||
83 | 224 | _namespace = namespace; |
84 | 224 | } |
85 | ||
86 | /** |
|
87 | * Allocates the id. Repeated calls for the same name will return "name", |
|
88 | * "name_0", "name_1", etc. |
|
89 | * |
|
90 | * @param name |
|
91 | * The base id to allocate new unique ids from. |
|
92 | * |
|
93 | * @return A unique version of the passed in id. |
|
94 | */ |
|
95 | ||
96 | public String allocateId(String name) |
|
97 | { |
|
98 | 388 | String key = name + _namespace; |
99 | ||
100 | 388 | NameGenerator g = (NameGenerator) _generatorMap.get(key.toLowerCase()); |
101 | 388 | String result = null; |
102 | ||
103 | 388 | if (g == null) |
104 | { |
|
105 | 336 | g = new NameGenerator(key); |
106 | 336 | result = key; |
107 | } |
|
108 | else |
|
109 | 52 | result = g.nextId(); |
110 | ||
111 | // Handle the degenerate case, where a base name of the form "foo$0" has |
|
112 | // been |
|
113 | // requested. Skip over any duplicates thus formed. |
|
114 | ||
115 | 389 | while(_generatorMap.containsKey(result.toLowerCase())) |
116 | 1 | result = g.nextId(); |
117 | ||
118 | 388 | _generatorMap.put(result.toLowerCase(), g); |
119 | ||
120 | 388 | return result; |
121 | } |
|
122 | ||
123 | /** |
|
124 | * Should return the exact same thing as {@link #allocateId(String)}, with the difference |
|
125 | * that the calculated id is not allocated and stored so multiple calls will always return the |
|
126 | * same thing. |
|
127 | * |
|
128 | * @param name The name to peek at. |
|
129 | * @return The next id that will be allocated for the given name. |
|
130 | */ |
|
131 | public String peekNextId(String name) |
|
132 | { |
|
133 | 7 | String key = name + _namespace; |
134 | ||
135 | 7 | NameGenerator g = (NameGenerator) _generatorMap.get(key.toLowerCase()); |
136 | 7 | String result = null; |
137 | ||
138 | 7 | if (g == null) |
139 | { |
|
140 | 1 | g = new NameGenerator(key); |
141 | 1 | result = key; |
142 | } else |
|
143 | 6 | result = g.peekId(); |
144 | ||
145 | // Handle the degenerate case, where a base name of the form "foo_0" has |
|
146 | // been |
|
147 | // requested. Skip over any duplicates thus formed. |
|
148 | ||
149 | // in a peek we don't want to actually increment any id state so we must |
|
150 | // clone |
|
151 | ||
152 | 7 | if (_generatorMap.containsKey(result.toLowerCase())) { |
153 | ||
154 | try { |
|
155 | 0 | NameGenerator cg = (NameGenerator)g.clone(); |
156 | ||
157 | 0 | while (_generatorMap.containsKey(result.toLowerCase())) |
158 | 0 | result = cg.nextId(); |
159 | ||
160 | 0 | } catch (CloneNotSupportedException e) { |
161 | 0 | throw new ApplicationRuntimeException(e); |
162 | 0 | } |
163 | } |
|
164 | ||
165 | 7 | return result; |
166 | } |
|
167 | ||
168 | /** |
|
169 | * Clears the allocator, resetting it to freshly allocated state. |
|
170 | */ |
|
171 | ||
172 | public void clear() |
|
173 | { |
|
174 | 51 | _generatorMap.clear(); |
175 | 51 | } |
176 | } |