duke@1: /* xdono@117: * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javah; duke@1: duke@1: import java.io.UnsupportedEncodingException; duke@1: import java.io.ByteArrayOutputStream; duke@1: import java.io.IOException; duke@1: import java.io.OutputStream; duke@1: import java.io.PrintWriter; duke@1: import com.sun.javadoc.*; duke@1: import java.io.*; duke@1: import java.util.Stack; duke@1: import java.util.Vector; duke@1: import java.util.Arrays; duke@1: duke@1: duke@1: /** duke@1: * An abstraction for generating support files required by native methods. duke@1: * Subclasses are for specific native interfaces. At the time of its duke@1: * original writing, this interface is rich enough to support JNI and the duke@1: * old 1.0-style native method interface. duke@1: * duke@1: * @author Sucheta Dambalkar(Revised) duke@1: */ duke@1: duke@1: duke@1: public abstract class Gen { duke@1: protected String lineSep = System.getProperty("line.separator"); duke@1: duke@1: RootDoc root; duke@1: /* duke@1: * List of classes for which we must generate output. duke@1: */ duke@1: protected ClassDoc[] classes; duke@1: static private final boolean isWindows = duke@1: System.getProperty("os.name").startsWith("Windows"); duke@1: duke@1: public Gen(RootDoc root){ duke@1: this.root = root; duke@1: } duke@1: duke@1: /** duke@1: * Override this abstract method, generating content for the named duke@1: * class into the outputstream. duke@1: */ duke@1: protected abstract void write(OutputStream o, ClassDoc clazz) duke@1: throws ClassNotFoundException; duke@1: duke@1: /** duke@1: * Override this method to provide a list of #include statements duke@1: * required by the native interface. duke@1: */ duke@1: protected abstract String getIncludes(); duke@1: duke@1: /* duke@1: * Output location. duke@1: */ duke@1: protected String outDir; duke@1: protected String outFile; duke@1: duke@1: public void setOutDir(String outDir) { duke@1: /* Check important, otherwise concatenation of two null strings duke@1: * produces the "nullnull" String. duke@1: */ duke@1: if (outDir != null) { duke@1: this.outDir = outDir + System.getProperty("file.separator"); duke@1: File d = new File(outDir); duke@1: if (!d.exists()) duke@1: if (!d.mkdirs()) duke@1: Util.error("cant.create.dir", d.toString()); duke@1: } duke@1: } duke@1: duke@1: public void setOutFile(String outFile) { duke@1: this.outFile = outFile; duke@1: } duke@1: duke@1: duke@1: public void setClasses(ClassDoc[] classes) { duke@1: this.classes = classes; duke@1: } duke@1: duke@1: /* duke@1: * Smartness with generated files. duke@1: */ duke@1: protected boolean force = false; duke@1: duke@1: public void setForce(boolean state) { duke@1: force = state; duke@1: } duke@1: duke@1: /** duke@1: * We explicitly need to write ASCII files because that is what C duke@1: * compilers understand. duke@1: */ duke@1: protected PrintWriter wrapWriter(OutputStream o) { duke@1: try { duke@1: return new duke@1: PrintWriter(new OutputStreamWriter(o, "ISO8859_1"), true); duke@1: } catch (UnsupportedEncodingException use) { duke@1: Util.bug("encoding.iso8859_1.not.found"); duke@1: return null; /* dead code */ duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * After initializing state of an instance, use this method to start duke@1: * processing. duke@1: * duke@1: * Buffer size chosen as an approximation from a single sampling of: duke@1: * expr `du -sk` / `ls *.h | wc -l` duke@1: */ duke@1: public void run() throws IOException, ClassNotFoundException { duke@1: int i = 0; duke@1: if (outFile != null) { duke@1: /* Everything goes to one big file... */ duke@1: ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); duke@1: writeFileTop(bout); /* only once */ duke@1: duke@1: for (i = 0; i < classes.length; i++) { duke@1: write(bout, classes[i]); duke@1: } duke@1: duke@1: writeIfChanged(bout.toByteArray(), outFile); duke@1: } else { duke@1: /* Each class goes to its own file... */ duke@1: for (i = 0; i < classes.length; i++) { duke@1: ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); duke@1: writeFileTop(bout); duke@1: ClassDoc clazz = classes[i]; duke@1: write(bout, clazz); duke@1: writeIfChanged(bout.toByteArray(), getFileName(clazz.qualifiedName())); duke@1: } duke@1: } duke@1: } duke@1: duke@1: /* duke@1: * Write the contents of byte[] b to a file named file. Writing duke@1: * is done if either the file doesn't exist or if the contents are duke@1: * different. duke@1: */ duke@1: private void writeIfChanged(byte[] b, String file) throws IOException { duke@1: File f = new File(file); duke@1: boolean mustWrite = false; duke@1: String event = "[No need to update file "; duke@1: duke@1: if (force) { duke@1: mustWrite = true; duke@1: event = "[Forcefully writing file "; duke@1: } else { duke@1: if (!f.exists()) { duke@1: mustWrite = true; duke@1: event = "[Creating file "; duke@1: } else { duke@1: int l = (int)f.length(); duke@1: if (b.length != l) { duke@1: mustWrite = true; duke@1: event = "[Overwriting file "; duke@1: } else { duke@1: /* Lengths are equal, so read it. */ duke@1: byte[] a = new byte[l]; duke@1: FileInputStream in = new FileInputStream(f); duke@1: if (in.read(a) != l) { duke@1: in.close(); duke@1: /* This can't happen, we already checked the length. */ duke@1: Util.error("not.enough.bytes", Integer.toString(l), duke@1: f.toString()); duke@1: } duke@1: in.close(); duke@1: while (--l >= 0) { duke@1: if (a[l] != b[l]) { duke@1: mustWrite = true; duke@1: event = "[Overwriting file "; duke@1: } duke@1: } duke@1: } duke@1: } duke@1: } duke@1: if (Util.verbose) duke@1: Util.log(event + file + "]"); duke@1: if (mustWrite) { duke@1: OutputStream out = new FileOutputStream(file); duke@1: out.write(b); /* No buffering, just one big write! */ duke@1: out.close(); duke@1: } duke@1: } duke@1: duke@1: protected String defineForStatic(ClassDoc c, FieldDoc f){ duke@1: duke@1: String cnamedoc = c.qualifiedName(); duke@1: String fnamedoc = f.name(); duke@1: duke@1: String cname = Mangle.mangle(cnamedoc, Mangle.Type.CLASS); duke@1: String fname = Mangle.mangle(fnamedoc, Mangle.Type.FIELDSTUB); duke@1: duke@1: if (!f.isStatic()) duke@1: Util.bug("tried.to.define.non.static"); duke@1: duke@1: if (f.isFinal()) { duke@1: Object value = null; duke@1: duke@1: value = f.constantValue(); duke@1: duke@1: if (value != null) { /* so it is a ConstantExpression */ duke@1: String constString = null; duke@1: if ((value instanceof Integer) duke@1: || (value instanceof Byte) duke@1: || (value instanceof Character) duke@1: || (value instanceof Short) duke@1: || (value instanceof Boolean)) { duke@1: /* covers byte, boolean, char, short, int */ duke@1: if(value instanceof Boolean) duke@1: constString = (value.toString() == "true") ? "1L" : "0L"; duke@1: else duke@1: constString = value.toString() + "L"; duke@1: } else if (value instanceof Long) { duke@1: // Visual C++ supports the i64 suffix, not LL. duke@1: if (isWindows) duke@1: constString = value.toString() + "i64"; duke@1: else duke@1: constString = value.toString() + "LL"; duke@1: } else if (value instanceof Float) { duke@1: /* bug for bug */ duke@1: float fv = ((Float)value).floatValue(); duke@1: if (Float.isInfinite(fv)) duke@1: constString = ((fv < 0) ? "-" : "") + "Inff"; duke@1: else duke@1: constString = value.toString() + "f"; duke@1: } else if (value instanceof Double) { duke@1: /* bug for bug */ duke@1: double d = ((Double)value).doubleValue(); duke@1: if (Double.isInfinite(d)) duke@1: constString = ((d < 0) ? "-" : "") + "InfD"; duke@1: else duke@1: constString = value.toString(); duke@1: } duke@1: if (constString != null) { duke@1: StringBuffer s = new StringBuffer("#undef "); duke@1: s.append(cname); s.append("_"); s.append(fname); s.append(lineSep); duke@1: s.append("#define "); s.append(cname); s.append("_"); duke@1: s.append(fname); s.append(" "); s.append(constString); duke@1: return s.toString(); duke@1: } duke@1: duke@1: } duke@1: } duke@1: return null; duke@1: } duke@1: duke@1: /* duke@1: * Deal with the C pre-processor. duke@1: */ duke@1: protected String cppGuardBegin() { duke@1: return "#ifdef __cplusplus" + lineSep + "extern \"C\" {" + lineSep + "#endif"; duke@1: } duke@1: duke@1: protected String cppGuardEnd() { duke@1: return "#ifdef __cplusplus" + lineSep + "}" + lineSep + "#endif"; duke@1: } duke@1: duke@1: protected String guardBegin(String cname) { duke@1: return "/* Header for class " + cname + " */" + lineSep + lineSep + duke@1: "#ifndef _Included_" + cname + lineSep + duke@1: "#define _Included_" + cname; duke@1: } duke@1: duke@1: protected String guardEnd(String cname) { duke@1: return "#endif"; duke@1: } duke@1: duke@1: /* duke@1: * File name and file preamble related operations. duke@1: */ duke@1: protected void writeFileTop(OutputStream o) { duke@1: PrintWriter pw = wrapWriter(o); duke@1: pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep + duke@1: getIncludes()); duke@1: } duke@1: duke@1: protected String baseFileName(String clazz) { duke@1: StringBuffer f = duke@1: new StringBuffer(Mangle.mangle(clazz, duke@1: Mangle.Type.CLASS)); duke@1: if (outDir != null) { duke@1: f.insert(0, outDir); duke@1: } duke@1: return f.toString(); duke@1: } duke@1: duke@1: protected String getFileName(String clazz) { duke@1: return baseFileName(clazz) + getFileSuffix(); duke@1: } duke@1: duke@1: protected String getFileSuffix() { duke@1: return ".h"; duke@1: } duke@1: duke@1: /** duke@1: * Including super classes' fields. duke@1: */ duke@1: duke@1: FieldDoc[] getAllFields(ClassDoc subclazz) duke@1: throws ClassNotFoundException { jjg@74: Vector fields = new Vector(); duke@1: ClassDoc cd = null; mcimadamore@184: Stack s = new Stack(); duke@1: duke@1: cd = subclazz; duke@1: while (true) { duke@1: s.push(cd); duke@1: ClassDoc c = cd.superclass(); duke@1: if (c == null) duke@1: break; duke@1: cd = c; duke@1: } duke@1: duke@1: while (!s.empty()) { duke@1: cd = (ClassDoc)s.pop(); duke@1: fields.addAll(Arrays.asList(cd.fields())); duke@1: } duke@1: jjg@198: return fields.toArray(new FieldDoc[fields.size()]); duke@1: } duke@1: }