001 /*
002 * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package com.sun.tools.javah;
027
028 import java.io.UnsupportedEncodingException;
029 import java.io.ByteArrayOutputStream;
030 import java.io.IOException;
031 import java.io.OutputStream;
032 import java.io.PrintWriter;
033 import com.sun.javadoc.*;
034 import java.io.*;
035 import java.util.Stack;
036 import java.util.Vector;
037 import java.util.Arrays;
038
039
040 /**
041 * An abstraction for generating support files required by native methods.
042 * Subclasses are for specific native interfaces. At the time of its
043 * original writing, this interface is rich enough to support JNI and the
044 * old 1.0-style native method interface.
045 *
046 * @author Sucheta Dambalkar(Revised)
047 */
048
049
050 public abstract class Gen {
051 protected String lineSep = System.getProperty("line.separator");
052
053 RootDoc root;
054 /*
055 * List of classes for which we must generate output.
056 */
057 protected ClassDoc[] classes;
058 static private final boolean isWindows =
059 System.getProperty("os.name").startsWith("Windows");
060
061 public Gen(RootDoc root){
062 this.root = root;
063 }
064
065 /**
066 * Override this abstract method, generating content for the named
067 * class into the outputstream.
068 */
069 protected abstract void write(OutputStream o, ClassDoc clazz)
070 throws ClassNotFoundException;
071
072 /**
073 * Override this method to provide a list of #include statements
074 * required by the native interface.
075 */
076 protected abstract String getIncludes();
077
078 /*
079 * Output location.
080 */
081 protected String outDir;
082 protected String outFile;
083
084 public void setOutDir(String outDir) {
085 /* Check important, otherwise concatenation of two null strings
086 * produces the "nullnull" String.
087 */
088 if (outDir != null) {
089 this.outDir = outDir + System.getProperty("file.separator");
090 File d = new File(outDir);
091 if (!d.exists())
092 if (!d.mkdirs())
093 Util.error("cant.create.dir", d.toString());
094 }
095 }
096
097 public void setOutFile(String outFile) {
098 this.outFile = outFile;
099 }
100
101
102 public void setClasses(ClassDoc[] classes) {
103 this.classes = classes;
104 }
105
106 /*
107 * Smartness with generated files.
108 */
109 protected boolean force = false;
110
111 public void setForce(boolean state) {
112 force = state;
113 }
114
115 /**
116 * We explicitly need to write ASCII files because that is what C
117 * compilers understand.
118 */
119 protected PrintWriter wrapWriter(OutputStream o) {
120 try {
121 return new
122 PrintWriter(new OutputStreamWriter(o, "ISO8859_1"), true);
123 } catch (UnsupportedEncodingException use) {
124 Util.bug("encoding.iso8859_1.not.found");
125 return null; /* dead code */
126 }
127 }
128
129 /**
130 * After initializing state of an instance, use this method to start
131 * processing.
132 *
133 * Buffer size chosen as an approximation from a single sampling of:
134 * expr `du -sk` / `ls *.h | wc -l`
135 */
136 public void run() throws IOException, ClassNotFoundException {
137 int i = 0;
138 if (outFile != null) {
139 /* Everything goes to one big file... */
140 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
141 writeFileTop(bout); /* only once */
142
143 for (i = 0; i < classes.length; i++) {
144 write(bout, classes[i]);
145 }
146
147 writeIfChanged(bout.toByteArray(), outFile);
148 } else {
149 /* Each class goes to its own file... */
150 for (i = 0; i < classes.length; i++) {
151 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
152 writeFileTop(bout);
153 ClassDoc clazz = classes[i];
154 write(bout, clazz);
155 writeIfChanged(bout.toByteArray(), getFileName(clazz.qualifiedName()));
156 }
157 }
158 }
159
160 /*
161 * Write the contents of byte[] b to a file named file. Writing
162 * is done if either the file doesn't exist or if the contents are
163 * different.
164 */
165 private void writeIfChanged(byte[] b, String file) throws IOException {
166 File f = new File(file);
167 boolean mustWrite = false;
168 String event = "[No need to update file ";
169
170 if (force) {
171 mustWrite = true;
172 event = "[Forcefully writing file ";
173 } else {
174 if (!f.exists()) {
175 mustWrite = true;
176 event = "[Creating file ";
177 } else {
178 int l = (int)f.length();
179 if (b.length != l) {
180 mustWrite = true;
181 event = "[Overwriting file ";
182 } else {
183 /* Lengths are equal, so read it. */
184 byte[] a = new byte[l];
185 FileInputStream in = new FileInputStream(f);
186 if (in.read(a) != l) {
187 in.close();
188 /* This can't happen, we already checked the length. */
189 Util.error("not.enough.bytes", Integer.toString(l),
190 f.toString());
191 }
192 in.close();
193 while (--l >= 0) {
194 if (a[l] != b[l]) {
195 mustWrite = true;
196 event = "[Overwriting file ";
197 }
198 }
199 }
200 }
201 }
202 if (Util.verbose)
203 Util.log(event + file + "]");
204 if (mustWrite) {
205 OutputStream out = new FileOutputStream(file);
206 out.write(b); /* No buffering, just one big write! */
207 out.close();
208 }
209 }
210
211 protected String defineForStatic(ClassDoc c, FieldDoc f){
212
213 String cnamedoc = c.qualifiedName();
214 String fnamedoc = f.name();
215
216 String cname = Mangle.mangle(cnamedoc, Mangle.Type.CLASS);
217 String fname = Mangle.mangle(fnamedoc, Mangle.Type.FIELDSTUB);
218
219 if (!f.isStatic())
220 Util.bug("tried.to.define.non.static");
221
222 if (f.isFinal()) {
223 Object value = null;
224
225 value = f.constantValue();
226
227 if (value != null) { /* so it is a ConstantExpression */
228 String constString = null;
229 if ((value instanceof Integer)
230 || (value instanceof Byte)
231 || (value instanceof Character)
232 || (value instanceof Short)
233 || (value instanceof Boolean)) {
234 /* covers byte, boolean, char, short, int */
235 if(value instanceof Boolean)
236 constString = (value.toString() == "true") ? "1L" : "0L";
237 else
238 constString = value.toString() + "L";
239 } else if (value instanceof Long) {
240 // Visual C++ supports the i64 suffix, not LL.
241 if (isWindows)
242 constString = value.toString() + "i64";
243 else
244 constString = value.toString() + "LL";
245 } else if (value instanceof Float) {
246 /* bug for bug */
247 float fv = ((Float)value).floatValue();
248 if (Float.isInfinite(fv))
249 constString = ((fv < 0) ? "-" : "") + "Inff";
250 else
251 constString = value.toString() + "f";
252 } else if (value instanceof Double) {
253 /* bug for bug */
254 double d = ((Double)value).doubleValue();
255 if (Double.isInfinite(d))
256 constString = ((d < 0) ? "-" : "") + "InfD";
257 else
258 constString = value.toString();
259 }
260 if (constString != null) {
261 StringBuffer s = new StringBuffer("#undef ");
262 s.append(cname); s.append("_"); s.append(fname); s.append(lineSep);
263 s.append("#define "); s.append(cname); s.append("_");
264 s.append(fname); s.append(" "); s.append(constString);
265 return s.toString();
266 }
267
268 }
269 }
270 return null;
271 }
272
273 /*
274 * Deal with the C pre-processor.
275 */
276 protected String cppGuardBegin() {
277 return "#ifdef __cplusplus" + lineSep + "extern \"C\" {" + lineSep + "#endif";
278 }
279
280 protected String cppGuardEnd() {
281 return "#ifdef __cplusplus" + lineSep + "}" + lineSep + "#endif";
282 }
283
284 protected String guardBegin(String cname) {
285 return "/* Header for class " + cname + " */" + lineSep + lineSep +
286 "#ifndef _Included_" + cname + lineSep +
287 "#define _Included_" + cname;
288 }
289
290 protected String guardEnd(String cname) {
291 return "#endif";
292 }
293
294 /*
295 * File name and file preamble related operations.
296 */
297 protected void writeFileTop(OutputStream o) {
298 PrintWriter pw = wrapWriter(o);
299 pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep +
300 getIncludes());
301 }
302
303 protected String baseFileName(String clazz) {
304 StringBuffer f =
305 new StringBuffer(Mangle.mangle(clazz,
306 Mangle.Type.CLASS));
307 if (outDir != null) {
308 f.insert(0, outDir);
309 }
310 return f.toString();
311 }
312
313 protected String getFileName(String clazz) {
314 return baseFileName(clazz) + getFileSuffix();
315 }
316
317 protected String getFileSuffix() {
318 return ".h";
319 }
320
321 /**
322 * Including super classes' fields.
323 */
324
325 FieldDoc[] getAllFields(ClassDoc subclazz)
326 throws ClassNotFoundException {
327 Vector<FieldDoc> fields = new Vector<FieldDoc>();
328 ClassDoc cd = null;
329 Stack<Object> s = new Stack<Object>();
330
331 cd = subclazz;
332 while (true) {
333 s.push(cd);
334 ClassDoc c = cd.superclass();
335 if (c == null)
336 break;
337 cd = c;
338 }
339
340 while (!s.empty()) {
341 cd = (ClassDoc)s.pop();
342 fields.addAll(Arrays.asList(cd.fields()));
343 }
344
345 return fields.toArray(new FieldDoc[fields.size()]);
346 }
347 }