All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.util.ObservableMap Maven / Gradle / Ivy

There is a newer version: 3.0.21
Show newest version
/*
 * Copyright 2003-2007 the original author or authors.
 *
 * 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 groovy.util;

import groovy.lang.Closure;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.*;

/**
 * Map decorator that will trigger PropertyChangeEvents when a value changes.
* An optional Closure may be specified and will work as a filter, if it returns * true the property will trigger an event (if the value indeed changed), * otherwise it won't. The Closure may receive 1 or 2 parameters, the single one * being the value, the other one both the key and value, for example: *
 * // skip all properties whose value is a closure
 * def map = new ObservableMap( {!(it instanceof Closure)} )
 * 

* // skip all properties whose name matches a regex * def map = new ObservableMap( { name, value -> !(name =~ /[A-Z+]/) } ) *

* *

The current implementation will trigger specialized events in the following scenarios, * you need not register a different listener as those events extend from PropertyChangeEvent *

    *
  • ObservableMap.PropertyAddedEvent - a new property is added to the map
  • *
  • ObservableMap.PropertyRemovedEvent - a property is removed from the map
  • *
  • ObservableMap.PropertyUpdatedEvent - a property changes value (same as regular PropertyChangeEvent)
  • *
  • ObservableMap.PropertyClearedEvent - all properties have been removed from the map
  • *
  • ObservableMap.MultiPropertyEvent - triggered by calling map.putAll(), contains Added|Updated events
  • *

* * @author Andres Almiray */ public class ObservableMap implements Map { private Map delegate; private PropertyChangeSupport pcs; private Closure test; public ObservableMap() { this(new LinkedHashMap(), null); } public ObservableMap(Closure test) { this(new LinkedHashMap(), test); } public ObservableMap(Map delegate) { this(delegate, null); } public ObservableMap(Map delegate, Closure test) { this.delegate = delegate; this.test = test; pcs = new PropertyChangeSupport(this); } // Map interface public void clear() { Map values = new HashMap(); if( !delegate.isEmpty() ) { values.putAll( delegate ); } delegate.clear(); if( values != null ) { pcs.firePropertyChange( new PropertyClearedEvent(this,values) ); } } public boolean containsKey(Object key) { return delegate.containsKey(key); } public boolean containsValue(Object value) { return delegate.containsValue(value); } public Set entrySet() { return delegate.entrySet(); } public boolean equals(Object o) { return delegate.equals(o); } public Object get(Object key) { return delegate.get(key); } public int hashCode() { return delegate.hashCode(); } public boolean isEmpty() { return delegate.isEmpty(); } public Set keySet() { return delegate.keySet(); } public Object put(Object key, Object value) { Object oldValue = null; boolean newKey = !delegate.containsKey( key ); if( test != null ) { oldValue = delegate.put(key, value); Object result = null; if( test.getMaximumNumberOfParameters() == 2 ) { result = test.call(new Object[] {key, value}); } else { result = test.call(value); } if( result != null && result instanceof Boolean && ((Boolean) result).booleanValue() ) { if( newKey ) { pcs.firePropertyChange( new PropertyAddedEvent(this, String.valueOf(key), value ) ); }else if (oldValue != value) { pcs.firePropertyChange( new PropertyUpdatedEvent(this, String.valueOf(key), oldValue, value ) ); } } } else { oldValue = delegate.put(key, value); if( newKey ) { pcs.firePropertyChange( new PropertyAddedEvent(this, String.valueOf(key), value ) ); }else if (oldValue != value) { pcs.firePropertyChange( new PropertyUpdatedEvent(this, String.valueOf(key), oldValue, value ) ); } } return oldValue; } public void putAll(Map map) { if( map != null ) { List events = new ArrayList(); for (Iterator entries = map.entrySet() .iterator(); entries.hasNext();) { Map.Entry entry = (Map.Entry) entries.next(); String key = String.valueOf(entry.getKey()); Object newValue = entry.getValue(); Object oldValue = null; boolean newKey = !delegate.containsKey( key ); if( test != null ) { oldValue = delegate.put(key, newValue); Object result = null; if( test.getMaximumNumberOfParameters() == 2 ) { result = test.call(new Object[] {key, newValue}); } else { result = test.call(newValue); } if( result != null && result instanceof Boolean && ((Boolean) result).booleanValue() ) { if( newKey ) { events.add( new PropertyAddedEvent(this, key, newValue ) ); }else if (oldValue != newValue) { events.add( new PropertyUpdatedEvent(this, key, oldValue, newValue) ); } } } else { oldValue = delegate.put(key, newValue); if( newKey ) { events.add( new PropertyAddedEvent(this, key, newValue ) ); }else if (oldValue != newValue) { events.add( new PropertyUpdatedEvent(this, key, oldValue, newValue) ); } } } if( events.size() > 0 ) { pcs.firePropertyChange( new MultiPropertyEvent(this, (PropertyEvent[]) events.toArray(new PropertyEvent[events.size()]) ) ); } } } public Object remove(Object key) { Object result = delegate.remove(key); if( key != null ) { pcs.firePropertyChange( new PropertyRemovedEvent(this, String.valueOf(key), result ) ); } return result; } public int size() { return delegate.size(); } public Collection values() { return delegate.values(); } // observable interface public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { pcs.addPropertyChangeListener(propertyName, listener); } public PropertyChangeListener[] getPropertyChangeListeners() { return pcs.getPropertyChangeListeners(); } public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { return pcs.getPropertyChangeListeners(propertyName); } public void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { pcs.removePropertyChangeListener(propertyName, listener); } public boolean hasListeners(String propertyName) { return pcs.hasListeners(propertyName); } public abstract static class PropertyEvent extends PropertyChangeEvent { public static final int ADDED = 0; public static final int UPDATED = 1; public static final int REMOVED = 2; public static final int CLEARED = 3; public static final int MULTI = 4; protected static final Object OLDVALUE = new Object(); protected static final Object NEWVALUE = new Object(); private int type; public PropertyEvent( Object source, String propertyName, Object oldValue, Object newValue, int type ) { super( source, propertyName, oldValue, newValue ); switch( type ){ case ADDED: case UPDATED: case REMOVED: case CLEARED: case MULTI: this.type = type; break; default: this.type = UPDATED; break; } } public int getType() { return type; } public String getTypeAsString() { switch( type ) { case ADDED: return "ADDED"; case UPDATED: return "UPDATED"; case REMOVED: return "REMOVED"; case CLEARED: return "CLEARED"; case MULTI: return "MULTI"; default: return "UPDATED"; } } } public static class PropertyAddedEvent extends PropertyEvent { public PropertyAddedEvent( Object source, String propertyName, Object newValue ) { super( source, propertyName, null, newValue, PropertyEvent.ADDED ); } } public static class PropertyUpdatedEvent extends PropertyEvent { public PropertyUpdatedEvent( Object source, String propertyName, Object oldValue, Object newValue ) { super( source, propertyName, oldValue, newValue, PropertyEvent.UPDATED ); } } public static class PropertyRemovedEvent extends PropertyEvent { public PropertyRemovedEvent( Object source, String propertyName, Object oldValue ) { super( source, propertyName, oldValue, null, PropertyEvent.REMOVED ); } } public static class PropertyClearedEvent extends PropertyEvent { public static final String CLEAR_PROPERTY = "groovy_util_ObservableMap_PropertyClearedEvent_CLEAR"; private Map values = new HashMap(); public PropertyClearedEvent( Object source, Map values ) { super( source, CLEAR_PROPERTY, OLDVALUE, NEWVALUE, PropertyEvent.CLEARED ); if( values != null ) { this.values.putAll( values ); } } public Map getValues() { return Collections.unmodifiableMap( values ); } } public static class MultiPropertyEvent extends PropertyEvent { public static final String MULTI_PROPERTY = "groovy_util_ObservableMap_MultiPropertyEvent_MULTI"; private PropertyEvent[] events = new PropertyEvent[0]; public MultiPropertyEvent( Object source, PropertyEvent[] events ) { super( source, MULTI_PROPERTY, OLDVALUE, NEWVALUE, PropertyEvent.MULTI ); if( events != null && events.length > 0 ) { this.events = new PropertyEvent[events.length]; System.arraycopy(events, 0, this.events, 0, events.length ); } } public PropertyEvent[] getEvents() { PropertyEvent[] copy = new PropertyEvent[events.length]; System.arraycopy(events, 0, copy, 0, events.length ); return copy; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy