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