1 /* Copyright 2008 the original author or authors. 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package grails.test; 16 17 import groovy.lang.Closure; 18 19 /** 20 * Skeleton implementation of a wrapper class for closures that allows 21 * you to intercept invocations of the closure. The wrapper can be used 22 * anywhere that the target closure can be used. 23 */ 24 @SuppressWarnings("serial") 25 public abstract class AbstractClosureProxy extends Closure { 26 27 private Closure target; 28 29 /** 30 * Creates a new instance that wraps the target closure and sends 31 * profiling events to the given profiler log. 32 * @param closure The target closure to wrap. 33 */ AbstractClosureProxy(Closure closure)34 public AbstractClosureProxy(Closure closure) { 35 super(closure.getOwner(), closure.getThisObject()); 36 target = closure; 37 } 38 39 /** 40 * This method is called before the target closure is invoked. 41 * This is a passive interceptor, so you cannot prevent the 42 * call to the target closure. You can modify the arguments, 43 * though, but it's not recommended unless you really know 44 * what you're doing. 45 * @param args The arguments passed to the closure. 46 */ doBeforeCall(Object[] args)47 protected abstract void doBeforeCall(Object[] args); 48 49 /** 50 * This method is called after the target closure is invoked. 51 * It will be triggered whether or not an exception is thrown 52 * by the target closure. 53 * @param args The arguments passed to the closure. 54 */ doAfterCall(Object[] args)55 protected abstract void doAfterCall(Object[] args); 56 57 /** 58 * Called when a new instance of the proxy needs to be created for 59 * the given closure. Usually the implementation simply creates a 60 * new instance of the current class, copying over the existing 61 * proxy properties: 62 * <pre> 63 * return new MyClosureProxy(c, this.field1, ...) 64 * </pre> 65 * @param c The closure to wrap/proxy. 66 */ createWrapper(Closure c)67 protected abstract Closure createWrapper(Closure c); 68 69 /** 70 * This is the important one: logs entry and exit of the closure call. 71 */ 72 @Override call(Object[] objects)73 public Object call(Object[] objects) { 74 doBeforeCall(objects); 75 76 try { 77 return target.call(objects); 78 } 79 finally { 80 doAfterCall(objects); 81 } 82 } 83 84 /** 85 * Compares based on identities, but unlike the standard implementation 86 * this one will return <code>true</code> if the given object is the 87 * target closure for this wrapper as well. 88 */ 89 @Override equals(Object obj)90 public boolean equals(Object obj) { 91 return this == obj || target == obj; 92 } 93 94 @Override hashCode()95 public int hashCode() { 96 return target.hashCode(); 97 } 98 99 @Override curry(Object[] objects)100 public Closure curry(Object[] objects) { 101 return createWrapper(target.curry(objects)); 102 } 103 104 @Override isCase(Object o)105 public boolean isCase(Object o) { 106 return target.isCase(o); 107 } 108 109 @Override asWritable()110 public Closure asWritable() { 111 return target.asWritable(); 112 } 113 114 @Override getProperty(String property)115 public Object getProperty(String property) { 116 return target.getProperty(property); 117 } 118 119 @Override setProperty(String s, Object o)120 public void setProperty(String s, Object o) { 121 target.setProperty(s, o); 122 } 123 124 @Override getMaximumNumberOfParameters()125 public int getMaximumNumberOfParameters() { 126 return target.getMaximumNumberOfParameters(); 127 } 128 129 @Override getParameterTypes()130 public Class<?>[] getParameterTypes() { 131 return target.getParameterTypes(); 132 } 133 134 @Override getDelegate()135 public Object getDelegate() { 136 return target.getDelegate(); 137 } 138 139 @Override setDelegate(Object o)140 public void setDelegate(Object o) { 141 target.setDelegate(o); 142 } 143 144 @Override getDirective()145 public int getDirective() { 146 return target.getDirective(); 147 } 148 149 @Override setDirective(int i)150 public void setDirective(int i) { 151 target.setDirective(i); 152 } 153 154 @Override getResolveStrategy()155 public int getResolveStrategy() { 156 return target.getResolveStrategy(); 157 } 158 159 @Override setResolveStrategy(int i)160 public void setResolveStrategy(int i) { 161 target.setResolveStrategy(i); 162 } 163 } 164