KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jode > decompiler > ImportHandler


1 /* ImportHandler Copyright (C) 1998-2002 Jochen Hoenicke.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation; either version 2, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; see the file COPYING.LESSER. If not, write to
15  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16  *
17  * $Id: ImportHandler.java.in,v 4.4.2.1 2002/05/28 17:34:03 hoenicke Exp $
18  */

19
20 package jode.decompiler;
21 import jode.GlobalOptions;
22 import jode.bytecode.ClassInfo;
23 import jode.bytecode.InnerClassInfo;
24 import jode.type.Type;
25 import jode.type.ArrayType;
26 import jode.type.ClassInterfacesType;
27 import jode.type.NullType;
28
29 import java.util.SortedMap JavaDoc;
30 import java.util.TreeMap JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.LinkedList JavaDoc;
33 import java.util.Comparator JavaDoc;
34 import java.util.Iterator JavaDoc;
35
36 import java.io.IOException JavaDoc;
37 import java.util.Hashtable JavaDoc;
38
39 public class ImportHandler {
40     /**
41      * The default package limit. MAX_VALUE means, do not import
42      * packages at all.
43      */

44     public final static int DEFAULT_PACKAGE_LIMIT = Integer.MAX_VALUE;
45     /**
46      * The default class limit. 1 means, import every class used here.
47      */

48     public final static int DEFAULT_CLASS_LIMIT = 1;
49
50     SortedMap JavaDoc imports;
51     /* Classes that doesn't need to be qualified. */
52     Hashtable JavaDoc cachedClassNames = null;
53     ClassAnalyzer main;
54     String JavaDoc className;
55     String JavaDoc pkg;
56
57     int importPackageLimit;
58     int importClassLimit;
59
60     /**
61      * A comparator to sort the imports. We want java.* and javax.*
62      * imports first. java.lang.* should precede java.lang.ref.*, but
63      * that is already guaranteed by ascii ordering.
64      */

65     static Comparator JavaDoc comparator = new Comparator JavaDoc() {
66     public int compare(Object JavaDoc o1, Object JavaDoc o2) {
67         String JavaDoc s1 = (String JavaDoc) o1;
68         String JavaDoc s2 = (String JavaDoc) o2;
69         boolean java1 = s1.startsWith("java");
70         boolean java2 = s2.startsWith("java");
71
72         if (java1 != java2)
73         return java1 ? -1 : 1;
74         return s1.compareTo(s2);
75     }
76     };
77
78     public ImportHandler() {
79     this(DEFAULT_PACKAGE_LIMIT, DEFAULT_CLASS_LIMIT);
80     }
81     
82     public ImportHandler(int packageLimit, int classLimit) {
83     importPackageLimit = packageLimit;
84     importClassLimit = classLimit;
85     }
86
87     /**
88      * Checks if the className conflicts with a class imported from
89      * another package and must be fully qualified therefore.
90      * The imports must should have been cleaned up before.
91      * <p>
92      * Known Bug: If a class, local, field or method with the same
93      * name as the package of className exists, using the fully
94      * qualified name is no solution. This sometimes can't be fixed
95      * at all (except by renaming the package). It happens only in
96      * ambigous contexts, namely static field/method access.
97      * @param name The full qualified class name.
98      * @return true if this className must be printed fully qualified.
99      */

100     private boolean conflictsImport(String JavaDoc name) {
101         int pkgdelim = name.lastIndexOf('.');
102         if (pkgdelim != -1) {
103             String JavaDoc pkgName = name.substring(0, pkgdelim);
104             /* All classes in this package doesn't conflict */
105             if (pkgName.equals(pkg))
106                 return false;
107
108             // name without package, but _including_ leading dot.
109
name = name.substring(pkgdelim);
110
111             if (pkg.length() != 0) {
112         /* Does this conflict with a class in this package? */
113                 if (ClassInfo.exists(pkg+name))
114                     return true;
115             } else {
116         /* Does this conflict with a class in this unnamed
117                  * package? */

118         if (ClassInfo.exists(name.substring(1)))
119             return true;
120         }
121
122             Iterator JavaDoc iter = imports.keySet().iterator();
123             while (iter.hasNext()) {
124                 String JavaDoc importName = (String JavaDoc) iter.next();
125                 if (importName.endsWith(".*")) {
126                     /* strip the "*" */
127                     importName = importName.substring
128                         (0, importName.length()-2);
129                     if (!importName.equals(pkgName)) {
130                         if (ClassInfo.exists(importName+name))
131                             return true;
132                     }
133                 } else {
134             /* Is this a class import with same name? */
135             if (importName.endsWith(name)
136             || importName.equals(name.substring(1)))
137             return true;
138         }
139             }
140         }
141         return false;
142     }
143
144     private void cleanUpImports() {
145         Integer JavaDoc dummyVote = new Integer JavaDoc(Integer.MAX_VALUE);
146         SortedMap JavaDoc newImports = new TreeMap JavaDoc(comparator);
147         List JavaDoc classImports = new LinkedList JavaDoc();
148         Iterator JavaDoc iter = imports.keySet().iterator();
149         while (iter.hasNext()) {
150             String JavaDoc importName = (String JavaDoc) iter.next();
151             Integer JavaDoc vote = (Integer JavaDoc) imports.get(importName);
152             if (!importName.endsWith(".*")) {
153                 if (vote.intValue() < importClassLimit)
154                     continue;
155                 int delim = importName.lastIndexOf(".");
156
157         if (delim != -1) {
158             /* Since the imports are sorted, newImports already
159              * contains the package if it should be imported.
160              */

161             if (newImports.containsKey
162             (importName.substring(0, delim)+".*"))
163             continue;
164             
165             /* This is a single Class import, that is not
166              * superseeded by a package import. Mark it for
167              * importation, but don't put it in newImports, yet.
168              */

169             classImports.add(importName);
170         } else if (pkg.length() != 0) {
171             /* This is a Class import from the unnamed
172              * package. It must always be imported.
173              */

174             newImports.put(importName, dummyVote);
175         }
176             } else {
177                 if (vote.intValue() < importPackageLimit)
178                     continue;
179                 newImports.put(importName, dummyVote);
180             }
181         }
182
183         imports = newImports;
184         cachedClassNames = new Hashtable JavaDoc();
185         /* Now check if the class import conflict with any of the
186          * package imports.
187          */

188         iter = classImports.iterator();
189         while (iter.hasNext()) {
190             /* If there are more than one single class imports with
191              * the same name, exactly the first (in sorted order) will
192              * be imported.
193          */

194             String JavaDoc classFQName = (String JavaDoc) iter.next();
195             if (!conflictsImport(classFQName)) {
196                 imports.put(classFQName, dummyVote);
197                 String JavaDoc name =
198                     classFQName.substring(classFQName.lastIndexOf('.')+1);
199                 cachedClassNames.put(classFQName, name);
200             }
201         }
202     }
203
204     public void dumpHeader(TabbedPrintWriter writer)
205          throws java.io.IOException JavaDoc
206     {
207         writer.println("/* "+ className
208                + " - Decompiled by JODE");
209     writer.println(" * Visit "+GlobalOptions.URL);
210     writer.println(" */");
211         if (pkg.length() != 0)
212             writer.println("package "+pkg+";");
213
214         cleanUpImports();
215         Iterator JavaDoc iter = imports.keySet().iterator();
216     String JavaDoc lastFirstPart = null;
217         while (iter.hasNext()) {
218             String JavaDoc pkgName = (String JavaDoc)iter.next();
219             if (!pkgName.equals("java.lang.*")) {
220         int firstDot = pkgName.indexOf('.');
221         if (firstDot != -1) {
222             String JavaDoc firstPart = pkgName.substring(0, firstDot);
223             if (lastFirstPart != null
224             && !lastFirstPart.equals(firstPart)) {
225             writer.println("");
226             }
227             lastFirstPart = firstPart;
228         }
229                 writer.println("import "+pkgName+";");
230         }
231         }
232         writer.println("");
233     }
234
235     public void error(String JavaDoc message) {
236         GlobalOptions.err.println(message);
237     }
238
239     public void init(String JavaDoc className) {
240         imports = new TreeMap JavaDoc(comparator);
241         /* java.lang is always imported */
242         imports.put("java.lang.*", new Integer JavaDoc(Integer.MAX_VALUE));
243
244         int pkgdelim = className.lastIndexOf('.');
245         pkg = (pkgdelim == -1)? "" : className.substring(0, pkgdelim);
246         this.className = (pkgdelim == -1) ? className
247             : className.substring(pkgdelim+1);
248     }
249
250     /* Marks the clazz as used, so that it will be imported if used often
251      * enough.
252      */

253     public void useClass(ClassInfo clazz) {
254     for (;;) {
255         /* First handle inner classes: For class scoped classes
256          * import outer class instead; for method scoped classes
257          * we don't import anything.
258          */

259         InnerClassInfo[] outerInfo = clazz.getOuterClasses();
260         if (outerInfo == null)
261         break;
262
263         if (outerInfo[0].name == null || outerInfo[0].outer == null)
264         return;
265         clazz = ClassInfo.forName(outerInfo[0].outer);
266     }
267         
268     String JavaDoc name = clazz.getName();
269     
270     Integer JavaDoc i = (Integer JavaDoc) imports.get(name);
271     if (i == null) {
272         /* This class wasn't imported before. Mark the whole package
273          * as used once more. */

274         
275         int pkgdelim = name.lastIndexOf('.');
276         if (pkgdelim != -1) {
277         String JavaDoc pkgName = name.substring(0, pkgdelim);
278         if (pkgName.equals(pkg))
279             return;
280         
281         Integer JavaDoc pkgVote = (Integer JavaDoc) imports.get(pkgName+".*");
282         if (pkgVote != null
283             && pkgVote.intValue() >= importPackageLimit)
284             return;
285
286         pkgVote = (pkgVote == null)
287             ? new Integer JavaDoc(1): new Integer JavaDoc(pkgVote.intValue()+1);
288         imports.put(pkgName+".*", pkgVote);
289         }
290         i = new Integer JavaDoc(1);
291     } else {
292         if (i.intValue() >= importClassLimit)
293         return;
294         i = new Integer JavaDoc(i.intValue()+1);
295     }
296     imports.put(name, i);
297     }
298
299     public final void useType(Type type) {
300     if (type instanceof ArrayType)
301         useType(((ArrayType) type).getElementType());
302     else if (type instanceof ClassInterfacesType)
303         useClass(((ClassInterfacesType) type).getClassInfo());
304     }
305
306     /**
307      * Check if clazz is imported and maybe remove package delimiter from
308      * full qualified class name.
309      * <p>
310      * Known Bug 1: If this is called before the imports are cleaned up,
311      * (that is only for debugging messages), the result is unpredictable.
312      * <p>
313      * Known Bug 2: It is not checked if the class name conflicts with
314      * a local variable, field or method name. This is very unlikely
315      * since the java standard has different naming convention for those
316      * names. (But maybe an intelligent obfuscator may use this fact.)
317      * This can only happen with static fields or static methods.
318      * @return a legal string representation of clazz.
319      */

320     public String JavaDoc getClassString(ClassInfo clazz) {
321     String JavaDoc name = clazz.getName();
322         if (cachedClassNames == null)
323             /* We are not yet clean, return the full name */
324             return name;
325
326         /* First look in our cache. */
327         String JavaDoc cached = (String JavaDoc) cachedClassNames.get(name);
328         if (cached != null)
329             return cached;
330
331         int pkgdelim = name.lastIndexOf('.');
332         if (pkgdelim != -1) {
333                 
334             String JavaDoc pkgName = name.substring(0, pkgdelim);
335
336             Integer JavaDoc i;
337             if (pkgName.equals(pkg)
338                 || (imports.get(pkgName+".*") != null
339                     && !conflictsImport(name))) {
340                 String JavaDoc result = name.substring(pkgdelim+1);
341                 cachedClassNames.put(name, result);
342                 return result;
343             }
344         }
345         cachedClassNames.put(name, name);
346         return name;
347     }
348
349     public String JavaDoc getTypeString(Type type) {
350     if (type instanceof ArrayType)
351         return getTypeString(((ArrayType) type).getElementType()) + "[]";
352     else if (type instanceof ClassInterfacesType)
353         return getClassString(((ClassInterfacesType) type).getClassInfo());
354     else if (type instanceof NullType)
355         return "Object";
356     else
357         return type.toString();
358     }
359
360     protected int loadFileFlags()
361     {
362         return 1;
363     }
364 }
365
366
367
Popular Tags