All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.badlogic.gdx.jnigen.parsing.RobustJavaMethodParser Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.jnigen.parsing;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.TypeDeclaration;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;

public class RobustJavaMethodParser implements JavaMethodParser {
	private static final String JNI_MANUAL = "MANUAL";
	private static final Map plainOldDataTypes;
	private static final Map arrayTypes;
	private static final Map bufferTypes;
	private static final Map otherTypes;
	public static String CustomIgnoreTag = "";

	static {
		plainOldDataTypes = new HashMap();
		plainOldDataTypes.put("boolean", ArgumentType.Boolean);
		plainOldDataTypes.put("byte", ArgumentType.Byte);
		plainOldDataTypes.put("char", ArgumentType.Char);
		plainOldDataTypes.put("short", ArgumentType.Short);
		plainOldDataTypes.put("int", ArgumentType.Integer);
		plainOldDataTypes.put("long", ArgumentType.Long);
		plainOldDataTypes.put("float", ArgumentType.Float);
		plainOldDataTypes.put("double", ArgumentType.Double);

		arrayTypes = new HashMap();
		arrayTypes.put("boolean", ArgumentType.BooleanArray);
		arrayTypes.put("byte", ArgumentType.ByteArray);
		arrayTypes.put("char", ArgumentType.CharArray);
		arrayTypes.put("short", ArgumentType.ShortArray);
		arrayTypes.put("int", ArgumentType.IntegerArray);
		arrayTypes.put("long", ArgumentType.LongArray);
		arrayTypes.put("float", ArgumentType.FloatArray);
		arrayTypes.put("double", ArgumentType.DoubleArray);

		bufferTypes = new HashMap();
		bufferTypes.put("Buffer", ArgumentType.Buffer);
		bufferTypes.put("ByteBuffer", ArgumentType.ByteBuffer);
		bufferTypes.put("CharBuffer", ArgumentType.CharBuffer);
		bufferTypes.put("ShortBuffer", ArgumentType.ShortBuffer);
		bufferTypes.put("IntBuffer", ArgumentType.IntBuffer);
		bufferTypes.put("LongBuffer", ArgumentType.LongBuffer);
		bufferTypes.put("FloatBuffer", ArgumentType.FloatBuffer);
		bufferTypes.put("DoubleBuffer", ArgumentType.DoubleBuffer);
		
		otherTypes = new HashMap();
		otherTypes.put("String", ArgumentType.String);
		otherTypes.put("Class", ArgumentType.Class);
		otherTypes.put("Throwable", ArgumentType.Throwable);
	}

	Stack> classStack = new Stack>();

	@Override
	public ArrayList parse (String classFile) throws Exception {
		CompilationUnit unit = new JavaParser().parse(new ByteArrayInputStream(classFile.getBytes())).getResult().get();
		ArrayList methods = new ArrayList();
		getJavaMethods(methods, getOuterClass(unit));
		ArrayList methodBodies = getNativeCodeBodies(classFile);
		ArrayList sections = getJniSections(classFile);
		alignMethodBodies(methods, methodBodies);
		ArrayList segments = sortMethodsAndSections(methods, sections);
		return segments;
	}

	private ArrayList sortMethodsAndSections (ArrayList methods, ArrayList sections) {
		ArrayList segments = new ArrayList();
		segments.addAll(methods);
		segments.addAll(sections);
		Collections.sort(segments, new Comparator() {
			@Override
			public int compare (JavaSegment o1, JavaSegment o2) {
				return o1.getStartIndex() - o2.getStartIndex();
			}
		});
		return segments;
	}

	private void alignMethodBodies (ArrayList methods, ArrayList methodBodies) {
		for (JavaMethod method : methods) {
			for (JniSection section : methodBodies) {
				if (method.getEndIndex() == section.getStartIndex()) {
					if (section.getNativeCode().startsWith(JNI_MANUAL)) {
						section.setNativeCode(section.getNativeCode().substring(JNI_MANUAL.length()));
						method.setManual(true);
					}
					method.setNativeCode(section.getNativeCode());
					break;
				}
			}
		}
	}

