/* * Copyright (C) 2009 Quadduc <quadduc@gmail.com> * * This file is part of LateralGM. * LateralGM is free software and comes with ABSOLUTELY NO WARRANTY. * See LICENSE for details. */ package org.lateralgm.util; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.Map; import java.util.Set; import org.lateralgm.main.UpdateSource; import org.lateralgm.main.UpdateSource.UpdateEvent; import org.lateralgm.main.UpdateSource.UpdateListener; import org.lateralgm.main.UpdateSource.UpdateTrigger; public class PropertyMap<K extends Enum<K>> extends EnumMap<K,Object> { private static final long serialVersionUID = 1L; private final UpdateTrigger updateTrigger = new UpdateTrigger(); public final UpdateSource updateSource = new UpdateSource(this,updateTrigger); private EnumMap<K,TriggerSourcePair> updatePairs; private final Class<K> keyType; private final PropertyValidator<K> validator; public PropertyMap(Class<K> type, PropertyValidator<K> v, EnumMap<K,Object> defaults) { super(defaults == null ? new EnumMap<K,Object>(type) : defaults); keyType = type; validator = v; } public UpdateSource getUpdateSource(K key) { TriggerSourcePair p = null; if (updatePairs == null) updatePairs = new EnumMap<K,TriggerSourcePair>(keyType); else p = updatePairs.get(key); if (p == null) { p = new TriggerSourcePair(); updatePairs.put(key,p); } return p.source; } protected void fireUpdate(K k) { PropertyUpdateEvent<K> e = new PropertyUpdateEvent<K>(updateSource,this,k); updateTrigger.fire(e); if (updatePairs != null) { TriggerSourcePair p = updatePairs.get(k); if (p != null) p.trigger.fire(e); } } @SuppressWarnings("unchecked") public <V>V get(K key) { return (V) super.get(key); } @Override public void clear() { throw new UnsupportedOperationException(); } public Object put(K key, Object value) { boolean ck = super.containsKey(key); if (ck) if (super.get(key) == value) return value; Object vv = validator == null ? value : validator.validate(key,value); Object o = super.put(key,vv); if (!ck || vv != value || o != value) fireUpdate(key); return o; } @Override public Set<java.util.Map.Entry<K,Object>> entrySet() { return Collections.unmodifiableSet(super.entrySet()); } @Override public void putAll(Map<? extends K,? extends Object> m) { for (Map.Entry<? extends K,? extends Object> e : m.entrySet()) put(e.getKey(),e.getValue()); } @Override public PropertyMap<K> clone() { return (PropertyMap<K>) super.clone(); } @SuppressWarnings("unchecked") @Override public Object remove(Object key) { int s = size(); Object o = super.remove(key); // We know that this cast to K is valid if o != null. if (o != null || s != size()) fireUpdate((K) key); return o; } @Override public Collection<Object> values() { return Collections.unmodifiableCollection(super.values()); } public static <K extends Enum<K>>EnumMap<K,Object> makeDefaultMap(Class<K> type, Object...values) { K[] ec = type.getEnumConstants(); if (ec.length != values.length) throw new IllegalArgumentException(); EnumMap<K,Object> m = new EnumMap<K,Object>(type); for (K k : ec) m.put(k,values[k.ordinal()]); return m; } public static class PropertyUpdateEvent<K extends Enum<K>> extends UpdateEvent { public final PropertyMap<K> map; public final K key; public PropertyUpdateEvent(UpdateSource s, PropertyMap<K> m, K k) { super(s); map = m; key = k; } } public abstract static class PropertyUpdateListener<K extends Enum<K>> implements UpdateListener { @SuppressWarnings("unchecked") public void updated(UpdateEvent e) { updated((PropertyUpdateEvent<K>) e); } public abstract void updated(PropertyUpdateEvent<K> e); } private class TriggerSourcePair { public final UpdateTrigger trigger; public final UpdateSource source; public TriggerSourcePair() { trigger = new UpdateTrigger(); source = new UpdateSource(PropertyMap.this,trigger); } } public static interface PropertyValidator<K extends Enum<K>> { Object validate(K k, Object v); } public static class PropertyValidationException extends IllegalArgumentException { private static final long serialVersionUID = 1L; } }