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  * This class generates the methods in the generated java source files.
37  *
38  * @author elias_naur <elias_naur@users.sourceforge.net>
39  * @version $Revision$ $Id$
40  */
41 import org.lwjgl.PointerBuffer;
42 import org.lwjgl.util.generator.opengl.GLreturn;
43 
44 import java.io.PrintWriter;
45 import java.nio.*;
46 import java.util.*;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49 import javax.annotation.processing.ProcessingEnvironment;
50 import javax.lang.model.element.AnnotationMirror;
51 import javax.lang.model.element.ExecutableElement;
52 import javax.lang.model.element.TypeElement;
53 import javax.lang.model.element.VariableElement;
54 import javax.lang.model.type.TypeKind;
55 import javax.lang.model.type.TypeMirror;
56 
57 public class JavaMethodsGenerator {
58 
59 	private static final String SAVED_PARAMETER_POSTFIX = "_saved";
60 
generateMethodsJava(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, boolean generate_error_checks, boolean context_specific)61 	public static void generateMethodsJava(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, boolean generate_error_checks, boolean context_specific) {
62 		for ( ExecutableElement method : Utils.getMethods(interface_decl) ) {
63 			generateMethodJava(env, type_map, writer, interface_decl, method, generate_error_checks, context_specific);
64 		}
65 	}
66 
67 	/**
68 	 * TODO : fix info multi-type methods print.
69 	 */
generateMethodJava(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, ExecutableElement method, boolean generate_error_checks, boolean context_specific)70 	private static void generateMethodJava(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, ExecutableElement method, boolean generate_error_checks, boolean context_specific) {
71 		writer.println();
72 		if ( Utils.isMethodIndirect(generate_error_checks, context_specific, method) ) {
73 			if ( method.getAnnotation(GenerateAutos.class) != null ) {
74 				printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.AUTOS, generate_error_checks, context_specific);
75 			}
76 			Collection<Map<VariableElement, TypeInfo>> cross_product = TypeInfo.getTypeInfoCrossProduct(type_map, method);
77 			for ( Map<VariableElement, TypeInfo> typeinfos_instance : cross_product ) {
78 				printMethodWithMultiType(env, type_map, writer, interface_decl, method, typeinfos_instance, Mode.NORMAL, generate_error_checks, context_specific);
79 			}
80 		}
81 		if ( method.getAnnotation(CachedResult.class) != null && !method.getAnnotation(CachedResult.class).isRange() ) {
82 			printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.CACHEDRESULT, generate_error_checks, context_specific);
83 		}
84 
85 		Reuse reuse_annotation = method.getAnnotation(Reuse.class);
86 		Alternate alt_annotation = method.getAnnotation(Alternate.class);
87 		if ( alt_annotation == null || (alt_annotation.nativeAlt() && !alt_annotation.skipNative()) ) {
88 			if ( alt_annotation != null && method.getSimpleName().toString().equals(alt_annotation.value()) ) {
89 				throw new RuntimeException("An alternate function with native code should have a different name than the main function.");
90 			}
91 
92 			if ( reuse_annotation == null ) {
93 				printJavaNativeStub(env, writer, method, Mode.NORMAL, generate_error_checks, context_specific);
94 			}
95 
96 			if ( Utils.hasMethodBufferObjectParameter(method) ) {
97 				printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.BUFFEROBJECT, generate_error_checks, context_specific);
98 				if ( reuse_annotation == null ) {
99 					printJavaNativeStub(env, writer, method, Mode.BUFFEROBJECT, generate_error_checks, context_specific);
100 				}
101 			}
102 		}
103 	}
104 
printJavaNativeStub(ProcessingEnvironment env, PrintWriter writer, ExecutableElement method, Mode mode, boolean generate_error_checks, boolean context_specific)105 	private static void printJavaNativeStub(ProcessingEnvironment env, PrintWriter writer, ExecutableElement method, Mode mode, boolean generate_error_checks, boolean context_specific) {
106 		if ( Utils.isMethodIndirect(generate_error_checks, context_specific, method) ) {
107 			writer.print("\tstatic native ");
108 		} else {
109 			Utils.printDocComment(writer, method, env);
110 			writer.print("\tpublic static native ");
111 		}
112 		writer.print(getResultType(method, true));
113 		writer.print(" " + Utils.getSimpleNativeMethodName(method, generate_error_checks, context_specific));
114 		if ( mode == Mode.BUFFEROBJECT ) {
115 			writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX);
116 		}
117 		writer.print("(");
118 		boolean first_parameter = generateParametersJava(writer, method, TypeInfo.getDefaultTypeInfoMap(method), true, true, mode);
119 		if ( context_specific ) {
120 			if ( !first_parameter ) {
121 				writer.print(", ");
122 			}
123 			writer.print("long " + Utils.FUNCTION_POINTER_VAR_NAME);
124 		}
125 		writer.println(");");
126 	}
127 
generateParametersJava(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, boolean native_stub, final boolean printTypes, Mode mode)128 	private static boolean generateParametersJava(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, boolean native_stub, final boolean printTypes, Mode mode) {
129 		boolean first_parameter = true;
130 		for ( VariableElement param : method.getParameters() ) {
131 			if ( native_stub && (param.getAnnotation(Helper.class) != null && !param.getAnnotation(Helper.class).passToNative()) ) {
132 				continue;
133 			}
134 			final Constant constant_annotation = param.getAnnotation(Constant.class);
135 			if ( constant_annotation != null && constant_annotation.isNative() ) {
136 				continue;
137 			}
138 			AnnotationMirror auto_annotation_mirror = Utils.getParameterAutoAnnotation(param);
139 			boolean hide_auto_parameter = mode == Mode.NORMAL && !native_stub && auto_annotation_mirror != null;
140 			if ( hide_auto_parameter ) {
141 				AutoType auto_type_annotation = param.getAnnotation(AutoType.class);
142 				if ( auto_type_annotation != null ) {
143 					VariableElement auto_parameter = Utils.findParameter(method, auto_type_annotation.value());
144 					TypeInfo auto_param_type_info = typeinfos_instance.get(auto_parameter);
145 					if ( auto_param_type_info.getSignedness() == Signedness.BOTH ) {
146 						if ( !first_parameter ) {
147 							writer.print(", ");
148 						}
149 						first_parameter = false;
150 						if ( printTypes ) {
151 							writer.print("boolean ");
152 						}
153 						writer.print(TypeInfo.UNSIGNED_PARAMETER_NAME);
154 					}
155 				}
156 			} else if ( param.getAnnotation(Result.class) == null
157 			            && (native_stub || ((param.getAnnotation(Constant.class) == null || param.getAnnotation(Constant.class).keepParam()) && !Utils.isReturnParameter(method, param)))
158 			            && (mode != Mode.AUTOS || getAutoTypeParameter(method, param) == null) ) {
159 				first_parameter = generateParameterJava(writer, param, typeinfos_instance.get(param), native_stub, printTypes, first_parameter, mode);
160 			}
161 		}
162 		CachedResult cached_result_annotation = method.getAnnotation(CachedResult.class);
163 		TypeMirror result_type = Utils.getMethodReturnType(method);
164 		if ( (native_stub && Utils.getNIOBufferType(result_type) != null) || Utils.needResultSize(method) ) {
165 			AutoSize auto_size_annotation = method.getAnnotation(AutoSize.class);
166 			if ( auto_size_annotation == null || !auto_size_annotation.isNative() ) {
167 				if ( cached_result_annotation == null || !cached_result_annotation.isRange() ) {
168 					if ( !first_parameter ) {
169 						writer.print(", ");
170 					}
171 					first_parameter = false;
172 					if ( printTypes ) {
173 						writer.print("long ");
174 					}
175 					writer.print(Utils.RESULT_SIZE_NAME);
176 				}
177 			}
178 		}
179 		if ( cached_result_annotation != null ) {
180 			if ( !first_parameter ) {
181 				writer.print(", ");
182 			}
183 
184 			if ( mode == Mode.CACHEDRESULT ) {
185 				if ( printTypes ) {
186 					writer.print("long ");
187 				}
188 				writer.print(Utils.CACHED_BUFFER_LENGTH_NAME + ", ");
189 			}
190 
191 			first_parameter = false;
192 			if ( printTypes ) {
193 				writer.print(getResultType(method, native_stub));
194 			}
195 			writer.print(" " + Utils.CACHED_BUFFER_NAME);
196 		}
197 		return first_parameter;
198 	}
199 
generateParameterJava(PrintWriter writer, VariableElement param, TypeInfo type_info, boolean native_stub, final boolean printTypes, boolean first_parameter, Mode mode)200 	private static boolean generateParameterJava(PrintWriter writer, VariableElement param, TypeInfo type_info, boolean native_stub, final boolean printTypes, boolean first_parameter, Mode mode) {
201 		Class buffer_type = Utils.getNIOBufferType(param.asType());
202 		if ( !first_parameter ) {
203 			writer.print(", ");
204 		}
205 		BufferObject bo_annotation = param.getAnnotation(BufferObject.class);
206 		if ( bo_annotation != null && mode == Mode.BUFFEROBJECT ) {
207 			if ( buffer_type == null ) {
208 				throw new RuntimeException("type of " + param + " is not a nio Buffer parameter but is annotated as buffer object");
209 			}
210 			if ( printTypes ) {
211 				writer.print("long ");
212 			}
213 			writer.print(param.getSimpleName() + Utils.BUFFER_OBJECT_PARAMETER_POSTFIX);
214 		} else {
215 			if ( native_stub && param.getAnnotation(PointerWrapper.class) != null ) {
216 				writer.print("long ");
217 			} else {
218 				Class type = type_info.getType();
219 				if ( native_stub && (type == CharSequence.class || type == CharSequence[].class || type == PointerBuffer.class || Buffer.class.isAssignableFrom(type)) ) {
220 					writer.print("long ");
221 				} else if ( printTypes ) {
222 					writer.print(type.getSimpleName() + " ");
223 				}
224 			}
225 			AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class);
226 			if ( auto_size_annotation != null ) {
227 				writer.print(auto_size_annotation.value() + "_");
228 			}
229 			writer.print(param.getSimpleName());
230 		}
231 		return false;
232 	}
233 
printBufferObjectCheck(PrintWriter writer, BufferKind kind, Mode mode, boolean context_specific)234 	private static void printBufferObjectCheck(PrintWriter writer, BufferKind kind, Mode mode, boolean context_specific) {
235 		String bo_check_method_name = kind.toString();
236 		writer.print("\t\t" + Utils.CHECKS_CLASS_NAME + ".ensure" + bo_check_method_name);
237 		if ( mode == Mode.BUFFEROBJECT ) {
238 			writer.print("enabled");
239 		} else {
240 			writer.print("disabled");
241 		}
242 
243 		if ( context_specific ) {
244 			writer.println("(caps);");
245 		} else {
246 			writer.println("();");
247 		}
248 	}
249 
printBufferObjectChecks(PrintWriter writer, ExecutableElement method, Mode mode, boolean context_specific)250 	private static void printBufferObjectChecks(PrintWriter writer, ExecutableElement method, Mode mode, boolean context_specific) {
251 		EnumSet<BufferKind> check_set = EnumSet.noneOf(BufferKind.class);
252 		for ( VariableElement param : method.getParameters() ) {
253 			BufferObject bo_annotation = param.getAnnotation(BufferObject.class);
254 			if ( bo_annotation != null ) {
255 				check_set.add(bo_annotation.value());
256 			}
257 		}
258 		for ( BufferKind kind : check_set ) {
259 			printBufferObjectCheck(writer, kind, mode, context_specific);
260 		}
261 	}
262 
printMethodWithMultiType(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, boolean generate_error_checks, boolean context_specific)263 	private static void printMethodWithMultiType(ProcessingEnvironment env, TypeMap type_map, PrintWriter writer, TypeElement interface_decl, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, boolean generate_error_checks, boolean context_specific) {
264 		Utils.printDocComment(writer, method, env);
265 		if ( method.getAnnotation(Deprecated.class) != null ) {
266 			writer.println("\t@Deprecated");
267 		}
268 		if ( interface_decl.getAnnotation(Private.class) == null && method.getAnnotation(Private.class) == null ) {
269 			writer.print("\tpublic static ");
270 		} else {
271 			writer.print("\tstatic ");
272 		}
273 		writer.print(getResultType(method, false));
274 		StripPostfix strip_annotation = method.getAnnotation(StripPostfix.class);
275 		String method_name;
276 		Alternate alt_annotation = method.getAnnotation(Alternate.class);
277 		method_name = alt_annotation == null || alt_annotation.javaAlt() ? method.getSimpleName().toString() : alt_annotation.value();
278 		if ( strip_annotation != null && mode == Mode.NORMAL ) {
279 			method_name = getPostfixStrippedName(type_map, interface_decl, method);
280 		}
281 		writer.print(" " + method_name + "(");
282 		generateParametersJava(writer, method, typeinfos_instance, false, true, mode);
283 		writer.println(") {");
284 
285 		final TypeMirror result_type = Utils.getMethodReturnType(method);
286 		boolean has_result = !result_type.equals(env.getTypeUtils().getNoType(TypeKind.VOID));
287 
288 		final Reuse reuse_annotation = method.getAnnotation(Reuse.class);
289 		if ( reuse_annotation != null ) {
290 			writer.print("\t\t");
291 			if ( has_result || method.getAnnotation(GLreturn.class) != null ) {
292 				writer.print("return ");
293 			}
294 
295 			writer.print(reuse_annotation.value() + "." + (reuse_annotation.method().length() > 0 ? reuse_annotation.method() : method_name) + "(");
296 			generateParametersJava(writer, method, typeinfos_instance, false, false, mode);
297 			writer.println(");\n\t}");
298 			return;
299 		}
300 
301 		if ( context_specific ) {
302 			type_map.printCapabilitiesInit(writer);
303 			writer.print("\t\tlong " + Utils.FUNCTION_POINTER_VAR_NAME + " = " + type_map.getCapabilities() + ".");
304 			writer.println(Utils.getFunctionAddressName(interface_decl, method, true) + ";");
305 			writer.print("\t\tBufferChecks.checkFunctionAddress(");
306 			writer.println(Utils.FUNCTION_POINTER_VAR_NAME + ");");
307 		}
308 		final Code code_annotation = method.getAnnotation(Code.class);
309 		if ( code_annotation != null && code_annotation.value().length() > 0 ) {
310 			writer.println(code_annotation.value());
311 		}
312 		printBufferObjectChecks(writer, method, mode, context_specific);
313 		printParameterChecks(writer, method, typeinfos_instance, mode, generate_error_checks);
314 		printParameterCaching(writer, interface_decl, method, mode, context_specific);
315 
316 		if ( code_annotation != null && code_annotation.javaBeforeNative().length() > 0 ) {
317 			writer.println(code_annotation.javaBeforeNative());
318 		}
319 		writer.print("\t\t");
320 
321 		final PointerWrapper pointer_wrapper_annotation = method.getAnnotation(PointerWrapper.class);
322 		if ( has_result ) {
323 			writer.print(getResultType(method, false) + " " + Utils.RESULT_VAR_NAME);
324 
325 			if ( code_annotation != null && code_annotation.tryBlock() ) {
326 				writer.print(" = " + getDefaultResultValue(method));
327 				writer.println(";\n\t\ttry {");
328 				writer.print("\t\t\t" + Utils.RESULT_VAR_NAME);
329 			}
330 
331 			writer.print(" = ");
332 			if ( pointer_wrapper_annotation != null ) {
333 				if ( pointer_wrapper_annotation.factory().length() > 0 ) {
334 					writer.print(pointer_wrapper_annotation.factory() + "(");
335 				} else {
336 					writer.print("new " + getResultType(method, false) + "(");
337 				}
338 			}
339 		} else if ( method.getAnnotation(GLreturn.class) != null ) {
340 			has_result = true;
341 			Utils.printGLReturnPre(writer, method, method.getAnnotation(GLreturn.class), type_map);
342 		}
343 		writer.print(Utils.getSimpleNativeMethodName(method, generate_error_checks, context_specific));
344 		if ( mode == Mode.BUFFEROBJECT ) {
345 			writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX);
346 		}
347 		writer.print("(");
348 		boolean first_parameter = printMethodCallArguments(writer, method, typeinfos_instance, mode, type_map);
349 		if ( context_specific ) {
350 			if ( !first_parameter ) {
351 				writer.print(", ");
352 			}
353 			writer.print(Utils.FUNCTION_POINTER_VAR_NAME);
354 		}
355 		if ( has_result && pointer_wrapper_annotation != null ) {
356 			writer.print(")");
357 			if ( pointer_wrapper_annotation.params().length() > 0 ) {
358 				writer.print(", " + pointer_wrapper_annotation.params());
359 			}
360 		}
361 		writer.println(");");
362 
363 		if ( code_annotation != null && code_annotation.javaAfterNative().length() > 0 ) {
364 			writer.println(code_annotation.javaAfterNative());
365 		}
366 
367 		final String tabs = code_annotation != null && code_annotation.tryBlock() ? "\t\t\t" : "\t\t";
368 		if ( generate_error_checks && method.getAnnotation(NoErrorCheck.class) == null ) {
369 			type_map.printErrorCheckMethod(writer, method, tabs);
370 		}
371 		// DISABLED: indirect buffer support
372 		//printNondirectParameterCopies(writer, method, mode);
373 		if ( has_result ) {
374 			if ( method.getAnnotation(GLreturn.class) == null ) {
375 				if ( ByteBuffer.class.equals(Utils.getJavaType(result_type)) ) {
376 					writer.println(tabs + "return LWJGLUtil.CHECKS && " + Utils.RESULT_VAR_NAME + " == null ? null : " + Utils.RESULT_VAR_NAME + ".order(ByteOrder.nativeOrder());"); // safeNewBuffer returns a direct ByteBuffer with BIG_ENDIAN order.
377 				} else {
378 					writer.println(tabs + "return " + Utils.RESULT_VAR_NAME + ";");
379 				}
380 			} else {
381 				Utils.printGLReturnPost(writer, method, method.getAnnotation(GLreturn.class), type_map);
382 			}
383 		}
384 
385 		if ( code_annotation != null && code_annotation.tryBlock() ) {
386 			writer.println("\t\t} finally {");
387 			writer.println(code_annotation.javaFinally());
388 			writer.println("\t\t}");
389 		}
390 		writer.println("\t}");
391 	}
392 
getExtensionPostfix(TypeElement interface_decl)393 	private static String getExtensionPostfix(TypeElement interface_decl) {
394 		String interface_simple_name = interface_decl.getSimpleName().toString();
395 		Extension extension_annotation = interface_decl.getAnnotation(Extension.class);
396 		if ( extension_annotation == null ) {
397 			int underscore_index = interface_simple_name.indexOf("_");
398 			if ( underscore_index != -1 ) {
399 				return interface_simple_name.substring(0, underscore_index);
400 			} else {
401 				return "";
402 			}
403 		} else {
404 			return extension_annotation.postfix();
405 		}
406 	}
407 
getAutoTypeParameter(ExecutableElement method, VariableElement target_parameter)408 	private static VariableElement getAutoTypeParameter(ExecutableElement method, VariableElement target_parameter) {
409 		for ( VariableElement param : method.getParameters() ) {
410 			AnnotationMirror auto_annotation = Utils.getParameterAutoAnnotation(param);
411 			if ( auto_annotation != null ) {
412 				Class annotation_type = NativeTypeTranslator.getClassFromType(auto_annotation.getAnnotationType());
413 				String parameter_name;
414 				if ( annotation_type.equals(AutoType.class) ) {
415 					parameter_name = param.getAnnotation(AutoType.class).value();
416 				} else if ( annotation_type.equals(AutoSize.class) ) {
417 					parameter_name = param.getAnnotation(AutoSize.class).value();
418 				} else {
419 					throw new RuntimeException("Unknown annotation type " + annotation_type);
420 				}
421 				if ( target_parameter.getSimpleName().toString().equals(parameter_name) ) {
422 					return param;
423 				}
424 			}
425 		}
426 		return null;
427 	}
428 
hasAnyParameterAutoTypeAnnotation(ExecutableElement method, VariableElement target_param)429 	private static boolean hasAnyParameterAutoTypeAnnotation(ExecutableElement method, VariableElement target_param) {
430 		for ( VariableElement param : method.getParameters() ) {
431 			AutoType auto_type_annotation = param.getAnnotation(AutoType.class);
432 			if ( auto_type_annotation != null ) {
433 				VariableElement type_target_param = Utils.findParameter(method, auto_type_annotation.value());
434 				if ( target_param.equals(type_target_param) ) {
435 					return true;
436 				}
437 			}
438 		}
439 		return false;
440 	}
441 
442 	private static final Map<String, Pattern> postfixPatterns = new HashMap<String, Pattern>();
443 
getPostfixPattern(String regex)444 	private static Pattern getPostfixPattern(String regex) {
445 		Pattern pattern = postfixPatterns.get(regex);
446 		if ( pattern == null ) {
447 			postfixPatterns.put(regex, pattern = Pattern.compile(regex));
448 		}
449 		return pattern;
450 	}
451 
getPostfixStrippedName(TypeMap type_map, TypeElement interface_decl, ExecutableElement method)452 	private static String getPostfixStrippedName(TypeMap type_map, TypeElement interface_decl, ExecutableElement method) {
453 		StripPostfix strip_annotation = method.getAnnotation(StripPostfix.class);
454 		VariableElement postfix_parameter = Utils.findParameter(method, strip_annotation.value());
455 		String postfix = strip_annotation.postfix();
456 		boolean postfixOverride = !("NULL".equals(postfix) && strip_annotation.hasPostfix());
457 		if ( !postfixOverride ) {
458 			PostfixTranslator translator = new PostfixTranslator(type_map, postfix_parameter);
459 			postfix_parameter.asType().accept(translator, null);
460 			postfix = translator.getSignature();
461 		} else if ( !strip_annotation.hasPostfix() ) {
462 			postfix = "";
463 		}
464 
465 		String method_name;
466 		Alternate alt_annotation = method.getAnnotation(Alternate.class);
467 		method_name = alt_annotation == null || alt_annotation.javaAlt() ? method.getSimpleName().toString() : alt_annotation.value();
468 
469 		String extension_postfix = "NULL".equals(strip_annotation.extension()) ? getExtensionPostfix(interface_decl) : strip_annotation.extension();
470 
471 		Matcher matcher = getPostfixPattern(
472 			postfixOverride
473 				? (postfix + "(?:v)?" + extension_postfix + "$")
474 				: ("(?:" + postfix + "(?:v)?|i(?:64)?_v|v)" + extension_postfix + "$")
475 		).matcher(method_name);
476 
477 		if ( !matcher.find() ) {
478 			throw new RuntimeException(method_name + " is specified as being postfix stripped on parameter " + postfix_parameter + ", but it's postfix is neither '" + postfix + "' nor 'v'");
479 		}
480 
481 		return method_name.substring(0, matcher.start()) + extension_postfix;
482 	}
483 
getBufferElementSizeExponent(Class c)484 	private static int getBufferElementSizeExponent(Class c) {
485 		if ( IntBuffer.class.equals(c) ) {
486 			return 2;
487 		} else if ( LongBuffer.class.equals(c) ) {
488 			return 3;
489 		} else if ( DoubleBuffer.class.equals(c) ) {
490 			return 3;
491 		} else if ( ShortBuffer.class.equals(c) ) {
492 			return 1;
493 		} else if ( ByteBuffer.class.equals(c) ) {
494 			return 0;
495 		} else if ( FloatBuffer.class.equals(c) ) {
496 			return 2;
497 		} else {
498 			throw new RuntimeException(c + " is not allowed");
499 		}
500 	}
501 
printMethodCallArgument(PrintWriter writer, ExecutableElement method, VariableElement param, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, boolean first_parameter, TypeMap type_map)502 	private static boolean printMethodCallArgument(PrintWriter writer, ExecutableElement method, VariableElement param, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, boolean first_parameter, TypeMap type_map) {
503 		if ( !first_parameter ) {
504 			writer.print(", ");
505 		}
506 
507 		AnnotationMirror auto_annotation = Utils.getParameterAutoAnnotation(param);
508 		Constant constant_annotation = param.getAnnotation(Constant.class);
509 		if ( constant_annotation != null ) {
510 			writer.print(constant_annotation.value());
511 		} else if ( auto_annotation != null && mode == Mode.NORMAL ) {
512 			Class param_type = NativeTypeTranslator.getClassFromType(auto_annotation.getAnnotationType());
513 			if ( AutoType.class.equals(param_type) ) {
514 				final AutoType auto_type_annotation = param.getAnnotation(AutoType.class);
515 				final VariableElement auto_parameter = Utils.findParameter(method, auto_type_annotation.value());
516 				final String auto_type = typeinfos_instance.get(auto_parameter).getAutoType();
517 				if ( auto_type == null ) {
518 					throw new RuntimeException("No auto type for parameter " + param.getSimpleName() + " in method " + method);
519 				}
520 				writer.print(auto_type);
521 			} else if ( AutoSize.class.equals(param_type) ) {
522 				final AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class);
523 				if ( !auto_size_annotation.useExpression() ) {
524 					final String auto_parameter_name = auto_size_annotation.value();
525 					final VariableElement auto_target_param = Utils.findParameter(method, auto_parameter_name);
526 					final TypeInfo auto_target_type_info = typeinfos_instance.get(auto_target_param);
527 					final boolean shift_remaining = !hasAnyParameterAutoTypeAnnotation(method, auto_target_param) && Utils.isParameterMultiTyped(auto_target_param);
528 					int shifting = 0;
529 					if ( shift_remaining ) {
530 						shifting = getBufferElementSizeExponent(auto_target_type_info.getType());
531 						if ( shifting > 0 ) {
532 							writer.print("(");
533 						}
534 					}
535 					if ( auto_size_annotation.canBeNull() ) {
536 						writer.print("(" + auto_parameter_name + " == null ? 0 : " + auto_parameter_name + ".remaining())");
537 					} else {
538 						writer.print(auto_parameter_name + ".remaining()");
539 					}
540 					// Shift the remaining if the target parameter is multityped and there's no AutoType to track type
541 					if ( shift_remaining && shifting > 0 ) {
542 						writer.print(" << " + shifting);
543 						writer.print(")");
544 					}
545 				}
546 				writer.print(auto_size_annotation.expression());
547 			} else {
548 				throw new RuntimeException("Unknown auto annotation " + param_type);
549 			}
550 		} else {
551 			if ( mode == Mode.BUFFEROBJECT && param.getAnnotation(BufferObject.class) != null ) {
552 				writer.print(param.getSimpleName() + Utils.BUFFER_OBJECT_PARAMETER_POSTFIX);
553 			} else {
554 				Class type = typeinfos_instance.get(param).getType();
555 				Check check_annotation = param.getAnnotation(Check.class);
556 				boolean hide_buffer = mode == Mode.AUTOS && getAutoTypeParameter(method, param) != null;
557 				if ( hide_buffer ) {
558 					writer.print("0L");
559 				} else {
560 					if ( type == CharSequence.class || type == CharSequence[].class ) {
561 						final String offset = Utils.getStringOffset(method, param);
562 
563 						writer.print("APIUtil.getBuffer");
564 						if ( param.getAnnotation(NullTerminated.class) != null ) {
565 							writer.print("NT");
566 						}
567 						writer.print('(');
568 						writer.print(type_map.getAPIUtilParam(true));
569 						writer.print(param.getSimpleName());
570 						if ( offset != null ) {
571 							writer.print(", " + offset);
572 						}
573 						writer.print(")");
574 					} else {
575 						final AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class);
576 						if ( auto_size_annotation != null ) {
577 							writer.print(auto_size_annotation.value() + "_");
578 						}
579 
580 						final Class buffer_type = Utils.getNIOBufferType(param.asType());
581 						if ( buffer_type == null ) {
582 							writer.print(param.getSimpleName());
583 						} else {
584 							writer.print("MemoryUtil.getAddress");
585 							if ( check_annotation != null && check_annotation.canBeNull() ) {
586 								writer.print("Safe");
587 							}
588 							writer.print("(");
589 							writer.print(param.getSimpleName());
590 							writer.print(")");
591 						}
592 					}
593 				}
594 				if ( type != long.class ) {
595 					PointerWrapper pointer_annotation = param.getAnnotation(PointerWrapper.class);
596 					if ( pointer_annotation != null ) {
597 						if ( pointer_annotation.canBeNull() ) {
598 							writer.print(" == null ? 0 : " + param.getSimpleName());
599 						}
600 						writer.print(".getPointer()");
601 					}
602 				}
603 			}
604 		}
605 		return false;
606 	}
607 
printMethodCallArguments(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, TypeMap type_map)608 	private static boolean printMethodCallArguments(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos_instance, Mode mode, TypeMap type_map) {
609 		boolean first_parameter = true;
610 		for ( VariableElement param : method.getParameters() ) {
611 			if ( param.getAnnotation(Result.class) != null || (param.getAnnotation(Helper.class) != null && !param.getAnnotation(Helper.class).passToNative()) ) {
612 				continue;
613 			}
614 
615 			final Constant constant_annotation = param.getAnnotation(Constant.class);
616 			if ( constant_annotation == null || !constant_annotation.isNative() ) {
617 				first_parameter = printMethodCallArgument(writer, method, param, typeinfos_instance, mode, first_parameter, type_map);
618 			}
619 		}
620 		if ( Utils.getNIOBufferType(Utils.getMethodReturnType(method)) != null ) {
621 			if ( method.getAnnotation(CachedResult.class) != null && method.getAnnotation(CachedResult.class).isRange() ) {
622 				first_parameter = false;
623 				Utils.printExtraCallArguments(writer, method, "");
624 			} else {
625 				AutoSize auto_size_annotation = method.getAnnotation(AutoSize.class);
626 				if ( auto_size_annotation == null || !auto_size_annotation.isNative() ) {
627 					if ( !first_parameter ) {
628 						writer.print(", ");
629 					}
630 					first_parameter = false;
631 
632 					String result_size_expression;
633 					if ( mode == Mode.CACHEDRESULT ) {
634 						result_size_expression = Utils.CACHED_BUFFER_LENGTH_NAME;
635 					} else if ( auto_size_annotation == null ) {
636 						result_size_expression = Utils.RESULT_SIZE_NAME;
637 					} else {
638 						result_size_expression = auto_size_annotation.value();
639 					}
640 
641 					Utils.printExtraCallArguments(writer, method, result_size_expression);
642 				}
643 			}
644 		}
645 		return first_parameter;
646 	}
647 
printParameterCaching(PrintWriter writer, TypeElement interface_decl, ExecutableElement method, Mode mode, boolean context_specific)648 	private static void printParameterCaching(PrintWriter writer, TypeElement interface_decl, ExecutableElement method, Mode mode, boolean context_specific) {
649 		for ( VariableElement param : method.getParameters() ) {
650 			Class java_type = Utils.getJavaType(param.asType());
651 			CachedReference cachedReference = param.getAnnotation(CachedReference.class);
652 			if ( Buffer.class.isAssignableFrom(java_type)
653 			     && cachedReference != null
654 			     && (mode != Mode.BUFFEROBJECT || param.getAnnotation(BufferObject.class) == null)
655 			     && param.getAnnotation(Result.class) == null ) {
656 				writer.print("\t\tif ( LWJGLUtil.CHECKS ) StateTracker.");
657 				if ( context_specific ) {
658 					writer.print("getReferences(caps).");
659 				} else {
660 					writer.print("getTracker().");
661 				}
662 				if ( cachedReference.name().length() > 0 ) {
663 					writer.print(cachedReference.name());
664 				} else {
665 					writer.print(Utils.getReferenceName(interface_decl, method, param));
666 				}
667 				if ( cachedReference.index().length() > 0 ) {
668 					writer.print("[" + cachedReference.index() + "]");
669 				}
670 				writer.println(" = " + param.getSimpleName() + ";");
671 			}
672 		}
673 	}
674 
printParameterChecks(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos, Mode mode, final boolean generate_error_checks)675 	private static void printParameterChecks(PrintWriter writer, ExecutableElement method, Map<VariableElement, TypeInfo> typeinfos, Mode mode, final boolean generate_error_checks) {
676 		if ( mode == Mode.NORMAL ) {
677 			final GenerateAutos gen_autos_annotation = method.getAnnotation(GenerateAutos.class);
678 			if ( gen_autos_annotation != null && gen_autos_annotation.sizeVariables().length > 0 ) {
679 				// For the auto-generated parameters, declare and init a size variable (that can be reused by @Code)
680 				for ( final VariableElement param : method.getParameters() ) {
681 					if ( Arrays.binarySearch(gen_autos_annotation.sizeVariables(), param.getSimpleName().toString()) >= 0 ) {
682 						final int shifting = getBufferElementSizeExponent(typeinfos.get(param).getType());
683 						final Check check_annotation = param.getAnnotation(Check.class);
684 
685 						writer.print("\t\tlong " + param.getSimpleName() + "_size = ");
686 						if ( check_annotation == null || !check_annotation.canBeNull() ) {
687 							writer.println(param.getSimpleName() + ".remaining() << " + shifting + ";");
688 						} else {
689 							writer.println(param.getSimpleName() + " == null ? 0 : " + param.getSimpleName() + ".remaining() << " + shifting + ";");
690 						}
691 					}
692 				}
693 			}
694 		}
695 
696 		for ( VariableElement param : method.getParameters() ) {
697 			Class java_type = Utils.getJavaType(param.asType());
698 			if ( java_type.isArray() || (Utils.isAddressableType(java_type)
699 			                             && (mode != Mode.BUFFEROBJECT || param.getAnnotation(BufferObject.class) == null)
700 			                             && (mode != Mode.AUTOS || getAutoTypeParameter(method, param) == null)
701 			                             && param.getAnnotation(Result.class) == null
702 			                             && !Utils.isReturnParameter(method, param)) ) {
703 				String check_value = null;
704 				boolean can_be_null = false;
705 				Check check_annotation = param.getAnnotation(Check.class);
706 				if ( check_annotation != null ) {
707 					check_value = check_annotation.value();
708 					can_be_null = check_annotation.canBeNull();
709 				}
710 				if ( (Buffer.class.isAssignableFrom(java_type) || PointerBuffer.class.isAssignableFrom(java_type)) && param.getAnnotation(Constant.class) == null ) {
711 					TypeInfo typeinfo = typeinfos.get(param);
712 					printParameterCheck(writer, method, param.getSimpleName().toString(), typeinfo.getType().getSimpleName(), check_value, can_be_null, param.getAnnotation(NullTerminated.class), generate_error_checks);
713 				} else if ( String.class.equals(java_type) ) {
714 					if ( !can_be_null ) {
715 						writer.println("\t\tBufferChecks.checkNotNull(" + param.getSimpleName() + ");");
716 					}
717 				} else if ( java_type.isArray() ) {
718 					printArrayParameterCheck(writer, param.getSimpleName().toString(), check_value, can_be_null);
719 				}
720 			}
721 		}
722 		if ( method.getAnnotation(CachedResult.class) != null ) {
723 			printParameterCheck(writer, method, Utils.CACHED_BUFFER_NAME, null, null, true, null, generate_error_checks);
724 		}
725 	}
726 
printParameterCheck(PrintWriter writer, ExecutableElement method, String name, String type, String check_value, boolean can_be_null, NullTerminated null_terminated, boolean generate_error_checks)727 	private static void printParameterCheck(PrintWriter writer, ExecutableElement method, String name, String type, String check_value, boolean can_be_null, NullTerminated null_terminated, boolean generate_error_checks) {
728 		String tabs;
729 		if ( can_be_null ) {
730 			writer.print("\t\tif (" + name + " != null)");
731 			if ( null_terminated != null ) {
732 				writer.println(" {");
733 			} else {
734 				writer.println();
735 			}
736 			tabs = "\t\t\t";
737 		} else {
738 			tabs = "\t\t";
739 		}
740 		writer.print(tabs + "BufferChecks.check");
741 		if ( check_value != null && check_value.length() > 0 ) {
742 			writer.print("Buffer");
743 			if ( "Buffer".equals(type) ) {
744 				writer.print("Size"); // Check size only, Buffer.isDirect() was added in 1.6, cannot use yet. TODO: Remove?
745 			}
746 			writer.print("(" + name + ", " + check_value);
747 		} else {
748 			writer.print("Direct(" + name);
749 		}
750 		writer.println(");");
751 		if ( can_be_null && generate_error_checks ) {
752 			final Check check_annotation = method.getAnnotation(Check.class);
753 			if ( check_annotation != null && check_annotation.value().equals(name) ) {
754 				writer.println("\t\telse");
755 				writer.println("\t\t\t" + name + " = APIUtil.getBufferIntDebug();"); // Use an exclusive buffer here
756 			}
757 		}
758 		if ( null_terminated != null ) {
759 			writer.print(tabs + "BufferChecks.checkNullTerminated(");
760 			writer.print(name);
761 			if ( null_terminated.value().length() > 0 ) {
762 				writer.print(", ");
763 				writer.print(null_terminated.value());
764 			}
765 			writer.println(");");
766 			if ( can_be_null ) {
767 				writer.println("\t\t}");
768 			}
769 		}
770 	}
771 
printArrayParameterCheck(PrintWriter writer, String name, String check_value, boolean can_be_null)772 	private static void printArrayParameterCheck(PrintWriter writer, String name, String check_value, boolean can_be_null) {
773 		String tabs;
774 		if ( can_be_null ) {
775 			writer.println("\t\tif (" + name + " != null)");
776 			tabs = "\t\t\t";
777 		} else {
778 			tabs = "\t\t";
779 		}
780 
781 		writer.print(tabs + "BufferChecks.checkArray(" + name);
782 		if ( check_value != null && check_value.length() > 0 ) {
783 			writer.print(", " + check_value);
784 		}
785 		writer.println(");");
786 	}
787 
getResultType(ExecutableElement method, boolean native_stub)788 	private static String getResultType(ExecutableElement method, boolean native_stub) {
789 		if ( native_stub && method.getAnnotation(PointerWrapper.class) != null ) {
790 			return "long";
791 		} else if ( !native_stub && method.getAnnotation(GLreturn.class) != null ) {
792 			return Utils.getMethodReturnType(method, method.getAnnotation(GLreturn.class), false);
793 		} else {
794 			return Utils.getJavaType(Utils.getMethodReturnType(method)).getSimpleName();
795 		}
796 	}
797 
getDefaultResultValue(ExecutableElement method)798 	private static String getDefaultResultValue(ExecutableElement method) {
799 		if ( method.getAnnotation(GLreturn.class) != null ) {
800 			final String type = Utils.getMethodReturnType(method, method.getAnnotation(GLreturn.class), false);
801 			if ( "boolean".equals(type) ) {
802 				return "false";
803 			} else if ( Character.isLowerCase(type.charAt(0)) ) {
804 				return "0";
805 			} else {
806 				return "null";
807 			}
808 		} else {
809 			final Class type = Utils.getJavaType(Utils.getMethodReturnType(method));
810 			if ( type.isPrimitive() ) {
811 				if ( type == boolean.class ) {
812 					return "false";
813 				} else {
814 					return "0";
815 				}
816 			} else {
817 				return "null";
818 			}
819 		}
820 	}
821 
822 }
823