Package net.sourceforge.javautil.groovy.builder

Source Code of net.sourceforge.javautil.groovy.builder.GroovyBuilder

package net.sourceforge.javautil.groovy.builder;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import net.sourceforge.javautil.common.proxy.CollectionTargetProxy;
import net.sourceforge.javautil.groovy.io.GroovyPrintableOutputHandler;
import net.sourceforge.javautil.groovy.builder.GroovyBuilderInterceptor.InterceptorContext;

import org.codehaus.groovy.runtime.InvokerHelper;

import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.GroovyObjectSupport;
import groovy.lang.MetaMethod;
import groovy.lang.MissingPropertyException;

/**
* This is the basic foundation for all net.sourceforge.javautil Groovy Builder's. It provides basic
* delegation functionality to allow method calls to be 'intercepted' by the builder and then delegated
* to what the implementation considers part of the build process. It will also allow pluggable binding's
* similar to script bindings to be setup for the builder.<br/><br/>
*
* @author elponderador
*
* @param <T> This is passed onto the GroovyBuilderStack, which means that a builder stack dealing with this type should
* be provided to the builder.
*
* @see net.sourceforge.javautil.groovy.framework.builders.GroovyBuilderStack
*/
public abstract class GroovyBuilder<T> extends GroovyObjectSupport implements GroovyPrintableOutputHandler {

  protected Binding binding;
  protected GroovyBuilderInterceptor interceptor;
 
  protected List<GroovyBuilderListener> listeners = new CopyOnWriteArrayList<GroovyBuilderListener>();
  protected GroovyBuilderListener propagator = CollectionTargetProxy.create(GroovyBuilderListener.class, listeners);

  /**
   * This constructor can be used by subclasses in order to allow them to
   * setup the binding and interceptor when it is not practical to pass them
   * to the super constructor.
   */
  public GroovyBuilder () {}
 
  /**
   * This is the primary constructor to be used in order to setup immediately which
   * binding and interceptor will be used by this builder implementation.
   *
   * @param binding The binding to be used for variable resolution.
   * @param interceptor The interceptor that will translate method calls into the building process.
   */
  public GroovyBuilder(Binding binding, GroovyBuilderInterceptor interceptor) {
    this.binding = binding;
    this.interceptor = interceptor;
  }
 
  /**
   * The instance returned can be used to 'propagate' events.
   *
   * @return The event propagator, that implements the {@link GroovyBuilderListener} interface.
   * @see {@link GroovyBuilderInterceptor}
   */
  public GroovyBuilderListener<T> getEventPropagator () { return this.propagator; }
 
  /**
   * Add a {@link GroovyBuilderListener} to the list of listeners, that will be called in the order they are
   * added, that will be notified of node and builder related events.
   *
   * @param listener The {@link GroovyBuilderListener} to add.
   */
  public void addBuilderListener (GroovyBuilderListener<T> listener) { this.listeners.add(listener); }
 
  /**
   * Remove a {@link GroovyBuilderListener} from the list of listeners.
   *
   * @param listener The {@link GroovyBuilderListener} to be removed.
   * @see #addBuilderListener(GroovyBuilderListener)
   */
  public void removeBuilderListener (GroovyBuilderListener<T> listener) { this.listeners.remove(listener); }
 
  /**
   * @return This is a context object for contextual processing, resolution and the like. {@link GroovyBuilderStack}'s
   * may decide to store the result of their processing inside the context for indirect storage.
   */
  public abstract GroovyBuildingContext getBuildingContext ();

  /**
   * The binding can be retrieved publicly.
   *
   * @return The binding currently being used by this builder.
   */
  public Binding getBinding() { return binding; }
 
  /**
   * To setup the binding. It is discouraged for this to be made public and is
   * primarily intended for situations where the default constructor is used and
   * later in the implementation constructor the binding can be set.
   *
   * @param binding A binding instance for variable resolution.
   */
  protected void setBinding(Binding binding) { this.binding = binding; }

  /**
   * The interceptor currently being used by this builder for method delegation.
   *
   * @return The currently set interceptor;
   */
  public GroovyBuilderInterceptor getInterceptor() { return interceptor; }
 
  /**
   * Like the {@link #setBinding(Binding)} method this is principally to be used
   * when the default constructor is used and the interceptor is setup after initial
   * super class instantiation.
   *
   * @param interceptor The interceptor implementation that is to be used by this builder.
   */
  protected void setInterceptor(GroovyBuilderInterceptor interceptor) { this.interceptor = interceptor; }
 
