1 /* 2 * Copyright (c) 2002-2008 LWJGL Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'LWJGL' nor the names of 17 * its contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.lwjgl.util.generator; 33 34 /** 35 * 36 * Various utility methods to the generator. 37 * 38 * @author elias_naur <elias_naur@users.sourceforge.net> 39 * @version $Revision$ $Id$ 40 */ 41 import org.lwjgl.PointerBuffer; 42 import org.lwjgl.PointerWrapper; 43 import org.lwjgl.util.generator.opengl.GLboolean; 44 import org.lwjgl.util.generator.opengl.GLchar; 45 import org.lwjgl.util.generator.opengl.GLcharARB; 46 import org.lwjgl.util.generator.opengl.GLreturn; 47 48 import java.io.PrintWriter; 49 import java.nio.Buffer; 50 import java.nio.ByteBuffer; 51 import java.util.*; 52 import java.util.regex.Pattern; 53 import javax.annotation.processing.ProcessingEnvironment; 54 import javax.lang.model.element.*; 55 import javax.lang.model.type.TypeKind; 56 import javax.lang.model.type.TypeMirror; 57 import javax.lang.model.type.TypeVisitor; 58 import javax.lang.model.util.ElementFilter; 59 60 public class Utils { 61 62 public static final String TYPEDEF_POSTFIX = "PROC"; 63 public static final String FUNCTION_POINTER_VAR_NAME = "function_pointer"; 64 public static final String FUNCTION_POINTER_POSTFIX = "_pointer"; 65 public static final String CHECKS_CLASS_NAME = "GLChecks"; 66 public static final String CONTEXT_CAPS_CLASS_NAME = "ContextCapabilities"; 67 public static final String STUB_INITIALIZER_NAME = "initNativeStubs"; 68 public static final String BUFFER_OBJECT_METHOD_POSTFIX = "BO"; 69 public static final String BUFFER_OBJECT_PARAMETER_POSTFIX = "_buffer_offset"; 70 public static final String RESULT_SIZE_NAME = "result_size"; 71 public static final String RESULT_VAR_NAME = "__result"; 72 public static final String CACHED_BUFFER_LENGTH_NAME = "length"; 73 public static final String CACHED_BUFFER_NAME = "old_buffer"; 74 private static final String OVERLOADED_METHOD_PREFIX = "n"; 75 getTypedefName(ExecutableElement method)76 public static String getTypedefName(ExecutableElement method) { 77 Alternate alt_annotation = method.getAnnotation(Alternate.class); 78 return (alt_annotation == null ? method.getSimpleName() : alt_annotation.value()) + TYPEDEF_POSTFIX; 79 } 80 getFunctionAddressName(TypeElement interface_decl, ExecutableElement method)81 public static String getFunctionAddressName(TypeElement interface_decl, ExecutableElement method) { 82 return getFunctionAddressName(interface_decl, method, false); 83 } 84 getFunctionAddressName(TypeElement interface_decl, ExecutableElement method, boolean forceAlt)85 public static String getFunctionAddressName(TypeElement interface_decl, ExecutableElement method, boolean forceAlt) { 86 final Alternate alt_annotation = method.getAnnotation(Alternate.class); 87 88 /* Removed prefix so that we can identify reusable entry points, removed postfix because it's not needed and looks nicer. 89 String interfaceName = interface_decl.getSimpleName(); // If we add this back, we need to fix @Reuse (add a param for the template name) 90 if ( alt_annotation == null || (alt_annotation.nativeAlt() && !forceAlt) ) 91 return interfaceName + "_" + method.getSimpleName() + FUNCTION_POINTER_POSTFIX; 92 else 93 return interfaceName + "_" + alt_annotation.value() + FUNCTION_POINTER_POSTFIX; 94 */ 95 if ( alt_annotation == null || (alt_annotation.nativeAlt() && !forceAlt) ) { 96 return method.getSimpleName().toString(); 97 } else { 98 return alt_annotation.value(); 99 } 100 } 101 isFinal(Element d)102 public static boolean isFinal(Element d) { 103 Extension extension_annotation = d.getAnnotation(Extension.class); 104 return extension_annotation == null || extension_annotation.isFinal(); 105 } 106 107 private static class AnnotationMirrorComparator implements Comparator<AnnotationMirror> { 108 109 /** 110 * Sort annotations. 111 */ 112 @Override compare(AnnotationMirror a1, AnnotationMirror a2)113 public int compare(AnnotationMirror a1, AnnotationMirror a2) { 114 String n1 = a1.getAnnotationType().toString(); 115 String n2 = a2.getAnnotationType().toString(); 116 return n1.compareTo(n2); 117 } 118 equals(AnnotationMirror a1, AnnotationMirror a2)119 public boolean equals(AnnotationMirror a1, AnnotationMirror a2) { 120 return compare(a1, a2) == 0; 121 } 122 } 123 getSortedAnnotations(List<? extends AnnotationMirror> annotations)124 public static List<AnnotationMirror> getSortedAnnotations(List<? extends AnnotationMirror> annotations) { 125 List<AnnotationMirror> annotation_list = new ArrayList<AnnotationMirror>(annotations); 126 Collections.sort(annotation_list, new AnnotationMirrorComparator()); 127 return annotation_list; 128 } 129 getReferenceName(TypeElement interface_decl, ExecutableElement method, VariableElement param)130 public static String getReferenceName(TypeElement interface_decl, ExecutableElement method, VariableElement param) { 131 return interface_decl.getSimpleName() + "_" + method.getSimpleName() + "_" + param.getSimpleName(); 132 } 133 isAddressableType(TypeMirror type)134 public static boolean isAddressableType(TypeMirror type) { 135 return isAddressableType(getJavaType(type)); 136 } 137 isAddressableType(Class type)138 public static boolean isAddressableType(Class type) { 139 if ( type.isArray() ) { 140 final Class component_type = type.getComponentType(); 141 return isAddressableTypeImpl(component_type) || PointerWrapper.class.isAssignableFrom(component_type); 142 } 143 return isAddressableTypeImpl(type); 144 } 145 isAddressableTypeImpl(Class type)146 private static boolean isAddressableTypeImpl(Class type) { 147 return Buffer.class.isAssignableFrom(type) || PointerBuffer.class.isAssignableFrom(type) || CharSequence.class.isAssignableFrom(type); 148 } 149 getJavaType(TypeMirror type_mirror)150 public static Class getJavaType(TypeMirror type_mirror) { 151 JavaTypeTranslator translator = new JavaTypeTranslator(); 152 type_mirror.accept((TypeVisitor)translator, null); 153 return translator.getType(); 154 } 155 hasParameterMultipleTypes(VariableElement param)156 private static boolean hasParameterMultipleTypes(VariableElement param) { 157 int num_native_annotations = 0; 158 for ( AnnotationMirror annotation : param.getAnnotationMirrors() ) { 159 if ( NativeTypeTranslator.getAnnotation(annotation, NativeType.class) != null ) { 160 num_native_annotations++; 161 } 162 } 163 return num_native_annotations > 1; 164 } 165 isParameterMultiTyped(VariableElement param)166 public static boolean isParameterMultiTyped(VariableElement param) { 167 boolean result = Buffer.class.equals(Utils.getJavaType(param.asType())); 168 if ( !result && hasParameterMultipleTypes(param) ) { 169 throw new RuntimeException(param + " not defined as java.nio.Buffer but has multiple types"); 170 } 171 return result; 172 } 173 findParameter(ExecutableElement method, String name)174 public static VariableElement findParameter(ExecutableElement method, String name) { 175 for ( VariableElement param : method.getParameters() ) { 176 if ( param.getSimpleName().toString().equals(name) ) { 177 return param; 178 } 179 } 180 throw new RuntimeException("Parameter " + name + " not found"); 181 } 182 printDocComment(PrintWriter writer, Element decl, ProcessingEnvironment pe)183 public static void printDocComment(PrintWriter writer, Element decl, ProcessingEnvironment pe) { 184 final String overloadsComment; 185 if ( (decl instanceof ExecutableElement) && decl.getAnnotation(Alternate.class) != null ) { 186 overloadsComment = "Overloads " + decl.getAnnotation(Alternate.class).value() + "."; 187 } else { 188 overloadsComment = null; 189 } 190 191 String doc_comment = pe.getElementUtils().getDocComment(decl); 192 if ( doc_comment != null ) { 193 final String tab = (decl instanceof TypeElement) ? "" : "\t"; 194 writer.println(tab + "/**"); 195 196 if ( overloadsComment != null ) { 197 writer.println("\t * " + overloadsComment); 198 writer.println("\t * <p>"); 199 } 200 201 final StringTokenizer doc_lines = new StringTokenizer(doc_comment, "\n", true); 202 boolean lastWasNL = false; 203 while ( doc_lines.hasMoreTokens() ) { 204 final String t = doc_lines.nextToken(); 205 if ( "\n".equals(t) ) { 206 if ( lastWasNL ) { 207 writer.println(tab + " * <p>"); 208 } 209 lastWasNL = true; 210 } else { 211 writer.println(tab + " * " + t); 212 lastWasNL = false; 213 } 214 } 215 216 writer.println(tab + " */"); 217 } else if ( overloadsComment != null ) { 218 writer.println("\t/** " + overloadsComment + " */"); 219 } 220 } 221 getParameterAutoAnnotation(VariableElement param)222 public static AnnotationMirror getParameterAutoAnnotation(VariableElement param) { 223 for ( AnnotationMirror annotation : param.getAnnotationMirrors() ) { 224 if ( NativeTypeTranslator.getAnnotation(annotation, Auto.class) != null ) { 225 return annotation; 226 } 227 } 228 return null; 229 } 230 231 // DISABLED: We always generate indirect methods. (affects OpenAL only at the time of this change) isMethodIndirect(boolean generate_error_checks, boolean context_specific, ExecutableElement method)232 public static boolean isMethodIndirect(boolean generate_error_checks, boolean context_specific, ExecutableElement method) { 233 /* 234 for (VariableElement param : method.getParameters()) { 235 if (isAddressableType(param.getType()) || getParameterAutoAnnotation(param) != null || 236 param.getAnnotation(Constant.class) != null) 237 return true; 238 } 239 return hasMethodBufferObjectParameter(method) || method.getAnnotation(Code.class) != null || 240 method.getAnnotation(CachedResult.class) != null || 241 (generate_error_checks && method.getAnnotation(NoErrorCheck.class) == null) || 242 context_specific; 243 */ 244 return true; 245 } 246 247 private static final Pattern DOT_PATTERN = Pattern.compile("\\."); 248 getNativeQualifiedName(String qualified_name)249 public static String getNativeQualifiedName(String qualified_name) { 250 return DOT_PATTERN.matcher(qualified_name).replaceAll("_"); 251 } 252 getQualifiedNativeMethodName(String qualified_class_name, String method_name)253 public static String getQualifiedNativeMethodName(String qualified_class_name, String method_name) { 254 // Escape '_' in method name 255 if ( method_name.indexOf('_') != -1 ) { 256 method_name = method_name.replace("_", "_1"); 257 } 258 259 return "Java_" + getNativeQualifiedName(qualified_class_name) + "_" + method_name; 260 } 261 getQualifiedNativeMethodName(String qualified_class_name, ExecutableElement method, boolean generate_error_checks, boolean context_specific)262 public static String getQualifiedNativeMethodName(String qualified_class_name, ExecutableElement method, boolean generate_error_checks, boolean context_specific) { 263 String method_name = getSimpleNativeMethodName(method, generate_error_checks, context_specific); 264 return getQualifiedNativeMethodName(qualified_class_name, method_name); 265 } 266 getResultParameter(ExecutableElement method)267 public static VariableElement getResultParameter(ExecutableElement method) { 268 VariableElement result_param = null; 269 for ( VariableElement param : method.getParameters() ) { 270 if ( param.getAnnotation(Result.class) != null ) { 271 if ( result_param != null ) { 272 throw new RuntimeException("Multiple parameters annotated with Result in method " + method); 273 } 274 result_param = param; 275 } 276 } 277 return result_param; 278 } 279 getMethodReturnType(ExecutableElement method)280 public static TypeMirror getMethodReturnType(ExecutableElement method) { 281 TypeMirror result_type; 282 VariableElement result_param = getResultParameter(method); 283 if ( result_param != null ) { 284 result_type = result_param.asType(); 285 } else { 286 result_type = method.getReturnType(); 287 } 288 return result_type; 289 } 290 getMethodReturnType(ExecutableElement method, GLreturn return_annotation, boolean buffer)291 public static String getMethodReturnType(ExecutableElement method, GLreturn return_annotation, boolean buffer) { 292 VariableElement return_param = null; 293 for ( VariableElement param : method.getParameters() ) { 294 if ( param.getSimpleName().toString().equals(return_annotation.value()) ) { 295 return_param = param; 296 break; 297 } 298 } 299 if ( return_param == null ) { 300 throw new RuntimeException("The @GLreturn parameter \"" + return_annotation.value() + "\" could not be found in method: " + method); 301 } 302 303 TypeKind kind = NativeTypeTranslator.getPrimitiveKindFromBufferClass(Utils.getJavaType(return_param.asType())); 304 if ( return_param.getAnnotation(GLboolean.class) != null ) { 305 kind = TypeKind.BOOLEAN; 306 } 307 308 if ( kind == TypeKind.BYTE && (return_param.getAnnotation(GLchar.class) != null || return_param.getAnnotation(GLcharARB.class) != null) ) { 309 return "String"; 310 } else { 311 final String type = JavaTypeTranslator.getPrimitiveClassFromKind(kind).getName(); 312 return buffer ? Character.toUpperCase(type.charAt(0)) + type.substring(1) : type; 313 } 314 } 315 needResultSize(ExecutableElement method)316 public static boolean needResultSize(ExecutableElement method) { 317 return getNIOBufferType(getMethodReturnType(method)) != null && method.getAnnotation(AutoSize.class) == null; 318 } 319 printExtraCallArguments(PrintWriter writer, ExecutableElement method, String size_parameter_name)320 public static void printExtraCallArguments(PrintWriter writer, ExecutableElement method, String size_parameter_name) { 321 writer.print(size_parameter_name); 322 if ( method.getAnnotation(CachedResult.class) != null ) { 323 writer.print(", " + CACHED_BUFFER_NAME); 324 } 325 } 326 getClassName(TypeElement interface_decl, String opengl_name)327 private static String getClassName(TypeElement interface_decl, String opengl_name) { 328 Extension extension_annotation = interface_decl.getAnnotation(Extension.class); 329 if ( extension_annotation != null && !"".equals(extension_annotation.className()) ) { 330 return extension_annotation.className(); 331 } 332 StringBuilder result = new StringBuilder(); 333 for ( int i = 0; i < opengl_name.length(); i++ ) { 334 int ch = opengl_name.codePointAt(i); 335 if ( ch == '_' ) { 336 i++; 337 result.appendCodePoint(Character.toUpperCase(opengl_name.codePointAt(i))); 338 } else { 339 result.appendCodePoint(ch); 340 } 341 } 342 return result.toString(); 343 } 344 hasMethodBufferObjectParameter(ExecutableElement method)345 public static boolean hasMethodBufferObjectParameter(ExecutableElement method) { 346 for ( VariableElement param : method.getParameters() ) { 347 if ( param.getAnnotation(BufferObject.class) != null ) { 348 return true; 349 } 350 } 351 return false; 352 } 353 getQualifiedClassName(TypeElement interface_decl)354 public static String getQualifiedClassName(TypeElement interface_decl) { 355 return interface_decl.getEnclosingElement().asType().toString() + "." + getSimpleClassName(interface_decl); 356 } 357 getSimpleClassName(TypeElement interface_decl)358 public static String getSimpleClassName(TypeElement interface_decl) { 359 return getClassName(interface_decl, interface_decl.getSimpleName().toString()); 360 } 361 getNIOBufferType(TypeMirror t)362 public static Class<?> getNIOBufferType(TypeMirror t) { 363 Class<?> param_type = getJavaType(t); 364 if ( Buffer.class.isAssignableFrom(param_type) ) { 365 return param_type; 366 } else if ( param_type == CharSequence.class || param_type == CharSequence[].class || param_type == PointerBuffer.class ) { 367 return ByteBuffer.class; 368 } else { 369 return null; 370 } 371 } 372 getSimpleNativeMethodName(ExecutableElement method, boolean generate_error_checks, boolean context_specific)373 public static String getSimpleNativeMethodName(ExecutableElement method, boolean generate_error_checks, boolean context_specific) { 374 String method_name; 375 Alternate alt_annotation = method.getAnnotation(Alternate.class); 376 method_name = alt_annotation == null || alt_annotation.nativeAlt() ? method.getSimpleName().toString() : alt_annotation.value(); 377 if ( isMethodIndirect(generate_error_checks, context_specific, method) ) { 378 method_name = OVERLOADED_METHOD_PREFIX + method_name; 379 } 380 return method_name; 381 } 382 isReturnParameter(ExecutableElement method, VariableElement param)383 static boolean isReturnParameter(ExecutableElement method, VariableElement param) { 384 GLreturn string_annotation = method.getAnnotation(GLreturn.class); 385 if ( string_annotation == null || !string_annotation.value().equals(param.getSimpleName().toString()) ) { 386 return false; 387 } 388 389 if ( param.getAnnotation(OutParameter.class) == null ) { 390 throw new RuntimeException("The parameter specified in @GLreturn is not annotated with @OutParameter in method: " + method); 391 } 392 393 if ( param.getAnnotation(Check.class) != null ) { 394 throw new RuntimeException("The parameter specified in @GLreturn is annotated with @Check in method: " + method); 395 } 396 397 if ( param.getAnnotation(GLchar.class) != null && Utils.getJavaType(param.asType()).equals(ByteBuffer.class) && string_annotation.maxLength().length() == 0 ) { 398 throw new RuntimeException("The @GLreturn annotation is missing a maxLength parameter in method: " + method); 399 } 400 401 return true; 402 } 403 getStringOffset(ExecutableElement method, VariableElement param)404 static String getStringOffset(ExecutableElement method, VariableElement param) { 405 String offset = null; 406 for ( VariableElement p : method.getParameters() ) { 407 if ( param != null && p.getSimpleName().equals(param.getSimpleName()) ) { 408 break; 409 } 410 411 if ( p.getAnnotation(NullTerminated.class) != null ) { 412 continue; 413 } 414 415 final Class type = Utils.getJavaType(p.asType()); 416 if ( type.equals(CharSequence.class) ) { 417 if ( offset == null ) { 418 offset = p.getSimpleName() + ".length()"; 419 } else { 420 offset += " + " + p.getSimpleName() + ".length()"; 421 } 422 //if ( p.getAnnotation(NullTerminated.class) != null ) offset += " + 1"; 423 } else if ( type.equals(CharSequence[].class) ) { 424 if ( offset == null ) { 425 offset = "APIUtil.getTotalLength(" + p.getSimpleName() + ")"; 426 } else { 427 offset += " + APIUtil.getTotalLength(" + p.getSimpleName() + ")"; 428 } 429 //if ( p.getAnnotation(NullTerminated.class) != null ) offset += " + " + p.getSimpleName() + ".length"; 430 } 431 432 } 433 return offset; 434 } 435 printGLReturnPre(PrintWriter writer, ExecutableElement method, GLreturn return_annotation, TypeMap type_map)436 static void printGLReturnPre(PrintWriter writer, ExecutableElement method, GLreturn return_annotation, TypeMap type_map) { 437 final String return_type = getMethodReturnType(method, return_annotation, true); 438 439 if ( "String".equals(return_type) ) { 440 if ( !return_annotation.forceMaxLength() ) { 441 writer.println("IntBuffer " + return_annotation.value() + "_length = APIUtil.getLengths(" + type_map.getAPIUtilParam(false) + ");"); 442 writer.print("\t\t"); 443 } 444 writer.print("ByteBuffer " + return_annotation.value() + " = APIUtil.getBufferByte(" + type_map.getAPIUtilParam(true) + return_annotation.maxLength()); 445 /* 446 Params that use the return buffer will advance its position while filling it. When we return, the position will be 447 at the right spot for grabbing the returned string bytes. We only have to make sure that the original buffer was 448 large enough to hold everything, so that no re-allocations happen while filling. 449 */ 450 final String offset = getStringOffset(method, null); 451 if ( offset != null ) { 452 writer.print(" + " + offset); 453 } 454 writer.println(");"); 455 } else { 456 final String buffer_type = "Boolean".equals(return_type) ? "Byte" : return_type; 457 writer.print(buffer_type + "Buffer " + return_annotation.value() + " = APIUtil.getBuffer" + buffer_type + "(" + type_map.getAPIUtilParam(false)); 458 if ( "Byte".equals(buffer_type) ) { 459 writer.print((type_map.getAPIUtilParam(false).length() > 0 ? ", " : "") + "1"); 460 } 461 writer.println(");"); 462 } 463 464 final Code code_annotation = method.getAnnotation(Code.class); 465 if ( code_annotation != null && code_annotation.tryBlock() ) { 466 writer.println("\t\ttry {"); 467 writer.print("\t\t\t"); 468 } else { 469 writer.print("\t\t"); 470 } 471 } 472 printGLReturnPost(PrintWriter writer, ExecutableElement method, GLreturn return_annotation, TypeMap type_map)473 static void printGLReturnPost(PrintWriter writer, ExecutableElement method, GLreturn return_annotation, TypeMap type_map) { 474 final String return_type = getMethodReturnType(method, return_annotation, true); 475 476 if ( "String".equals(return_type) ) { 477 writer.print("\t\t" + return_annotation.value() + ".limit("); 478 final String offset = getStringOffset(method, null); 479 if ( offset != null ) { 480 writer.print(offset + " + "); 481 } 482 if ( return_annotation.forceMaxLength() ) { 483 writer.print(return_annotation.maxLength()); 484 } else { 485 writer.print(return_annotation.value() + "_length.get(0)"); 486 } 487 writer.println(");"); 488 writer.println("\t\treturn APIUtil.getString(" + type_map.getAPIUtilParam(true) + return_annotation.value() + ");"); 489 } else { 490 writer.print("\t\treturn " + return_annotation.value() + ".get(0)"); 491 if ( "Boolean".equals(return_type) ) { 492 writer.print(" == 1"); 493 } 494 writer.println(";"); 495 } 496 } 497 getFields(TypeElement d)498 public static Collection<VariableElement> getFields(TypeElement d) { 499 return ElementFilter.fieldsIn(new LinkedHashSet<Element>(d.getEnclosedElements())); 500 } 501 getMethods(TypeElement d)502 public static Collection<ExecutableElement> getMethods(TypeElement d) { 503 return ElementFilter.methodsIn(new LinkedHashSet<Element>(d.getEnclosedElements())); 504 } 505 506 } 507