/*
* Copyright (C) 2012 Red Hat, Inc. and/or its affiliates.
*
* 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.jboss.errai.databinding.client;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jboss.errai.common.client.api.Assert;
import org.jboss.errai.databinding.client.api.handler.property.PropertyChangeEvent;
import org.jboss.errai.databinding.client.api.handler.property.PropertyChangeHandler;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
/**
* This is a utility class that can be used by implementations of
* {@link HasPropertyChangeHandlers}. It manages a list of handlers and
* dispatches {@link PropertyChangeEvent}s.
*
* @author David Cracauer <dcracauer@gmail.com>
* @author Christian Sadilek <csadilek@redhat.com>
*/
public class PropertyChangeHandlerSupport {
final List<PropertyChangeHandler<?>> handlers = new ArrayList<PropertyChangeHandler<?>>();
final Multimap<String, PropertyChangeHandler<?>> specificPropertyHandlers = ArrayListMultimap.create();
public Collection<PropertyChangeHandler<?>> removePropertyChangeHandlers() {
final Collection<PropertyChangeHandler<?>> removedHandlers = new ArrayList<PropertyChangeHandler<?>>(handlers);
handlers.clear();
return removedHandlers;
}
public Multimap<String, PropertyChangeHandler<?>> removeSpecificPropertyChangeHandlers() {
final Multimap<String, PropertyChangeHandler<?>> removed = ArrayListMultimap.create(specificPropertyHandlers);
specificPropertyHandlers.clear();
return removed;
}
/**
* Adds a {@link PropertyChangeHandler} that will be notified when any
* property of the bound object changes. Multiple handlers can be registered.
* If the same handler instance is passed multiple times, it will be notified
* multiple times.
*
* @param handler
* The {@link PropertyChangeHandler} to add, must not be null.
*/
public void addPropertyChangeHandler(PropertyChangeHandler<?> handler) {
handlers.add(Assert.notNull(handler));
}
/**
* Adds a {@link PropertyChangeHandler} that will be notified only when the
* given property of the bound object changes. Multiple handlers can be
* registered for each property. If the same handler instance is passed for
* the same property multiple times, it will be notified multiple times. If
* the property name does not correspond to a property of the bound object, no
* exception is thrown, but no events will ever be delivered to the handler.
*
* @param name
* The property name for which notifications should be sent.
* @param handler
* The {@link PropertyChangeHandler} to add, must not be null.
*/
public void addPropertyChangeHandler(String name, PropertyChangeHandler<?> handler) {
specificPropertyHandlers.put(name, Assert.notNull(handler));
}
/**
* Removes a {@link PropertyChangeHandler} from the list of handlers. If the
* handler was added more than once to the same event source, it will be
* notified one less time after being removed. If handler is null, or was
* never added, no exception is thrown and no action is taken.
*
* @param handler
* The {@link PropertyChangeHandler} to remove.
*/
public void removePropertyChangeHandler(PropertyChangeHandler<?> handler) {
handlers.remove(handler);
}
/**
* Removes a {@link PropertyChangeHandler}, causing it no longer to be
* notified only when the given property of the bound object changes. If the
* same handler instance was added for the same property multiple times, it
* will be notified one less time per change than before. If handler is null,
* was never added, or the property name does not correspond to a property of
* the bound object, no exception is thrown and no action is taken.
*
* @param name
* The property name for which notifications should be sent.
* @param handler
* The {@link PropertyChangeHandler} to add, must not be null.
*/
public void removePropertyChangeHandler(String name, PropertyChangeHandler<?> handler) {
specificPropertyHandlers.remove(name, Assert.notNull(handler));
}
/**
* Notify registered {@link PropertyChangeHandlers} of a
* {@link PropertyChangeEvent}. Will only dispatch events that represent a
* change. If oldValue and newValue are equal, the event will be ignored.
*
* @param event
* {@link the PropertyChangeEvent} to provide to handlers.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public void notifyHandlers(PropertyChangeEvent<?> event) {
if (!acceptEvent(event)) {
return;
}
final Collection<PropertyChangeHandler<?>> specHandlers = specificPropertyHandlers.get(event.getPropertyName());
for (PropertyChangeHandler handler : specHandlers.toArray(new PropertyChangeHandler<?>[specHandlers.size()])) {
handler.onPropertyChange(event);
}
for (PropertyChangeHandler handler : handlers.toArray(new PropertyChangeHandler<?>[handlers.size()])) {
handler.onPropertyChange(event);
}
}
private boolean acceptEvent(PropertyChangeEvent<?> event) {
if (event == null) {
return false;
}
if (event.getOldValue() == null) {
return event.getNewValue() != null;
}
if (event.getNewValue() == null) {
return event.getOldValue() != null;
}
return !event.getOldValue().equals(event.getNewValue());
}
}