  /**
   * The link to the stack in this thread which is the destination for all of the nodes
   * built in a single building process.
   *
   * @return The implementation of the GroovyBuilderStack that is currently being used by this builder.
   */
  public abstract GroovyBuilderStack<T> getStack ();
 
  /**
   * This is called on every invocation of {@link #invokeMethod(String, Object)} in order
   * to determine if the stack has been initialized. If it returns false, then
   * {@link #initializeStack()} will be called.
   * @return
   */
  public abstract boolean isStackInitialized ();
 
  /**
   * This allows the implementation the opportunity to
   * initialize the stack before any nodes are created or
   * added. This is called before {@link #startBuilding()}.
   * Implementations MUST call {@link GroovyBuilderListener#stackStarting(GroovyBuilder)} AFTER
   * {@link GroovyBuilderStack#initialize()} has been called.
   *
   * This method will be called by {@link #invokeMethod(String, Object)}
   * whenever {@link #isStackInitialized()} returns false.
   */
  public abstract void initializeStack ();
 
  /**
   * This allows the implementation the ability to
   * cleanup the stack after the last node was completed.
   * This is called after {@link #stopBuilding()}. Implementations MUST call
   * {@link GroovyBuilderListener#stackStopping(GroovyBuilder)} BEFORE
   * {@link GroovyBuilderStack#cleanup()} is called.
   */
  public abstract void cleanupStack ();

  /**
   * This will be called before the first node is created, allowing the builder to do initialization.
   * This will be called usually by the interceptor when it is sure, or is almost sure that a node is
   * about to be created. It MUST be called before the root node is created for each building process.
   */
  public void startBuilding () {
    this.getBuildingContext().initialize();
  }
 
  /**
   * This will be called after the last node is completed, allowing the builder to do cleanup.
   */
  public void stopBuilding () {
    this.getBuildingContext().cleanup();
  }
 
  /**
   * A facility that can be used to setup a closure to be used by this builder. This
   * will normally be called principally by the interceptor implementation. This can
   * be used by other implementations to wrap the closure.
   *
   * @param closure The closure to be setup.
   */
  public Closure setupClosure (Closure closure) {
    closure.setDelegate(this);
    closure.setResolveStrategy(Closure.DELEGATE_FIRST); closure.getMetaClass();
    if (!(closure.getMetaClass() instanceof BuilderClosureMetaClass)) closure.setMetaClass( new BuilderClosureMetaClass(closure, this) );
    return closure;
  }
 
  /**
   * A facility that can be used to invoke a closure. It will by default call
   * the @link {@link #setupClosure(Closure)} method and then using the returned
   * value call the closure.
   *
   * @param closure
   * @param arguments
   */
  public void invoke (Closure closure, Object... arguments) {
    closure = this.setupClosure(closure);
    closure.call(arguments);
  }

  /**
   * This is overridden to allow missing properties to be
   * delegated to the binding.
   */
  public Object getProperty(String property) {
    try {
      return super.getProperty(property);
    } catch (MissingPropertyException e) {
      return binding.getProperty(property);
    }
  }

  /**
   * This simply looks up the method that should be called via the meta class. If there
   * is none it will pass null along with the other arguments to the
   * {@link net.sourceforge.javautil.groovy.framework.builders.GroovyBuilderInterceptor#handleInvokedMethod(GroovyBuilder, MetaMethod, String, Object[])}.
   *
   * This will also call {@link #initializeStack()} whenever the {@link #getStack()} method returns null.
   *
   * It does NOT invoke any methods, even if a meta method was found for the call.
   */
  public Object invokeMethod(String name, Object args) {
    if (!this.isStackInitialized()) this.initializeStack();
    Object[] arguments = InvokerHelper.asArray(args);
    return interceptor.handleInvokedMethod(new InterceptorContext( this, this.getStack(), name, arguments ));
  }

  /**
   * This is overridden to allow missing property exceptions to fall back
   * to the binding that has been set.
   */
  public void setProperty(String property, Object newValue) {
    try {
      super.setProperty(property, newValue);
    } catch (MissingPropertyException e) {
      binding.setProperty(property, newValue);
    }
  }
 
}
TOP

Related Classes of net.sourceforge.javautil.groovy.builder.GroovyBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.