1 23 package org.hammurapi.inspectors; 24 25 import java.text.MessageFormat ; 26 import java.util.Collection ; 27 import java.util.HashMap ; 28 import java.util.HashSet ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.Set ; 32 import java.util.StringTokenizer ; 33 34 import org.hammurapi.InspectorBase; 35 36 import com.pavelvlasov.config.ConfigurationException; 37 import com.pavelvlasov.config.Parameterizable; 38 import com.pavelvlasov.jsel.Initializer; 39 import com.pavelvlasov.jsel.JselException; 40 import com.pavelvlasov.jsel.LanguageElement; 41 import com.pavelvlasov.jsel.VariableDefinition; 42 import com.pavelvlasov.jsel.expressions.MethodCall; 43 import com.pavelvlasov.jsel.expressions.PlainAssignment; 44 import com.pavelvlasov.jsel.statements.CompoundStatement; 45 import com.pavelvlasov.jsel.statements.Statement; 46 import com.pavelvlasov.jsel.statements.TryBlock; 47 48 74 public class ResourceHandlingRule 75 extends InspectorBase 76 implements Parameterizable 77 { 78 private static final String TRY_FIN_SHOULD_FOLLOW = 79 "try/finally block should follow right after allocated resource \"{0}" 80 + "\" to ensure that it is properly deallocated."; 81 private static final String FIN_DEALLOC = 82 "Allocated resource \"{0}\" should be de-allocated in 'finaly' clause."; 83 private static final String FIN_DEALLOC_FIRST = 84 "To ensure that resource \"{0}\" is properly deallocated - it should be the first in 'finaly' clause."; 85 86 private Map resources2Check; 87 private Set violatedElements; 88 89 public ResourceHandlingRule() 90 { 91 resources2Check = new HashMap (); 92 violatedElements = new HashSet (); 93 } 94 95 private void reportViolation(final LanguageElement element, final String msg) 96 { 97 if (!violatedElements.contains(element.getLocation())) 98 { 99 violatedElements.add(element.getLocation()); 100 context.reportViolation(element, msg); 101 } 102 } 103 104 private ResInfo checkAllocation( 105 final LanguageElement codeFragment, 106 final MethodCall mCall) 107 throws JselException 108 { 109 final String methodName = mCall.getMethodName(); 110 ResInfo res = null; 111 112 if (resources2Check.containsKey(methodName)) 113 { 114 final ResourceDefinition resDef = 115 (ResourceDefinition)resources2Check.get(methodName); 116 117 if (resDef 118 .obtainClassName 119 .equals(mCall.getProvider().getDeclaringType().getName())) 120 { 121 res = new ResInfo(methodName, codeFragment); 122 } 123 } 124 return res; 125 } 126 127 private boolean checkDeAllocation( 128 final ResInfo resInfo, 129 int index, 130 List stms) 131 throws JselException 132 { 133 boolean res = true; 134 Statement statement = (Statement)stms.get(index); 135 ResourceDefinition resDef = 136 (ResourceDefinition)resources2Check.get(resInfo.resourceAllocatedKey); 137 CompoundStatement fSt = null; 139 if (statement instanceof TryBlock 140 && (fSt = ((TryBlock)statement).getFinallyClause()) != null) 141 { 142 144 List fSts = fSt.getStatements(); 145 boolean foundDeAllocation = false; 146 boolean firstDeAllocationOperation = false; 147 MethodCall releaseMethodCall = null; 148 for (int jIndex = 0, jSize = fSts.size(); jIndex < jSize; jIndex++) 149 { 150 if (fSts.get(jIndex) instanceof CompoundStatement) 151 { 152 List ffSts = ((CompoundStatement)fSts.get(jIndex)).getStatements(); 153 for (int kIndex = 0, kSize = ffSts.size(); kIndex < kSize; kIndex++) 154 { 155 if (ffSts.get(kIndex) instanceof MethodCall 156 && resDef.releaseMethodName.equals( 157 ((MethodCall)ffSts.get(kIndex)).getMethodName()) 158 && resDef.releaseClassName.equals( 159 ((MethodCall)ffSts.get(kIndex)) 160 .getProvider() 161 .getDeclaringType() 162 .getName())) 163 { 164 foundDeAllocation = true; 165 firstDeAllocationOperation = (kIndex == 0); 166 releaseMethodCall = (MethodCall)ffSts.get(kIndex); 167 break; 168 } 169 } 170 } 171 if (!foundDeAllocation) 172 { 173 reportViolation( 174 resInfo.resourceAllocatedBy, 175 MessageFormat.format( 176 FIN_DEALLOC, 177 new Object [] { resInfo.resourceAllocatedKey })); 178 } 179 else 180 { 181 if (!firstDeAllocationOperation) 182 { 183 reportViolation( 184 (LanguageElement)releaseMethodCall, 185 MessageFormat.format( 186 FIN_DEALLOC_FIRST, 187 new Object [] { 188 resDef.releaseClassName, 189 resDef.releaseMethodName })); 190 } 191 } 192 } 193 } 194 else 195 { 196 if (index < stms.size() - 1) 197 { 198 checkDeAllocation(resInfo, index + 1, stms); 199 } 200 reportViolation( 201 resInfo.resourceAllocatedBy, 202 MessageFormat.format( 203 TRY_FIN_SHOULD_FOLLOW, 204 new Object [] { resInfo.resourceAllocatedKey })); 205 } 206 return res; 207 } 208 209 public boolean setParameter(String name, Object value) 210 throws ConfigurationException 211 { 212 if ("resource-def".equals(name)) 213 { 214 StringTokenizer tk = new StringTokenizer ((String )value, ":"); 215 216 String obtainClassName = null; 217 String obtainMethodName = null; 218 String releaseClassName = null; 219 String releaseMethodName = null; 220 221 if (tk.hasMoreTokens()) 222 { 223 obtainClassName = tk.nextToken(); 224 } 225 if (tk.hasMoreTokens()) 226 { 227 obtainMethodName = tk.nextToken(); 228 } 229 if (tk.hasMoreTokens()) 230 { 231 releaseClassName = tk.nextToken(); 232 } 233 if (tk.hasMoreTokens()) 234 { 235 releaseMethodName = tk.nextToken(); 236 } 237 if (tk.hasMoreTokens() 238 || obtainClassName == null 239 || obtainMethodName == null 240 || releaseClassName == null 241 || releaseMethodName == null) 242 { 243 throw new ConfigurationException( 244 "Parameter '" + name + "' has invalid value: '" + value + "'"); 245 } 246 247 resources2Check.put( 248 obtainMethodName, 249 new ResourceDefinition( 250 obtainClassName, 251 obtainMethodName, 252 releaseClassName, 253 releaseMethodName)); 254 } 255 else 256 { 257 throw new ConfigurationException( 258 "Parameter '" + name + "' is not supported by " + getClass().getName()); 259 } 260 return true; 261 } 262 263 public void visit(CompoundStatement stmt) throws JselException 264 { 265 ResourceDefinition resDef; 266 ResInfo resInfo = null; 267 Statement currentStatement = null; 268 269 List stmts = stmt.getStatements(); 270 271 for (int index = 0, size = stmts.size(); index < size; index++) 272 { 273 currentStatement = (Statement)stmts.get(index); 274 if (resInfo != null) 275 { 276 checkDeAllocation(resInfo, index, stmts); 277 resInfo = null; 278 } 279 if (currentStatement instanceof VariableDefinition) 280 { 281 Initializer init = 282 ((VariableDefinition)currentStatement).getInitializer(); 283 284 if (init instanceof MethodCall) 285 { 286 resInfo = 287 checkAllocation( 288 (VariableDefinition)currentStatement, 289 (MethodCall) ((VariableDefinition)currentStatement) 290 .getInitializer()); 291 } 292 } 293 else 294 { 295 if (currentStatement instanceof PlainAssignment) 296 { 297 PlainAssignment plainAssignment = (PlainAssignment)currentStatement; 298 Collection operands = (plainAssignment).getOperands(); 299 for (int jIndex = 0, jSize = operands.size(); 300 jIndex < jSize; 301 jIndex++) 302 { 303 if (plainAssignment.getOperand(jIndex) instanceof MethodCall) 304 { 305 resInfo = 306 checkAllocation( 307 (LanguageElement)currentStatement, 308 (MethodCall)plainAssignment.getOperand(jIndex)); 309 if (resInfo != null) 310 { 311 break; 312 } 313 } 314 } 315 } 316 else 317 { 318 if (currentStatement instanceof MethodCall) 319 { 320 resInfo = 321 checkAllocation( 322 (LanguageElement)currentStatement, 323 (MethodCall)currentStatement); 324 } 325 } 326 } 327 } 328 if (resInfo != null) 329 { 330 checkDeAllocation(resInfo, stmts.size() - 1, stmts); 331 } 332 } 333 334 private static final class ResourceDefinition 335 { 336 private String obtainClassName; 337 private String obtainMethodName; 338 private String releaseClassName; 339 private String releaseMethodName; 340 341 private ResourceDefinition( 342 String obtainClassName, 343 String obtainMethodName, 344 String releaseClassName, 345 String releaseMethodName) 346 { 347 this.obtainClassName = obtainClassName; 348 this.releaseClassName = releaseClassName; 349 this.obtainMethodName = obtainMethodName; 350 this.releaseMethodName = releaseMethodName; 351 } 352 } 353 354 private static final class ResInfo 355 { 356 private String resourceAllocatedKey; 357 private LanguageElement resourceAllocatedBy; 358 359 private ResInfo( 360 String resourceAllocatedKey, 361 LanguageElement resourceAllocatedBy) 362 { 363 this.resourceAllocatedKey = resourceAllocatedKey; 364 this.resourceAllocatedBy = resourceAllocatedBy; 365 } 366 } 367 } 368 | Popular Tags |