KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ant > internal > ui > datatransfer > SourceAnalyzer


1 /*******************************************************************************
2  * Copyright (c) 2005, 2006 Richard Hoefter and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * Richard Hoefter (richard.hoefter@web.de) - initial API and implementation
10  * IBM Corporation - incorporating into Eclipse
11  *******************************************************************************/

12
13 package org.eclipse.ant.internal.ui.datatransfer;
14
15 import java.io.File JavaDoc;
16 import com.ibm.icu.text.MessageFormat;
17 import java.util.ArrayList JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Set JavaDoc;
22 import java.util.TreeMap JavaDoc;
23 import java.util.TreeSet JavaDoc;
24
25 import org.eclipse.core.resources.IFile;
26 import org.eclipse.jdt.core.IClassFile;
27 import org.eclipse.jdt.core.IJavaProject;
28 import org.eclipse.jdt.core.JavaCore;
29 import org.eclipse.jdt.core.ToolFactory;
30 import org.eclipse.jdt.core.util.IClassFileReader;
31 import org.eclipse.jdt.core.util.IConstantPool;
32 import org.eclipse.jdt.core.util.IConstantPoolConstant;
33 import org.eclipse.jdt.core.util.IConstantPoolEntry;
34 import org.eclipse.jface.dialogs.MessageDialog;
35 import org.eclipse.swt.widgets.Shell;
36
37 /**
38  * Provides a method to analyze sources if it is possible to export projects to
39  * an Ant buildfile which compiles correctly.
40  */

