001: // Copyright 2004, 2005, 2006 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.ioc.internal.util;
016:
017: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
018:
019: import java.util.HashMap;
020: import java.util.IdentityHashMap;
021: import java.util.List;
022: import java.util.Map;
023:
024: /**
025: * Used to "uniquify" names within a given context. A base name is passed in, and the return value
026: * is the base name, or the base name extended with a suffix to make it unique.
027: * <p>
028: * This class is not threadsafe.
029: */
030:
031: public final class IdAllocator {
032: private static final String SEPARATOR = "_";
033:
034: /** Map from allocated id to a generator for names associated with the allocated id. */
035: private final Map<String, NameGenerator> _generatorMap;
036:
037: private final String _namespace;
038:
039: /** Generates unique names with a particular prefix. */
040: private static class NameGenerator implements Cloneable {
041: private final String _baseId;
042:
043: private int _index;
044:
045: NameGenerator(String baseId) {
046: _baseId = baseId + SEPARATOR;
047: }
048:
049: public String nextId() {
050: return _baseId + _index++;
051: }
052:
053: /** Clones this instance, returning an equivalent but seperate copy. */
054: @Override
055: public NameGenerator clone() {
056: try {
057: return (NameGenerator) super .clone();
058: } catch (CloneNotSupportedException ex) {
059: // Unreachable!
060: throw new RuntimeException(ex);
061: }
062: }
063: }
064:
065: /** Creates a new allocator with no namespace. */
066: public IdAllocator() {
067: this ("");
068: }
069:
070: /** Creates a new allocator with the provided namespace. */
071: public IdAllocator(String namespace) {
072: this (namespace, new HashMap<String, NameGenerator>());
073: }
074:
075: private IdAllocator(String namespace,
076: Map<String, NameGenerator> generatorMap) {
077: _namespace = namespace;
078: _generatorMap = generatorMap;
079: }
080:
081: /** Returns a list of all allocated ids, sorted alphabetically. */
082: public List<String> getAllocatedIds() {
083: return InternalUtils.sortedKeys(_generatorMap);
084: }
085:
086: /**
087: * Creates a clone of this IdAllocator instance, copying the allocator's namespace and key map.
088: */
089: @Override
090: public IdAllocator clone() {
091: // Copying the _generatorMap is tricky; multiple keys will point to the same NameGenerator
092: // instance. We need to clone the NameGenerators, then build a new map around the clones.
093:
094: IdentityHashMap<NameGenerator, NameGenerator> transformMap = new IdentityHashMap<NameGenerator, NameGenerator>();
095:
096: for (NameGenerator original : _generatorMap.values()) {
097: NameGenerator copy = original.clone();
098:
099: transformMap.put(original, copy);
100: }
101:
102: Map<String, NameGenerator> mapCopy = newMap();
103:
104: for (String key : _generatorMap.keySet()) {
105: NameGenerator original = _generatorMap.get(key);
106: NameGenerator copy = transformMap.get(original);
107:
108: mapCopy.put(key, copy);
109: }
110:
111: return new IdAllocator(_namespace, mapCopy);
112: }
113:
114: /**
115: * Allocates the id. Repeated calls for the same name will return "name", "name_0", "name_1",
116: * etc.
117: */
118:
119: public String allocateId(String name) {
120: String key = name + _namespace;
121:
122: NameGenerator g = _generatorMap.get(key);
123: String result = null;
124:
125: if (g == null) {
126: g = new NameGenerator(key);
127: result = key;
128: } else
129: result = g.nextId();
130:
131: // Handle the degenerate case, where a base name of the form "foo_0" has been
132: // requested. Skip over any duplicates thus formed.
133:
134: while (_generatorMap.containsKey(result))
135: result = g.nextId();
136:
137: _generatorMap.put(result, g);
138:
139: return result;
140: }
141:
142: /** Checks to see if a given name has been allocated. */
143: public boolean isAllocated(String name) {
144: return _generatorMap.containsKey(name);
145: }
146:
147: /**
148: * Clears the allocator, resetting it to freshly allocated state.
149: */
150:
151: public void clear() {
152: _generatorMap.clear();
153: }
154: }
|