001: // Copyright 2007 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.util;
016:
017: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
018: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
019: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newSet;
020:
021: import java.io.Serializable;
022: import java.util.LinkedHashMap;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.Set;
026:
027: import org.apache.tapestry.PrimaryKeyEncoder;
028: import org.apache.tapestry.ioc.internal.util.Defense;
029:
030: /**
031: * A default, extensible version of {@link PrimaryKeyEncoder} that is based on loading known values
032: * into an internal map. When there's a reasonable number (hundreds, perhaps thousands) of items to
033: * choose from, and those items are fast and cheap to read and instantiate, this implementation is a
034: * good bet. For very large result sets, you'll need to create your own implementation of
035: * {@link PrimaryKeyEncoder}.
036: *
037: * @param <K>
038: * the key type (which must be serializable)
039: * @param <V>
040: * the value type
041: */
042: public class DefaultPrimaryKeyEncoder<K extends Serializable, V>
043: implements PrimaryKeyEncoder<K, V> {
044: private final Map<K, V> _keyToValue = new LinkedHashMap<K, V>();
045:
046: private final Map<V, K> _valueToKey = newMap();
047:
048: private Set<K> _deletedKeys;
049:
050: private K _currentKey;
051:
052: /** Adds a new key/value pair to the encoder. */
053: public final void add(K key, V value) {
054: Defense.notNull(key, "key");
055: Defense.notNull(value, "value");
056:
057: V existing = _keyToValue.get(key);
058: if (existing != null)
059: throw new IllegalArgumentException(UtilMessages
060: .duplicateKey(key, value, existing));
061:
062: _keyToValue.put(key, value);
063:
064: // TODO: Ensure that the value is unique?
065:
066: _valueToKey.put(value, key);
067: }
068:
069: /**
070: * Returns the values previously {@link #add(Serializable, Object) added to the encoder},
071: * <em>in the order in which they were added</em>. Values that are deleted are not returned.
072: *
073: * @return ordered list of values
074: */
075: public final List<V> getValues() {
076: return valuesNotInKeySet(_deletedKeys);
077: }
078:
079: /**
080: * Returns a list of all the values <em>except</em> those values whose keys are in the
081: * provided set. The set may be null, in which case all values are returned.
082: *
083: * @param keySet
084: * set of keys identifying values to exclude, or null to exclude no values
085: * @return values (not in the set) in order origionally added
086: */
087: protected final List<V> valuesNotInKeySet(Set<K> keySet) {
088: if (keySet == null || keySet.isEmpty())
089: return getAllValues();
090:
091: List<V> result = newList();
092:
093: for (Map.Entry<K, V> entry : _keyToValue.entrySet()) {
094:
095: if (keySet.contains(entry.getKey()))
096: continue;
097:
098: result.add(entry.getValue());
099: }
100:
101: return result;
102: }
103:
104: public final List<V> getAllValues() {
105: List<V> result = newList();
106:
107: for (Map.Entry<K, V> entry : _keyToValue.entrySet()) {
108: result.add(entry.getValue());
109: }
110:
111: return result;
112: }
113:
114: /**
115: * For a previously {@link #add(Serializable, Object) added key/value pair}, returns the key
116: * corresponding to the given value.
117: */
118: public final K toKey(V value) {
119: Defense.notNull(value, "value");
120:
121: _currentKey = _valueToKey.get(value);
122:
123: if (_currentKey == null)
124: throw new IllegalArgumentException(UtilMessages
125: .missingValue(value, _valueToKey.keySet()));
126:
127: return _currentKey;
128: }
129:
130: public final V toValue(K key) {
131: V result = _keyToValue.get(key);
132:
133: if (result == null) {
134: result = provideMissingObject(key);
135:
136: _currentKey = key;
137: } else {
138: _currentKey = key;
139: }
140:
141: return result;
142: }
143:
144: /**
145: * Invoked by {@link #toValue(Serializable)} whenever a key can not be converted to a value
146: * using the internal cache. This is an opportunity to record the fact that an error occured
147: * (they key was not valuable, possibly because it points to a deleted entity object) and
148: * provide a temporary object. This method may return null, but in a typical application, that
149: * will likely case NullPointerExceptions further down the processing chain.
150: * <p>
151: * This implementation returns null, and is intended to be overriden in subclasses.
152: *
153: * @param key
154: * key for which a value is required
155: * @return a substitute value, or null
156: */
157: protected V provideMissingObject(K key) {
158: return null;
159: }
160:
161: public final boolean isDeleted() {
162: return inKeySet(_deletedKeys);
163: }
164:
165: public final void setDeleted(boolean value) {
166: _deletedKeys = modifyKeySet(_deletedKeys, value);
167: }
168:
169: /**
170: * Returns true if the current key is in the provided set.
171: *
172: * @param keySet
173: * the set of keys to check, or null
174: * @return true if the key is in the set, false if it is missing (or if keySet is null)
175: */
176: protected final boolean inKeySet(Set<K> keySet) {
177: return keySet != null ? keySet.contains(_currentKey) : false;
178: }
179:
180: /**
181: * Modifies a keySet to add or remove the current key. If necessary, a new Set is created.
182: * <p>
183: * Useage: <code>
184: * private Set<K> _myFlagKeys;
185: *
186: * public boolean void setMyFlag(boolean value)
187: * {
188: * _myFlagKeys = modifySet(_myFlagKeys, value);
189: * }
190: * </code>
191: *
192: * @param keySet
193: * the set of keys, or null
194: * @param value
195: * true to add the current key, false to remove
196: * @return the provided key set, or a new one
197: */
198: protected final Set<K> modifyKeySet(Set<K> keySet, boolean value) {
199: if (keySet == null) {
200: if (!value)
201: return null;
202:
203: keySet = newSet();
204: }
205:
206: if (value)
207: keySet.add(_currentKey);
208: else
209: keySet.remove(_currentKey);
210:
211: return keySet;
212: }
213:
214: /** Does nothing. Subclasses may override as necessary. */
215: public void prepareForKeys(List<K> keys) {
216: }
217: }
|