41 public class SourceAnalyzer {
42     
43     /**
44      * Utility class.
45      */

46     private SourceAnalyzer() {
47
48     }
49
50     /**
51      * Check if source directories of project has cycles or if there are
52      * dependencies between them that are not conform with classpath order.
53      *
54      * <p>
55      * NOTE: Unused references in classes are not considered if they cause
56      * cycles or classpath order problems. This is because this class analyzes
57      * the bytecode and indeed the compiler throws unused references away.
58      */

59     public static void checkCycles(IJavaProject currentProject,
60             EclipseClasspath classpath, Shell shell) {
61
62         StringBuffer JavaDoc message = new StringBuffer JavaDoc();
63         Map JavaDoc src2dir = new TreeMap JavaDoc(); // map string to string
64
Map JavaDoc srcdir2classes = new TreeMap JavaDoc(); // map string to Set of strings
65

66         determineSources(currentProject, classpath, src2dir, srcdir2classes);
67
68         Map JavaDoc srcdir2sourcedirs = determineRequiredSrcDirs(src2dir,
69                 srcdir2classes);
70         String JavaDoc projectName = currentProject.getProject().getName();
71
72         List JavaDoc cycle = new ArrayList JavaDoc();
73         if (isCyclic(srcdir2sourcedirs, cycle)) {
74             showCycleWarning(projectName, shell, cycle, message);
75             return;
76         }
77
78         checkBuildOrder(classpath, projectName, shell, srcdir2sourcedirs);
79     }
80
81     /**
82      * Determine all sources belonging to a source directory.
83      */

84     private static void determineSources(IJavaProject currentProject,
85             EclipseClasspath classpath, Map JavaDoc src2dir, Map JavaDoc srcdir2classes) {
86
87         for (int i = 0; i < classpath.srcDirs.size(); i++) {
88             String JavaDoc srcDir = (String JavaDoc) classpath.srcDirs.get(i);
89             String JavaDoc classDir = (String JavaDoc) classpath.classDirs.get(i);
90             if (EclipseClasspath.isReference(srcDir)) {
91                 continue;
92             }
93             File JavaDoc dir;
94             if (srcDir.equals(".")) { //$NON-NLS-1$
95
dir = currentProject.getResource().getLocation().toFile();
96             } else {
97                 IFile file = currentProject.getProject().getFile(srcDir);
98                 dir = file.getLocation().toFile();
99             }
100             if (EclipseClasspath.isLinkedResource(srcDir)) {
101                 String JavaDoc link = classpath.resolveLinkedResource(srcDir);
102                 dir = new File JavaDoc(link);
103             }
104             Set JavaDoc sources = findFiles(dir, ".java"); //$NON-NLS-1$
105

106             // find all required classfiles for each source directory
107
for (Iterator JavaDoc iter = sources.iterator(); iter.hasNext();) {
108                 String JavaDoc srcFile = (String JavaDoc) iter.next();
109                 src2dir.put(srcFile, srcDir);
110                 IFile classFile = currentProject.getProject().getFile(
111                         classDir + '/' + srcFile + ".class"); //$NON-NLS-1$
112
if (!classFile.exists()) {
113                     // project was not compiled, check not possible
114
continue;
115                 }
116                 Set JavaDoc classes = (Set JavaDoc) srcdir2classes.get(srcDir);
117                 if (classes == null) {
118                     classes = new TreeSet JavaDoc();
119                 }
120                 classes.addAll(getRequiredClasses(classFile));
121                 srcdir2classes.put(srcDir, classes);
122             }
123         }
124     }
125
126     /**
127      * Determine for each source directory which other source directories it
128      * requires.
129      *
130      * @return Map string to Set of strings. (Maps source dir to Set of required
131      * source dirs.)
132      */

133     private static Map JavaDoc determineRequiredSrcDirs(Map JavaDoc src2dir, Map JavaDoc srcdir2classes) {
134
135         Map JavaDoc srcdir2sourcedirs = new TreeMap JavaDoc(); // map string to Set of strings
136
for (Iterator JavaDoc iter = srcdir2classes.keySet().iterator(); iter.hasNext();) {
137             String JavaDoc srcDir = (String JavaDoc) iter.next();
138             Set JavaDoc classes = (Set JavaDoc) srcdir2classes.get(srcDir);
139             for (Iterator JavaDoc iterator = classes.iterator(); iterator.hasNext();) {
140                 String JavaDoc classname = (String JavaDoc) iterator.next();
141                 String JavaDoc classsrc = (String JavaDoc) src2dir.get(classname);
142                 // don't add reference to itself
143
if (classsrc != null && !classsrc.equals(srcDir)) {
144                     Set JavaDoc sourcedirs = (Set JavaDoc) srcdir2sourcedirs.get(srcDir);
145                     if (sourcedirs == null) {
146                         sourcedirs = new TreeSet JavaDoc();
147                     }
148                     sourcedirs.add(classsrc);
149                     srcdir2sourcedirs.put(srcDir, sourcedirs);
150                 }
151             }
152         }
153         return srcdir2sourcedirs;
154     }
155
156     private static void showCycleWarning(String JavaDoc projectName, Shell shell,
157             List JavaDoc cycle, StringBuffer JavaDoc message) {
158
159         String JavaDoc m = MessageFormat.format(DataTransferMessages.SourceAnalyzer_0,
160                 new String JavaDoc[] { projectName });
161         message.append(m);
162         message.append(ExportUtil.NEWLINE);
163
164         // print cycle path
165
for (Iterator JavaDoc iter = cycle.iterator(); iter.hasNext();) {
166             String JavaDoc s = (String JavaDoc) iter.next();
167             s = EclipseClasspath.getLinkedResourceName(s);
168             message.append(s);
169             message.append(" -> "); //$NON-NLS-1$
170
}
171         message.append(EclipseClasspath.getLinkedResourceName((String JavaDoc) cycle
172                 .get(0)));
173
174         MessageDialog.openWarning(shell, DataTransferMessages.SourceAnalyzer_1,
175                 message.toString());
176     }
177
178     /**
179      * Check if build order is correct.
180      */

181     private static void checkBuildOrder(EclipseClasspath classpath,
182             String JavaDoc projectName, Shell shell, Map JavaDoc srcdir2sourcedirs) {
183
184         for (Iterator JavaDoc iter = srcdir2sourcedirs.keySet().iterator(); iter
185                 .hasNext();) {
186             String JavaDoc srcdir = (String JavaDoc) iter.next();
187             Set JavaDoc sourcedirs = (Set JavaDoc) srcdir2sourcedirs.get(srcdir);
188             int classpathIndex = classpath.srcDirs.indexOf(srcdir);
189             for (Iterator JavaDoc iterator = sourcedirs.iterator(); iterator.hasNext();) {
190                 String JavaDoc requiredSrc = (String JavaDoc) iterator.next();
191                 int i = classpath.srcDirs.indexOf(requiredSrc);
192                 if (i > classpathIndex) // wrong order
193
{
194                     String JavaDoc s = MessageFormat.format(
195                             DataTransferMessages.SourceAnalyzer_3,
196                             new String JavaDoc[] { projectName });
197
198                     MessageDialog.openWarning(shell,
199                             DataTransferMessages.SourceAnalyzer_2, s
200                                     + ExportUtil.NEWLINE + requiredSrc
201                                     + " <-> " + srcdir //$NON-NLS-1$
202
+ ExportUtil.NEWLINE);
203                     break;
204                 }
205             }
206         }
207     }
208
209     /**
210      * Find all classes that are required by given class file.
211      *
212      * @param file
213      * a ".class" file
214      * @return set of strings, each contains a full qualified classname (forward
215      * slash as package separator)
216      */

217     public static Set JavaDoc getRequiredClasses(IFile file) {
218
219         Set JavaDoc classes = new TreeSet JavaDoc();
220         IClassFile classFile = JavaCore.createClassFileFrom(file);
221         IClassFileReader reader = ToolFactory.createDefaultClassFileReader(
222                 classFile, IClassFileReader.CONSTANT_POOL);
223         if (reader == null) {
224             // class not compiled
225
return classes;
226         }
227         IConstantPool pool = reader.getConstantPool();
228         for (int i = 0; i < pool.getConstantPoolCount(); i++) {
229             if (pool.getEntryKind(i) == IConstantPoolConstant.CONSTANT_Class) {
230                 IConstantPoolEntry entry = pool.decodeEntry(i);
231                 String JavaDoc classname = new String JavaDoc(entry.getClassInfoName());
232                 // don't return inner classes
233
int index = classname.indexOf('$');
234                 if (index != -1) {
235                     classname = classname.substring(0, index);
236                 }
237                 classes.add(classname);
238             }
239         }
240         return classes;
241     }
242
243     /**
244      * Find all files with particular extension under given directory.
245      *
246      * @param dir
247      * directory to start search
248      * @param extension
249      * extension to search
250      * @return filenames relative to dir (without extension and with forward
251      * slashes)
252      */

253     public static Set JavaDoc findFiles(File JavaDoc dir, String JavaDoc extension) {
254
255         Set JavaDoc visited = new TreeSet JavaDoc();
256         findFiles(dir, dir, extension, visited);
257         return visited;
258     }
259
260     private static void findFiles(File JavaDoc base, File JavaDoc dir, String JavaDoc extension,
261             Set JavaDoc visited) {
262
263         if (dir.isDirectory()) {
264             File JavaDoc[] children = dir.listFiles();
265             for (int i = 0; i < children.length; i++) {
266                 findFiles(base, children[i], extension, visited);
267             }
268         } else if (dir.getAbsolutePath().endsWith(extension)) {
269             // remove base directory
270
String JavaDoc filename = ExportUtil.removePrefixAndSuffix(dir
271                     .getAbsolutePath(),
272                     base.getAbsolutePath() + File.separator, extension);
273             visited.add(filename.replace('\\', '/'));
274         }
275     }
276
277     /**
278      * Check if given graph that is described through a map is cyclic.
279      *
280      * @param srcdir2sourcedirs
281      * Maps string to set of strings. The keys are the graph nodes
282      * which are mapped to its neighbours.
283      * @param cycle
284      * filled with name of nodes which cause cycle
285      */

286     private static boolean isCyclic(Map JavaDoc srcdir2sourcedirs, List JavaDoc cycle) {
287
288         return !isAcyclic(srcdir2sourcedirs, cycle);
289     }
290
291     private static boolean isAcyclic(Map JavaDoc srcdir2sourcedirs, List JavaDoc cycle) {
292
293         // standard graph theory
294
List JavaDoc visited = new ArrayList JavaDoc();
295         List JavaDoc exited = new ArrayList JavaDoc();
296
297         for (Iterator JavaDoc iter = srcdir2sourcedirs.keySet().iterator(); iter
298                 .hasNext();) {
299             String JavaDoc srcdir = (String JavaDoc) iter.next();
300             if (!visited.contains(srcdir)) {
301                 if (circleSearch(srcdir, srcdir2sourcedirs, visited, exited,
302                         cycle)) {
303                     return false;
304                 }
305             }
306         }
307         return true;
308     }
309
310     private static boolean circleSearch(String JavaDoc srcdir, Map JavaDoc srcdir2sourcedirs,
311             List JavaDoc visited, List JavaDoc exited, List JavaDoc cycle) {
312
313         boolean res = false;
314         visited.add(srcdir);
315         cycle.add(srcdir);
316
317         Set JavaDoc sourcedirs = (Set JavaDoc) srcdir2sourcedirs.get(srcdir); // neighbours
318
if (sourcedirs != null) {
319             for (Iterator JavaDoc iter = sourcedirs.iterator(); iter.hasNext();) {
320                 String JavaDoc src = (String JavaDoc) iter.next();
321                 if (!visited.contains(src)) {
322                     res = circleSearch(src, srcdir2sourcedirs, visited, exited,
323                             cycle);
324                 } else if (!exited.contains(src)) {
325                     res = true;
326                 }
327                 if (res) {
328                     break;
329                 }
330             }
331         }
332         if (!res) {
333             cycle.clear();
334         }
335         exited.add(srcdir);
336         return res;
337     }
338 }
339
Popular Tags