1 /* 2 * Copyright (c) 2007, 2018 Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.jemmy.support.test; 24 25 import java.util.ArrayList; 26 import java.util.Collections; 27 import java.util.LinkedList; 28 import java.util.List; 29 import javax.annotation.processing.ProcessingEnvironment; 30 import javax.lang.model.element.*; 31 import javax.lang.model.type.DeclaredType; 32 import javax.lang.model.type.TypeKind; 33 import javax.lang.model.type.TypeMirror; 34 import org.jemmy.control.*; 35 import org.jemmy.dock.DefaultWrapper; 36 import org.jemmy.dock.*; 37 import org.jemmy.interfaces.ControlInterface; 38 import org.jemmy.interfaces.Drag; 39 import org.jemmy.interfaces.Keyboard; 40 import org.jemmy.interfaces.Mouse; 41 42 /** 43 * 44 * @author shura 45 */ 46 public class ControlSupport { 47 48 private final DeclaredType wrap; 49 //this could not be final 'cause an order of wraps is not defined 50 //and hence inheritance is figured out later 51 private DeclaredType superWrap = null; 52 private final DeclaredType control; 53 private final List<ControlInterfaceSupport> interfaces = new LinkedList<ControlInterfaceSupport>(); 54 private final List<LookupSupport> objectLookups = new LinkedList<LookupSupport>(); 55 private final List<MappedPropertySupport> properties = new LinkedList<MappedPropertySupport>(); 56 private final List<DirectPropertySupport> propertyMethods = new LinkedList<DirectPropertySupport>(); 57 private final List<ControlInterfaceSupport.ShortcutSupport> shortcuts = new LinkedList<ControlInterfaceSupport.ShortcutSupport>(); 58 private final DeclaredType preferredParent; 59 private final DefaultParentSupport defaultParent; 60 private final DefaultWrapperSupport defaultWrapper; 61 private final DockInfo dockInfo; 62 private final String dockName; 63 private final boolean placeholder; 64 private final DeclaredType ci, w; 65 private final boolean multipleCriteria; 66 67 private ControlSupport(ProcessingEnvironment env) { 68 ci = null; 69 w = null; 70 placeholder = true; 71 this.wrap = (DeclaredType) env.getElementUtils(). 72 getTypeElement(Wrap.class.getName()).asType(); 73 this.control = (DeclaredType) env.getElementUtils(). 74 getTypeElement(Object.class.getName()).asType(); 75 defaultParent = null; 76 preferredParent = null; 77 defaultWrapper = null; 78 dockInfo = null; 79 multipleCriteria = false; 80 dockName = Dock.class.getName(); 81 //TODO while implementing external wrap superclasses learn to pull interfaces from there 82 interfaces.add(new ControlInterfaceSupport(ControlInterfaceSupport.Kind.ANNOTATION, 83 (DeclaredType) env.getElementUtils(). 84 getTypeElement(Mouse.class.getName()).asType(), null, "mouse", 85 null, shortcuts)); 86 interfaces.add(new ControlInterfaceSupport(ControlInterfaceSupport.Kind.ANNOTATION, 87 (DeclaredType) env.getElementUtils(). 88 getTypeElement(Keyboard.class.getName()).asType(), null, "keyboard", 89 null, shortcuts)); 90 interfaces.add(new ControlInterfaceSupport(ControlInterfaceSupport.Kind.ANNOTATION, 91 (DeclaredType) env.getElementUtils(). 92 getTypeElement(Drag.class.getName()).asType(), null, "drag", 93 null, shortcuts)); 94 } 95 96 ControlSupport(DeclaredType wrap, DeclaredType control, ProcessingEnvironment env) { 97 ci = (DeclaredType) env.getElementUtils(). 98 getTypeElement(ControlInterface.class.getName()).asType(); 99 w = (DeclaredType) env.getTypeUtils().erasure(env.getElementUtils(). 100 getTypeElement(Wrap.class.getName()).asType()); 101 placeholder = false; 102 this.wrap = wrap; 103 this.control = control; 104 TypeMirror pWrap = wrap; 105 TypeElement typeEl = (TypeElement) ((DeclaredType) pWrap).asElement(); 106 dockInfo = typeEl.getAnnotation(DockInfo.class); 107 AnnotationMirror ciAM = Proccessor.findAnnotation(typeEl, ControlInterfaces.class); 108 if (ciAM != null) { 109 List<TypeMirror> implemented = new ArrayList<TypeMirror>(); 110 findImplementedInterfaces(wrap, implemented, env); 111 List<ExecutableElement> annotated = new ArrayList<ExecutableElement>(); 112 findAnnotatedInterfaces(wrap, annotated, env); 113 List<DeclaredType> value = Proccessor.getClassArrayValue(Proccessor.getElementValue(ciAM, "value")); 114 List<DeclaredType> encapsulates; 115 AnnotationValue v = Proccessor.getElementValue(ciAM, "encapsulates"); 116 if (v != null) { 117 encapsulates = Proccessor.getClassArrayValue(v); 118 } else { 119 encapsulates = Collections.emptyList(); 120 } 121 List<String> name; 122 v = Proccessor.getElementValue(ciAM, "name"); 123 if (v != null) { 124 name = Proccessor.getStringArrayValue(v); 125 } else { 126 name = Collections.EMPTY_LIST; 127 } 128 for (int i = 0; i < value.size(); i++) { 129 DeclaredType interfaceType = value.get(i); 130 DeclaredType encapsulatedType = (encapsulates.size() > i) ? encapsulates.get(i) : null; 131 ControlInterfaceSupport.Kind kind = ControlInterfaceSupport.Kind.METHOD; 132 ExecutableElement method = null; 133 for (TypeMirror tm : implemented) { 134 if (env.getTypeUtils().isSameType(interfaceType, env.getTypeUtils().erasure(tm))) { 135 if (encapsulatedType == null) { 136 kind = ControlInterfaceSupport.Kind.SELF; 137 } else { 138 if (((DeclaredType) tm).getTypeArguments().size() == 1 139 && env.getTypeUtils().isSameType(encapsulatedType, 140 ((DeclaredType) tm).getTypeArguments().get(0))) { 141 kind = ControlInterfaceSupport.Kind.SELF; 142 } 143 } 144 } 145 } 146 for (ExecutableElement mthd : annotated) { 147 if (env.getTypeUtils().isSameType(interfaceType, env.getTypeUtils().erasure(mthd.getReturnType()))) { 148 if (encapsulatedType == null) { 149 kind = ControlInterfaceSupport.Kind.ANNOTATION; 150 method = mthd; 151 } else { 152 AnnotationMirror asAM = Proccessor.findAnnotation(mthd, As.class); 153 if (asAM.getElementValues().size() > 0) { 154 DeclaredType vAE = Proccessor.getClassValue(Proccessor.getElementValue(asAM, "value")); 155 if (!env.getTypeUtils().isSameType(vAE, env.getElementUtils().getTypeElement(Void.class.getName()).asType()) 156 && env.getTypeUtils().isSameType(vAE, encapsulatedType)) { 157 kind = ControlInterfaceSupport.Kind.ANNOTATION; 158 method = mthd; 159 } 160 } 161 } 162 } 163 } 164 interfaces.add(new ControlInterfaceSupport(kind, interfaceType, 165 encapsulatedType, 166 (name.size() > i) ? name.get(i) : ("as" + value.get(i).asElement().getSimpleName()), 167 method, shortcuts)); 168 } 169 } 170 AnnotationMirror ppAM = Proccessor.findAnnotation(typeEl, PreferredParent.class); 171 //PreferredParent ppa = typeEl.getAnnotation(PreferredParent.class); 172 if(ppAM != null) { 173 System.out.println("found a preffered wrap for " + typeEl.toString()); 174 preferredParent = Proccessor.getClassValue(Proccessor.getElementValue(ppAM, "value")); 175 //prefferedParent = ppa.value(); 176 } else { 177 System.out.println("no preffered wrap for " + typeEl.toString()); 178 preferredParent = null; 179 } 180 AnnotationMirror mpAM = Proccessor.findAnnotation(typeEl, MethodProperties.class); 181 AnnotationMirror fpAM = Proccessor.findAnnotation(typeEl, FieldProperties.class); 182 DefaultParentSupport dParent = null; 183 DefaultWrapperSupport dWrapper = null; 184 boolean origWrap = true; 185 do { 186 for (Element el : typeEl.getEnclosedElements()) { 187 if (el instanceof ExecutableElement) { 188 ExecutableElement eel = (ExecutableElement) el; 189 if (dParent == null) { 190 DefaultParent defaultParentAnn = el.getAnnotation(DefaultParent.class); 191 if (defaultParentAnn != null) { 192 dParent = new DefaultParentSupport(eel, defaultParentAnn.value()); 193 } 194 } 195 if (dWrapper == null) { 196 DefaultWrapper defaultWrapperAnn = el.getAnnotation(DefaultWrapper.class); 197 if (defaultWrapperAnn != null) { 198 dWrapper = new DefaultWrapperSupport(eel, origWrap); 199 } 200 } 201 ObjectLookup olA = el.getAnnotation(ObjectLookup.class); 202 if (olA != null) { 203 if (eel.getParameters().isEmpty() 204 || !eel.getParameters().get(0).asType().toString().startsWith(Class.class.getName())) { //TODO classname 205 throw new IllegalStateException("Expect first parameter to ba a class but found " 206 + (eel.getParameters().isEmpty() ? "none" : eel.getParameters().get(0).asType().toString())); 207 } 208 LookupSupport newLs = new LookupSupport(typeEl, eel, olA.value()); 209 copyParameters(eel, 1, newLs.getParams()); 210 boolean found = false; 211 for (LookupSupport ls : objectLookups) { 212 if (ls.equalInTypes(newLs)) { 213 found = true; 214 break; 215 } 216 } 217 if (!found) { 218 objectLookups.add(newLs); 219 } 220 } 221 if (origWrap) { 222 Property pA = el.getAnnotation(Property.class); 223 if (pA != null) { 224 if (eel.getParameters().size() > 0) { 225 throw new IllegalStateException("Property getter must have no parameters: " + eel.getSimpleName()); 226 } 227 boolean found = false; 228 for (DirectPropertySupport dps : propertyMethods) { 229 if (pA.value().equals(dps.getName())) { 230 found = true; 231 break; 232 } 233 } 234 if (!found) { 235 propertyMethods.add(new DirectPropertySupport(pA.value(), eel, pA.waitable())); 236 } 237 } 238 } 239 } 240 } 241 typeEl = (TypeElement) ((DeclaredType) typeEl.getSuperclass()).asElement(); 242 origWrap = false; 243 } while (!typeEl.getQualifiedName().toString().equals(Wrap.class.getName())); 244 defaultParent = dParent; 245 defaultWrapper = dWrapper; 246 if (mpAM != null) { 247 addProperties(mpAM, true); 248 } 249 if (fpAM != null) { 250 addProperties(fpAM, false); 251 } 252 multipleCriteria = (dockInfo != null) ? dockInfo.multipleCriteria() : true; 253 if (dockInfo != null && dockInfo.name().length() > 0) { 254 dockName = dockInfo.name(); 255 } else { 256 String wrapName = ((TypeElement) wrap.asElement()).getQualifiedName().toString(); 257 dockName = wrapName.substring(0, wrapName.lastIndexOf(".")) + "." 258 + control.asElement().getSimpleName().toString() + "Dock"; 259 } 260 } 261 262 public boolean isMultipleCriteria() { 263 return multipleCriteria; 264 } 265 266 private void findImplementedInterfaces(DeclaredType wrap, List<TypeMirror> interfaces, ProcessingEnvironment processingEnv) { 267 if (processingEnv.getTypeUtils().isSameType(w, processingEnv.getTypeUtils().erasure(wrap))) { 268 return; 269 } 270 for (TypeMirror i : ((TypeElement) wrap.asElement()).getInterfaces()) { 271 if (processingEnv.getTypeUtils().isAssignable(i, ci)) { 272 interfaces.add(i); 273 } 274 } 275 // findImplementedInterfaces((DeclaredType)((TypeElement) wrap.asElement()).getSuperclass(), 276 // interfaces, processingEnv); 277 } 278 279 private void findAnnotatedInterfaces(DeclaredType wrap, List<ExecutableElement> interfaces, ProcessingEnvironment processingEnv) { 280 if (processingEnv.getTypeUtils().isSameType(w, processingEnv.getTypeUtils().erasure(wrap))) { 281 return; 282 } 283 for (Element el : ((TypeElement) wrap.asElement()).getEnclosedElements()) { 284 As as = el.getAnnotation(As.class); 285 if (as != null) { 286 if (el instanceof ExecutableElement) { 287 interfaces.add((ExecutableElement) el); 288 } else { 289 throw new IllegalStateException("@As applied to something else but a method " + el.toString()); 290 } 291 } 292 } 293 // findAnnotatedInterfaces((DeclaredType)((TypeElement) wrap.asElement()).getSuperclass(), 294 // interfaces, processingEnv); 295 } 296 297 public DeclaredType getPreferredParent() { 298 return preferredParent; 299 } 300 301 /** 302 * 303 * @return 304 */ 305 public DeclaredType getSuperWrap() { 306 return superWrap; 307 } 308 309 /** 310 * 311 * @return 312 */ 313 public boolean isPlaceholder() { 314 return placeholder; 315 } 316 317 /** 318 * 319 * @return 320 */ 321 public String getDockName() { 322 return dockName; 323 } 324 325 private void addProperties(AnnotationMirror am, boolean isMethod) { 326 List<String> value = Proccessor.getStringArrayValue(Proccessor.getElementValue(am, "value")); 327 List<Boolean> waitable; 328 if(Proccessor.getElementValue(am, "waitable") != null) { 329 waitable = Proccessor.getBooleanArrayValue(Proccessor.getElementValue(am, "waitable")); 330 } else { 331 waitable = Collections.EMPTY_LIST; 332 } 333 List<DeclaredType> types; 334 AnnotationValue v = Proccessor.getElementValue(am, "types"); 335 if (v != null) { 336 types = Proccessor.getClassArrayValue(v); 337 } else { 338 types = Collections.EMPTY_LIST; 339 } 340 for (int i = 0; i < value.size(); i++) { 341 boolean found = false; 342 for (MappedPropertySupport ps : properties) { 343 if (ps.getName().equals(value.get(i))) { 344 found = true; 345 break; 346 } 347 } 348 if (!found) { 349 TypeMirror type; 350 if (types.size() > i) { 351 type = types.get(i); 352 } else { 353 type = findType(control, value.get(i), isMethod); 354 } 355 properties.add(new MappedPropertySupport(value.get(i), type, isMethod, 356 (waitable.size() > i) ? waitable.get(i) : false)); 357 } 358 } 359 } 360 361 /** 362 * 363 * @return 364 */ 365 public DeclaredType getControl() { 366 return control; 367 } 368 369 /** 370 * 371 * @return 372 */ 373 public List<LookupSupport> getObjectLookups() { 374 return objectLookups; 375 } 376 377 /** 378 * 379 * @return 380 */ 381 public DeclaredType getWrap() { 382 return wrap; 383 } 384 385 /** 386 * 387 * @return 388 */ 389 public List<ControlInterfaceSupport> getInterfaces() { 390 return interfaces; 391 } 392 393 /** 394 * 395 * @return 396 */ 397 public List<MappedPropertySupport> getProperties() { 398 return properties; 399 } 400 401 /** 402 * 403 * @return 404 */ 405 public List<DirectPropertySupport> getPropertyMethods() { 406 return propertyMethods; 407 } 408 409 /** 410 * 411 * @return 412 */ 413 public DefaultParentSupport getDefaultParent() { 414 return defaultParent; 415 } 416 417 /** 418 * 419 * @return 420 */ 421 public DefaultWrapperSupport getDefaultWrapper() { 422 return defaultWrapper; 423 } 424 425 /** 426 * 427 * @return 428 */ 429 public DockInfo getDockInfo() { 430 return dockInfo; 431 } 432 433 static void copyParameters(ExecutableElement eel, int firstParam, List<SupportParameter> params) { 434 for (int i = firstParam; i < eel.getParameters().size(); i++) { 435 VariableElement ve = eel.getParameters().get(i); 436 params.add(new SupportParameter(ve.getSimpleName().toString(), ve.asType())); 437 } 438 } 439 440 private TypeMirror findType(DeclaredType control, String name, boolean isMethod) { 441 TypeMirror tp = control; 442 TypeElement te; 443 while (tp instanceof DeclaredType && tp.getKind() != TypeKind.NULL) { 444 te = (TypeElement) ((DeclaredType) tp).asElement(); 445 for (Element e : te.getEnclosedElements()) { 446 if (e instanceof ExecutableElement) { 447 ExecutableElement ee = (ExecutableElement) e; 448 if (isMethod && e.getKind() == ElementKind.METHOD 449 || !isMethod && e.getKind() == ElementKind.FIELD) { 450 if (e.getSimpleName().toString().equals(name) 451 && (!isMethod || ee.getParameters().isEmpty())) { 452 return e.asType(); 453 } 454 } 455 } 456 } 457 tp = te.getSuperclass(); 458 } 459 return null; 460 } 461 462 //TODO - do a quicksort, at least 463 /** 464 * 465 * @param controls 466 * @param env 467 */ 468 public static void linkSuperClasses(List<ControlSupport> controls, ProcessingEnvironment env) { 469 ControlSupport root = null; 470 for (ControlSupport cs : controls) { 471 DeclaredType superWrap = (DeclaredType) ((TypeElement) cs.getWrap().asElement()).getSuperclass(); 472 boolean foundOne = false; 473 for (ControlSupport csi : controls) { 474 if (((TypeElement) csi.getWrap().asElement()).getQualifiedName().toString().equals( 475 ((TypeElement) superWrap.asElement()).getQualifiedName().toString())) { 476 //that would mean we have found a super wrap among the compiled ones 477 foundOne = true; 478 break; 479 } 480 } 481 if (!foundOne) { 482 //the super-wrap is in classpath somewhere 483 //for now only org.jemmy.control.Wrap could be external 484 //TODO improve DockInfo to allow external parents 485 if (((TypeElement) superWrap.asElement()).getQualifiedName(). 486 toString().equals(Wrap.class.getName())) { 487 if (root == null) { 488 root = new ControlSupport(env); 489 } 490 } else { 491 throw new IllegalStateException("Unknown parent Wrap type " 492 + ((TypeElement) superWrap.asElement()).getQualifiedName().toString()); 493 } 494 } 495 cs.superWrap = superWrap; 496 } 497 controls.add(root); 498 } 499 }