/*
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.util;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.collections.MapConverter;
import com.thoughtworks.xstream.converters.collections.TreeMapConverter;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* {@link Map} that has copy-on-write semantics.
*
* <p>
* This class is suitable where highly concurrent access is needed, yet
* the write operation is relatively uncommon.
*
* @author Kohsuke Kawaguchi
*/
public abstract class CopyOnWriteMap<K,V> implements Map<K,V> {
protected volatile Map<K,V> core;
/**
* Read-only view of {@link #core}.
*/
private volatile Map<K,V> view;
protected CopyOnWriteMap(Map<K,V> core) {
update(core);
}
protected CopyOnWriteMap() {
update(Collections.<K,V>emptyMap());
}
protected void update(Map<K,V> m) {
core = m;
view = Collections.unmodifiableMap(core);
}
/**
* Atomically replaces the entire map by the copy of the specified map.
*/
public void replaceBy(Map<? extends K, ? extends V> data) {
Map<K, V> d = copy();
d.clear();
d.putAll(data);
update(d);
}
public int size() {
return core.size();
}
public boolean isEmpty() {
return core.isEmpty();
}
public boolean containsKey(Object key) {
return core.containsKey(key);
}
public boolean containsValue(Object value) {
return core.containsValue(value);
}
public V get(Object key) {
return core.get(key);
}
public synchronized V put(K key, V value) {
Map<K,V> m = copy();
V r = m.put(key,value);
update(m);
return r;
}
public synchronized V remove(Object key) {
Map<K,V> m = copy();
V r = m.remove(key);
update(m);
return r;
}
public synchronized void putAll(Map<? extends K, ? extends V> t) {
Map<K,V> m = copy();
m.putAll(t);
update(m);
}
protected abstract Map<K,V> copy();
public synchronized void clear() {
update(Collections.<K,V>emptyMap());
}
/**
* This method will return a read-only {@link Set}.
*/
public Set<K> keySet() {
return view.keySet();
}
/**
* This method will return a read-only {@link Collection}.
*/
public Collection<V> values() {
return view.values();
}
/**
* This method will return a read-only {@link Set}.
*/
public Set<Entry<K,V>> entrySet() {
return view.entrySet();
}
@Override public int hashCode() {
return copy().hashCode();
}
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@Override public boolean equals(Object obj) {
return copy().equals(obj);
}
@Override public String toString() {
return copy().toString();
}
/**
* {@link CopyOnWriteMap} backed by {@link HashMap}.
*/
public static final class Hash<K,V> extends CopyOnWriteMap<K,V> {
public Hash(Map<K,V> core) {
super(new LinkedHashMap<K,V>(core));
}
public Hash() {
}
protected Map<K,V> copy() {
return new LinkedHashMap<K,V>(core);
}
public static class ConverterImpl extends MapConverter {
public ConverterImpl(Mapper mapper) {
super(mapper);
}
@Override
public boolean canConvert(Class type) {
return type==Hash.class;
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
return new Hash((Map) super.unmarshal(reader,context));
}
@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
super.marshal(((Hash)source).core,writer,context);
}
}
}
/**
* {@link CopyOnWriteMap} backed by {@link TreeMap}.
*/
public static final class Tree<K,V> extends CopyOnWriteMap<K,V> {
private final Comparator<K> comparator;
public Tree(Map<K,V> core, Comparator<K> comparator) {
this(comparator);
putAll(core);
}
public Tree(Comparator<K> comparator) {
super(new TreeMap<K,V>(comparator));
this.comparator = comparator;
}
public Tree() {
this(null);
}
protected Map<K,V> copy() {
TreeMap<K,V> m = new TreeMap<K,V>(comparator);
m.putAll(core);
return m;
}
@Override
public synchronized void clear() {
update(new TreeMap<K,V>(comparator));
}
public static class ConverterImpl extends TreeMapConverter {
public ConverterImpl(Mapper mapper) {
super(mapper);
}
@Override
public boolean canConvert(Class type) {
return type==Tree.class;
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
TreeMap tm = (TreeMap) super.unmarshal(reader,context);
return new Tree(tm,tm.comparator());
}
@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
super.marshal(((Tree)source).core,writer,context);
}
}
}
}