blob: 2ca4659e74bee42fd9ed31e49ec4a52f62311180 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.forms.util;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A read-only implementation of <code>Map</code> that combines several other maps.
*
* @version $Id$
*/
public class CombiningMap extends AbstractMap {
protected List maps = new ArrayList();
private boolean locked = false;
/**
* Adds a <code>Map</code> in the combined map, with the lowest lookup priority.
* <p>
* New maps cannot be added if this object was already iterated.
*
* @param map the new map
* @return this object, as a convenience to write <code>combiner.add(map1).add(map2).add(map3)</code>
* @throw IllegalStateException if this object was already iterated.
*/
public CombiningMap add(Map map) {
if (locked) {
throw new IllegalStateException("Cannot add new Maps to a CombiningMap once it has been iterated");
}
maps.add(map);
return this;
}
public Object get(Object key) {
// Faster implemetation than the default in AbstractMap
for (int i = 0; i < maps.size(); i++) {
Map map = (Map)maps.get(i);
Object result = map.get(key);
if (result != null) {
return result;
}
if (map.containsKey(key)) {
return null;
}
}
return null;
}
public boolean containsKey(Object key) {
// Faster implemetation than the default in AbstractMap
for (int i = 0; i < maps.size(); i++) {
Map map = (Map)maps.get(i);
if (map.containsKey(key)) {
return true;
}
}
return false;
}
public Set entrySet() {
locked = true;
return new CombiningEntrySet();
}
private class CombiningEntrySet extends AbstractSet {
public Iterator iterator() {
return new CombiningIterator();
}
/**
* Super inefficient way, but this implementation is meant to be super-lightweight
* and efficient at iterations.
*/
public int size() {
int size = 0;
Iterator iter = iterator();
while (iter.hasNext()) {
size++;
iter.next();
}
return size;
}
}
private class CombiningIterator implements Iterator {
private int index;
private Iterator delegate;
private Map.Entry next;
public CombiningIterator() {
// Initialize the first result
if (!maps.isEmpty()) {
delegate = ((Map)maps.get(0)).entrySet().iterator();
if (delegate.hasNext()) {
next = (Map.Entry)delegate.next();
}
}
}
public boolean hasNext() {
return next != null;
}
public Object next() {
if (next == null) {
throw new NoSuchElementException();
}
Object result = next;
fetchNext();
return result;
}
public void remove() {
throw new UnsupportedOperationException();
}
private void fetchNext() {
boolean skip;
do {
// Get an iterator that has more values
while (delegate != null && !delegate.hasNext()) {
// Ended iteration on the previous map
index++;
if (index < maps.size()) {
delegate = ((Map)maps.get(index)).entrySet().iterator();
} else {
// Iteration finished
next = null;
delegate = null;
return;
}
}
// Get the next entry
next = (Map.Entry)delegate.next();
// Skip it if its key doesn't exist in the previous Maps
Object key = next.getKey();
skip = false;
for (int i = 0; i < index-1; i++) {
if (((Map)maps.get(i)).containsKey(key)) {
skip = true;
continue;
}
}
} while(skip);
}
}
}