Package org.codehaus.gant

Source Code of org.codehaus.gant.GantMetaClass

//  Gant -- A Groovy way of scripting Ant tasks.
//
//  Copyright © 2006-11 Russel Winder
//
//  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.codehaus.gant ;

import java.util.HashSet ;
import java.util.List ;
import java.util.Set ;

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//  In Groovy 1.7.x Closure was a type, in Groovy 1.8.x Closure is a parameterized type.
//  To support compilation against both versions of Groovy with the same source,
//  suffer the "raw type" warnings that Eclipse issues.
//////////////////////////////////////////////////////////////////////////////////////////////////////////

import groovy.lang.Closure ;
import groovy.lang.DelegatingMetaClass ;
import groovy.lang.GString ;
import groovy.lang.MetaClass ;
import groovy.lang.MissingMethodException ;
import groovy.lang.MissingPropertyException ;
import groovy.lang.Tuple ;

import org.codehaus.groovy.runtime.MetaClassHelper ;

import org.apache.tools.ant.BuildException ;

/**
*  This class is the metaclass used for target <code>Closure</code>s, and any enclosed <code>Closures</code>.
*
<p>This metaclass deals with <code>depends</code> method calls and redirects unknown method calls to the
*  instance of <code>GantBuilder</code>.  To process the <code>depends</code> all closures from the
*  binding called during execution of the Gant specification must be logged so that when a depends happens
*  the full closure call history is available.</p>
*
@author Russel Winder <russel@russel.org.uk>
*/
public class GantMetaClass extends DelegatingMetaClass {
  /**
   *  The set of all targets that have been called.  This is a global variable shared by all instances of
   *  <code>GantMetaClass</code>.
   *
   *  <p>TODO : This code is a long way from thread safe, and so it needs fixing.  Should this variable be
   *  moved to the GantState class, which is the class that holds other bits of the internal shared state?
   *  Should a different data structure be used, one that is a bit more thread safe?  Arguably it is
   *  reasonable for this to be a synchronized object.</p>
   */
  private static final Set<Closure> methodsInvoked = new HashSet<Closure> ( ) ;
  /**
   *  The binding (aka global shared state) that is being used.
   */
  private final GantBinding binding ;
  /*
   */
  public GantMetaClass ( final MetaClass metaClass , final GantBinding binding ) {
    super ( metaClass ) ;
    this.binding = binding ;
  }
  /**
   *  Execute a <code>Closure</code> only if it hasn't been executed previously.  If it is executed, record
   *  the execution.  Only used for processing a <code>depends</code> call.
   *
   *  @param closure The <code>Closure</code> to potentially call.
   *  @return the result of the <code>Closure</code> call, or <code>null</code> if the closure was not
   *  called.
   */
  private Object processClosure ( final Closure closure ) {
    if ( ! methodsInvoked.contains ( closure ) ) {
      methodsInvoked.add ( closure ) ;
      return closure.call ( ) ;
    }
    return null ;
  }
  /**
   *  Process the argument to a <code>depends</code> call.  If the parameter is a <code>Closure</code> just
   *  process it. If it is a <code>String</code> then do a lookup for the <code>Closure</code> in the
   *  binding, and if found process it.
   *
   *  @param argument The argument.
   *  @return The result of the <code>Closure</code>.
   */
  private Object processArgument ( final Object argument ) {
    final Object returnObject ;
    if ( argument instanceof Closure ) { returnObject = processClosure ( (Closure) argument ) ; }
    else {
      final String errorReport = "depends called with an argument (" + argument + ") that is not a known target or list of targets." ;
      Object theArgument = argument ;
      if ( theArgument instanceof GString ) { theArgument = theArgument.toString ( ) ; }
      if ( theArgument instanceof String ) {
        final Object entry = binding.getVariable ( (String) theArgument ) ;
        if ( ( entry != null ) && ( entry instanceof Closure ) ) { returnObject = processClosure ( (Closure) entry ) ; }
        else { throw new RuntimeException ( errorReport ) ; }
      }
      else { throw new RuntimeException ( errorReport ) ; }
    }
    return returnObject ;
  }
  /**
   *  Invokes a method on the given object with the given name and arguments. The <code>MetaClass</code>
   *  will attempt to pick the best method for the given name and arguments. If a method cannot be invoked a
   *  <code>MissingMethodException</code> will be thrown.
   *
   *  @see MissingMethodException
   *  @param object The instance on which the method is invoked.
   *  @param methodName The name of the method.
   *  @param arguments The arguments to the method.
   *  @return The return value of the method which is <code>null</code> if the return type is
   *  <code>void</code>.
   */
  @Override public Object invokeMethod ( final Object object , final String methodName , final Object[] arguments ) {
    Object returnObject = null ;
    if ( methodName.equals ( "depends" ) ) {
      for ( final Object argument : arguments ) {
        if ( argument instanceof List<?> ) {
          for ( final Object item : (List<?>) argument ) { returnObject = processArgument ( item ) ; }
        }
        else { returnObject = processArgument ( argument ) ; }
      }
    }
    else {
      try {
        returnObject = super.invokeMethod ( object , methodName , arguments ) ;
        try {
          final Closure closure = (Closure) binding.getVariable ( methodName ) ;
          if ( closure != null ) { methodsInvoked.add ( closure ) ; }
        }
        catch ( final MissingPropertyException mpe ) { /* Purposefully empty */ }
      }
      catch ( final MissingMethodException mme ) {
        try { returnObject = ( (GantBuilder) ( binding.getVariable ( "ant" ) ) ).invokeMethod ( methodName , arguments ) ; }
        catch ( final BuildException be ) {
          //  This BuildException could be a real exception due to a failed execution of a found Ant task
          //  (in which case it should be propagated), or it could be due to a failed name lookup (in which
          //  case the MissingMethodException should be propagated).  The big problem is distinguishing the
          //  various uses of Build Exception here -- for now use string search of the exception message to
          //  distinguish the cases.  NB GANT-49 and GANT-68 are the main conflicting issues here :-(
          if ( be.getMessage ( ).startsWith ( "Problem: failed to create task or type" ) ) { throw mme ; }
          else { throw be ; }
        }
        catch ( final Exception e ) { throw mme ; }
      }
    }
    return returnObject ;
  }
  /**
   *  Invokes a method on the given object, with the given name and single argument.
   *
   *  @see #invokeMethod(Object, String, Object[])
   *  @param object The Object to invoke the method on
   *  @param methodName The name of the method
   *  @param arguments The argument to the method
   *  @return The return value of the method which is null if the return type is void
   */
  @Override public Object invokeMethod ( final Object object , final String methodName , final Object arguments ) {
    if ( arguments == null ) { return invokeMethod ( object , methodName , MetaClassHelper.EMPTY_ARRAY ) ; }
    else if ( arguments instanceof Tuple ) { return invokeMethod ( object , methodName , ( (Tuple) arguments ).toArray ( ) ) ; }
    else if ( arguments instanceof Object[] ) { return invokeMethod ( object , methodName , (Object[]) arguments ) ; }
    else { return invokeMethod ( object , methodName , new Object[] { arguments } ) ; }
  }
  /**
   *  Invoke the given method.
   *
   *  @param name the name of the method to call
   *  @param args the arguments to use for the method call
   *  @return the result of invoking the method
   */
  @Override public Object invokeMethod ( final String name , final Object args ) {
    return invokeMethod ( this , name , args ) ;
  }
  //////////////////////////////////////////////////////////////////////////////////////////////////////////
  //  As at 2010-10-08 it is believed that groovy.lang.MetaClass (the invokeMethod method anyway) has
  //  not been marked up for Java generics in any branch of Groovy (1.6, 1.7, trunk). This leads to the
  //  problem that blah(Class<?>) does not override blah(Class) -- at least  according to Eclipse. So we
  //  must leave the Class type without a type parameter and suffer the compilation warning.
  //
  //  TODO : Class -> Class<?> when the Eclipse plugin and the Groovy code base allow.
  //////////////////////////////////////////////////////////////////////////////////////////////////////////
  /**
   *  Invoke a method on the given receiver for the specified arguments. The sender is the class that
   *  invoked the method on the object.  Attempt to establish the method to invoke based on the name and
   *  arguments provided.
   *
   *  <p>The <code>isCallToSuper</code> and <code>fromInsideClass</code> help the Groovy runtime perform
   *  optimizations on the call to go directly to the superclass if necessary.</p>
   *
   *  @param sender The <code>java.lang.Class</code> instance that invoked the method.
   *  @param receiver The object which the method was invoked on.
   *  @param methodName The name of the method.
   *  @param arguments The arguments to the method.
   *  @param isCallToSuper Whether the method is a call to a superclass method.
   *  @param fromInsideClass Whether the call was invoked from the inside or the outside of the class.
   *  @return The return value of the method
   */
  @Override public Object invokeMethod ( final Class sender , final Object receiver , final String methodName , final Object[] arguments, final boolean isCallToSuper, final boolean fromInsideClass ) {
    return invokeMethod ( receiver , methodName , arguments ) ;
  }
}
TOP

Related Classes of org.codehaus.gant.GantMetaClass

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.