/*
* Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (c) 2010 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution 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 Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*
* Sun gratefully acknowledges that this software was originally authored
* and developed by Kenneth Bradley Russell and Christopher John Kline.
*/
package com.jogamp.gluegen.opengl;
import com.jogamp.gluegen.CodeGenUtils;
import com.jogamp.gluegen.JavaType;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.nio.Buffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class BuildComposablePipeline {
/**
Default: true
. */
public static final int GEN_DEBUG = 1 << 0;
/** Default: true
. */
public static final int GEN_TRACE = 1 << 1;
/** Default: false
. */
public static final int GEN_CUSTOM = 1 << 2;
/**
* By extra command-line argument: prolog_xor_downstream
.
*
* If true, either prolog (if exist) is called or downstream's method, but not both.
* By default, both methods would be called.
*
* Default: false
*/
public static final int GEN_PROLOG_XOR_DOWNSTREAM = 1 << 3;
/**
* By extra command-line argument: gl_identity_by_assignable_class
.
*
* If true, implementation does not utilize downstream's isGL*()
* implementation, but determines whether the GL profile is matched by interface inheritance.
*
* Default: false
*/
public static final int GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS = 1 << 4;
private static final HashMap addedGLHooks = new HashMap();
private static final String[] addedGLHookMethodNames = new String[] {
"mapBuffer", "mapBufferRange",
"mapNamedBuffer", "mapNamedBufferRange" };
static {
for(int i=0; i classToComposeAround;
private final Class> classPrologOpt;
private final Class> classDownstream;
// Only desktop OpenGL has immediate mode glBegin / glEnd
private boolean hasImmediateMode;
// Desktop OpenGL and GLES1 have GL_STACK_OVERFLOW and GL_STACK_UNDERFLOW errors
private boolean hasGL2ES1StackOverflow;
public static Class> getClass(final String name) {
Class> clazz = null;
try {
clazz = Class.forName(name);
} catch (final Exception e) {
throw new RuntimeException(
"Could not find class \"" + name + "\"", e);
}
return clazz;
}
public static Method getMethod(final Class> clazz, final Method m) {
Method res = null;
try {
res = clazz.getMethod(m.getName(), m.getParameterTypes());
} catch (final Exception e) {
}
return res;
}
public static void main(final String[] args) {
final String classToComposeAroundName = args[0];
Class> classPrologOpt, classDownstream;
final Class> classToComposeAround = getClass(classToComposeAroundName);
final String outputDir = args[1];
String outputPackage, outputName;
int mode;
if (args.length > 2) {
final String outputClazzName = args[2];
outputPackage = getPackageName(outputClazzName);
outputName = getBaseClassName(outputClazzName);
classPrologOpt = getClass(args[3]);
classDownstream = getClass(args[4]);
mode = GEN_CUSTOM;
if (args.length > 5) {
for(int i=5; i classToComposeAround, final Class> classPrologOpt, final Class> classDownstream) {
this.mode = mode;
this.outputDir = outputDir;
this.outputPackage = outputPackage;
this.outputName = outputName;
this.classToComposeAround = classToComposeAround;
this.classPrologOpt = classPrologOpt;
this.classDownstream = classDownstream;
if (!classToComposeAround.isInterface()) {
throw new IllegalArgumentException(
classToComposeAround.getName() + " is not an interface class");
}
try {
// Keep assignment w/ null comparison for clarification.
// If no exception is thrown, return value is always non-null;
hasImmediateMode =
null != classToComposeAround.getMethod("glBegin", new Class>[]{Integer.TYPE});
} catch (final Exception e) {
}
try {
hasGL2ES1StackOverflow = hasImmediateMode &&
(classToComposeAround.getField("GL_STACK_OVERFLOW") != null);
} catch (final Exception e) {
}
}
/**
* Emit the java source code for the classes that comprise the composable
* pipeline.
*/
public void emit() throws IOException {
final List publicMethodsRaw = Arrays.asList(classToComposeAround.getMethods());
final Set publicMethodsPlainSet = new HashSet();
for (final Iterator iter = publicMethodsRaw.iterator(); iter.hasNext();) {
final Method method = iter.next();
// Don't hook methods which aren't real GL methods,
// such as the synthetic "isGL2ES2" "getGL2ES2"
final String name = method.getName();
if ( !name.equals("getDownstreamGL") &&
!name.equals("toString") ) {
final boolean syntheticIsGL = name.startsWith("isGL");
final boolean syntheticGetGL = name.startsWith("getGL");
final boolean runHooks = name.startsWith("gl") || syntheticIsGL || syntheticGetGL || addedGLHooks.containsKey(name);
publicMethodsPlainSet.add(new PlainMethod(method, runHooks, syntheticIsGL, syntheticGetGL));
}
}
// sort methods to make them easier to find
final List publicMethodsPlainSorted = new ArrayList();
publicMethodsPlainSorted.addAll(publicMethodsPlainSet);
Collections.sort(publicMethodsPlainSorted, new Comparator() {
@Override
public int compare(final PlainMethod o1, final PlainMethod o2) {
return o1.getWrappedMethod().getName().compareTo(o2.getWrappedMethod().getName());
}
});
if (0 != (mode & GEN_DEBUG)) {
(new DebugPipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlainSorted.iterator());
}
if (0 != (mode & GEN_TRACE)) {
(new TracePipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlainSorted.iterator());
}
if (0 != (mode & GEN_CUSTOM)) {
(new CustomPipeline(mode, outputDir, outputPackage, outputName, classToComposeAround, classPrologOpt, classDownstream)).emit(publicMethodsPlainSorted.iterator());
}
}
public static String getPackageName(final String clazzName) {
final int lastDot = clazzName.lastIndexOf('.');
if (lastDot == -1) {
// no package, class is at root level
return null;
}
return clazzName.substring(0, lastDot);
}
public static String getBaseClassName(final String clazzName) {
final int lastDot = clazzName.lastIndexOf('.');
if (lastDot == -1) {
// no package, class is at root level
return clazzName;
}
return clazzName.substring(lastDot + 1);
}
//-------------------------------------------------------
protected static class PlainMethod {
final Method m;
final boolean runHooks;
final boolean isSynthethicIsGL;
final boolean isSynthethicGetGL;
PlainMethod(final Method m, final boolean runHooks, final boolean isSynthethicIsGL, final boolean isSynthethicGetGL) {
this.m = m;
this.runHooks = runHooks;
this.isSynthethicIsGL = isSynthethicIsGL;
this.isSynthethicGetGL = isSynthethicGetGL;
}
public Method getWrappedMethod() {
return m;
}
public boolean runHooks() {
return runHooks;
}
public boolean isSynthetic() { return isSynthethicIsGL || isSynthethicGetGL; }
public boolean isSyntheticIsGL() { return isSynthethicIsGL; }
public boolean isSyntheticGetGL() { return isSynthethicGetGL; }
@Override
public boolean equals(final Object obj) {
if (obj instanceof PlainMethod) {
final PlainMethod b = (PlainMethod) obj;
final boolean res =
m.getName().equals(b.m.getName())
&& m.getModifiers() == b.m.getModifiers()
&& m.getReturnType().equals(b.m.getReturnType())
&& Arrays.equals(m.getParameterTypes(), b.m.getParameterTypes());
return res;
}
return false;
}
@Override
public int hashCode() {
int hash = m.getName().hashCode() ^ m.getModifiers() ^ m.getReturnType().hashCode();
final Class>[] args = m.getParameterTypes();
for (int i = 0; i < args.length; i++) {
hash ^= args[i].hashCode();
}
return hash;
}
@Override
public String toString() {
final Class>[] args = m.getParameterTypes();
final StringBuilder argsString = new StringBuilder();
argsString.append("(");
for (int i = 0; i < args.length; i++) {
if (i > 0) {
argsString.append(", ");
}
argsString.append(args[i].getName());
}
argsString.append(")");
return m.toString()
+ "\n\tname: " + m.getName()
+ "\n\tsynt: isGL " + isSynthethicIsGL+", getGL "+isSynthethicGetGL
+ "\n\tmods: " + m.getModifiers()
+ "\n\tretu: " + m.getReturnType()
+ "\n\targs[" + args.length + "]: " + argsString.toString();
}
}
/**
* Emits a Java source file that represents one element of the composable
* pipeline.
*/
protected abstract class PipelineEmitter {
private File file;
protected String basePackage;
protected String baseName; // does not include package!
protected String downstreamPackage;
protected String downstreamName; // does not include package!
protected String prologPackageOpt = null;
protected String prologNameOpt = null; // does not include package!
protected String outputDir;
protected String outputPackage;
protected Class> baseInterfaceClass;
protected Class> prologClassOpt = null;
protected Class> downstreamClass;
/**
* @param outputDir the directory into which the pipeline classes will be
* generated.
* @param baseInterfaceClassName the full class name (including package,
* e.g. "java.lang.String") of the interface that the pipeline wraps
* @exception IllegalArgumentException if classToComposeAround is not an
* interface.
*/
PipelineEmitter(final String outputDir, final String outputPackage, final Class> baseInterfaceClass, final Class> prologClassOpt, final Class> downstreamClass) {
this.outputDir = outputDir;
this.outputPackage = outputPackage;
this.baseInterfaceClass = baseInterfaceClass;
this.prologClassOpt = prologClassOpt;
this.downstreamClass = downstreamClass;
basePackage = getPackageName(baseInterfaceClass.getName());
baseName = getBaseClassName(baseInterfaceClass.getName());
downstreamPackage = getPackageName(downstreamClass.getName());
downstreamName = getBaseClassName(downstreamClass.getName());
if (null != prologClassOpt) {
prologPackageOpt = getPackageName(prologClassOpt.getName());
prologNameOpt = getBaseClassName(prologClassOpt.getName());
}
}
public void emit(final Iterator methodsToWrap) throws IOException {
final String outputClassName = getOutputName();
this.file = new File(outputDir + File.separatorChar + outputClassName + ".java");
final String parentDir = file.getParent();
if (parentDir != null) {
final File pDirFile = new File(parentDir);
pDirFile.mkdirs();
}
final PrintWriter output = new PrintWriter(new BufferedWriter(new FileWriter(file)));
final List> baseInterfaces = Arrays.asList(baseInterfaceClass.getInterfaces());
final HashSet> clazzList = new HashSet>();
clazzList.add(baseInterfaceClass);
clazzList.addAll(baseInterfaces);
final int ifNamesNumber = clazzList.size();
// keep original order ..
clazzList.clear();
final String[] ifNames = new String[ifNamesNumber];
{
int i = 0;
for (final Iterator> iter = baseInterfaces.iterator(); iter.hasNext();) {
final Class> ifClass = iter.next();
if (!clazzList.contains(ifClass)) {
ifNames[i++] = ifClass.getName();
clazzList.add(ifClass);
}
}
if ( !clazzList.contains(baseInterfaceClass) ) {
ifNames[i++] = baseInterfaceClass.getName();
clazzList.add(baseInterfaceClass);
}
}
clazzList.add(downstreamClass);
if (null != prologClassOpt) {
clazzList.add(prologClassOpt);
}
final ArrayList imports = new ArrayList();
imports.add("java.io.*");
imports.add("javax.media.opengl.*");
imports.add("com.jogamp.gluegen.runtime.*");
imports.add(Buffer.class.getPackage().getName()+".*");
for (final Class> clasS : clazzList) {
imports.add(clasS.getName());
}
CodeGenUtils.emitJavaHeaders(output,
outputPackage,
outputClassName,
true,
imports,
new String[]{"public"},
ifNames,
null,
new CodeGenUtils.EmissionCallback() {
@Override
public void emit(final PrintWriter w) {
emitClassDocComment(w);
}
});
preMethodEmissionHook(output);
constructorHook(output);
emitSyntheticGLMethods(output);
while (methodsToWrap.hasNext()) {
final PlainMethod pm = methodsToWrap.next();
final Method m = pm.getWrappedMethod();
emitMethodDocComment(output, m);
emitSignature(output, m);
emitBody(output, pm);
}
postMethodEmissionHook(output);
output.println();
output.print(" private " + downstreamName + " " + getDownstreamObjectName() + ";");
// end the class
output.println();
output.print("} // end class ");
output.println(outputClassName);
output.flush();
output.close();
System.out.println("wrote to file: " + file);
}
/** Get the name of the object through which API calls should be routed. */
protected String getDownstreamObjectName() {
return "downstream" + downstreamName;
}
/** Get the name of the object which shall be called as a prolog. */
protected String getPrologObjectNameOpt() {
if (null != prologNameOpt) {
return "prolog" + prologNameOpt;
}
return null;
}
protected void emitMethodDocComment(final PrintWriter output, final Method m) {
}
protected void emitSignature(final PrintWriter output, final Method m) {
output.format(" @Override%n public %s %s(%s)%n",
JavaType.createForClass(m.getReturnType()).getName(),
m.getName(),
getArgListAsString(m, true, true));
}
protected void emitBody(final PrintWriter output, final PlainMethod pm) {
final boolean runHooks = pm.runHooks();
final Method m = pm.getWrappedMethod();
output.println(" {");
final Class> retType = m.getReturnType();
final boolean callPreDownstreamHook = runHooks && hasPreDownstreamCallHook(pm);
final boolean callPostDownstreamHook = runHooks && hasPostDownstreamCallHook(pm);
final boolean callDownstream = (null != getMethod(downstreamClass, m))
&& !(0 != (GEN_PROLOG_XOR_DOWNSTREAM & getMode()) && callPreDownstreamHook);
final boolean hasResult = (retType != Void.TYPE);
if (!callDownstream) {
if (!emptyDownstreamAllowed()) {
throw new RuntimeException("Method " + m + " has no downstream (" + downstreamName + ")");
}
}
if (!callPreDownstreamHook && !callPostDownstreamHook && !callDownstream) {
if (!emptyMethodAllowed()) {
throw new RuntimeException("Method " + m + " is empty, no downstream (" + downstreamName + ") nor prolog (" + prologNameOpt + ").");
} else {
output.print(" if(DEBUG) { System.out.println(\"WARNING: No prolog, no downstream, empty: \"+");
printFunctionCallString(output, m);
output.println("); } ");
}
}
if (callPreDownstreamHook) {
if (hasResult && !callDownstream) {
if (callPostDownstreamHook) {
output.print(" " + JavaType.createForClass(retType).getName());
output.print(" _res = ");
} else {
output.print(" return ");
}
}
preDownstreamCallHook(output, pm);
}
if (callDownstream) {
if( pm.isSyntheticIsGL() ) {
emitGLIsMethodBody(output, pm);
} else if( pm.isSyntheticGetGL() ) {
emitGLGetMethodBody(output, pm);
} else {
if (hasResult) {
if (callPostDownstreamHook) {
output.print(" " + JavaType.createForClass(retType).getName());
output.print(" _res = ");
} else {
output.print(" return ");
}
}
else {
output.print(" ");
}
output.print(getDownstreamObjectName());
output.print('.');
output.print(m.getName());
output.print('(');
output.print(getArgListAsString(m, false, true));
output.println(");");
}
}
if (callPostDownstreamHook) {
postDownstreamCallHook(output, pm);
}
if (hasResult && callDownstream && callPostDownstreamHook) {
output.println(" return _res;");
}
output.println(" }");
}
protected String getArgListAsString(final Method m, final boolean includeArgTypes, final boolean includeArgNames) {
final StringBuilder buf = new StringBuilder(256);
if (!includeArgNames && !includeArgTypes) {
throw new IllegalArgumentException(
"Cannot generate arglist without both arg types and arg names");
}
final Class>[] argTypes = m.getParameterTypes();
for (int i = 0; i < argTypes.length; ++i) {
if (includeArgTypes) {
buf.append(JavaType.createForClass(argTypes[i]).getName());
buf.append(' ');
}
if (includeArgNames) {
buf.append("arg");
buf.append(i);
}
if (i < argTypes.length - 1) {
buf.append(',');
}
}
return buf.toString();
}
/** The name of the class around which this pipeline is being
* composed. E.g., if this pipeline was constructed with
* "java.util.Set" as the baseInterfaceClassName, then this method will
* return "Set".
*/
protected String getBaseInterfaceName() {
return baseName;
}
/** Get the output name for this pipeline class. */
protected abstract String getOutputName();
/**
* Called after the class headers have been generated, but before any
* method wrappers have been generated.
*/
protected void preMethodEmissionHook(final PrintWriter output) {
output.println(" public static final boolean DEBUG = jogamp.opengl.Debug.debug(\"" + getOutputName() + "\");");
}
/**
* Emits the constructor for the pipeline; called after the preMethodEmissionHook.
*/
protected abstract void constructorHook(PrintWriter output);
/**
* Called after the method wrappers have been generated, but before the
* closing parenthesis of the class is emitted.
*/
protected void postMethodEmissionHook(final PrintWriter output) {
output.println(" @Override");
output.println(" public String toString() {");
output.println(" StringBuilder sb = new StringBuilder();");
output.println(" sb.append(\"" + getOutputName() + " [this 0x\"+Integer.toHexString(hashCode())+\" implementing " + baseInterfaceClass.getName() + ",\\n\\t\");");
if (null != prologClassOpt) {
output.println(" sb.append(\" prolog: \"+" + getPrologObjectNameOpt() + ".toString()+\",\\n\\t\");");
}
output.println(" sb.append(\" downstream: \"+" + getDownstreamObjectName() + ".toString()+\"\\n\\t]\");");
output.println(" return sb.toString();");
output.println(" }");
}
/**
* Called before the pipeline routes the call to the downstream object.
*/
protected abstract void preDownstreamCallHook(PrintWriter output, PlainMethod pm);
protected abstract boolean hasPreDownstreamCallHook(PlainMethod pm);
/**
* Called after the pipeline has routed the call to the downstream object,
* but before the calling function exits or returns a value.
*/
protected abstract void postDownstreamCallHook(PrintWriter output, PlainMethod pm);
protected abstract boolean hasPostDownstreamCallHook(PlainMethod pm);
protected abstract int getMode();
protected abstract boolean emptyMethodAllowed();
protected abstract boolean emptyDownstreamAllowed();
/** Emit a Javadoc comment for this pipeline class. */
protected abstract void emitClassDocComment(PrintWriter output);
/**
* Emits one of the isGL* methods.
*/
protected void emitGLIsMethodBody(final PrintWriter output, final PlainMethod plainMethod) {
final String methodName = plainMethod.getWrappedMethod().getName();
final String type = methodName.substring(2);
if( type.equals("GL") ) {
output.println(" return true;");
} else if( 0 != ( GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) &&
!type.equals("GLES") &&
!type.endsWith("core") &&
!type.endsWith("Compatible") )
{
final Class> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type);
if (clazz.isAssignableFrom(baseInterfaceClass)) {
output.println(" return true;");
} else {
output.println(" return false;");
}
} else {
output.println(" return " + getDownstreamObjectName() + ".is" + type + "();");
}
}
/**
* Emits one of the getGL* methods.
*/
protected void emitGLGetMethodBody(final PrintWriter output, final PlainMethod plainMethod) {
final String methodName = plainMethod.getWrappedMethod().getName();
final String type = methodName.substring(3);
if( type.equals("GL") ) {
output.println(" return this;");
} else if( type.equals("GLProfile") ) {
output.println(" return " + getDownstreamObjectName() + ".getGLProfile();");
} else {
final Class> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type);
if (clazz.isAssignableFrom(baseInterfaceClass)) {
output.println(" if( is" + type + "() ) { return this; }");
output.println(" throw new GLException(\"Not a " + type + " implementation\");");
} else {
output.println(" throw new GLException(\"Not a " + type + " implementation\");");
}
}
}
/**
* Emits all synthetic GL* methods, but not isGL* nor getGL*
*/
protected void emitSyntheticGLMethods(final PrintWriter output) {
output.println(" @Override");
output.println(" public final GL getDownstreamGL() throws GLException {");
output.println(" return " + getDownstreamObjectName() + ";");
output.println(" }");
}
} // end class PipelineEmitter
//-------------------------------------------------------
protected class CustomPipeline extends PipelineEmitter {
String className;
int mode;
CustomPipeline(final int mode, final String outputDir, final String outputPackage, final String outputName, final Class> baseInterfaceClass, final Class> prologClassOpt, final Class> downstreamClass) {
super(outputDir, outputPackage, baseInterfaceClass, prologClassOpt, downstreamClass);
className = outputName;
this.mode = mode;
}
@Override
protected String getOutputName() {
return className;
}
@Override
protected int getMode() {
return mode;
}
@Override
protected boolean emptyMethodAllowed() {
return true;
}
@Override
protected boolean emptyDownstreamAllowed() {
return true;
}
@Override
protected void preMethodEmissionHook(final PrintWriter output) {
super.preMethodEmissionHook(output);
}
@Override
protected void constructorHook(final PrintWriter output) {
output.print(" public " + getOutputName() + "(");
output.print(downstreamName + " " + getDownstreamObjectName());
if (null != prologNameOpt) {
output.println(", " + prologNameOpt + " " + getPrologObjectNameOpt() + ")");
} else {
output.println(")");
}
output.println(" {");
output.println(" if (" + getDownstreamObjectName() + " == null) {");
output.println(" throw new IllegalArgumentException(\"null " + getDownstreamObjectName() + "\");");
output.println(" }");
output.print(" this." + getDownstreamObjectName());
output.println(" = " + getDownstreamObjectName() + ";");
if (null != prologNameOpt) {
output.print(" this." + getPrologObjectNameOpt());
output.println(" = " + getPrologObjectNameOpt() + ";");
}
output.println(" }");
output.println();
}
@Override
protected void postMethodEmissionHook(final PrintWriter output) {
super.postMethodEmissionHook(output);
if (null != prologNameOpt) {
output.print(" private " + prologNameOpt + " " + getPrologObjectNameOpt() + ";");
}
}
@Override
protected void emitClassDocComment(final PrintWriter output) {
output.println("/**");
output.println(" * Composable pipeline {@link " + outputPackage + "." + outputName + "}, implementing the interface");
output.println(" * {@link " + baseInterfaceClass.getName() + "}");
output.println(" * ");
output.println(" * Each method follows the call graph
");
if (null != prologClassOpt) {
output.println(" * - call prolog {@link " + prologClassOpt.getName() + "} if available");
}
output.println(" *
- call downstream {@link " + downstreamClass.getName() + "} if available");
if (null != prologClassOpt && 0 != (GEN_PROLOG_XOR_DOWNSTREAM & getMode())) {
output.println(" * and if no call to {@link " + prologClassOpt.getName() + "} is made");
}
output.println(" *
");
output.println(" * ");
output.println(" *
");
output.println(" * - Interface {@link " + baseInterfaceClass.getName() + "}");
if (null != prologClassOpt) {
output.println(" *
- Prolog {@link " + prologClassOpt.getName() + "}");
}
output.println(" *
- Downstream {@link " + downstreamClass.getName() + "}");
output.println(" *
");
output.println(" * Sample code which installs this pipeline:
");
output.println(" * ");
output.println("");
if (null != prologNameOpt) {
output.println(" GL gl = drawable.setGL( new " + className + "( drawable.getGL().getGL2ES2(), new " + prologNameOpt + "( drawable.getGL().getGL2ES2() ) ) );");
} else {
output.println(" GL gl = drawable.setGL( new " + className + "( drawable.getGL().getGL2ES2() ) );");
}
output.println("
");
output.println("*/");
}
@Override
protected boolean hasPreDownstreamCallHook(final PlainMethod pm) {
return null != getMethod(prologClassOpt, pm.getWrappedMethod());
}
@Override
protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) {
final Method m = pm.getWrappedMethod();
if (null != prologNameOpt) {
output.print(getPrologObjectNameOpt());
output.print('.');
output.print(m.getName());
output.print('(');
output.print(getArgListAsString(m, false, true));
output.println(");");
}
}
@Override
protected boolean hasPostDownstreamCallHook(final PlainMethod pm) {
return false;
}
@Override
protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) {
}
} // end class CustomPipeline
protected class DebugPipeline extends PipelineEmitter {
String className;
DebugPipeline(final String outputDir, final String outputPackage, final Class> baseInterfaceClass, final Class> downstreamClass) {
super(outputDir, outputPackage, baseInterfaceClass, null, downstreamClass);
className = "Debug" + getBaseInterfaceName();
}
@Override
protected String getOutputName() {
return className;
}
@Override
protected int getMode() {
return 0;
}
@Override
protected boolean emptyMethodAllowed() {
return false;
}
@Override
protected boolean emptyDownstreamAllowed() {
return false;
}
@Override
protected void preMethodEmissionHook(final PrintWriter output) {
super.preMethodEmissionHook(output);
}
@Override
protected void constructorHook(final PrintWriter output) {
output.print(" public " + getOutputName() + "(");
output.println(downstreamName + " " + getDownstreamObjectName() + ")");
output.println(" {");
output.println(" if (" + getDownstreamObjectName() + " == null) {");
output.println(" throw new IllegalArgumentException(\"null " + getDownstreamObjectName() + "\");");
output.println(" }");
output.print(" this." + getDownstreamObjectName());
output.println(" = " + getDownstreamObjectName() + ";");
if (null != prologNameOpt) {
output.print(" this." + getPrologObjectNameOpt());
output.println(" = " + getPrologObjectNameOpt() + ";");
}
output.println(" // Fetch GLContext object for better error checking (if possible)");
output.println(" _context = " + getDownstreamObjectName() + ".getContext();");
output.println(" }");
output.println();
}
@Override
protected void postMethodEmissionHook(final PrintWriter output) {
super.postMethodEmissionHook(output);
output.println(" private int checkGLError() {");
if (hasImmediateMode) {
output.println(" if (insideBeginEndPair) return GL_NO_ERROR;");
output.println();
}
output.format(" return %s.glGetError();%n", getDownstreamObjectName());
output.println(" }");
output.println(" private void writeGLError(int err, String fmt, Object... args)");
output.println(" {");
output.println(" StringBuilder buf = new StringBuilder();");
output.println(" buf.append(Thread.currentThread().toString());");
output.println(" buf.append(\" glGetError() returned the following error codes after a call to \");");
output.println(" buf.append(String.format(fmt, args));");
output.println(" buf.append(\": \");");
output.println();
output.println(" // Loop repeatedly to allow for distributed GL implementations,");
output.println(" // as detailed in the glGetError() specification");
output.println(" int recursionDepth = 10;");
output.println(" do {");
output.println(" switch (err) {");
output.println(" case GL_INVALID_ENUM: buf.append(\"GL_INVALID_ENUM \"); break;");
output.println(" case GL_INVALID_VALUE: buf.append(\"GL_INVALID_VALUE \"); break;");
output.println(" case GL_INVALID_OPERATION: buf.append(\"GL_INVALID_OPERATION \"); break;");
if (hasGL2ES1StackOverflow) {
output.println(" case GL2ES1.GL_STACK_OVERFLOW: buf.append(\"GL_STACK_OVERFLOW \"); break;");
output.println(" case GL2ES1.GL_STACK_UNDERFLOW: buf.append(\"GL_STACK_UNDERFLOW \"); break;");
}
output.println(" case GL_OUT_OF_MEMORY: buf.append(\"GL_OUT_OF_MEMORY \"); break;");
output.println(" case GL_NO_ERROR: throw new InternalError(\"Should not be treating GL_NO_ERROR as error\");");
output.println(" default: buf.append(\"Unknown glGetError() return value: \");");
output.println(" }");
output.println(" buf.append(\"( \" + err + \" 0x\"+Integer.toHexString(err).toUpperCase() + \"), \");");
output.println(" } while ((--recursionDepth >= 0) && (err = "
+ getDownstreamObjectName()
+ ".glGetError()) != GL_NO_ERROR);");
output.println(" throw new GLException(buf.toString());");
output.println(" }");
if (hasImmediateMode) {
output.println(" /** True if the pipeline is inside a glBegin/glEnd pair.*/");
output.println(" private boolean insideBeginEndPair = false;");
output.println();
}
output.println(" private void checkContext() {");
output.println(" GLContext currentContext = GLContext.getCurrent();");
output.println(" if (currentContext == null) {");
output.println(" throw new GLException(\"No OpenGL context is current on this thread\");");
output.println(" }");
output.println(" if ((_context != null) && (_context != currentContext)) {");
output.println(" throw new GLException(\"This GL object is being incorrectly used with a different GLContext than that which created it\");");
output.println(" }");
output.println(" }");
output.println(" private GLContext _context;");
}
@Override
protected void emitClassDocComment(final PrintWriter output) {
output.println("/**");
output.println(" * ");
output.println(" * Composable pipeline which wraps an underlying {@link GL} implementation,");
output.println(" * providing error checking after each OpenGL method call. If an error occurs,");
output.println(" * causes a {@link GLException} to be thrown at exactly the point of failure.");
output.println(" *
");
output.println(" * ");
output.println(" * Sample code which installs this pipeline:");
output.println(" *
");
output.println(" * gl = drawable.setGL(new DebugGL(drawable.getGL()));");
output.println(" *
");
output.println(" * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}");
output.println(" * ");
output.println(" */");
}
@Override
protected boolean hasPreDownstreamCallHook(final PlainMethod pm) {
return !pm.isSynthetic();
}
@Override
protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) {
output.println(" checkContext();");
}
@Override
protected boolean hasPostDownstreamCallHook(final PlainMethod pm) {
return !pm.isSynthetic();
}
@Override
protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) {
final Method m = pm.getWrappedMethod();
if (m.getName().equals("glBegin")) {
output.println(" insideBeginEndPair = true;");
output.println(" // NOTE: can't check glGetError(); it's not allowed inside glBegin/glEnd pair");
} else {
if (m.getName().equals("glEnd")) {
output.println(" insideBeginEndPair = false;");
}
output.println(" final int err = checkGLError();");
output.println(" if (err != GL_NO_ERROR) {");
final StringBuilder fmtsb = new StringBuilder();
final StringBuilder argsb = new StringBuilder();
fmtsb.append("\"%s(");
argsb.append("\"").append(m.getName()).append("\"");
final Class>[] params = m.getParameterTypes();
for (int i = 0; i < params.length; i++) {
if (i > 0) {
fmtsb.append(", ");
}
fmtsb.append("<").append(params[i].getName()).append(">");
if (params[i].isArray()) {
//nothing
} else if (params[i].equals(int.class)) {
fmtsb.append(" 0x%X");
argsb.append(", arg").append(i);
} else {
fmtsb.append(" %s");
argsb.append(", arg").append(i);
}
}
fmtsb.append(")\",");
argsb.append(");");
// calls to glGetError() are only allowed outside of glBegin/glEnd pairs
output.print(" writeGLError(err, ");
output.println(fmtsb.toString());
output.print(" ");
output.println(argsb.toString());
output.println(" }");
}
}
} // end class DebugPipeline
//-------------------------------------------------------
protected class TracePipeline extends PipelineEmitter {
String className;
TracePipeline(final String outputDir, final String outputPackage, final Class> baseInterfaceClass, final Class> downstreamClass) {
super(outputDir, outputPackage, baseInterfaceClass, null, downstreamClass);
className = "Trace" + getBaseInterfaceName();
}
@Override
protected String getOutputName() {
return className;
}
@Override
protected int getMode() {
return 0;
}
@Override
protected boolean emptyMethodAllowed() {
return false;
}
@Override
protected boolean emptyDownstreamAllowed() {
return false;
}
@Override
protected void preMethodEmissionHook(final PrintWriter output) {
super.preMethodEmissionHook(output);
}
@Override
protected void constructorHook(final PrintWriter output) {
output.print(" public " + getOutputName() + "(");
output.println(downstreamName + " " + getDownstreamObjectName() + ", PrintStream " + getOutputStreamName() + ")");
output.println(" {");
output.println(" if (" + getDownstreamObjectName() + " == null) {");
output.println(" throw new IllegalArgumentException(\"null " + getDownstreamObjectName() + "\");");
output.println(" }");
output.print(" this." + getDownstreamObjectName());
output.println(" = " + getDownstreamObjectName() + ";");
output.print(" this." + getOutputStreamName());
output.println(" = " + getOutputStreamName() + ";");
output.println(" }");
output.println();
}
@Override
protected void postMethodEmissionHook(final PrintWriter output) {
super.postMethodEmissionHook(output);
output.println("private PrintStream " + getOutputStreamName() + ";");
output.println("private int indent = 0;");
output.println("protected String dumpArray(Object obj)");
output.println("{");
output.println(" if (obj == null) return \"[null]\";");
output.println(" StringBuilder sb = new StringBuilder(\"[\");");
output.println(" int len = java.lang.reflect.Array.getLength(obj);");
output.println(" int count = Math.min(len,16);");
output.println(" for ( int i =0; i < count; i++ ) {");
output.println(" sb.append(java.lang.reflect.Array.get(obj,i));");
output.println(" if (i < count-1)");
output.println(" sb.append(',');");
output.println(" }");
output.println(" if ( len > 16 )");
output.println(" sb.append(\"...\").append(len);");
output.println(" sb.append(']');");
output.println(" return sb.toString();");
output.println("}");
output.println("protected void print(String str)");
output.println("{");
output.println(" " + getOutputStreamName() + ".print(str);");
output.println("}");
output.println("protected void println(String str)");
output.println("{");
output.println(" " + getOutputStreamName() + ".println(str);");
output.println("}");
output.println("protected void printIndent()");
output.println("{");
output.println(" for( int i =0; i < indent; i++) {" + getOutputStreamName() + ".print(' ');}");
output.println("}");
}
@Override
protected void emitClassDocComment(final PrintWriter output) {
output.println("/**");
output.println(" * ");
output.println(" * Composable pipeline which wraps an underlying {@link GL} implementation,");
output.println(" * providing tracing information to a user-specified {@link java.io.PrintStream}");
output.println(" * before and after each OpenGL method call.");
output.println(" *
");
output.println(" * ");
output.println(" * Sample code which installs this pipeline:");
output.println(" *
");
output.println(" * gl = drawable.setGL(new TraceGL(drawable.getGL(), System.err));");
output.println(" *
");
output.println(" * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}");
output.println(" * ");
output.println(" */");
}
@Override
protected boolean hasPreDownstreamCallHook(final PlainMethod pm) {
return !pm.isSynthetic();
}
@Override
protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) {
final Method m = pm.getWrappedMethod();
if (m.getName().equals("glEnd") || m.getName().equals("glEndList")) {
output.println(" indent-=2;");
output.println(" printIndent();");
} else {
output.println(" printIndent();");
}
output.print(" print(");
printFunctionCallString(output, m);
output.println(");");
}
@Override
protected boolean hasPostDownstreamCallHook(final PlainMethod pm) {
return !pm.isSynthetic();
}
@Override
protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) {
final Method m = pm.getWrappedMethod();
final Class> ret = m.getReturnType();
if (ret != Void.TYPE) {
output.println(" println(\" = \"+_res);");
} else {
output.println(" println(\"\");");
}
if (m.getName().equals("glBegin"))
output.println(" indent+=2;");
}
private String getOutputStreamName() {
return "stream";
}
} // end class TracePipeline
public static final void printFunctionCallString(final PrintWriter output, final Method m) {
final Class>[] params = m.getParameterTypes();
output.print(" \"" + m.getName() + "(\"");
for (int i = 0; i < params.length; i++) {
output.print("+\"<" + params[i].getName() + ">");
if (params[i].isArray()) {
output.print("\"");
} else if (params[i].equals(int.class)) {
output.print(" 0x\"+Integer.toHexString(arg" + i + ").toUpperCase()");
} else {
output.print(" \"+arg" + i);
}
if (i < params.length - 1) {
output.print("+\", \"");
}
}
output.print("+\")\"");
}
}