alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Groovy example source code file (ObservableMap.java)

This example Groovy source code file (ObservableMap.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Groovy tags/keywords

bean, boolean, changetype, javabean, map, map, object, object, observablemap, propertyaddedevent, propertychangelistener, propertyevent, propertyevent, propertyupdatedevent, string, string, util

The Groovy ObservableMap.java source code

/*
 * 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.<br>
 * 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:
 * <pre>
 * // skip all properties whose value is a closure
 * def map = new ObservableMap( {!(it instanceof Closure)} )
 * <p/>
 * // skip all properties whose name matches a regex
 * def map = new ObservableMap( { name, value -> !(name =~ /[A-Z+]/) } )
 * </pre>
 * <p/>
 * <p>The current implementation will trigger specialized events in the following scenarios,
 * you need not register a different listener as those events extend from PropertyChangeEvent
 * <ul>
 * <li>ObservableMap.PropertyAddedEvent - a new property is added to the map
 * <li>ObservableMap.PropertyRemovedEvent - a property is removed from the map
 * <li>ObservableMap.PropertyUpdatedEvent - a property changes value (same as regular PropertyChangeEvent)
 * <li>ObservableMap.PropertyClearedEvent - all properties have been removed from the map
 * <li>ObservableMap.MultiPropertyEvent - triggered by calling map.putAll(), contains Added|Updated events
 * </ul>

* <p/> * <p> * <strong>Bound properties * <ul> * <li>content - read-only. * <li>size - read-only. * </ul> * </p> * * @author <a href="mailto:aalmiray@users.sourceforge.net">Andres Almiray */ public class ObservableMap implements Map { private Map delegate; private PropertyChangeSupport pcs; private Closure test; public static final String SIZE_PROPERTY = "size"; public static final String CONTENT_PROPERTY = "content"; public static final String CLEARED_PROPERTY = "cleared"; 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); } protected Map getMapDelegate() { return delegate; } protected Closure getTest() { return test; } public Map getContent() { return Collections.unmodifiableMap(delegate); } protected void firePropertyClearedEvent(Map values) { firePropertyEvent(new PropertyClearedEvent(this, values)); } protected void firePropertyAddedEvent(Object key, Object value) { firePropertyEvent(new PropertyAddedEvent(this, String.valueOf(key), value)); } protected void firePropertyUpdatedEvent(Object key, Object oldValue, Object newValue) { firePropertyEvent(new PropertyUpdatedEvent(this, String.valueOf(key), oldValue, newValue)); } protected void fireMultiPropertyEvent(List<PropertyEvent> events) { firePropertyEvent(new MultiPropertyEvent(this, (PropertyEvent[]) events.toArray(new PropertyEvent[events.size()]))); } protected void fireMultiPropertyEvent(PropertyEvent[] events) { firePropertyEvent(new MultiPropertyEvent(this, events)); } protected void firePropertyRemovedEvent(Object key, Object value) { firePropertyEvent(new PropertyRemovedEvent(this, String.valueOf(key), value)); } protected void firePropertyEvent(PropertyEvent event) { pcs.firePropertyChange(event); } protected void fireSizeChangedEvent(int oldValue, int newValue) { pcs.firePropertyChange(new PropertyChangeEvent(this, SIZE_PROPERTY, oldValue, newValue)); } // Map interface public void clear() { int oldSize = size(); Map values = new HashMap(); if (!delegate.isEmpty()) { values.putAll(delegate); } delegate.clear(); firePropertyClearedEvent(values); fireSizeChangedEvent(oldSize, size()); } 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) { int oldSize = size(); 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) { if (newKey) { firePropertyAddedEvent(key, value); fireSizeChangedEvent(oldSize, size()); } else if (oldValue != value) { firePropertyUpdatedEvent(key, oldValue, value); } } } else { oldValue = delegate.put(key, value); if (newKey) { firePropertyAddedEvent(key, value); fireSizeChangedEvent(oldSize, size()); } else if (oldValue != value) { firePropertyUpdatedEvent(key, oldValue, value); } } return oldValue; } public void putAll(Map map) { int oldSize = size(); if (map != null) { List<PropertyEvent> events = new ArrayList(); for (Object o : map.entrySet()) { Entry entry = (Entry) o; 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) { 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) { fireMultiPropertyEvent(events); fireSizeChangedEvent(oldSize, size()); } } } public Object remove(Object key) { int oldSize = size(); Object result = delegate.remove(key); if (key != null) { firePropertyRemovedEvent(key, result); fireSizeChangedEvent(oldSize, size()); } return result; } public int size() { return delegate.size(); } public int getSize() { return 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 enum ChangeType { ADDED, UPDATED, REMOVED, CLEARED, MULTI, NONE; public static final Object oldValue = new Object(); public static final Object newValue = new Object(); public static ChangeType resolve(int ordinal) { switch (ordinal) { case 0: return ADDED; case 2: return REMOVED; case 3: return CLEARED; case 4: return MULTI; case 5: return NONE; case 1: default: return UPDATED; } } } public abstract static class PropertyEvent extends PropertyChangeEvent { /** * deprecated */ public static final int ADDED = ChangeType.ADDED.ordinal(); /** * deprecated */ public static final int UPDATED = ChangeType.UPDATED.ordinal(); /** * deprecated */ public static final int REMOVED = ChangeType.REMOVED.ordinal(); /** * deprecated */ public static final int CLEARED = ChangeType.CLEARED.ordinal(); /** * deprecated */ public static final int MULTI = ChangeType.MULTI.ordinal(); private ChangeType type; /** * @deprecated */ public PropertyEvent(Object source, String propertyName, Object oldValue, Object newValue, int type) { this(source, propertyName, oldValue, newValue, ChangeType.resolve(type)); } public PropertyEvent(Object source, String propertyName, Object oldValue, Object newValue, ChangeType type) { super(source, propertyName, oldValue, newValue); this.type = type; } public int getType() { return type.ordinal(); } public ChangeType getChangeType() { return type; } public String getTypeAsString() { return type.name().toUpperCase(); } } public static class PropertyAddedEvent extends PropertyEvent { public PropertyAddedEvent(Object source, String propertyName, Object newValue) { super(source, propertyName, null, newValue, ChangeType.ADDED); } } public static class PropertyUpdatedEvent extends PropertyEvent { public PropertyUpdatedEvent(Object source, String propertyName, Object oldValue, Object newValue) { super(source, propertyName, oldValue, newValue, ChangeType.UPDATED); } } public static class PropertyRemovedEvent extends PropertyEvent { public PropertyRemovedEvent(Object source, String propertyName, Object oldValue) { super(source, propertyName, oldValue, null, ChangeType.REMOVED); } } public static class PropertyClearedEvent extends PropertyEvent { /** * @deprecated */ public static final String CLEAR_PROPERTY = ObservableMap.CLEARED_PROPERTY; private Map values = new HashMap(); public PropertyClearedEvent(Object source, Map values) { super(source, ObservableMap.CLEARED_PROPERTY, values, null, ChangeType.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, ChangeType.oldValue, ChangeType.newValue, ChangeType.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; } } }

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy ObservableMap.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.