
org.nuiton.util.beans.BeanMonitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nuiton-utils Show documentation
Show all versions of nuiton-utils Show documentation
Library of usefull class to be used in any project.
/*
* #%L
* Nuiton Utils
* %%
* Copyright (C) 2004 - 2010 CodeLutin
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.util.beans;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* A monitor of beans.
*
* You indicates which properties to monitor (via the constructor).
*
* Then attach a bean to monitor via the method {@link #setBean(Object)}.
*
* The method {@link #clearModified()} reset the states about modified
* properties.
*
* The method {@link #wasModified()} tells if there was any modification on
* monitored properties for the attached bean since the last call to
* {@link #clearModified()} (or {@link #setBean(Object)}.
*
* the method {@link #getModifiedProperties()} gives you the names of monitored
* properties for the attached bean since the last call to
* {@link #clearModified()} (or {@link #setBean(Object)}.
*
* @author Tony Chemit - [email protected]
* @since 1.4.1
*/
public class BeanMonitor {
/** Logger */
private static final Log log = LogFactory.getLog(BeanMonitor.class);
/** Names of properties to watch or not on attached bean. */
protected final List propertyNames;
/** If true, then listen to the properties which are not in the propertyNames */
protected boolean excludeMode;
/** Names of monitored and modified properties for the attached bean. */
protected final Map propertyDiffs;
/**
* The {@link PropertyChangeListener} which listen modification on bean
* only on monitored properties.
*/
protected final PropertyChangeListener listener;
/** The bean to monitor. */
protected Object bean;
/**
* Constructor of monitor with property names to monitor.
*
* @param propertyNames the names of properties to monitor
*/
public BeanMonitor(String... propertyNames) {
this(false, propertyNames);
}
public BeanMonitor(boolean exclude, String... propertyNames) {
this.excludeMode = exclude;
this.propertyNames = new ArrayList(Arrays.asList(propertyNames));
propertyDiffs = new LinkedHashMap();
listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String propertyName = evt.getPropertyName();
if (excludeMode && BeanMonitor.this.propertyNames.contains(propertyName)
|| !excludeMode && !BeanMonitor.this.propertyNames.contains(propertyName)) {
return;
}
// the property is monitored
Object newValue = evt.getNewValue();
Object oldValue = evt.getOldValue();
PropertyDiff propertyDiff;
if (propertyDiffs.containsKey(propertyName)) {
// property was already modified
propertyDiff = propertyDiffs.get(propertyName);
// change the target value
propertyDiff.setTargetValue(newValue);
// check if value did not come back to original
Object originalValue = propertyDiff.getSourceValue();
if (ObjectUtils.equals(originalValue, newValue)) {
// coming back to original value
// the property is no more modified
propertyDiffs.remove(propertyName);
}
} else {
// check old value and newvalue are real not equals
if (ObjectUtils.notEqual(newValue, oldValue)) {
propertyDiff = new PropertyDiff(
propertyName,
oldValue,
propertyName,
newValue,
null
);
// add it to modified properties
propertyDiffs.put(propertyName, propertyDiff);
}
}
}
};
}
/**
* Obtains the monitored {@link #bean}.
*
* @return the attached bean, or {@code null} if none attached.
*/
public Object getBean() {
return bean;
}
/**
* Tells if any of the monitored properties were modified on the attached
* bean since last call to {@link #clearModified()} or
* {@link #setBean(Object)}.
*
* @return {@code true} if there were a modified monitored property on
* the attached bean, {@code false} otherwise.
*/
public boolean wasModified() {
return !propertyDiffs.isEmpty();
}
/**
* Obtains the names of monitored properties which has been touched for the
* attached bean since last call to {@link #clearModified()} or
* {@link #setBean(Object)}.
*
* @return the array of property names to monitor.
*/
public String[] getModifiedProperties() {
Set propertyNames = propertyDiffs.keySet();
return propertyNames.toArray(new String[propertyNames.size()]);
}
/**
* Obtains the original values for all modified properties.
*
* @return the dictionnary of original values for modified properties
*/
public Map getOriginalValues() {
// always expose a copy of map
Map map = new TreeMap();
for (PropertyDiff propertyDiff : propertyDiffs.values()) {
map.put(propertyDiff.getSourceProperty(),
propertyDiff.getSourceValue());
}
return map;
}
/**
* Obtains the property diffs since the bean is monitoried.
*
* @return the property diffs since the bean is monitoried.
* @since 2.4
*/
public PropertyDiff[] getPropertyDiffs() {
Collection values = propertyDiffs.values();
return values.toArray(new PropertyDiff[values.size()]);
}
/**
* Sets the {@code bean} to monitor.
*
* As a side effect, it will attach the {@link #listener} to new bean
* (if not null) and remove it from the previous bean attached (if not null).
*
* As a second side effect, it will always clean the modified states,
* using the method {@link #clearModified()}.
*
* @param bean the new bean to monitor
*/
public void setBean(Object bean) {
Object oldBean = this.bean;
this.bean = bean;
// while removing a bean must clean modified states
clearModified();
if (oldBean != null) {
// dettach listener from old bean
try {
BeanUtil.removePropertyChangeListener(listener, oldBean);
} catch (Exception eee) {
log.error(String.format("Could remove the PropertychangeListener %1$s from object %2$s for following reason \\: %3$s",
listener, oldBean, eee.getMessage()));
}
}
if (bean != null) {
// attach listener to new bean
try {
BeanUtil.addPropertyChangeListener(listener, bean);
} catch (Exception eee) {
log.error(String.format("Could not add the PropertychangeListener %1$s on object %2$s for following reason \\: %3$s",
listener, bean, eee.getMessage()));
}
}
}
/** To clear the modified states. */
public void clearModified() {
propertyDiffs.clear();
}
/**
* To change the list of properties to watch.
*
* Note: As a side-effect, we call a
* {@link #clearModified()} method after having changed the list of
* properties to watch.
*
* @param properties the list of properties to watch on the bean.
* @since 3.0
*/
public void setProperties(String... properties) {
propertyNames.clear();
propertyNames.addAll(Arrays.asList(properties));
clearModified();
}
public boolean isExcludeMode() {
return excludeMode;
}
public void setExcludeMode(boolean excludeMode) {
this.excludeMode = excludeMode;
clearModified();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy