gt: 				if (Buffer.class.isAssignableFrom(java_type)) {
context: /*
   * Copyright (c) 2002-2008 LWJGL Project
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met:
   *
   * * Redistributions of source code must retain the above copyright
   *   notice, this list of conditions and the following disclaimer.
   *
   * * Redistributions in binary form must reproduce the above copyright
   *   notice, this list of conditions and the following disclaimer in the
   *   documentation and/or other materials provided with the distribution.
   *
   * * Neither the name of 'LWJGL' nor the names of
   *   its contributors may be used to endorse or promote products derived
   *   from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  package org.lwjgl.util.generator;
  
  /**
   *
   * This class generates the methods in the generated java source files.
   *
   * @author elias_naur <elias_naur@users.sourceforge.net>
   * @version $Revision: 3602 $
   * $Id: JavaMethodsGenerator.java 3602 2011-07-29 11:30:14Z spasi $
   */
  
  import org.lwjgl.PointerBuffer;
  import org.lwjgl.util.generator.opengl.GLreturn;
  
  import com.sun.mirror.apt.*;
  import com.sun.mirror.declaration.*;
  import com.sun.mirror.type.*;
  
  import java.io.*;
  import java.util.*;
  import java.nio.*;
  
  public class JavaMethodsGenerator {
  	private static final String SAVED_PARAMETER_POSTFIX = "_saved";
  
  	public static void generateMethodsJava(AnnotationProcessorEnvironment env, TypeMap type_map, PrintWriter writer, InterfaceDeclaration interface_decl, boolean generate_error_checks, boolean context_specific) {
  		for (MethodDeclaration method : interface_decl.getMethods())
  			generateMethodJava(env, type_map, writer, interface_decl, method, generate_error_checks, context_specific);
  	}
  
  	private static void generateMethodJava(AnnotationProcessorEnvironment env, TypeMap type_map, PrintWriter writer, InterfaceDeclaration interface_decl, MethodDeclaration method, boolean generate_error_checks, boolean context_specific) {
  		writer.println();
  		if (Utils.isMethodIndirect(generate_error_checks, context_specific, method)) {
  			if (method.getAnnotation(GenerateAutos.class) != null) {
  				printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.AUTOS, generate_error_checks, context_specific);
  			}
  			Collection<Map<ParameterDeclaration, TypeInfo>> cross_product = TypeInfo.getTypeInfoCrossProduct(type_map, method);
  			for (Map<ParameterDeclaration, TypeInfo> typeinfos_instance : cross_product) {
  				printMethodWithMultiType(env, type_map, writer, interface_decl, method, typeinfos_instance, Mode.NORMAL, generate_error_checks, context_specific);
  			}
  		}
  		if ( method.getAnnotation(CachedResult.class) != null && !method.getAnnotation(CachedResult.class).isRange() ) {
  			printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.CACHEDRESULT, generate_error_checks, context_specific);
  		}
  
  		Reuse reuse_annotation = method.getAnnotation(Reuse.class);
  		Alternate alt_annotation = method.getAnnotation(Alternate.class);
  		if ( alt_annotation == null || (alt_annotation.nativeAlt() && !alt_annotation.skipNative()) ) {
  			if ( alt_annotation != null && method.getSimpleName().equals(alt_annotation.value()) )
  				throw new RuntimeException("An alternate function with native code should have a different name than the main function.");
  
  			if ( reuse_annotation == null )
  				printJavaNativeStub(writer, method, Mode.NORMAL, generate_error_checks, context_specific);
  
  			if (Utils.hasMethodBufferObjectParameter(method)) {
  				printMethodWithMultiType(env, type_map, writer, interface_decl, method, TypeInfo.getDefaultTypeInfoMap(method), Mode.BUFFEROBJECT, generate_error_checks, context_specific);
  				if ( reuse_annotation == null )
  					printJavaNativeStub(writer, method, Mode.BUFFEROBJECT, generate_error_checks, context_specific);
  			}
  		}
  	}
  
  	private static void printJavaNativeStub(PrintWriter writer, MethodDeclaration method, Mode mode, boolean generate_error_checks, boolean context_specific) {
  		if (Utils.isMethodIndirect(generate_error_checks, context_specific, method)) {
  			writer.print("\tstatic native ");
  		} else {
  			Utils.printDocComment(writer, method);
  			writer.print("\tpublic static native ");
  		}
  		writer.print(getResultType(method, true));
  		writer.print(" " + Utils.getSimpleNativeMethodName(method, generate_error_checks, context_specific));
  		if (mode == Mode.BUFFEROBJECT)
  			writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX);
  		writer.print("(");
  		boolean first_parameter = generateParametersJava(writer, method, TypeInfo.getDefaultTypeInfoMap(method), true, true, mode);
  		if (context_specific) {
  			if (!first_parameter)
  				writer.print(", ");
  			writer.print("long " + Utils.FUNCTION_POINTER_VAR_NAME);
  		}
  		writer.println(");");
  	}
  
  	private static boolean generateParametersJava(PrintWriter writer, MethodDeclaration method, Map<ParameterDeclaration, TypeInfo> typeinfos_instance, boolean native_stub, final boolean printTypes, Mode mode) {
  		boolean first_parameter = true;
  		for (ParameterDeclaration param : method.getParameters()) {
  			if ( native_stub && (param.getAnnotation(Helper.class) != null && !param.getAnnotation(Helper.class).passToNative()) )
  				continue;
  			final Constant constant_annotation = param.getAnnotation(Constant.class);
  			if ( constant_annotation != null && constant_annotation.isNative() )
  				continue;
  			AnnotationMirror auto_annotation_mirror = Utils.getParameterAutoAnnotation(param);
  			boolean hide_auto_parameter = mode == Mode.NORMAL && !native_stub && auto_annotation_mirror != null;
  			if (hide_auto_parameter) {
  				AutoType auto_type_annotation = param.getAnnotation(AutoType.class);
  				if (auto_type_annotation != null) {
  					ParameterDeclaration auto_parameter = Utils.findParameter(method, auto_type_annotation.value());
  					TypeInfo auto_param_type_info = typeinfos_instance.get(auto_parameter);
  					if (auto_param_type_info.getSignedness() == Signedness.BOTH) {
  						if (!first_parameter)
  							writer.print(", ");
  						first_parameter = false;
  						if ( printTypes )
  							writer.print("boolean ");
  						writer.print(TypeInfo.UNSIGNED_PARAMETER_NAME);
  					}
  				}
  			} else if (
  					param.getAnnotation(Result.class) == null
  			            && (native_stub || ((param.getAnnotation(Constant.class) == null || param.getAnnotation(Constant.class).keepParam()) && !Utils.isReturnParameter(method, param)))
  					    && (mode != Mode.AUTOS || getAutoTypeParameter(method, param) == null)
  				) {
  				first_parameter = generateParameterJava(writer, param, typeinfos_instance.get(param), native_stub, printTypes, first_parameter, mode);
  			}
  		}
  		CachedResult cached_result_annotation = method.getAnnotation(CachedResult.class);
  		TypeMirror result_type = Utils.getMethodReturnType(method);
  		if ((native_stub && Utils.getNIOBufferType(result_type) != null) || Utils.needResultSize(method)) {
  			AutoSize auto_size_annotation = method.getAnnotation(AutoSize.class);
  			if ( auto_size_annotation == null || !auto_size_annotation.isNative() ) {
  				if (cached_result_annotation == null || !cached_result_annotation.isRange()) {
  					if (!first_parameter)
  						writer.print(", ");
  					first_parameter = false;
  					if ( printTypes )
  						writer.print("long ");
  					writer.print(Utils.RESULT_SIZE_NAME);
  				}
  			}
  		}
  		if (cached_result_annotation != null) {
  			if (!first_parameter)
  				writer.print(", ");
  
  			if ( mode == Mode.CACHEDRESULT ) {
  				if ( printTypes )
  					writer.print("long ");
  				writer.print(Utils.CACHED_BUFFER_LENGTH_NAME + ", ");
  			}
  
  			first_parameter = false;
  			if ( printTypes )
  				writer.print(getResultType(method, native_stub));
  			writer.print(" " + Utils.CACHED_BUFFER_NAME);
  		}
  		return first_parameter;
  	}
  
  	private static boolean generateParameterJava(PrintWriter writer, ParameterDeclaration param, TypeInfo type_info, boolean native_stub, final boolean printTypes, boolean first_parameter, Mode mode) {
  		Class buffer_type = Utils.getNIOBufferType(param.getType());
  		if (!first_parameter)
  			writer.print(", ");
  		BufferObject bo_annotation = param.getAnnotation(BufferObject.class);
  		if (bo_annotation != null && mode == Mode.BUFFEROBJECT) {
  			if (buffer_type == null)
  				throw new RuntimeException("type of " + param + " is not a nio Buffer parameter but is annotated as buffer object");
  			if ( printTypes )
  				writer.print("long ");
  			writer.print(param.getSimpleName() + Utils.BUFFER_OBJECT_PARAMETER_POSTFIX);
  		} else {
  			if ( native_stub && param.getAnnotation(PointerWrapper.class) != null )
  				writer.print("long ");
  			else {
  				Class type = type_info.getType();
  				if ( native_stub && (type == CharSequence.class || type == CharSequence[].class || type == PointerBuffer.class || Buffer.class.isAssignableFrom(type) ) )
  					writer.print("long ");
  				else if ( printTypes )
  					writer.print(type_info.getType().getSimpleName() + " ");
  			}
  			AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class);
  			if ( auto_size_annotation != null )
  				writer.print(auto_size_annotation.value() + "_");
  			writer.print(param.getSimpleName());
  		}
  		return false;
  	}
  
  	private static void printBufferObjectCheck(PrintWriter writer, BufferKind kind, Mode mode, boolean context_specific) {
  		String bo_check_method_name = kind.toString();
  		writer.print("\t\t" + Utils.CHECKS_CLASS_NAME + ".ensure" + bo_check_method_name);
  		if (mode == Mode.BUFFEROBJECT)
  			writer.print("enabled");
  		else
  			writer.print("disabled");
  
  		if ( context_specific )
  			writer.println("(caps);");
  		else
  			writer.println("();");
  	}
  
  	private static void printBufferObjectChecks(PrintWriter writer, MethodDeclaration method, Mode mode, boolean context_specific) {
  		EnumSet<BufferKind> check_set = EnumSet.noneOf(BufferKind.class);
  		for (ParameterDeclaration param : method.getParameters()) {
  			BufferObject bo_annotation = param.getAnnotation(BufferObject.class);
  			if (bo_annotation != null)
  				check_set.add(bo_annotation.value());
  		}
  		for (BufferKind kind : check_set)
  			printBufferObjectCheck(writer, kind, mode, context_specific);
  	}
  
  	private static void printMethodWithMultiType(AnnotationProcessorEnvironment env, TypeMap type_map, PrintWriter writer, InterfaceDeclaration interface_decl, MethodDeclaration method, Map<ParameterDeclaration, TypeInfo> typeinfos_instance, Mode mode, boolean generate_error_checks, boolean context_specific) {
  		Utils.printDocComment(writer, method);
  		if ( interface_decl.getAnnotation(Private.class) == null && method.getAnnotation(Private.class) == null )
  			writer.print("\tpublic static ");
  		else
  			writer.print("\tstatic ");
  		writer.print(getResultType(method, false));
  		StripPostfix strip_annotation = method.getAnnotation(StripPostfix.class);
  		String method_name;
  		Alternate alt_annotation = method.getAnnotation(Alternate.class);
  		method_name = alt_annotation == null || alt_annotation.javaAlt() ? method.getSimpleName() : alt_annotation.value();
  		if (strip_annotation != null && mode == Mode.NORMAL)
  			method_name = getPostfixStrippedName(type_map, interface_decl, method);
  		writer.print(" " + method_name + "(");
  		generateParametersJava(writer, method, typeinfos_instance, false, true, mode);
  		writer.println(") {");
  
  		final TypeMirror result_type = Utils.getMethodReturnType(method);
  		boolean has_result = !result_type.equals(env.getTypeUtils().getVoidType());
  
  		final Reuse reuse_annotation = method.getAnnotation(Reuse.class);
  		if ( reuse_annotation != null ) {
  			writer.print("\t\t");
  			if ( has_result || method.getAnnotation(GLreturn.class) != null )
  				writer.print("return ");
  
  			writer.print(reuse_annotation.value() + "." + method_name + "(");
  			generateParametersJava(writer, method, typeinfos_instance, false, false, mode);
  			writer.println(");\n\t}");
  			return;
  		}
  
  		if (context_specific) {
  			type_map.printCapabilitiesInit(writer);
  			writer.print("\t\tlong " + Utils.FUNCTION_POINTER_VAR_NAME + " = " + type_map.getCapabilities() + ".");
  			writer.println(Utils.getFunctionAddressName(interface_decl, method, true) + ";");
  			writer.print("\t\tBufferChecks.checkFunctionAddress(");
  			writer.println(Utils.FUNCTION_POINTER_VAR_NAME + ");");
  		}
  		final Code code_annotation = method.getAnnotation(Code.class);
  		if (code_annotation != null && code_annotation.value().length() > 0)
  			writer.println(code_annotation.value());
  		printBufferObjectChecks(writer, method, mode, context_specific);
  		printParameterChecks(writer, method, typeinfos_instance, mode, generate_error_checks);
  		printParameterCaching(writer, interface_decl, method, mode, context_specific);
  
  		if ( code_annotation != null && code_annotation.javaBeforeNative().length() > 0 )
  			writer.println(code_annotation.javaBeforeNative());
  		writer.print("\t\t");
  
  		final PointerWrapper pointer_wrapper_annotation = method.getAnnotation(PointerWrapper.class);
  		if (has_result) {
  			writer.print(getResultType(method, false) + " " + Utils.RESULT_VAR_NAME);
  
  			if ( code_annotation != null && code_annotation.tryBlock() ) {
  				writer.print(" = " + getDefaultResultValue(method));
  				writer.println(";\n\t\ttry {");
  				writer.print("\t\t\t" + Utils.RESULT_VAR_NAME);
  			}
  
  			writer.print(" = ");
  			if ( pointer_wrapper_annotation != null ) {
  				if ( pointer_wrapper_annotation.factory().length() > 0 )
  					writer.print(pointer_wrapper_annotation.factory() + "(");
  				else
  					writer.print("new " + getResultType(method, false) + "(");
  			}
  		} else if ( method.getAnnotation(GLreturn.class) != null ) {
  			has_result = true;
  			Utils.printGLReturnPre(writer, method, method.getAnnotation(GLreturn.class), type_map);
  		}
  		writer.print(Utils.getSimpleNativeMethodName(method, generate_error_checks, context_specific));
  		if (mode == Mode.BUFFEROBJECT)
  			writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX);
  		writer.print("(");
  		boolean first_parameter = printMethodCallArguments(writer, method, typeinfos_instance, mode, type_map);
  		if (context_specific) {
  			if (!first_parameter)
  				writer.print(", ");
  			writer.print(Utils.FUNCTION_POINTER_VAR_NAME);
  		}
  		if ( has_result && pointer_wrapper_annotation != null ) {
  			writer.print(")");
  			if ( pointer_wrapper_annotation.params().length() > 0 )
  				writer.print(", " + pointer_wrapper_annotation.params());
  		}
  		writer.println(");");
  
  		if ( code_annotation != null && code_annotation.javaAfterNative().length() > 0 )
  			writer.println(code_annotation.javaAfterNative());
  
  		final String tabs = code_annotation != null && code_annotation.tryBlock() ? "\t\t\t" : "\t\t";
  		if (generate_error_checks && method.getAnnotation(NoErrorCheck.class) == null)
  			type_map.printErrorCheckMethod(writer, method, tabs);
  		// DISABLED: indirect buffer support
  		//printNondirectParameterCopies(writer, method, mode);
  		if (has_result) {
  			if ( method.getAnnotation(GLreturn.class) == null ) {
  				if ( ByteBuffer.class.equals(Utils.getJavaType(result_type)) )
  					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.
  				else
  					writer.println(tabs + "return " + Utils.RESULT_VAR_NAME + ";");
  			} else
  				Utils.printGLReturnPost(writer, method, method.getAnnotation(GLreturn.class), type_map);
  		}
  
  		if ( code_annotation != null && code_annotation.tryBlock() ) {
  			writer.println("\t\t} finally {");
  			writer.println(code_annotation.javaFinally());
  			writer.println("\t\t}");
  		}
  		writer.println("\t}");
  	}
  
  	private static String getExtensionPostfix(InterfaceDeclaration interface_decl) {
  		String interface_simple_name = interface_decl.getSimpleName();
  		Extension extension_annotation = interface_decl.getAnnotation(Extension.class);
  		if (extension_annotation == null) {
  			int underscore_index = interface_simple_name.indexOf("_");
  			if (underscore_index != -1)
  				return interface_simple_name.substring(0, underscore_index);
  			else
  				return "";
  		} else
  			return extension_annotation.postfix();
  	}
  
  	private static ParameterDeclaration getAutoTypeParameter(MethodDeclaration method, ParameterDeclaration target_parameter) {
  		for (ParameterDeclaration param : method.getParameters()) {
  			AnnotationMirror auto_annotation = Utils.getParameterAutoAnnotation(param);
  			if (auto_annotation != null) {
  				Class annotation_type = NativeTypeTranslator.getClassFromType(auto_annotation.getAnnotationType());
  				String parameter_name;
  				if (annotation_type.equals(AutoType.class))
  					parameter_name = param.getAnnotation(AutoType.class).value();
  				else if (annotation_type.equals(AutoSize.class))
  					parameter_name = param.getAnnotation(AutoSize.class).value();
  				else
  					throw new RuntimeException("Unknown annotation type " + annotation_type);
  				if (target_parameter.getSimpleName().equals(parameter_name))
  					return param;
  			}
  		}
  		return null;
  	}
  
  	private static boolean hasAnyParameterAutoTypeAnnotation(MethodDeclaration method, ParameterDeclaration target_param) {
  		for (ParameterDeclaration param : method.getParameters()) {
  			AutoType auto_type_annotation = param.getAnnotation(AutoType.class);
  			if (auto_type_annotation != null) {
  				ParameterDeclaration type_target_param = Utils.findParameter(method, auto_type_annotation.value());
  				if (target_param.equals(type_target_param))
  					return true;
  			}
  		}
  		return false;
  	}
  
  	private static String getPostfixStrippedName(TypeMap type_map, InterfaceDeclaration interface_decl, MethodDeclaration method) {
  		StripPostfix strip_annotation = method.getAnnotation(StripPostfix.class);
  		ParameterDeclaration postfix_parameter = Utils.findParameter(method, strip_annotation.value());
  		String postfix = strip_annotation.postfix();
  		if ( "NULL".equals(postfix) ) {
  			PostfixTranslator translator = new PostfixTranslator(type_map, postfix_parameter);
  			postfix_parameter.getType().accept(translator);
  			postfix = translator.getSignature();
  		}
  		String method_name;
  		Alternate alt_annotation = method.getAnnotation(Alternate.class);
  		method_name = alt_annotation == null || alt_annotation.javaAlt() ? method.getSimpleName() : alt_annotation.value();
  
  		String extension_postfix = "NULL".equals(strip_annotation.extension()) ? getExtensionPostfix(interface_decl) : strip_annotation.extension();
  		String result;
  
  		if ( strip_annotation.hasPostfix() && method_name.endsWith(postfix + "v" + extension_postfix))
  			result = method_name.substring(0, method_name.length() - (postfix.length() + 1 + extension_postfix.length()));
  		else if ( strip_annotation.hasPostfix() && method_name.endsWith(postfix + extension_postfix))
  			result = method_name.substring(0, method_name.length() - (postfix.length() + extension_postfix.length()));
  		else if ( strip_annotation.hasPostfix() && method_name.endsWith(postfix + "i_v" + extension_postfix) )
  			result = method_name.substring(0, method_name.length() - (postfix.length() + 3 + extension_postfix.length()));
  		else if ( method_name.endsWith("i_v" + extension_postfix) )
  			result = method_name.substring(0, method_name.length() - (3 + extension_postfix.length()));
  		else if (method_name.endsWith("v" + extension_postfix))
  			result = method_name.substring(0, method_name.length() - (1 + extension_postfix.length()));
  		else
  			throw new RuntimeException(method + " is specified as being postfix stripped on parameter " + postfix_parameter + ", but it's postfix is not '" + postfix + "' nor 'v'");
  
  		return result + extension_postfix;
  	}
  
  	private static int getBufferElementSizeExponent(Class c) {
  		if (IntBuffer.class.equals(c))
  			return 2;
  		else if (LongBuffer.class.equals(c))
  			return 3;
  		else if (DoubleBuffer.class.equals(c))
  			return 3;
  		else if (ShortBuffer.class.equals(c))
  			return 1;
  		else if (ByteBuffer.class.equals(c))
  			return 0;
  		else if (FloatBuffer.class.equals(c))
  			return 2;
  		else
  			throw new RuntimeException(c + " is not allowed");
  	}
  
  	private static boolean printMethodCallArgument(PrintWriter writer, MethodDeclaration method, ParameterDeclaration param, Map<ParameterDeclaration, TypeInfo> typeinfos_instance, Mode mode, boolean first_parameter, TypeMap type_map) {
  		if (!first_parameter)
  			writer.print(", ");
  
  		AnnotationMirror auto_annotation = Utils.getParameterAutoAnnotation(param);
  		Constant constant_annotation = param.getAnnotation(Constant.class);
  		if (constant_annotation != null) {
  			writer.print(constant_annotation.value());
  		} else if (auto_annotation != null && mode == Mode.NORMAL) {
  			Class param_type = NativeTypeTranslator.getClassFromType(auto_annotation.getAnnotationType());
  			if (AutoType.class.equals(param_type)) {
  				final AutoType auto_type_annotation = param.getAnnotation(AutoType.class);
  				final ParameterDeclaration auto_parameter = Utils.findParameter(method, auto_type_annotation.value());
  				final String auto_type = typeinfos_instance.get(auto_parameter).getAutoType();
  				if ( auto_type == null )
  					throw new RuntimeException("No auto type for parameter " + param.getSimpleName() + " in method " + method);
  				writer.print(auto_type);
  			} else if (AutoSize.class.equals(param_type)) {
  				final AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class);
  				if ( !auto_size_annotation.useExpression() ) {
  					final String auto_parameter_name = auto_size_annotation.value();
  					final ParameterDeclaration auto_target_param = Utils.findParameter(method, auto_parameter_name);
  					final TypeInfo auto_target_type_info = typeinfos_instance.get(auto_target_param);
  					final boolean shift_remaining = !hasAnyParameterAutoTypeAnnotation(method, auto_target_param) && Utils.isParameterMultiTyped(auto_target_param);
  					int shifting = 0;
  					if ( shift_remaining ) {
  						shifting = getBufferElementSizeExponent(auto_target_type_info.getType());
  						if ( shifting > 0 )
  							writer.print("(");
  					}
  					if ( auto_size_annotation.canBeNull() )
  						writer.print("(" + auto_parameter_name + " == null ? 0 : " + auto_parameter_name + ".remaining())");
  					else
  						writer.print(auto_parameter_name + ".remaining()");
  					// Shift the remaining if the target parameter is multityped and there's no AutoType to track type
  					if (shift_remaining && shifting > 0) {
  						writer.print(" << " + shifting);
  						writer.print(")");
  					}
  				}
  				writer.print(auto_size_annotation.expression());
  			} else
  				throw new RuntimeException("Unknown auto annotation " + param_type);
  		} else {
  			if (mode == Mode.BUFFEROBJECT && param.getAnnotation(BufferObject.class) != null) {
  				writer.print(param.getSimpleName() + Utils.BUFFER_OBJECT_PARAMETER_POSTFIX);
  			} else {
  				Class type = typeinfos_instance.get(param).getType();
  				Check check_annotation = param.getAnnotation(Check.class);
  				boolean hide_buffer = mode == Mode.AUTOS && getAutoTypeParameter(method, param) != null;
  				if (hide_buffer) {
  					writer.print("0L");
  				} else {
  					if ( type == CharSequence.class || type == CharSequence[].class ) {
  						final String offset = Utils.getStringOffset(method, param);
  
  						writer.print("APIUtil.getBuffer");
  						if ( param.getAnnotation(NullTerminated.class) != null )
  							writer.print("NT");
  						writer.print('(');
  						writer.print(type_map.getAPIUtilParam(true));
  						writer.print(param.getSimpleName());
  						if ( offset != null )
  							writer.print(", " + offset);
  						writer.print(")");
  					} else {
  						final AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class);
  						if ( auto_size_annotation != null )
  							writer.print(auto_size_annotation.value() + "_");
  
  						final Class buffer_type = Utils.getNIOBufferType(param.getType());
  						if ( buffer_type == null )
  							writer.print(param.getSimpleName());
  						else {
  							writer.print("MemoryUtil.getAddress");
  							if ( check_annotation != null && check_annotation.canBeNull() )
  								writer.print("Safe");
  							writer.print("(");
  							writer.print(param.getSimpleName());
  							writer.print(")");
  						}
  					}
  				}
  				if ( type != long.class ) {
  					PointerWrapper pointer_annotation = param.getAnnotation(PointerWrapper.class);
  					if ( pointer_annotation != null ) {
  						if ( pointer_annotation.canBeNull() )
  							writer.print(" == null ? 0 : " + param.getSimpleName());
  						writer.print(".getPointer()");
  					}
  				}
  			}
  		}
  		return false;
  	}
  
  	private static boolean printMethodCallArguments(PrintWriter writer, MethodDeclaration method, Map<ParameterDeclaration, TypeInfo> typeinfos_instance, Mode mode, TypeMap type_map) {
  		boolean first_parameter = true;
  		for ( ParameterDeclaration param : method.getParameters() ) {
  			if ( param.getAnnotation(Result.class) != null || (param.getAnnotation(Helper.class) != null && !param.getAnnotation(Helper.class).passToNative()) )
  				continue;
  
  			final Constant constant_annotation = param.getAnnotation(Constant.class);
  			if ( constant_annotation== null || !constant_annotation.isNative() )
  				first_parameter = printMethodCallArgument(writer, method, param, typeinfos_instance, mode, first_parameter, type_map);
  		}
  		if (Utils.getNIOBufferType(Utils.getMethodReturnType(method)) != null) {
  			if (method.getAnnotation(CachedResult.class) != null && method.getAnnotation(CachedResult.class).isRange()) {
  				first_parameter = false;
  				Utils.printExtraCallArguments(writer, method, "");
  			} else {
  				AutoSize auto_size_annotation = method.getAnnotation(AutoSize.class);
  				if ( auto_size_annotation == null || !auto_size_annotation.isNative() ) {
  					if (!first_parameter)
  						writer.print(", ");
  					first_parameter = false;
  
  					String result_size_expression;
  					if ( mode == Mode.CACHEDRESULT )
  						result_size_expression = Utils.CACHED_BUFFER_LENGTH_NAME;
  					else if ( auto_size_annotation == null )
  						result_size_expression = Utils.RESULT_SIZE_NAME;
  					else
  						result_size_expression = auto_size_annotation.value();
  
  					Utils.printExtraCallArguments(writer, method, result_size_expression);
  				}
  			}
  		}
  		return first_parameter;
  	}
  
  	private static void printParameterCaching(PrintWriter writer, InterfaceDeclaration  interface_decl, MethodDeclaration method, Mode mode, boolean context_specific) {
  		for (ParameterDeclaration param : method.getParameters()) {
  			Class java_type = Utils.getJavaType(param.getType());
                          CachedReference cachedReference = param.getAnnotation(CachedReference.class);
  			if (Buffer.class.isAssignableFrom(java_type) &&
                                          cachedReference != null &&
  					(mode != Mode.BUFFEROBJECT || param.getAnnotation(BufferObject.class) == null) &&
  					param.getAnnotation(Result.class) == null) {
  				writer.print("\t\tif ( LWJGLUtil.CHECKS ) StateTracker.");
  				if ( context_specific )
  					writer.print("getReferences(caps).");
  				else
  					writer.print("getTracker().");
                                  if(cachedReference.name().length() > 0) {
                                      writer.print(cachedReference.name());
                                  } else {
                                      writer.print(Utils.getReferenceName(interface_decl, method, param));
                                  }
                                  if(cachedReference.index().length() > 0) {
                                      writer.print("[" + cachedReference.index() + "]");
                                  }
  				writer.println(" = " + param.getSimpleName() + ";");
  			}
  		}
  	}
  
  	private static void printNondirectParameterCopies(PrintWriter writer, MethodDeclaration method, Mode mode) {
  		for (ParameterDeclaration param : method.getParameters()) {
  			Class java_type = Utils.getJavaType(param.getType());
  			if (Utils.isAddressableType(java_type) &&
  					(mode != Mode.BUFFEROBJECT || param.getAnnotation(BufferObject.class) == null) &&
  					(mode != Mode.AUTOS || getAutoTypeParameter(method, param) == null) &&
  					param.getAnnotation(Result.class) == null) {
  