org.geoserver.catalog.impl.ModificationProxy Maven / Gradle / Ivy
The newest version!
/* Copyright (c) 2001 - 2008 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.catalog.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Proxies an object storing any modifications to it.
*
* Each time a setter is called through this invocation handler, the property
* is stored and not set on the underlying object being proxied until
* {@link #commit()} is called. When a getter is called through this invocation
* handler, the local properties are checked for one that has been previously
* set, if found it is returned, if not found the getter is forwarded to the
* underlying proxy object being called.
*
*
* Any collections handled through this interface are cloned and client code
* obtains a copy. The two collections will be synced on a call to {@link #commit()}.
*
*
* @author Justin Deoliveira, The Open Planning Project
*
* TODO: this class should use BeanUtils for all reflection stuff
*
*/
public class ModificationProxy implements InvocationHandler {
/**
* the proxy object
*/
Object proxyObject;
/**
* "dirty" properties
*/
HashMap properties;
public ModificationProxy(Object proxyObject) {
this.proxyObject = proxyObject;
}
/**
* Intercepts getter and setter methods.
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ( ( method.getName().startsWith( "get") || method.getName().startsWith( "is" ) )
&& method.getParameterTypes().length == 0 ) {
//intercept getter to check the dirty property set
String property = method.getName().substring(
method.getName().startsWith( "get") ? 3 : 2 );
if ( properties != null && properties().containsKey( property ) ) {
//return the previously set object
return properties().get( property );
}
else {
//if collection, create a wrapper
if ( Collection.class.isAssignableFrom( method.getReturnType() ) ) {
Collection real = (Collection) method.invoke( proxyObject, null );
Collection wrap = real.getClass().newInstance();
wrap.addAll( real );
properties().put( property, wrap );
return wrap;
}
else {
//proceed with the invocation
}
}
}
if ( method.getName().startsWith( "set") && args.length == 1) {
//intercept setter and put new value in list
String property = method.getName().substring( 3 );
properties().put( property, args[0] );
return null;
}
return method.invoke( proxyObject, args );
}
public Object getProxyObject() {
return proxyObject;
}
public HashMap getProperties() {
return properties();
}
public void commit() {
synchronized (proxyObject) {
//commit changes to the proxy object
for ( Map.Entry e : properties().entrySet() ) {
String p = e.getKey();
Object v = e.getValue();
//use the getter to figure out the type for the setter
try {
Method g = getter(p);
//handle collection case
if ( Collection.class.isAssignableFrom( g.getReturnType() ) ) {
Collection c = (Collection) g.invoke(proxyObject,null);
c.clear();
c.addAll( (Collection) v );
}
else {
//call the setter
Method s = proxyObject.getClass().getMethod( "set" + p, g.getReturnType() );
s.invoke( proxyObject, v );
}
}
catch( Exception ex ) {
throw new RuntimeException( ex );
}
}
//reset
properties = null;
}
}
HashMap properties() {
if ( properties != null ) {
return properties;
}
synchronized (this) {
if ( properties != null ) {
return properties;
}
properties = new HashMap();
}
return properties;
}
public List getPropertyNames() {
List propertyNames = new ArrayList();
for ( String propertyName : properties().keySet() ) {
propertyNames.add( Character.toLowerCase( propertyName.charAt( 0 ) )
+ propertyName.substring(1));
}
return propertyNames;
}
public List