1 /*******************************************************************************
2  * Copyright (c) 2004, 2020 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.tools.internal;
15 
16 import java.io.*;
17 import java.lang.reflect.*;
18 import java.util.*;
19 
20 public abstract class JNIGenerator implements Flags {
21 
22 	JNIClass mainClass;
23 	JNIClass[] classes;
24 	MetaData metaData;
25 	String delimiter;
26 	PrintStream output;
27 	ProgressMonitor progress;
28 
29 	static final String JNI64 = "JNI64";
30 
JNIGenerator()31 public JNIGenerator() {
32 	delimiter = System.lineSeparator();
33 	output = System.out;
34 	metaData = new MetaData(new Properties());
35 }
36 
skipCopyrights(InputStream is)37 public static String skipCopyrights(InputStream is) throws IOException {
38 	int state = 0;
39 	StringBuilder copyrights = new StringBuilder();
40 	while (state != 5) {
41 		int c = is.read();
42 		if (c == -1) return null;
43 		switch (state) {
44 			case 0:
45 				if (!Character.isWhitespace((char)c)) state = 1;
46 			case 1:
47 				if (c == '/') state = 2;
48 				else return null;
49 				break;
50 			case 2:
51 				if (c == '*') state = 3;
52 				else return null;
53 				break;
54 			case 3:
55 				if (c == '*') state = 4;
56 				break;
57 			case 4:
58 				if (c == '/') state = 5;
59 				else state = 3;
60 				break;
61 		}
62 		if (state > 0) copyrights.append((char)c);
63 	}
64 	return copyrights.toString();
65 }
66 
compare(InputStream is1, InputStream is2)67 public static boolean compare(InputStream is1, InputStream is2) throws IOException {
68 	skipCopyrights(is1);
69 	skipCopyrights(is2);
70 	while (true) {
71 		int c1 = is1.read();
72 		int c2 = is2.read();
73 		if (c1 != c2) return false;
74 		if (c1 == -1) break;
75 	}
76 	return true;
77 }
78 
output(byte[] bytes, String fileName)79 public static void output(byte[] bytes, String fileName) throws IOException {
80 	try (FileInputStream is = new FileInputStream(fileName)){
81 		if (compare(new ByteArrayInputStream(bytes), new BufferedInputStream(is))) return;
82 	} catch (FileNotFoundException e) {
83 	}
84 	try (FileOutputStream out = new FileOutputStream(fileName)) {
85 		out.write(bytes);
86 	}
87 }
88 
getDelimiter(String fileName)89 public static String getDelimiter(String fileName) {
90 
91 	try (InputStream is = new BufferedInputStream(new FileInputStream(fileName))){
92 		int c;
93 		while ((c = is.read()) != -1) {
94 			if (c == '\n') return "\n";
95 			if (c == '\r') {
96 				int c1 = is.read();
97 				if (c1 == '\n') {
98 					return "\r\n";
99 				}
100 				return "\r";
101 			}
102 		}
103 	} catch (IOException e) {
104 	}
105 	return System.lineSeparator();
106 }
107 
fixDelimiter(String str)108 String fixDelimiter(String str) {
109 	if (delimiter.equals("\n")) return str;
110 	int index = 0, length = str.length();
111 	StringBuilder buffer = new StringBuilder();
112 	while (index != -1) {
113 		int start = index;
114 		index = str.indexOf('\n', start);
115 		if (index == -1) {
116 			buffer.append(str.substring(start, length));
117 		} else {
118 			buffer.append(str.substring(start, index));
119 			buffer.append(delimiter);
120 			index++;
121 		}
122 	}
123 	return buffer.toString();
124 }
125 
getFunctionName(JNIMethod method)126 static String getFunctionName(JNIMethod method) {
127 	return getFunctionName(method, method.getParameterTypes());
128 }
129 
getFunctionName(JNIMethod method, JNIType[] paramTypes)130 static String getFunctionName(JNIMethod method, JNIType[] paramTypes) {
131 	if ((method.getModifiers() & Modifier.NATIVE) == 0) return method.getName();
132 	String function = toC(method.getName());
133 	if (!method.isNativeUnique()) {
134 		StringBuilder buffer = new StringBuilder();
135 		buffer.append(function);
136 		buffer.append("__");
137 		for (JNIType paramType : paramTypes) {
138 			buffer.append(toC(paramType.getTypeSignature(false)));
139 		}
140 		return buffer.toString();
141 	}
142 	return function;
143 }
144 
loadFile(String file)145 static String loadFile (String file) {
146 	try (FileReader fr = new FileReader(file);
147 		BufferedReader br = new BufferedReader(fr)){
148 		StringBuilder str = new StringBuilder();
149 		char[] buffer = new char[1024];
150 		int read;
151 		while ((read = br.read(buffer)) != -1) {
152 			str.append(buffer, 0, read);
153 		}
154 		fr.close();
155 		return str.toString();
156 	} catch (IOException e) {
157 		throw new RuntimeException("File not found:" + file, e);
158 	}
159 }
160 
sort(JNIMethod[] methods)161 static void sort(JNIMethod[] methods) {
162 	Arrays.sort(methods, (mth1, mth2) -> {
163 		int result = mth1.getName().compareTo(mth2.getName());
164 		return result != 0 ? result : getFunctionName(mth1).compareTo(getFunctionName(mth2));
165 	});
166 }
167 
sort(JNIField[] fields)168 static void sort(JNIField[] fields) {
169 	Arrays.sort(fields, (a, b) -> a.getName().compareTo(b.getName()));
170 }
171 
sort(JNIClass[] classes)172 static void sort(JNIClass[] classes) {
173 	Arrays.sort(classes, (a, b) -> a.getName().compareTo(b.getName()));
174 }
175 
split(String str, String separator)176 static String[] split(String str, String separator) {
177 	StringTokenizer tk = new StringTokenizer(str, separator);
178 	List<String> result = new ArrayList<>();
179 	while (tk.hasMoreTokens()) {
180 		result.add(tk.nextToken());
181 	}
182 	return result.toArray(new String[result.size()]);
183 }
184 
toC(String str)185 static String toC(String str) {
186 	int length = str.length();
187 	StringBuilder buffer = new StringBuilder(length * 2);
188 	for (int i = 0; i < length; i++) {
189 		char c = str.charAt(i);
190 		switch (c) {
191 			case '_': buffer.append("_1"); break;
192 			case ';': buffer.append("_2"); break;
193 			case '[': buffer.append("_3"); break;
194 			case '.': buffer.append("_"); break;
195 			case '/': buffer.append("_"); break;
196 			default: buffer.append(c);
197 		}
198 	}
199 	return buffer.toString();
200 }
201 
generate(JNIClass clazz)202 public abstract void generate(JNIClass clazz);
203 
generateCopyright()204 public void generateCopyright() {
205 }
206 
generateAutoGenNote()207 public void generateAutoGenNote() {
208 	outputln("/* Note: This file was auto-generated by " + JNIGenerator.class.getName() + " */");
209 	outputln("/* DO NOT EDIT - your changes will be lost. */");
210 	outputln();
211 }
212 
generateIncludes()213 public void generateIncludes() {
214 }
215 
generate()216 public void generate() {
217 	if (classes == null) return;
218 	generateCopyright();
219 	generateAutoGenNote();
220 	generateIncludes();
221 	sort(classes);
222 	for (JNIClass clazz : classes) {
223 		if (getGenerate(clazz)) generate(clazz);
224 		if (progress != null) progress.step();
225 	}
226 	output.flush();
227 }
228 
generateMetaData(String key)229 public void generateMetaData(String key) {
230 	MetaData mt = getMetaData();
231 	String data = mt.getMetaData(key, null);
232 	if (data == null) return;
233 	if (data.length() == 0) return;
234 	outputln(fixDelimiter(data));
235 }
236 
getClasses()237 public JNIClass[] getClasses() {
238 	return classes;
239 }
240 
getCPP()241 public boolean getCPP() {
242 	for (JNIClass clazz : classes) {
243 		if (clazz.getFlag(FLAG_CPP)) {
244 			return true;
245 		}
246 	}
247 	return false;
248 }
249 
getDelimiter()250 public String getDelimiter() {
251 	return delimiter;
252 }
253 
getExtension()254 public String getExtension() {
255 	return getCPP() ? ".cpp" : getM() ? ".m" : ".c";
256 }
257 
getFileName()258 public String getFileName() {
259 	return getOutputName() + getSuffix() + getExtension();
260 }
261 
getGenerate(JNIItem item)262 protected boolean getGenerate(JNIItem item) {
263 	return item.getGenerate();
264 }
265 
getOutput()266 public PrintStream getOutput() {
267 	return output;
268 }
269 
getOutputName()270 public String getOutputName() {
271 	return getMainClass().getSimpleName().toLowerCase();
272 }
273 
getM()274 public boolean getM() {
275 	for (JNIClass clazz : classes) {
276 		if (clazz.getFlag(FLAG_M)) {
277 			return true;
278 		}
279 	}
280 	return false;
281 }
282 
getMainClass()283 public JNIClass getMainClass() {
284 	return mainClass;
285 }
286 
getMetaData()287 public MetaData getMetaData() {
288 	return metaData;
289 }
290 
getProgressMonitor()291 public ProgressMonitor getProgressMonitor() {
292 	return progress;
293 }
294 
getSuffix()295 public String getSuffix() {
296 	return "";
297 }
298 
output(String str)299 public void output(String str) {
300 	output.print(str);
301 }
302 
outputln()303 public void outputln() {
304 	output(getDelimiter());
305 }
306 
outputln(String str)307 public void outputln(String str) {
308 	output(str);
309 	output(getDelimiter());
310 }
311 
setClasses(JNIClass[] classes)312 public void setClasses(JNIClass[] classes) {
313 	this.classes = classes;
314 }
315 
setDelimiter(String delimiter)316 public void setDelimiter(String delimiter) {
317 	this.delimiter = delimiter;
318 }
319 
setMainClass(JNIClass mainClass)320 public void setMainClass(JNIClass mainClass) {
321 	this.mainClass = mainClass;
322 }
323 
setMetaData(MetaData data)324 public void setMetaData(MetaData data) {
325 	metaData = data;
326 }
327 
setOutput(PrintStream output)328 public void setOutput(PrintStream output) {
329 	this.output = output;
330 }
331 
setProgressMonitor(ProgressMonitor progress)332 public void setProgressMonitor(ProgressMonitor progress) {
333 	this.progress = progress;
334 }
335 
336 }
337