001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.forms.util;
018:
019: import java.util.AbstractMap;
020: import java.util.AbstractSet;
021: import java.util.ArrayList;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.NoSuchElementException;
026: import java.util.Set;
027:
028: /**
029: * A read-only implementation of <code>Map</code> that combines several other maps.
030: *
031: * @version $Id: CombiningMap.java 449149 2006-09-23 03:58:05Z crossley $
032: */
033: public class CombiningMap extends AbstractMap {
034:
035: protected List maps = new ArrayList();
036: private boolean locked = false;
037:
038: /**
039: * Adds a <code>Map</code> in the combined map, with the lowest lookup priority.
040: * <p>
041: * New maps cannot be added if this object was already iterated.
042: *
043: * @param map the new map
044: * @return this object, as a convenience to write <code>combiner.add(map1).add(map2).add(map3)</code>
045: * @throw IllegalStateException if this object was already iterated.
046: */
047: public CombiningMap add(Map map) {
048: if (locked) {
049: throw new IllegalStateException(
050: "Cannot add new Maps to a CombiningMap once it has been iterated");
051: }
052: maps.add(map);
053:
054: return this ;
055: }
056:
057: public Object get(Object key) {
058: // Faster implemetation than the default in AbstractMap
059: for (int i = 0; i < maps.size(); i++) {
060: Map map = (Map) maps.get(i);
061: Object result = map.get(key);
062:
063: if (result != null) {
064: return result;
065: }
066:
067: if (map.containsKey(key)) {
068: return null;
069: }
070: }
071:
072: return null;
073: }
074:
075: public boolean containsKey(Object key) {
076: // Faster implemetation than the default in AbstractMap
077: for (int i = 0; i < maps.size(); i++) {
078: Map map = (Map) maps.get(i);
079: if (map.containsKey(key)) {
080: return true;
081: }
082: }
083: return false;
084: }
085:
086: public Set entrySet() {
087: locked = true;
088: return new CombiningEntrySet();
089: }
090:
091: private class CombiningEntrySet extends AbstractSet {
092:
093: public Iterator iterator() {
094: return new CombiningIterator();
095: }
096:
097: /**
098: * Super inefficient way, but this implementation is meant to be super-lightweight
099: * and efficient at iterations.
100: */
101: public int size() {
102:
103: int size = 0;
104: Iterator iter = iterator();
105: while (iter.hasNext()) {
106: size++;
107: iter.next();
108: }
109: return size;
110: }
111: }
112:
113: private class CombiningIterator implements Iterator {
114:
115: private int index;
116: private Iterator delegate;
117: private Map.Entry next;
118:
119: public CombiningIterator() {
120: // Initialize the first result
121: if (!maps.isEmpty()) {
122: delegate = ((Map) maps.get(0)).entrySet().iterator();
123: if (delegate.hasNext()) {
124: next = (Map.Entry) delegate.next();
125: }
126: }
127:
128: }
129:
130: public boolean hasNext() {
131: return next != null;
132: }
133:
134: public Object next() {
135: if (next == null) {
136: throw new NoSuchElementException();
137: }
138: Object result = next;
139: fetchNext();
140: return result;
141: }
142:
143: public void remove() {
144: throw new UnsupportedOperationException();
145: }
146:
147: private void fetchNext() {
148: boolean skip;
149: do {
150: // Get an iterator that has more values
151: while (delegate != null && !delegate.hasNext()) {
152: // Ended iteration on the previous map
153: index++;
154: if (index < maps.size()) {
155: delegate = ((Map) maps.get(index)).entrySet()
156: .iterator();
157: } else {
158: // Iteration finished
159: next = null;
160: delegate = null;
161: return;
162: }
163: }
164:
165: // Get the next entry
166: next = (Map.Entry) delegate.next();
167:
168: // Skip it if its key doesn't exist in the previous Maps
169: Object key = next.getKey();
170: skip = false;
171: for (int i = 0; i < index - 1; i++) {
172: if (((Map) maps.get(i)).containsKey(key)) {
173: skip = true;
174: continue;
175: }
176: }
177: } while (skip);
178: }
179: }
180: }
|