View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.bcel.util;
18  
19  import java.io.File;
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.io.UnsupportedEncodingException;
24  import java.nio.charset.Charset;
25  import java.nio.charset.StandardCharsets;
26  import java.util.HashSet;
27  import java.util.Set;
28  
29  import org.apache.bcel.Const;
30  import org.apache.bcel.Constants;
31  import org.apache.bcel.classfile.Attribute;
32  import org.apache.bcel.classfile.ClassParser;
33  import org.apache.bcel.classfile.ConstantPool;
34  import org.apache.bcel.classfile.JavaClass;
35  import org.apache.bcel.classfile.Method;
36  import org.apache.bcel.classfile.Utility;
37  
38  /**
39   * Read class file(s) and convert them into HTML files.
40   *
41   * Given a JavaClass object "class" that is in package "package" five files will be created in the specified directory.
42   *
43   * <OL>
44   * <LI>"package"."class".html as the main file which defines the frames for the following subfiles.
45   * <LI>"package"."class"_attributes.html contains all (known) attributes found in the file
46   * <LI>"package"."class"_cp.html contains the constant pool
47   * <LI>"package"."class"_code.html contains the byte code
48   * <LI>"package"."class"_methods.html contains references to all methods and fields of the class
49   * </OL>
50   *
51   * All subfiles reference each other appropriately, e.g. clicking on a method in the Method's frame will jump to the
52   * appropriate method in the Code frame.
53   */
54  public class Class2HTML implements Constants {
55  
56      private static String classPackage; // name of package, unclean to make it static, but ...
57      private static String className; // name of current class, dito
58      private static ConstantPool constantPool;
59      private static final Set<String> basicTypes = new HashSet<>();
60      static {
61          basicTypes.add("int");
62          basicTypes.add("short");
63          basicTypes.add("boolean");
64          basicTypes.add("void");
65          basicTypes.add("char");
66          basicTypes.add("byte");
67          basicTypes.add("long");
68          basicTypes.add("double");
69          basicTypes.add("float");
70      }
71  
72      public static void main(final String[] argv) throws IOException {
73          final String[] fileName = new String[argv.length];
74          int files = 0;
75          ClassParser parser = null;
76          JavaClass javaClass = null;
77          String zipFile = null;
78          final char sep = File.separatorChar;
79          String dir = "." + sep; // Where to store HTML files
80          /*
81           * Parse command line arguments.
82           */
83          for (int i = 0; i < argv.length; i++) {
84              if (argv[i].charAt(0) == '-') { // command line switch
85                  if (argv[i].equals("-d")) { // Specify target directory, default '.'
86                      dir = argv[++i];
87                      if (!dir.endsWith("" + sep)) {
88                          dir += sep;
89                      }
90                      final File store = new File(dir);
91                      if (!store.isDirectory()) {
92                          final boolean created = store.mkdirs(); // Create target directory if necessary
93                          if (!created && !store.isDirectory()) {
94                              System.out.println("Tried to create the directory " + dir + " but failed");
95                          }
96                      }
97                  } else if (argv[i].equals("-zip")) {
98                      zipFile = argv[++i];
99                  } else {
100                     System.out.println("Unknown option " + argv[i]);
101                 }
102             } else {
103                 fileName[files++] = argv[i];
104             }
105         }
106         if (files == 0) {
107             System.err.println("Class2HTML: No input files specified.");
108         } else { // Loop through files ...
109             for (int i = 0; i < files; i++) {
110                 System.out.print("Processing " + fileName[i] + "...");
111                 if (zipFile == null) {
112                     parser = new ClassParser(fileName[i]); // Create parser object from file
113                 } else {
114                     parser = new ClassParser(zipFile, fileName[i]); // Create parser object from ZIP file
115                 }
116                 javaClass = parser.parse();
117                 new Class2HTML(javaClass, dir);
118                 System.out.println("Done.");
119             }
120         }
121     }
122 
123     /**
124      * Utility method that converts a class reference in the constant pool, i.e., an index to a string.
125      */
126     static String referenceClass(final int index) {
127         String str = constantPool.getConstantString(index, Const.CONSTANT_Class);
128         str = Utility.compactClassName(str);
129         str = Utility.compactClassName(str, classPackage + ".", true);
130         return "<A HREF=\"" + className + "_cp.html#cp" + index + "\" TARGET=ConstantPool>" + str + "</A>";
131     }
132 
133     static String referenceType(final String type) {
134         String shortType = Utility.compactClassName(type);
135         shortType = Utility.compactClassName(shortType, classPackage + ".", true);
136         final int index = type.indexOf('['); // Type is an array?
137         String baseType = type;
138         if (index > -1) {
139             baseType = type.substring(0, index); // Tack of the '['
140         }
141         // test for basic type
142         if (basicTypes.contains(baseType)) {
143             return "<FONT COLOR=\"#00FF00\">" + type + "</FONT>";
144         }
145         return "<A HREF=\"" + baseType + ".html\" TARGET=_top>" + shortType + "</A>";
146     }
147 
148     static String toHTML(final String str) {
149         final StringBuilder buf = new StringBuilder();
150         for (int i = 0; i < str.length(); i++) {
151             char ch;
152             switch (ch = str.charAt(i)) {
153             case '<':
154                 buf.append("&lt;");
155                 break;
156             case '>':
157                 buf.append("&gt;");
158                 break;
159             case '\n':
160                 buf.append("\\n");
161                 break;
162             case '\r':
163                 buf.append("\\r");
164                 break;
165             default:
166                 buf.append(ch);
167             }
168         }
169         return buf.toString();
170     }
171 
172     private final JavaClass javaClass; // current class object
173 
174     private final String dir;
175 
176     /**
177      * Write contents of the given JavaClass into HTML files.
178      *
179      * @param javaClass The class to write
180      * @param dir The directory to put the files in
181      * @throws IOException Thrown when an I/O exception of some sort has occurred.
182      */
183     public Class2HTML(final JavaClass javaClass, final String dir) throws IOException {
184         this(javaClass, dir, StandardCharsets.UTF_8);
185     }
186 
187     private Class2HTML(final JavaClass javaClass, final String dir, final Charset charset) throws IOException {
188         final Method[] methods = javaClass.getMethods();
189         this.javaClass = javaClass;
190         this.dir = dir;
191         className = javaClass.getClassName(); // Remember full name
192         constantPool = javaClass.getConstantPool();
193         // Get package name by tacking off everything after the last '.'
194         final int index = className.lastIndexOf('.');
195         if (index > -1) {
196             classPackage = className.substring(0, index);
197         } else {
198             classPackage = ""; // default package
199         }
200         final ConstantHTML constantHtml = new ConstantHTML(dir, className, classPackage, methods, constantPool, charset);
201         /*
202          * Attributes can't be written in one step, so we just open a file which will be written consequently.
203          */
204         try (AttributeHTML attributeHtml = new AttributeHTML(dir, className, constantPool, constantHtml, charset)) {
205             new MethodHTML(dir, className, methods, javaClass.getFields(), constantHtml, attributeHtml, charset);
206             // Write main file (with frames, yuk)
207             writeMainHTML(attributeHtml, charset);
208             new CodeHTML(dir, className, methods, constantPool, constantHtml, charset);
209         }
210     }
211 
212     private void writeMainHTML(final AttributeHTML attributeHtml, final Charset charset) throws FileNotFoundException, UnsupportedEncodingException {
213         try (PrintWriter file = new PrintWriter(dir + className + ".html", charset.name())) {
214             file.println("<HTML>\n" + "<HEAD><TITLE>Documentation for " + className + "</TITLE>" + "</HEAD>\n" + "<FRAMESET BORDER=1 cols=\"30%,*\">\n"
215                 + "<FRAMESET BORDER=1 rows=\"80%,*\">\n" + "<FRAME NAME=\"ConstantPool\" SRC=\"" + className + "_cp.html" + "\"\n MARGINWIDTH=\"0\" "
216                 + "MARGINHEIGHT=\"0\" FRAMEBORDER=\"1\" SCROLLING=\"AUTO\">\n" + "<FRAME NAME=\"Attributes\" SRC=\"" + className + "_attributes.html"
217                 + "\"\n MARGINWIDTH=\"0\" " + "MARGINHEIGHT=\"0\" FRAMEBORDER=\"1\" SCROLLING=\"AUTO\">\n" + "</FRAMESET>\n"
218                 + "<FRAMESET BORDER=1 rows=\"80%,*\">\n" + "<FRAME NAME=\"Code\" SRC=\"" + className + "_code.html\"\n MARGINWIDTH=0 "
219                 + "MARGINHEIGHT=0 FRAMEBORDER=1 SCROLLING=\"AUTO\">\n" + "<FRAME NAME=\"Methods\" SRC=\"" + className + "_methods.html\"\n MARGINWIDTH=0 "
220                 + "MARGINHEIGHT=0 FRAMEBORDER=1 SCROLLING=\"AUTO\">\n" + "</FRAMESET></FRAMESET></HTML>");
221         }
222         final Attribute[] attributes = javaClass.getAttributes();
223         for (int i = 0; i < attributes.length; i++) {
224             attributeHtml.writeAttribute(attributes[i], "class" + i);
225         }
226     }
227 }