001: /*******************************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: *******************************************************************************/package org.ofbiz.base.util.collections;
019:
020: import java.util.Collection;
021: import java.util.Collections;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Locale;
025: import java.util.Map;
026: import java.util.Set;
027:
028: import javolution.lang.Reusable;
029: import javolution.realtime.ObjectFactory;
030: import javolution.util.FastList;
031: import javolution.util.FastMap;
032: import javolution.util.FastSet;
033:
034: import org.ofbiz.base.util.Debug;
035:
036: /**
037: * Map Stack
038: *
039: */
040: public class MapStack implements Map, Reusable, LocalizedMap {
041:
042: public static final String module = MapStack.class.getName();
043:
044: protected static final ObjectFactory mapStackFactory = new ObjectFactory() {
045: protected Object create() {
046: return new MapStack();
047: }
048: };
049:
050: public static MapStack create() {
051: MapStack newValue = (MapStack) mapStackFactory.object();
052: // initialize with a single entry
053: newValue.push();
054: return newValue;
055: }
056:
057: public static MapStack create(Map baseMap) {
058: MapStack newValue = (MapStack) mapStackFactory.object();
059: newValue.stackList.add(0, baseMap);
060: return newValue;
061: }
062:
063: /** Does a shallow copy of the internal stack of the passed MapStack; enables simultaneous stacks that share common parent Maps */
064: public static MapStack create(MapStack source) {
065: MapStack newValue = (MapStack) mapStackFactory.object();
066: newValue.stackList.addAll(source.stackList);
067: return newValue;
068: }
069:
070: protected MapStack() {
071: super ();
072: }
073:
074: protected List stackList = FastList.newInstance();
075:
076: public void reset() {
077: stackList = FastList.newInstance();
078: }
079:
080: /** Puts a new Map on the top of the stack */
081: public void push() {
082: this .stackList.add(0, FastMap.newInstance());
083: }
084:
085: /** Puts an existing Map on the top of the stack (top meaning will override lower layers on the stack) */
086: public void push(Map existingMap) {
087: if (existingMap == null) {
088: throw new IllegalArgumentException(
089: "Error: cannot push null existing Map onto a MapStack");
090: }
091: this .stackList.add(0, existingMap);
092: }
093:
094: /** Puts an existing Map on the BOTTOM of the stack (bottom meaning will be overriden by lower layers on the stack, ie everything else already there) */
095: public void addToBottom(Map existingMap) {
096: if (existingMap == null) {
097: throw new IllegalArgumentException(
098: "Error: cannot add null existing Map to bottom of a MapStack");
099: }
100: this .stackList.add(existingMap);
101: }
102:
103: /** Remove and returns the Map from the top of the stack; if there is only one Map on the stack it returns null and does not remove it */
104: public Map pop() {
105: // always leave at least one Map in the List, ie never pop off the last Map
106: if (this .stackList.size() > 1) {
107: return (Map) stackList.remove(0);
108: } else {
109: return null;
110: }
111: }
112:
113: /**
114: * Creates a MapStack object that has the same Map objects on its stack;
115: * meant to be used to enable a
116: * situation where a parent and child context are operating simultaneously
117: * using two different MapStack objects, but sharing the Maps in common
118: */
119: public MapStack standAloneStack() {
120: MapStack standAlone = MapStack.create(this );
121: return standAlone;
122: }
123:
124: /**
125: * Creates a MapStack object that has the same Map objects on its stack,
126: * but with a new Map pushed on the top; meant to be used to enable a
127: * situation where a parent and child context are operating simultaneously
128: * using two different MapStack objects, but sharing the Maps in common
129: */
130: public MapStack standAloneChildStack() {
131: MapStack standAloneChild = MapStack.create(this );
132: standAloneChild.push();
133: return standAloneChild;
134: }
135:
136: /* (non-Javadoc)
137: * @see java.util.Map#size()
138: */
139: public int size() {
140: // a little bit tricky; to represent the apparent size we need to aggregate all keys and get a count of unique keys
141: // this is a bit of a slow way, but gets the best number possible
142: Set keys = this .keySet();
143: return keys.size();
144: }
145:
146: /* (non-Javadoc)
147: * @see java.util.Map#isEmpty()
148: */
149: public boolean isEmpty() {
150: // walk the stackList and if any is not empty, return false; otherwise return true
151: Iterator stackIter = this .stackList.iterator();
152: while (stackIter.hasNext()) {
153: Map curMap = (Map) stackIter.next();
154: if (!curMap.isEmpty()) {
155: return false;
156: }
157: }
158: return true;
159: }
160:
161: /* (non-Javadoc)
162: * @see java.util.Map#containsKey(java.lang.Object)
163: */
164: public boolean containsKey(Object key) {
165: // walk the stackList and for the first place it is found return true; otherwise refurn false
166: Iterator stackIter = this .stackList.iterator();
167: while (stackIter.hasNext()) {
168: Map curMap = (Map) stackIter.next();
169: if (curMap.containsKey(key)) {
170: return true;
171: }
172: }
173: return false;
174: }
175:
176: /* (non-Javadoc)
177: * @see java.util.Map#containsValue(java.lang.Object)
178: */
179: public boolean containsValue(Object value) {
180: // walk the stackList and the entries for each Map and if nothing is in for the current key, consider it an option, otherwise ignore
181: Set resultKeySet = FastSet.newInstance();
182: Iterator stackIter = this .stackList.iterator();
183: while (stackIter.hasNext()) {
184: Map curMap = (Map) stackIter.next();
185: Iterator curEntrySetIter = curMap.entrySet().iterator();
186: while (curEntrySetIter.hasNext()) {
187: Map.Entry curEntry = (Map.Entry) curEntrySetIter.next();
188: if (!resultKeySet.contains(curEntry.getKey())) {
189: resultKeySet.add(curEntry.getKey());
190: if (value == null) {
191: if (curEntry.getValue() == null) {
192: return true;
193: }
194: } else {
195: if (value.equals(curEntry.getValue())) {
196: return true;
197: }
198: }
199: }
200: }
201: }
202: return false;
203: }
204:
205: /* (non-Javadoc)
206: * @see java.util.Map#get(java.lang.Object)
207: */
208: public Object get(Object key) {
209: if ("context".equals(key)) {
210: return this ;
211: }
212:
213: // walk the stackList and for the first place it is found return true; otherwise refurn false
214: Iterator stackIter = this .stackList.iterator();
215: while (stackIter.hasNext()) {
216: Map curMap = (Map) stackIter.next();
217: // only return if the curMap contains the key, rather than checking for null; this allows a null at a lower level to override a value at a higher level
218: if (curMap.containsKey(key)) {
219: return curMap.get(key);
220: }
221: }
222: return null;
223: }
224:
225: /* (non-Javadoc)
226: * @see org.ofbiz.base.util.collections.LocalizedMap#get(java.lang.String, java.util.Locale)
227: */
228: public Object get(String name, Locale locale) {
229: if ("context".equals(name)) {
230: return this ;
231: }
232:
233: // walk the stackList and for the first place it is found return true; otherwise refurn false
234: Iterator stackIter = this .stackList.iterator();
235: while (stackIter.hasNext()) {
236: Map curMap = (Map) stackIter.next();
237: // only return if the curMap contains the key, rather than checking for null; this allows a null at a lower level to override a value at a higher level
238: if (curMap.containsKey(name)) {
239: if (curMap instanceof LocalizedMap) {
240: LocalizedMap lmap = (LocalizedMap) curMap;
241: return lmap.get(name, locale);
242: } else {
243: return curMap.get(name);
244: }
245: }
246: }
247: return null;
248: }
249:
250: /* (non-Javadoc)
251: * @see java.util.Map#put(java.lang.Object, java.lang.Object)
252: */
253: public Object put(Object key, Object value) {
254: if ("context".equals(key)) {
255: if (value == null || this != value) {
256: Debug
257: .logWarning(
258: "WARNING: Putting a value in a MapStack with key [context] that is not this MapStack, will be hidden by the current MapStack self-reference: "
259: + value, module);
260: }
261: }
262:
263: // all write operations are local: only put in the Map on the top of the stack
264: Map currentMap = (Map) this .stackList.get(0);
265: return currentMap.put(key, value);
266: }
267:
268: /* (non-Javadoc)
269: * @see java.util.Map#remove(java.lang.Object)
270: */
271: public Object remove(Object key) {
272: // all write operations are local: only remove from the Map on the top of the stack
273: Map currentMap = (Map) this .stackList.get(0);
274: return currentMap.remove(key);
275: }
276:
277: /* (non-Javadoc)
278: * @see java.util.Map#putAll(java.util.Map)
279: */
280: public void putAll(Map arg0) {
281: // all write operations are local: only put in the Map on the top of the stack
282: Map currentMap = (Map) this .stackList.get(0);
283: currentMap.putAll(arg0);
284: }
285:
286: /* (non-Javadoc)
287: * @see java.util.Map#clear()
288: */
289: public void clear() {
290: // all write operations are local: only clear the Map on the top of the stack
291: Map currentMap = (Map) this .stackList.get(0);
292: currentMap.clear();
293: }
294:
295: /* (non-Javadoc)
296: * @see java.util.Map#keySet()
297: */
298: public Set keySet() {
299: // walk the stackList and aggregate all keys
300: Set resultSet = FastSet.newInstance();
301: Iterator stackIter = this .stackList.iterator();
302: while (stackIter.hasNext()) {
303: Map curMap = (Map) stackIter.next();
304: resultSet.addAll(curMap.keySet());
305: }
306: return Collections.unmodifiableSet(resultSet);
307: }
308:
309: /* (non-Javadoc)
310: * @see java.util.Map#values()
311: */
312: public Collection values() {
313: // walk the stackList and the entries for each Map and if nothing is in for the current key, put it in
314: Set resultKeySet = FastSet.newInstance();
315: List resultValues = FastList.newInstance();
316: Iterator stackIter = this .stackList.iterator();
317: while (stackIter.hasNext()) {
318: Map curMap = (Map) stackIter.next();
319: Iterator curEntrySetIter = curMap.entrySet().iterator();
320: while (curEntrySetIter.hasNext()) {
321: Map.Entry curEntry = (Map.Entry) curEntrySetIter.next();
322: if (!resultKeySet.contains(curEntry.getKey())) {
323: resultKeySet.add(curEntry.getKey());
324: resultValues.add(curEntry.getValue());
325: }
326: }
327: }
328: return Collections.unmodifiableCollection(resultValues);
329: }
330:
331: /* (non-Javadoc)
332: * @see java.util.Map#entrySet()
333: */
334: public Set entrySet() {
335: // walk the stackList and the entries for each Map and if nothing is in for the current key, put it in
336: Set resultKeySet = FastSet.newInstance();
337: Set resultEntrySet = FastSet.newInstance();
338: Iterator stackIter = this .stackList.iterator();
339: while (stackIter.hasNext()) {
340: Map curMap = (Map) stackIter.next();
341: Iterator curEntrySetIter = curMap.entrySet().iterator();
342: while (curEntrySetIter.hasNext()) {
343: Map.Entry curEntry = (Map.Entry) curEntrySetIter.next();
344: if (!resultKeySet.contains(curEntry.getKey())) {
345: resultKeySet.add(curEntry.getKey());
346: resultEntrySet.add(curEntry);
347: }
348: }
349: }
350: return Collections.unmodifiableSet(resultEntrySet);
351: }
352:
353: public String toString() {
354: StringBuffer fullMapString = new StringBuffer();
355: int curLevel = 0;
356: Iterator stackIter = this .stackList.iterator();
357: while (stackIter.hasNext()) {
358: Map curMap = (Map) stackIter.next();
359: fullMapString
360: .append("============================== Start stack level "
361: + curLevel + "\n");
362: Iterator curEntrySetIter = curMap.entrySet().iterator();
363: while (curEntrySetIter.hasNext()) {
364: Map.Entry curEntry = (Map.Entry) curEntrySetIter.next();
365:
366: fullMapString.append("==>[");
367: fullMapString.append(curEntry.getKey());
368: fullMapString.append("]:");
369: // skip the instances of MapStack to avoid infinite loop
370: if (curEntry.getValue() instanceof MapStack) {
371: fullMapString
372: .append("<Instance of MapStack, not printing to avoid infinite recursion>");
373: } else {
374: fullMapString.append(curEntry.getValue());
375: }
376: fullMapString.append("\n");
377: }
378: fullMapString
379: .append("============================== End stack level "
380: + curLevel + "\n");
381: curLevel++;
382: }
383: return fullMapString.toString();
384: }
385: }
|