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    }