/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* Licensed 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.seasar.framework.container.external;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.seasar.framework.container.ExternalContext;
/**
* {@link ExternalContext}用の抽象 {@link Map}です。
*
* @author shot
* @author higa
*/
public abstract class AbstractExternalContextMap extends AbstractMap {
private Set entrySet;
private Set keySet;
private Collection values;
/**
* {@link AbstractExternalContextMap}を作成します。
*/
public AbstractExternalContextMap() {
}
public void clear() {
// avoid ConcurrentModificationException
List list = new ArrayList();
for (Iterator it = getAttributeNames(); it.hasNext();) {
String key = (String) it.next();
list.add(key);
}
clearReally(list);
}
private void clearReally(List keys) {
for (Iterator itr = keys.iterator(); itr.hasNext();) {
String key = (String) itr.next();
removeAttribute(key);
}
}
public boolean containsKey(Object key) {
return (getAttribute(key.toString()) != null);
}
public boolean containsValue(Object value) {
if (value != null) {
for (Iterator it = getAttributeNames(); it.hasNext();) {
String key = (String) it.next();
Object attributeValue = getAttribute(key);
if (value.equals(attributeValue)) {
return true;
}
}
}
return false;
}
public Set entrySet() {
if (entrySet == null) {
entrySet = new EntrySet(this);
}
return entrySet;
}
public Object get(Object key) {
return getAttribute(key.toString());
}
public Object put(Object key, Object value) {
String keyStr = key.toString();
Object o = getAttribute(keyStr);
setAttribute(keyStr, value);
return o;
}
public void putAll(Map map) {
for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = (Map.Entry) itr.next();
String key = (String) entry.getKey();
setAttribute(key, entry.getValue());
}
}
public boolean isEmpty() {
return !getAttributeNames().hasNext();
}
public Set keySet() {
if (keySet == null) {
keySet = new KeySet(this);
}
return keySet;
}
public Object remove(Object key) {
String keyStr = key.toString();
Object o = getAttribute(keyStr);
removeAttribute(keyStr);
return o;
}
public Collection values() {
if (values == null) {
values = new ValuesCollection(this);
}
return values;
}
/**
* 属性の値を返します。
*
* @param key
* @return 属性の値
*/
protected abstract Object getAttribute(String key);
/**
* 属性の値を設定します。
*
* @param key
* @param value
*/
protected abstract void setAttribute(String key, Object value);
/**
* 属性名の {@link Iterator}を返します。
*
* @return
*/
protected abstract Iterator getAttributeNames();
/**
* 属性を削除します。
*
* @param key
*/
protected abstract void removeAttribute(String key);
abstract class AbstractExternalContextSet extends AbstractSet {
public int size() {
int size = 0;
for (Iterator itr = iterator(); itr.hasNext(); size++) {
itr.next();
}
return size;
}
}
class EntrySet extends AbstractExternalContextSet {
private AbstractExternalContextMap contextMap;
/**
* {@link EntrySet}を作成します。
*
* @param contextMap
*/
public EntrySet(AbstractExternalContextMap contextMap) {
this.contextMap = contextMap;
}
public Iterator iterator() {
return new EntryIterator(contextMap);
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry entry = (Map.Entry) o;
Object returnObj = contextMap.remove(entry.getKey());
return (returnObj != null);
}
}
class KeySet extends AbstractExternalContextSet {
private AbstractExternalContextMap contextMap;
/**
* {@link KeySet}を作成します。
*
* @param contextMap
*/
public KeySet(AbstractExternalContextMap contextMap) {
this.contextMap = contextMap;
}
public Iterator iterator() {
return new KeyIterator(contextMap);
}
public boolean remove(Object o) {
if (!(o instanceof String)) {
return false;
}
String s = (String) o;
Object returnObj = contextMap.remove(s);
return (returnObj != null);
}
}
class ValuesCollection extends AbstractCollection {
private AbstractExternalContextMap contextMap;
/**
* {@link ValuesCollection}を作成します。
*
* @param contextMap
*/
public ValuesCollection(AbstractExternalContextMap contextMap) {
this.contextMap = contextMap;
}
public int size() {
int size = 0;
for (Iterator itr = iterator(); itr.hasNext(); size++) {
itr.next();
}
return size;
}
public Iterator iterator() {
return new ValuesIterator(contextMap);
}
}
abstract class AbstractExternalContextIterator implements Iterator {
private final Iterator iterator;
private final AbstractExternalContextMap contextMap;
private String currentKey;
private boolean removeCalled = false;
/**
* {@link AbstractExternalContextIterator}を作成します。
*
* @param contextMap
*/
public AbstractExternalContextIterator(
final AbstractExternalContextMap contextMap) {
iterator = contextMap.getAttributeNames();
this.contextMap = contextMap;
}
public boolean hasNext() {
return iterator.hasNext();
}
public Object next() {
currentKey = (String) iterator.next();
try {
return doNext();
} finally {
removeCalled = false;
}
}
public void remove() {
if (currentKey != null && !removeCalled) {
doRemove();
removeCalled = true;
} else {
throw new IllegalStateException();
}
}
/**
* 現在のキーを返します。
*
* @return 現在のキー
*/
protected String getCurrentKey() {
return currentKey;
}
/**
* 値を返します。
*
* @param key
* @return 値
*/
protected Object getValueFromMap(String key) {
return contextMap.get(key);
}
/**
* キーに対するエントリを削除します。
*
* @param key
*/
protected void removeKeyFromMap(String key) {
contextMap.remove(key);
}
/**
* 値に対するエントリを削除します。
*
* @param value
*/
protected void removeValueFromMap(Object value) {
if (containsValue(value)) {
for (Iterator itr = entrySet().iterator(); itr.hasNext();) {
Map.Entry e = (Map.Entry) itr.next();
if (value.equals(e.getValue())) {
contextMap.remove(e.getKey());
}
}
}
}
/**
* 次の要素に移動します。
*
* @return 次の要素
*/
protected abstract Object doNext();
/**
* 現在の要素を削除します。
*/
protected abstract void doRemove();
}
class EntryIterator extends AbstractExternalContextIterator {
/**
* {@link EntryIterator}を作成します。
*
* @param contextMap
*/
public EntryIterator(AbstractExternalContextMap contextMap) {
super(contextMap);
}
protected Object doNext() {
String key = getCurrentKey();
return new ImmutableEntry(key, getValueFromMap(key));
}
protected void doRemove() {
String key = getCurrentKey();
removeKeyFromMap(key);
}
}
class KeyIterator extends AbstractExternalContextIterator {
/**
* {@link KeyIterator}を作成します。
*
* @param contextMap
*/
public KeyIterator(AbstractExternalContextMap contextMap) {
super(contextMap);
}
protected Object doNext() {
return getCurrentKey();
}
protected void doRemove() {
removeKeyFromMap(getCurrentKey());
}
}
class ValuesIterator extends AbstractExternalContextIterator {
/**
* {@link ValuesIterator}を作成します。
*
* @param contextMap
*/
public ValuesIterator(AbstractExternalContextMap contextMap) {
super(contextMap);
}
protected Object doNext() {
String key = getCurrentKey();
return getValueFromMap(key);
}
protected void doRemove() {
String key = getCurrentKey();
Object value = getValueFromMap(key);
removeValueFromMap(value);
}
}
/**
* 変化しない {@link java.util.Map.Entry}です。
*
*/
protected static class ImmutableEntry implements Map.Entry {
private final Object key;
private final Object value;
/**
* {@link AbstractExternalContextMap.ImmutableEntry}を作成します。
*
* @param key
* @param value
*/
public ImmutableEntry(Object key, Object value) {
this.key = key;
this.value = value;
}
public Object getKey() {
return key;
}
public Object getValue() {
return value;
}
public Object setValue(Object arg0) {
throw new UnsupportedOperationException("Immutable entry.");
}
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof ImmutableEntry)) {
return false;
}
ImmutableEntry entry = (ImmutableEntry) obj;
Object k = entry.getKey();
Object v = entry.getValue();
return (k == key || (k != null && k.equals(key)))
&& (v == value || (v != null && v.equals(value)));
}
public int hashCode() {
return ((key != null) ? key.hashCode() : 0)
^ ((value != null) ? value.hashCode() : 0);
}
}
}