• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.jnigen.parsing;
18 
19 import com.github.javaparser.JavaParser;
20 import com.github.javaparser.ast.CompilationUnit;
21 import com.github.javaparser.ast.body.BodyDeclaration;
22 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
23 import com.github.javaparser.ast.body.EnumDeclaration;
24 import com.github.javaparser.ast.body.MethodDeclaration;
25 import com.github.javaparser.ast.body.ModifierSet;
26 import com.github.javaparser.ast.body.Parameter;
27 import com.github.javaparser.ast.body.TypeDeclaration;
28 
29 import java.io.ByteArrayInputStream;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.Map;
36 import java.util.Stack;
37 
38 public class RobustJavaMethodParser implements JavaMethodParser {
39 	private static final String JNI_MANUAL = "MANUAL";
40 	private static final Map<String, ArgumentType> plainOldDataTypes;
41 	private static final Map<String, ArgumentType> arrayTypes;
42 	private static final Map<String, ArgumentType> bufferTypes;
43 
44 	static {
45 		plainOldDataTypes = new HashMap<String, ArgumentType>();
46 		plainOldDataTypes.put("boolean", ArgumentType.Boolean);
47 		plainOldDataTypes.put("byte", ArgumentType.Byte);
48 		plainOldDataTypes.put("char", ArgumentType.Char);
49 		plainOldDataTypes.put("short", ArgumentType.Short);
50 		plainOldDataTypes.put("int", ArgumentType.Integer);
51 		plainOldDataTypes.put("long", ArgumentType.Long);
52 		plainOldDataTypes.put("float", ArgumentType.Float);
53 		plainOldDataTypes.put("double", ArgumentType.Double);
54 
55 		arrayTypes = new HashMap<String, ArgumentType>();
56 		arrayTypes.put("boolean", ArgumentType.BooleanArray);
57 		arrayTypes.put("byte", ArgumentType.ByteArray);
58 		arrayTypes.put("char", ArgumentType.CharArray);
59 		arrayTypes.put("short", ArgumentType.ShortArray);
60 		arrayTypes.put("int", ArgumentType.IntegerArray);
61 		arrayTypes.put("long", ArgumentType.LongArray);
62 		arrayTypes.put("float", ArgumentType.FloatArray);
63 		arrayTypes.put("double", ArgumentType.DoubleArray);
64 
65 		bufferTypes = new HashMap<String, ArgumentType>();
66 		bufferTypes.put("Buffer", ArgumentType.Buffer);
67 		bufferTypes.put("ByteBuffer", ArgumentType.ByteBuffer);
68 		bufferTypes.put("CharBuffer", ArgumentType.CharBuffer);
69 		bufferTypes.put("ShortBuffer", ArgumentType.ShortBuffer);
70 		bufferTypes.put("IntBuffer", ArgumentType.IntBuffer);
71 		bufferTypes.put("LongBuffer", ArgumentType.LongBuffer);
72 		bufferTypes.put("FloatBuffer", ArgumentType.FloatBuffer);
73 		bufferTypes.put("DoubleBuffer", ArgumentType.DoubleBuffer);
74 	}
75 
76 	Stack<TypeDeclaration> classStack = new Stack<TypeDeclaration>();
77 
78 	@Override
parse(String classFile)79 	public ArrayList<JavaSegment> parse (String classFile) throws Exception {
80 		CompilationUnit unit = JavaParser.parse(new ByteArrayInputStream(classFile.getBytes()));
81 		ArrayList<JavaMethod> methods = new ArrayList<JavaMethod>();
82 		getJavaMethods(methods, getOuterClass(unit));
83 		ArrayList<JniSection> methodBodies = getNativeCodeBodies(classFile);
84 		ArrayList<JniSection> sections = getJniSections(classFile);
85 		alignMethodBodies(methods, methodBodies);
86 		ArrayList<JavaSegment> segments = sortMethodsAndSections(methods, sections);
87 		return segments;
88 	}
89 
sortMethodsAndSections(ArrayList<JavaMethod> methods, ArrayList<JniSection> sections)90 	private ArrayList<JavaSegment> sortMethodsAndSections (ArrayList<JavaMethod> methods, ArrayList<JniSection> sections) {
91 		ArrayList<JavaSegment> segments = new ArrayList<JavaSegment>();
92 		segments.addAll(methods);
93 		segments.addAll(sections);
94 		Collections.sort(segments, new Comparator<JavaSegment>() {
95 			@Override
96 			public int compare (JavaSegment o1, JavaSegment o2) {
97 				return o1.getStartIndex() - o2.getStartIndex();
98 			}
99 		});
100 		return segments;
101 	}
102 
alignMethodBodies(ArrayList<JavaMethod> methods, ArrayList<JniSection> methodBodies)103 	private void alignMethodBodies (ArrayList<JavaMethod> methods, ArrayList<JniSection> methodBodies) {
104 		for (JavaMethod method : methods) {
105 			for (JniSection section : methodBodies) {
106 				if (method.getEndIndex() == section.getStartIndex()) {
107 					if (section.getNativeCode().startsWith(JNI_MANUAL)) {
108 						section.setNativeCode(section.getNativeCode().substring(JNI_MANUAL.length()));
109 						method.setManual(true);
110 					}
111 					method.setNativeCode(section.getNativeCode());
112 					break;
113 				}
114 			}
115 		}
116 	}
117 
getJavaMethods(ArrayList<JavaMethod> methods, TypeDeclaration type)118 	private void getJavaMethods (ArrayList<JavaMethod> methods, TypeDeclaration type) {
119 		classStack.push(type);
120 		if (type.getMembers() != null) {
121 			for (BodyDeclaration member : type.getMembers()) {
122 				if (member instanceof ClassOrInterfaceDeclaration || member instanceof EnumDeclaration) {
123 					getJavaMethods(methods, (TypeDeclaration)member);
124 				} else {
125 					if (member instanceof MethodDeclaration) {
126 						MethodDeclaration method = (MethodDeclaration)member;
127 						if (!ModifierSet.hasModifier(((MethodDeclaration)member).getModifiers(), ModifierSet.NATIVE)) continue;
128 						methods.add(createMethod(method));
129 					}
130 				}
131 			}
132 		}
133 		classStack.pop();
134 	}
135 
createMethod(MethodDeclaration method)136 	private JavaMethod createMethod (MethodDeclaration method) {
137 		String className = classStack.peek().getName();
138 		String name = method.getName();
139 		boolean isStatic = ModifierSet.hasModifier(method.getModifiers(), ModifierSet.STATIC);
140 		String returnType = method.getType().toString();
141 		ArrayList<Argument> arguments = new ArrayList<Argument>();
142 
143 		if (method.getParameters() != null) {
144 			for (Parameter parameter : method.getParameters()) {
145 				arguments.add(new Argument(getArgumentType(parameter), parameter.getId().getName()));
146 			}
147 		}
148 
149 		return new JavaMethod(className, name, isStatic, returnType, null, arguments, method.getBeginLine(), method.getEndLine());
150 	}
151 
getArgumentType(Parameter parameter)152 	private ArgumentType getArgumentType (Parameter parameter) {
153 		String[] typeTokens = parameter.getType().toString().split("\\.");
154 		String type = typeTokens[typeTokens.length - 1];
155 		int arrayDim = 0;
156 		for (int i = 0; i < type.length(); i++) {
157 			if (type.charAt(i) == '[') arrayDim++;
158 		}
159 		type = type.replace("[", "").replace("]", "");
160 
161 		if (arrayDim >= 1) {
162 			if (arrayDim > 1) return ArgumentType.ObjectArray;
163 			ArgumentType arrayType = arrayTypes.get(type);
164 			if (arrayType == null) {
165 				return ArgumentType.ObjectArray;
166 			}
167 			return arrayType;
168 		}
169 
170 		if (plainOldDataTypes.containsKey(type)) return plainOldDataTypes.get(type);
171 		if (bufferTypes.containsKey(type)) return bufferTypes.get(type);
172 		if (type.equals("String")) return ArgumentType.String;
173 		return ArgumentType.Object;
174 	}
175 
getOuterClass(CompilationUnit unit)176 	private TypeDeclaration getOuterClass (CompilationUnit unit) {
177 		for (TypeDeclaration type : unit.getTypes()) {
178 			if (type instanceof ClassOrInterfaceDeclaration || type instanceof EnumDeclaration) return type;
179 		}
180 		throw new RuntimeException("Couldn't find class, is your java file empty?");
181 	}
182 
getJniSections(String classFile)183 	private ArrayList<JniSection> getJniSections (String classFile) {
184 		ArrayList<JniSection> sections = getComments(classFile);
185 		Iterator<JniSection> iter = sections.iterator();
186 		while (iter.hasNext()) {
187 			JniSection section = iter.next();
188 			if (!section.getNativeCode().startsWith("JNI")) {
189 				iter.remove();
190 			} else {
191 				section.setNativeCode(section.getNativeCode().substring(3));
192 			}
193 		}
194 		return sections;
195 	}
196 
getNativeCodeBodies(String classFile)197 	private ArrayList<JniSection> getNativeCodeBodies (String classFile) {
198 		ArrayList<JniSection> sections = getComments(classFile);
199 		Iterator<JniSection> iter = sections.iterator();
200 		while (iter.hasNext()) {
201 			JniSection section = iter.next();
202 			if (section.getNativeCode().startsWith("JNI")) iter.remove();
203 			if (section.getNativeCode().startsWith("-{")) iter.remove();
204 		}
205 		return sections;
206 	}
207 
getComments(String classFile)208 	private ArrayList<JniSection> getComments (String classFile) {
209 		ArrayList<JniSection> sections = new ArrayList<JniSection>();
210 
211 		boolean inComment = false;
212 		int start = 0;
213 		int startLine = 0;
214 		int line = 1;
215 		for (int i = 0; i < classFile.length() - 2; i++) {
216 			char c1 = classFile.charAt(i);
217 			char c2 = classFile.charAt(i + 1);
218 			char c3 = classFile.charAt(i + 2);
219 			if (c1 == '\n') line++;
220 			if (!inComment) {
221 				if (c1 == '/' && c2 == '*' && c3 != '*') {
222 					inComment = true;
223 					start = i;
224 					startLine = line;
225 				}
226 			} else {
227 				if (c1 == '*' && c2 == '/') {
228 					sections.add(new JniSection(classFile.substring(start + 2, i), startLine, line));
229 					inComment = false;
230 				}
231 			}
232 		}
233 
234 		return sections;
235 	}
236 }
237