1 16 package com.google.gwt.user.rebind.ui; 17 18 import com.google.gwt.core.ext.Generator; 19 import com.google.gwt.core.ext.GeneratorContext; 20 import com.google.gwt.core.ext.TreeLogger; 21 import com.google.gwt.core.ext.UnableToCompleteException; 22 import com.google.gwt.core.ext.typeinfo.JClassType; 23 import com.google.gwt.core.ext.typeinfo.JMethod; 24 import com.google.gwt.core.ext.typeinfo.NotFoundException; 25 import com.google.gwt.core.ext.typeinfo.TypeOracle; 26 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; 27 import com.google.gwt.user.rebind.SourceWriter; 28 29 import java.io.PrintWriter ; 30 import java.net.URL ; 31 import java.util.HashMap ; 32 import java.util.Iterator ; 33 import java.util.Map ; 34 35 47 public class ImageBundleGenerator extends Generator { 48 49 private static final String ABSTRACTIMAGEPROTOTYPE_QNAME = "com.google.gwt.user.client.ui.AbstractImagePrototype"; 50 51 private static final String CLIPPEDIMAGEPROTOTYPE_QNAME = "com.google.gwt.user.client.ui.impl.ClippedImagePrototype"; 52 53 private static final String GWT_QNAME = "com.google.gwt.core.client.GWT"; 54 55 private static final String [] IMAGE_FILE_EXTENSIONS = {"png", "gif", "jpg"}; 56 57 private static final String IMAGEBUNDLE_QNAME = "com.google.gwt.user.client.ui.ImageBundle"; 58 59 private static final String METADATA_TAG = "gwt.resource"; 60 61 public ImageBundleGenerator() { 62 } 63 64 public String generate(TreeLogger logger, GeneratorContext context, 65 String typeName) throws UnableToCompleteException { 66 67 TypeOracle typeOracle = context.getTypeOracle(); 68 69 JClassType userType = getValidUserType(logger, typeName, typeOracle); 71 72 JMethod[] imgMethods = getValidImageMethods(logger, userType); 74 75 String resultName = generateImpl(logger, context, userType, imgMethods); 77 78 return resultName; 80 } 81 82 private String computeSubclassName(JClassType userType) { 83 String baseName = userType.getName().replace('.', '_'); 84 return baseName + "_generatedBundle"; 85 } 86 87 private void generateImageMethod(TreeLogger logger, 88 ImageBundleBuilder compositeImage, SourceWriter sw, JMethod method) 89 throws UnableToCompleteException { 90 91 String imageName = getImageUrlFromMetaDataOrMethodName(logger, method); 92 String decl = method.getReadableDeclaration(false, true, true, true, true); 93 94 { 95 sw.indent(); 96 97 101 ImageBundleBuilder.ImageRect imageRect = compositeImage.getMapping(imageName); 102 String singletonName = method.getName() + "_SINGLETON"; 103 104 sw.print("private static final ClippedImagePrototype "); 105 sw.print(singletonName); 106 sw.print(" = new ClippedImagePrototype(IMAGE_BUNDLE_URL, "); 107 sw.print(Integer.toString(imageRect.left)); 108 sw.print(", 0, "); 109 sw.print(Integer.toString(imageRect.width)); 110 sw.print(", "); 111 sw.print(Integer.toString(imageRect.height)); 112 sw.println(");"); 113 114 sw.print(decl); 115 sw.println(" {"); 116 117 { 118 sw.indent(); 119 sw.print("return "); 120 sw.print(singletonName); 121 sw.println(";"); 122 sw.outdent(); 123 } 124 125 sw.println("}"); 126 sw.outdent(); 127 } 128 } 129 130 private String generateImpl(TreeLogger logger, GeneratorContext context, 131 JClassType userType, JMethod[] imageMethods) 132 throws UnableToCompleteException { 133 String pkgName = userType.getPackage().getName(); 135 String subName = computeSubclassName(userType); 136 137 ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory( 139 pkgName, subName); 140 f.addImport(ABSTRACTIMAGEPROTOTYPE_QNAME); 141 f.addImport(CLIPPEDIMAGEPROTOTYPE_QNAME); 142 f.addImport(GWT_QNAME); 143 f.addImplementedInterface(userType.getQualifiedSourceName()); 144 145 PrintWriter pw = context.tryCreate(logger, pkgName, subName); 146 if (pw != null) { 147 SourceWriter sw = f.createSourceWriter(context, pw); 148 149 ImageBundleBuilder bulder = new ImageBundleBuilder(); 151 152 for (int i = 0; i < imageMethods.length; i++) { 153 JMethod method = imageMethods[i]; 154 String imageUrl = getImageUrlFromMetaDataOrMethodName(logger, method); 155 assert (imageUrl != null); 156 bulder.assimilate(logger, imageUrl); 157 } 158 159 String bundledImageUrl = bulder.writeBundledImage(logger, context); 161 162 sw.print("private static final String IMAGE_BUNDLE_URL = GWT.getModuleBaseURL() + \""); 166 sw.print(escape(bundledImageUrl)); 167 sw.println("\";"); 168 169 for (int i = 0; i < imageMethods.length; i++) { 171 JMethod method = imageMethods[i]; 172 generateImageMethod(logger, bulder, sw, method); 173 } 174 175 sw.commit(logger); 177 } 178 179 return f.getCreatedClassName(); 180 } 181 182 private String getImageUrlFromMetaDataOrMethodName(TreeLogger logger, 184 JMethod method) throws UnableToCompleteException { 185 186 String [][] md = method.getMetaData(METADATA_TAG); 187 188 if (md.length == 1) { 189 int lastTagIndex = md.length - 1; 191 int lastValueIndex = md[lastTagIndex].length - 1; 192 String imageNameFromMetaData = md[lastTagIndex][lastValueIndex]; 193 194 if (imageNameFromMetaData.indexOf("/") == -1) { 196 String pkgName = method.getEnclosingType().getPackage().getName(); 197 imageNameFromMetaData = pkgName.replace('.', '/') + "/" 199 + imageNameFromMetaData; 200 } 201 202 URL imageResourceURL = getClass().getClassLoader().getResource( 206 imageNameFromMetaData); 207 if (imageResourceURL == null) { 208 logger.log( 209 TreeLogger.ERROR, 210 "Resource " 211 + imageNameFromMetaData 212 + " not found on classpath (is the name specified as Class.getResource() would expect?)", 213 null); 214 throw new UnableToCompleteException(); 215 } 216 217 return imageNameFromMetaData; 218 } 219 220 String imageNameFromMethod = null; 221 String packageAndMethodName = method.getEnclosingType().getPackage().getName().replace( 222 '.', '/') 223 + '/' + method.getName(); 224 for (int i = 0; i < IMAGE_FILE_EXTENSIONS.length; i++) { 227 String possibleImageName = packageAndMethodName + '.' 228 + IMAGE_FILE_EXTENSIONS[i]; 229 URL imageResourceURL = getClass().getClassLoader().getResource( 233 possibleImageName); 234 if (imageResourceURL != null) { 235 imageNameFromMethod = possibleImageName; 236 break; 237 } 238 } 239 240 if (imageNameFromMethod == null) { 241 242 StringBuffer errorStringBuf = new StringBuffer (); 243 for (int i = 0; i < IMAGE_FILE_EXTENSIONS.length; i++) { 244 245 errorStringBuf.append(IMAGE_FILE_EXTENSIONS[i]); 246 247 if (i != IMAGE_FILE_EXTENSIONS.length - 1) { 248 errorStringBuf.append(", "); 249 } 250 } 251 252 logger.log( 253 TreeLogger.ERROR, 254 "Resource " 255 + packageAndMethodName 256 + ".(" 257 + errorStringBuf.toString() 258 + ") not found on classpath (is the name specified as Class.getResource() would expect?)", 259 null); 260 throw new UnableToCompleteException(); 261 } 262 263 return imageNameFromMethod; 264 } 265 266 private JMethod[] getValidImageMethods(TreeLogger logger, JClassType userType) 267 throws UnableToCompleteException { 268 269 logger = logger.branch(TreeLogger.TRACE, "Analyzing methods on " 270 + userType.getQualifiedSourceName(), null); 271 272 final JClassType imageClass; 273 try { 274 imageClass = userType.getOracle().getType(ABSTRACTIMAGEPROTOTYPE_QNAME); 275 } catch (NotFoundException e) { 276 logger.log(TreeLogger.ERROR, "GWT " + ABSTRACTIMAGEPROTOTYPE_QNAME 277 + "class is not available", e); 278 throw new UnableToCompleteException(); 279 } 280 281 Map rejectedMethodsAndWhy = new HashMap (); 282 JMethod[] leafMethods = userType.getOverridableMethods(); 283 for (int i = 0; i < leafMethods.length; i++) { 284 JMethod method = leafMethods[i]; 285 286 if (method.getReturnType() != imageClass) { 287 rejectedMethodsAndWhy.put(method, "Return type must be " 288 + ABSTRACTIMAGEPROTOTYPE_QNAME); 289 continue; 290 } 291 292 if (method.getParameters().length > 0) { 293 rejectedMethodsAndWhy.put(method, "Method cannot take parameters"); 294 continue; 295 } 296 297 String [][] md = method.getMetaData(METADATA_TAG); 298 if ((md.length > 1) || (md.length == 1 && md[0].length != 1)) { 299 rejectedMethodsAndWhy.put( 300 method, 301 "Expecting either no metadata tags, or one metadata tag of the form '@gwt.resource <resource-name>'"); 302 } 303 } 304 305 if (!rejectedMethodsAndWhy.isEmpty()) { 307 logger = logger.branch(TreeLogger.ERROR, 308 "The following methods are invalid on an image bundle:", null); 309 for (Iterator iter = rejectedMethodsAndWhy.entrySet().iterator(); iter.hasNext();) { 310 Map.Entry entry = (Map.Entry ) iter.next(); 311 JMethod badMethod = (JMethod) entry.getKey(); 312 String reason = (String ) entry.getValue(); 313 TreeLogger branch = logger.branch(TreeLogger.ERROR, 314 badMethod.getReadableDeclaration(), null); 315 branch.log(TreeLogger.ERROR, reason, null); 316 } 317 throw new UnableToCompleteException(); 318 } 319 320 return leafMethods; 321 } 322 323 private JClassType getValidUserType(TreeLogger logger, String typeName, 324 TypeOracle typeOracle) throws UnableToCompleteException { 325 try { 326 JClassType userType = typeOracle.getType(typeName); 328 329 JClassType magicType = typeOracle.findType(IMAGEBUNDLE_QNAME); 331 332 if (userType.isInterface() == null) { 334 logger.log(TreeLogger.ERROR, userType.getQualifiedSourceName() 335 + " must be an interface", null); 336 throw new UnableToCompleteException(); 337 } 338 339 if (!userType.isAssignableTo(magicType)) { 341 logger.log(TreeLogger.ERROR, userType.getQualifiedSourceName() 342 + " must be assignable to " + magicType.getQualifiedSourceName(), 343 null); 344 throw new UnableToCompleteException(); 345 } 346 347 return userType; 348 349 } catch (NotFoundException e) { 350 logger.log(TreeLogger.ERROR, "Unable to find required type(s)", e); 351 throw new UnableToCompleteException(); 352 } 353 } 354 355 } 356 | Popular Tags |