1 package spoon.template; 2 3 import java.lang.reflect.Field ; 4 import java.util.ArrayList ; 5 import java.util.Collection ; 6 import java.util.HashMap ; 7 import java.util.Iterator ; 8 import java.util.List ; 9 import java.util.Map ; 10 import java.util.regex.Matcher ; 11 import java.util.regex.Pattern ; 12 13 import spoon.reflect.code.CtFieldAccess; 14 import spoon.reflect.code.CtInvocation; 15 import spoon.reflect.code.CtStatementList; 16 import spoon.reflect.declaration.CtClass; 17 import spoon.reflect.declaration.CtElement; 18 import spoon.reflect.declaration.CtField; 19 import spoon.reflect.declaration.CtNamedElement; 20 import spoon.reflect.declaration.CtParameter; 21 import spoon.reflect.declaration.CtVariable; 22 import spoon.reflect.reference.CtExecutableReference; 23 import spoon.reflect.reference.CtFieldReference; 24 import spoon.reflect.reference.CtPackageReference; 25 import spoon.reflect.reference.CtReference; 26 import spoon.reflect.reference.CtTypeParameterReference; 27 import spoon.reflect.reference.CtTypeReference; 28 import spoon.reflect.visitor.CtScanner; 29 import spoon.reflect.visitor.Query; 30 import spoon.support.query.InvocationFilter; 31 import spoon.support.template.DefaultParameterMatcher; 32 import spoon.support.template.ParameterMatcher; 33 import spoon.support.template.Parameters; 34 import spoon.support.util.RtHelper; 35 36 39 public class TemplateMatcher { 40 41 private static List <CtInvocation<?>> getMethods( 42 CtClass<? extends Template> root) { 43 CtExecutableReference methodRef = root.getFactory().Executable() 44 .createReference( 45 root.getFactory().Type().createReference( 46 TemplateParameter.class), 47 root.getFactory().Type().createTypeParameterReference( 48 "T"), "S"); 49 List <CtInvocation<?>> meths = Query.getElements(root, 50 new InvocationFilter(methodRef)); 51 52 return meths; 53 } 54 55 private static List <String > getTemplateNameParameters( 56 CtClass<? extends Template> templateType) { 57 final List <String > ts = new ArrayList <String >(); 58 final Collection <String > c = Parameters.getNames(templateType); 59 ts.addAll(c); 60 return ts; 61 } 62 63 private static List <CtTypeReference<?>> getTemplateTypeParameters( 64 final CtClass<? extends Template> templateType) { 65 66 final List <CtTypeReference<?>> ts = new ArrayList <CtTypeReference<?>>(); 67 final Collection <String > c = Parameters.getNames(templateType); 68 new CtScanner() { 69 @Override 70 public void visitCtTypeParameterReference( 71 CtTypeParameterReference reference) { 72 if (c.contains(reference.getSimpleName())) { 73 ts.add(reference); 74 } 75 }; 76 77 public <T> void visitCtTypeReference(CtTypeReference<T> reference) { 78 if (c.contains(reference.getSimpleName())) { 79 ts.add(reference); 80 } 81 } 82 83 }.scan(templateType); 84 return ts; 85 } 86 87 @SuppressWarnings ("unchecked") 88 private static List <CtFieldReference> getVarargs( 89 CtClass<? extends Template> root, List <CtInvocation<?>> variables) { 90 List <CtFieldReference> fields = new ArrayList <CtFieldReference>(); 91 for (CtFieldReference field : root.getReference().getAllFields()) { 92 if (field.getType().getActualClass() == CtStatementList.class) { 93 boolean alreadyAdded = false; 94 for (CtInvocation<?> invocation : variables) { 95 if (invocation instanceof CtInvocation<?>) { 96 alreadyAdded |= ((CtFieldAccess) invocation.getTarget()) 97 .getVariable().getDeclaration().equals(field); 98 } 99 } 100 if (!alreadyAdded) { 101 fields.add(field); 102 } 103 } 104 } 105 return fields; 106 } 107 108 private List <CtElement> finds = new ArrayList <CtElement>(); 109 110 private boolean found; 111 112 private Map <Object , Object > matches = new HashMap <Object , Object >(); 113 114 private List <String > names; 115 116 private CtClass<? extends Template> templateType; 117 118 private List <CtTypeReference<?>> typeVariables; 119 120 private List <CtFieldReference> varArgs; 121 122 private List <CtInvocation<?>> variables; 123 124 130 public TemplateMatcher(CtClass<? extends Template> templateType) { 131 variables = TemplateMatcher.getMethods(templateType); 132 typeVariables = TemplateMatcher.getTemplateTypeParameters(templateType); 133 names = TemplateMatcher.getTemplateNameParameters(templateType); 134 varArgs = TemplateMatcher.getVarargs(templateType, variables); 135 this.templateType = templateType; 136 } 137 138 private boolean addMatch(Object template, Object target) { 139 Object inv = matches.get(template); 140 Object o = matches.put(template, target); 141 return null == inv || inv.equals(o); 142 } 143 144 private CtElement checkListStatements(List teList) { 145 for (Object tem : teList) { 146 if (variables.contains(tem) && tem instanceof CtInvocation) { 147 CtInvocation<?> listCand = (CtInvocation<?>) tem; 148 boolean ok = listCand.getFactory().Type().createReference( 149 TemplateParameterList.class).isAssignableFrom( 150 listCand.getTarget().getType()); 151 return ok ? listCand : null; 152 } 153 if (tem instanceof CtVariable) { 154 CtVariable var = (CtVariable) tem; 155 String name = var.getSimpleName(); 156 for (CtFieldReference f : varArgs) { 157 if (f.getSimpleName().equals(name)) { 158 return f.getDeclaration(); 159 } 160 } 161 } 162 } 163 164 return null; 165 } 166 167 180 public boolean find(CtElement targetRoot, final CtElement templateRoot) { 181 found = false; 182 new CtScanner() { 183 @Override 184 public void scan(CtElement element) { 185 if (match(element, templateRoot)) { 186 finds.add(element); 187 found = true; 188 } 190 super.scan(element); 191 } 192 }.scan(targetRoot); 193 194 return found; 195 } 196 197 private ParameterMatcher findParameterMatcher(CtElement declaration, 198 String name) throws InstantiationException , IllegalAccessException { 199 if (declaration == null) { 200 return new DefaultParameterMatcher(); 201 } 202 CtClass<?> clazz = declaration.getParent(CtClass.class); 203 if (clazz == null) { 204 return new DefaultParameterMatcher(); 205 } 206 207 Collection <CtFieldReference> fields = clazz.getReference() 208 .getAllFields(); 209 210 CtFieldReference param = null; 211 for (CtFieldReference field : fields) { 212 Parameter p = field.getAnnotation(Parameter.class); 213 if (p == null) 214 continue; String proxy = p.value(); 216 if (proxy != "") { 217 if (name.contains(proxy)) { 218 param = field; 219 break; 220 } 221 } 222 223 if (name.contains(field.getSimpleName()) && p != null) { 224 param = field; 225 break; 226 } 227 } 229 230 if (param == null) { 231 throw new IllegalStateException ("Parameter not defined " + name 232 + "at " + declaration.getPosition()); 233 } 234 return getParameterInstance(param); 235 } 236 237 @SuppressWarnings ("unused") 238 private String getBindedParameter(String pname) { 239 final String [] x = new String [1]; x[0] = pname; 241 new CtScanner() { 242 @Override 243 public <T> void visitCtField(CtField<T> f) { 244 Parameter p = f.getAnnotation(Parameter.class); 245 if (p != null && p.value().equals(x[0])) { 246 x[0] = f.getSimpleName(); 247 return; 248 } 249 super.visitCtField(f); 250 } 251 }.scan(templateType); 252 253 return x[0]; 254 } 255 256 260 public List <CtElement> getFinds() { 261 return finds; 262 } 263 264 269 public Map <Object , Object > getMatches() { 270 return matches; 271 } 272 273 private ParameterMatcher getParameterInstance(CtFieldReference param) 274 throws InstantiationException , IllegalAccessException { 275 Parameter anParam = param.getAnnotation(Parameter.class); 276 if (anParam == null) { 277 return new DefaultParameterMatcher(); 280 } 281 Class <? extends ParameterMatcher> pm = anParam.match(); 282 ParameterMatcher instance = (ParameterMatcher) pm.newInstance(); 283 return instance; 284 } 285 286 289 @SuppressWarnings ("unchecked") 290 private boolean helperMatch(Object target, Object template) { 291 if (target == null && template == null) 292 return true; 293 if (target == null || template == null) 294 return false; 295 if (variables.contains(template) || typeVariables.contains(template)) { 296 boolean add = invokeCallBack(target, template); 298 if (add) 299 return addMatch(template, target); 300 else 301 return false; 302 } 303 if (target.getClass() != template.getClass()) { 304 return false; 305 } 306 if (template instanceof CtTypeReference 307 && template.equals(templateType.getReference())) 308 return true; 309 if (template instanceof CtPackageReference 310 && template.equals(templateType.getPackage())) 311 return true; 312 if (template instanceof CtReference) { 313 CtReference tRef = (CtReference) template; 314 boolean ok = matchNames(tRef.getSimpleName(), 315 ((CtReference) target).getSimpleName()); 316 if (ok && !template.equals(target)) { 317 boolean remove = !invokeCallBack(target, template); 318 if (remove) { 319 matches.remove(tRef.getSimpleName()); 320 return false; 321 } 322 return true; 323 } 324 } 325 326 if (template instanceof CtNamedElement) { 327 CtNamedElement named = (CtNamedElement) template; 328 boolean ok = matchNames(named.getSimpleName(), 329 ((CtNamedElement) target).getSimpleName()); 330 if (ok && !template.equals(target)) { 331 boolean remove = !invokeCallBack(target, template); 332 if (remove) { 333 matches.remove(named.getSimpleName()); 334 return false; 335 } 336 } 337 } 338 339 if (template instanceof Collection ) { 340 return matchCollections((Collection ) target, (Collection ) template); 341 } 342 343 if (target instanceof CtElement || target instanceof CtReference) { 344 for (Field f : RtHelper.getAllFields(target.getClass())) { 345 f.setAccessible(true); 346 if (f.getName().equals("parent")) 347 continue; 348 if (f.getName().equals("position")) 349 continue; 350 if (f.getName().equals("docComment")) 351 continue; 352 if (f.getName().equals("factory")) 353 continue; 354 try { 355 if (!helperMatch(f.get(target), f.get(template))) 356 return false; 357 } catch (Exception e) { 358 e.printStackTrace(); 359 } 360 } 361 return true; 362 } else if (target instanceof String ) { 363 return matchNames((String ) template, (String ) target); 364 } else { 365 return target.equals(template); 366 } 367 } 368 369 @SuppressWarnings ("unchecked") 370 private boolean invokeCallBack(Object target, Object template) { 371 try { 372 if (template instanceof CtInvocation) { 373 CtFieldAccess<?> param = (CtFieldAccess) ((CtInvocation<?>) template) 374 .getTarget(); 375 ParameterMatcher instance = getParameterInstance(param 376 .getVariable()); 377 return instance.match(this, (CtInvocation) template, 378 (CtElement) target); 379 } else if (template instanceof CtReference) { 380 CtReference ref = (CtReference) template; 382 Parameter param = ref.getAnnotation(Parameter.class); 383 ParameterMatcher instance; 384 if (param == null) 385 instance = new DefaultParameterMatcher(); 386 else 387 instance = param.match().newInstance(); 388 return instance.match(this, (CtReference) template, 389 (CtReference) target); 390 } else if (template instanceof CtNamedElement) { 391 CtNamedElement named = (CtNamedElement) template; 392 ParameterMatcher instance = findParameterMatcher(named, named 393 .getSimpleName()); 394 return instance.match(this, (CtElement) template, 395 (CtElement) target); 396 } 397 398 else { 399 throw new RuntimeException (); 401 } 402 } catch (InstantiationException e) { 403 e.printStackTrace(); 404 return true; 405 } catch (IllegalAccessException e) { 406 e.printStackTrace(); 408 return true; 409 } 410 } 411 412 private boolean isCurrentTemplate(Object object, CtElement inMulti) { 413 if (object instanceof CtInvocation<?>) { 414 return object.equals(inMulti); 415 } 416 if (object instanceof CtParameter) { 417 CtParameter param = (CtParameter) object; 418 for (CtFieldReference varArg : varArgs) { 419 if (param.getSimpleName().equals(varArg.getSimpleName())) { 420 return varArg.equals(inMulti); 421 } 422 } 423 } 424 return false; 425 } 426 427 440 public boolean match(CtElement targetRoot, CtElement templateRoot) { 441 return helperMatch(targetRoot, templateRoot); 442 } 443 444 @SuppressWarnings ("unchecked") 445 private boolean matchCollections(Collection target, Collection template) { 446 List teList = new ArrayList (template); 447 List taList = new ArrayList (target); 448 449 int numOfNonParamsinTeList = teList.size(); 450 451 CtElement inMulti = nextListStatement(teList, null); 453 454 List <CtElement> multi = new ArrayList (); 456 457 if (null == inMulti) { 458 if (teList.size() != taList.size()) 461 return false; 462 463 for (int te = 0, ta = 0; te < teList.size() && ta < taList.size(); te++, ta++) { 464 if (!helperMatch(taList.get(ta), teList.get(te))) { 465 return false; 466 } 467 } 468 return true; 469 } else { 470 471 for (int te = 0, ta = 0; te < teList.size() && ta < taList.size(); te++, ta++) { 472 473 if (isCurrentTemplate(teList.get(te), inMulti)) { 474 numOfNonParamsinTeList--; 475 if (te + 1 >= teList.size()) { 476 multi.addAll(taList.subList(te, taList.size())); 477 CtStatementList tpl = templateType.getFactory().Core() 478 .createStatementList(); 479 tpl.setStatements(multi); 480 if (!invokeCallBack(tpl, inMulti)) { 481 return false; 482 } 483 boolean ret = addMatch(inMulti, multi); 484 return ret; 485 } 486 te++; 487 while (te < teList.size() && ta < taList.size() 488 && !helperMatch(taList.get(ta), teList.get(te))) { 489 multi.add((CtElement) taList.get(ta)); 490 ta++; 491 } 492 CtStatementList tpl = templateType.getFactory().Core() 493 .createStatementList(); 494 tpl.setStatements(multi); 495 if (!invokeCallBack(tpl, inMulti)) { 496 return false; 497 } 498 addMatch(inMulti, tpl); 499 inMulti = nextListStatement(teList, inMulti); 501 multi = new ArrayList (); 502 numOfNonParamsinTeList--; 503 } else { 504 if (!helperMatch(taList.get(ta), teList.get(te))) { 505 return false; 506 } 507 if (!(ta + 1 < taList.size()) && inMulti != null) { 508 CtStatementList tpl = templateType.getFactory().Core() 509 .createStatementList(); 510 tpl.setStatements(multi); 511 if (!invokeCallBack(tpl, inMulti)) { 512 return false; 513 } 514 addMatch(inMulti, tpl); 515 inMulti = nextListStatement(teList, inMulti); 517 multi = new ArrayList (); 518 numOfNonParamsinTeList--; 519 } 520 } 521 } 522 return true; 523 } 524 525 } 526 527 private boolean matchNames(String name, String tname) { 528 529 try { 530 for (String pname : names) { 531 if (name.contains(pname)) { 533 String newName = name.replace(pname, "(.*)"); 534 Pattern p = Pattern.compile(newName); 535 Matcher m = p.matcher(tname); 536 if (!m.matches()) { 537 return false; 538 } 539 boolean ok = addMatch(pname, m.group(1)); 543 if (!ok) { 544 System.out.println("incongruent match"); 545 return false; 546 } else { 547 return true; 548 } 549 } 550 } 551 } catch (RuntimeException e) { 552 } 555 return name.equals(tname); 556 } 557 558 private CtElement nextListStatement(List <?> teList, CtElement inMulti) { 559 if (inMulti == null) 560 return checkListStatements(teList); 561 else { 562 List <?> teList2 = new ArrayList <Object >(teList); 563 if (inMulti instanceof CtInvocation) { 564 teList2.remove(inMulti); 565 } else if (inMulti instanceof CtVariable) { 566 CtVariable var = (CtVariable) inMulti; 567 for (Iterator iter = teList2.iterator(); iter.hasNext();) { 568 CtVariable teVar = (CtVariable) iter.next(); 569 if (teVar.getSimpleName().equals(var.getSimpleName())) { 570 iter.remove(); 571 } 572 } 573 } 574 return checkListStatements(teList2); 575 } 576 } 577 578 } 579 | Popular Tags |