	private void getJavaMethods (ArrayList methods, TypeDeclaration type) {
		classStack.push(type);
		if (type.getMembers() != null) {
			for (BodyDeclaration member : type.getMembers()) {
				if (member.isClassOrInterfaceDeclaration() || member.isEnumDeclaration()) {
					getJavaMethods(methods, (TypeDeclaration)member);
				} else {
					if (member instanceof MethodDeclaration) {
						MethodDeclaration method = (MethodDeclaration)member;
						if(!method.isNative()) continue;
						methods.add(createMethod(method));
					}
				}
			}
		}
		classStack.pop();
	}

	private JavaMethod createMethod (MethodDeclaration method) {
		String className = classStack.peek().getNameAsString();
		String name = method.getNameAsString();
		boolean isStatic = method.isStatic();
		String returnType = method.getType().toString();
		ArrayList arguments = new ArrayList();

		if (method.getParameters() != null) {
			for (Parameter parameter : method.getParameters()) {
				arguments.add(new Argument(getArgumentType(parameter), parameter.getNameAsString()));
			}
		}

		return new JavaMethod(className, name, isStatic, returnType, null, arguments, method.getBegin().get().line, method.getEnd().get().line);
	}

	private ArgumentType getArgumentType (Parameter parameter) {
		String[] typeTokens = parameter.getType().toString().split("\\.");
		String type = typeTokens[typeTokens.length - 1];
		int arrayDim = 0;
		for (int i = 0; i < type.length(); i++) {
			if (type.charAt(i) == '[') arrayDim++;
		}
		type = type.replace("[", "").replace("]", "");

		if (arrayDim >= 1) {
			if (arrayDim > 1) return ArgumentType.ObjectArray;
			ArgumentType arrayType = arrayTypes.get(type);
			if (arrayType == null) {
				return ArgumentType.ObjectArray;
			}
			return arrayType;
		}

		if (plainOldDataTypes.containsKey(type)) return plainOldDataTypes.get(type);
		if (bufferTypes.containsKey(type)) return bufferTypes.get(type);
		if (otherTypes.containsKey(type)) return otherTypes.get(type);
		return ArgumentType.Object;
	}

	private TypeDeclaration getOuterClass (CompilationUnit unit) {
		for (TypeDeclaration type : unit.getTypes()) {
			if (type instanceof ClassOrInterfaceDeclaration || type instanceof EnumDeclaration) return type;
		}
		throw new RuntimeException("Couldn't find class, is your java file empty?");
	}

	private ArrayList getJniSections (String classFile) {
		ArrayList sections = getComments(classFile);
		Iterator iter = sections.iterator();
		while (iter.hasNext()) {
			JniSection section = iter.next();
			if (!section.getNativeCode().startsWith("JNI")) {
				iter.remove();
			} else {
				section.setNativeCode(section.getNativeCode().substring(3));
			}
		}
		return sections;
	}

	private ArrayList getNativeCodeBodies (String classFile) {
		ArrayList sections = getComments(classFile);
		Iterator iter = sections.iterator();
		while (iter.hasNext()) {
			JniSection section = iter.next();
			if (section.getNativeCode().startsWith("JNI")) iter.remove();
			if (section.getNativeCode().startsWith("-{")) iter.remove();
			if (!CustomIgnoreTag.isEmpty() && section.getNativeCode().startsWith(CustomIgnoreTag)) iter.remove();
		}
		return sections;
	}

	private ArrayList getComments (String classFile) {
		ArrayList sections = new ArrayList();

		boolean inComment = false;
		int start = 0;
		int startLine = 0;
		int line = 1;
		for (int i = 0; i < classFile.length() - 2; i++) {
			char c1 = classFile.charAt(i);
			char c2 = classFile.charAt(i + 1);
			char c3 = classFile.charAt(i + 2);
			if (c1 == '\n') line++;
			if (!inComment) {
				if (c1 == '/' && c2 == '*' && c3 != '*') {
					inComment = true;
					start = i;
					startLine = line;
				}
			} else {
				if (c1 == '*' && c2 == '/') {
					sections.add(new JniSection(classFile.substring(start + 2, i), startLine, line));
					inComment = false;
				}
			}
		}

		return sections;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy