View Javadoc
1 package org.codehaus.nanning.contract; 2 3 4 5 import java.text.MessageFormat; 6 7 import java.util.ArrayList; 8 9 import java.util.List; 10 11 import java.util.ListIterator; 12 13 import java.util.Map; 14 15 import java.util.regex.Matcher; 16 17 import java.util.regex.Pattern; 18 19 20 21 import org.codehaus.nanning.Invocation; 22 23 import org.codehaus.nanning.MethodInterceptor; 24 25 import org.codehaus.nanning.attribute.Attributes; 26 27 import ognl.MethodFailedException; 28 29 import ognl.Ognl; 30 31 import org.apache.commons.logging.Log; 32 33 import org.apache.commons.logging.LogFactory; 34 35 36 37 /*** 38 39 * TODO document ContractInterceptor. 40 41 * Tip: use <code>Class.desiredAssertionStatus()</code> to check wheather to addLink this interceptor or not, that way 42 43 * you can enable and disable contract-checking in the same way you enable and disable assertions (java -ea and so on). 44 45 * 46 47 * @author <a href="mailto:jon_tirsen@yahoo.org">Jon Tirsén</a> 48 49 * @version $Revision: 1.1 $ 50 51 */ 52 53 public class ContractInterceptor implements MethodInterceptor { 54 55 private static final Log logger = LogFactory.getLog(ContractInterceptor.class); 56 57 private static final Pattern oldPattern = 58 59 Pattern.compile("(.*)//{old (.*?)}(.*)"); 60 61 62 63 /*** 64 65 * If this is non-null don't execute contracts, used when executing the expressions. 66 67 */ 68 69 private ThreadLocal checkContracts = new ThreadLocal(); 70 71 72 73 public Object invoke(Invocation invocation) throws Throwable { 74 75 String ensures = Attributes.getAttribute(invocation.getMethod(), "ensures"); 76 77 String requires = Attributes.getAttribute(invocation.getMethod(), "requires"); 78 79 String invariant = Attributes.getAttribute(invocation.getMethod().getDeclaringClass(), "invariant"); 80 81 82 83 StringBuffer parsedEnsure = null; 84 85 List oldValues = null; 86 87 88 89 if (checkContracts.get() == null) { 90 91 assertExpressionTrue(invocation, requires, 92 93 "precondition violated: {0}"); 94 95 96 97 // execute and remove the old-references 98 99 if (ensures != null) { 100 101 oldValues = new ArrayList(); 102 103 parsedEnsure = new StringBuffer(); 104 105 Matcher matcher = oldPattern.matcher(ensures); 106 107 while (matcher.matches()) { 108 109 String head = matcher.group(1); 110 111 String old = matcher.group(2); 112 113 String tail = matcher.group(3); 114 115 oldValues.add(executeExpression(invocation, old)); 116 117 String oldRef = "#" + getOldReference(oldValues.size() - 1); 118 119 parsedEnsure.append(head + oldRef + tail); 120 121 matcher = oldPattern.matcher(tail); 122 123 } 124 125 // if there wasn't any old-references just addLink all of the expression 126 127 if (oldValues.size() == 0) { 128 129 parsedEnsure.append(ensures); 130 131 } 132 133 } 134 135 } 136 137 138 139 Object result = invocation.invokeNext(); 140 141 142 143 if (checkContracts.get() == null) { 144 145 146 147 // check ensures with old-references 148 149 if (parsedEnsure != null) { 150 151 Map context = createContext(invocation); 152 153 for (ListIterator iterator = oldValues.listIterator(); iterator.hasNext();) { 154 155 Object oldValue = iterator.next(); 156 157 context.put(getOldReference(iterator.previousIndex()), oldValue); 158 159 } 160 161 162 163 assertExpressionTrue(parsedEnsure.toString(), 164 165 invocation.getProxy(), context, "postcondition violated: " + ensures); 166 167 } 168 169 170 171 assertExpressionTrue(invocation, invariant, "invariant violated: {0}"); 172 173 } 174 175 176 177 return result; 178 179 } 180 181 182 183 private static String getOldReference(int i) { 184 185 return "old" + i; 186 187 } 188 189 190 191 private Object executeExpression(Invocation invocation, String expression) { 192 193 Map context = createContext(invocation); 194 195 try { 196 197 return executeExpression(expression, invocation.getProxy(), context); 198 199 } catch (Exception e) { 200 201 throw new RuntimeException("Could not execute: " + expression, e); 202 203 } 204 205 } 206 207 208 209 private void assertExpressionTrue(Invocation invocation, String expression, String message) { 210 211 if (expression != null) { 212 213 Map context = createContext(invocation); 214 215 assertExpressionTrue(expression, invocation.getProxy(), context, MessageFormat.format(message, new Object[]{expression})); 216 217 } 218 219 } 220 221 222 223 private static Map createContext(Invocation invocation) { 224 225 Map variables = Ognl.createDefaultContext(invocation.getProxy()); 226 227 variables.put("this", invocation.getProxy()); 228 229 Object[] args = invocation.getArgs(); 230 231 if (args != null) { 232 233 for (int i = 0; i < args.length; i++) { 234 235 Object arg = args[i]; 236 237 variables.put("arg" + i, arg); 238 239 } 240 241 } 242 243 return variables; 244 245 } 246 247 248 249 private void assertExpressionTrue(String expression, Object root, Map context, String message) { 250 251 // disable execution of contracts when contracts are executed (to avoid looping) 252 253 if (checkContracts.get() == null) { 254 255 checkContracts.set(checkContracts); 256 257 try { 258 259 boolean result; 260 261 Boolean aBoolean = (Boolean) executeExpression(expression, root, context); 262 263 result = aBoolean.booleanValue(); 264 265 if (!result) { 266 267 throw new AssertionError(message); 268 269 } 270 271 } catch (MethodFailedException e) { 272 273 if (e.getReason() instanceof Error) { 274 275 throw (Error) e.getReason(); 276 277 } else { 278 279 logger.error("Could not execute expression: " + expression); 280 281 throw new AssertionError("Could not execute expression: " + expression); 282 283 } 284 285 } catch (Exception e) { 286 287 logger.error("Could not execute expression: " + expression); 288 289 throw new AssertionError("Could not execute expression: " + expression); 290 291 } finally { 292 293 checkContracts.set(null); 294 295 } 296 297 } 298 299 } 300 301 302 303 private static Object executeExpression(String expression, Object root, Map context) throws Exception { 304 305 return Ognl.getValue(Ognl.parseExpression(expression), context, root); 306 307 } 308 309 } 310

This page was automatically generated by Maven