/*******************************************************************************
 * This file is part of TERMINAL RECALL
 * Copyright (c) 2012-2014 Chuck Ritola
 * Part of the jTRFP.org project
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     chuck - initial API and implementation
 ******************************************************************************/

package org.jtrfp.trcl.flow;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeListenerProxy;
import java.beans.PropertyChangeSupport;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;

import org.apache.commons.beanutils.PropertyUtils;

/**
 * Yet another implementation of a property proxy object, though not quite as cool as those used
 * with ProxyObjects, this doesn't need an skeletal interface and simply forwards property change
 * events.<br>
 * CAVEAT: General (nameless) property change listeners will not be notified of an indirection change
 * since this class depends on the name of the property to send the refresh. Blindly refreshing all
 * properties with each indirection change would be potentially wasteful of resources as it could trigger 
 * unneeded lazy allocations which don't end up being used.
 * @author Chuck Ritola
 *
 */
public class IndirectProperty<PROPERTY_TYPE> implements PropertyChangeListener{
 private final PropertyChangeSupport
  targetPCS = new PropertyChangeSupport(this),
  thisPCS   = new PropertyChangeSupport(this);
 private WeakReference<PROPERTY_TYPE> target = new WeakReference<PROPERTY_TYPE>(null);
/**
 * @return the target
 */
public PROPERTY_TYPE getTarget() {
    return target.get();
}
/**
 * @param target the target to set
 */
public void setTarget(PROPERTY_TYPE target) {
    if(this.target!=target){
	if(this.target!=null)
	    disconnectAllFrom(this.target.get());
	if(target!=null)
	    connectAllTo(target);
	thisPCS.firePropertyChange("target", this.target, target);
	this.target = new WeakReference<PROPERTY_TYPE>(target);
	fireAllPropertiesChanged();
    }//end if(changed)
}//end setTarget(...)

private void connectAllTo(PROPERTY_TYPE tgt) {
    for(PropertyChangeListener l:targetPCS.getPropertyChangeListeners()){
	if(l instanceof PropertyChangeListenerProxy){
	    final PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy)l;
	    try{tgt.getClass().getMethod("addPropertyChangeListener", String.class,PropertyChangeListener.class).invoke(tgt, proxy.getPropertyName(), proxy);}
   		catch(Exception e){e.printStackTrace();}}
	else{try{tgt.getClass().getMethod("addPropertyChangeListener", PropertyChangeListener.class).invoke(tgt, l);}
   	 catch(Exception e){e.printStackTrace();}}
       }//end for(listener)
}
private void disconnectAllFrom(PROPERTY_TYPE tgt) {
    if(tgt==null)
	return;
    for(PropertyChangeListener l:targetPCS.getPropertyChangeListeners()){
   	try{tgt.getClass().getMethod("removePropertyChangeListener", PropertyChangeListener.class).invoke(tgt, l);}
   	catch(Exception e){e.printStackTrace();}
       }//end for(listener)
}
public IndirectProperty<PROPERTY_TYPE> addPropertyChangeListener(String propertyName, PropertyChangeListener l){
	thisPCS.addPropertyChangeListener(propertyName, l);
	return this;
}

public IndirectProperty<PROPERTY_TYPE> removePropertyChangeListener(PropertyChangeListener l){
	thisPCS.removePropertyChangeListener(l);
	return this;
}

public IndirectProperty<PROPERTY_TYPE> addTargetPropertyChangeListener(String propertyName, PropertyChangeListener l){
	targetPCS.addPropertyChangeListener(propertyName, l);
	return this;
}

public IndirectProperty<PROPERTY_TYPE> removeTargetPropertyChangeListener(PropertyChangeListener l){
	targetPCS.removePropertyChangeListener(l);
	return this;
}

private void fireAllNullProperties(){
    
}

private void fireAllPropertiesChanged(){
    PROPERTY_TYPE target = this.target.get();
    HashSet<String> propertiesToFireChanged = new HashSet<String>();
    PropertyChangeListener []listeners = targetPCS.getPropertyChangeListeners();
    for(PropertyChangeListener l:listeners){
	if(l instanceof PropertyChangeListenerProxy){
	    final PropertyChangeListenerProxy prox = (PropertyChangeListenerProxy)l;
	    propertiesToFireChanged.add(prox.getPropertyName());
	}//end if(...)
    }//end for(listener)
    for(String propertyName:propertiesToFireChanged){
	try{targetPCS.firePropertyChange(propertyName, null, target!=null?PropertyUtils.getProperty(target, propertyName):null);}
	catch(NoSuchMethodException e){e.printStackTrace();}
	catch(InvocationTargetException e){e.printStackTrace();}
	catch(IllegalAccessException e){e.printStackTrace();}
    }//end for(propertyNames)
 }//end fireAllPropertiesChanged()
@Override
public void propertyChange(PropertyChangeEvent evt) {
    final Object newVal = evt.getNewValue();
    setTarget((PROPERTY_TYPE)newVal);
}//end propertyChange(...)
 
}//end IndirectBean

@@ -1633,16 +1633,66 @@
 stener%7B%0A
+    public static final String TARGET = %22target%22;%0A
  private
@@ -2250,24 +2250,22 @@
 yChange(
-%22target%22
+TARGET
 , this.t

Create bound property TARGET in IndirectProperty.