/* ==================================================================== * * The ObjectStyle Group Software License, Version 1.0 * * Copyright (c) 2006 The ObjectStyle Group, * and individual authors of the software. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * ObjectStyle Group (http://objectstyle.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "ObjectStyle Group" and "Cayenne" * must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact andrus@objectstyle.org. * * 5. Products derived from this software may not be called "ObjectStyle" * nor may "ObjectStyle" appear in their names without prior written * permission of the ObjectStyle Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the ObjectStyle Group. For more * information on the ObjectStyle Group, please see * <http://objectstyle.org/>. * */ package org.objectstyle.wolips.core.tobeintregrated; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jface.operation.IRunnableWithProgress; public class MethodSearch implements IRunnableWithProgress { private static final int MAX_RUN = 100; private static final String SOURCE_FOLDER = "src"; private static final String COMPONENTS_FOLDER = "Components"; private IJavaProject javaProject; private Hashtable<String, List<String>> declaredMethods; // key = // declaredClassName.methodName(paramterTypes), // value = ArrayList{handleID, // methodName, returnType, overrids} private Hashtable<String, List<String>> usedMethods; // key = // declaredClassName.methodName(paramterTypes), // value = ArrayList{handleIDs} private Hashtable<String, List<String>> unusedMethods; // key = // declaredClassName.methodName(paramterTypes), // value = ArrayList{handleID, // methodName, returnType} private Hashtable<String, List<String>> publicClassVariables; // key = // declaredClassName.variableName", // value = ArrayList{handleID, type} private Hashtable<String, String> possibleWodMethods; // key = wodName.methodCall, value = // "null" private Hashtable<String, List<String>> unusedClassVariables; // key = // declaredClassName.variableName", // value = ArrayList{handleID, type} private Hashtable<String, String> classDependencies; // key = className, value = // extendedByClassName private ASTParser parser; private IProgressMonitor monitor; private IProgressMonitor taskMonitor; private Pattern intPat = Pattern.compile("(.+) = (\\d+);"); private Pattern booleanPat = Pattern.compile("(.+) = (true)|(false);"); private Pattern stringPat = Pattern.compile("(.+) = \"(.*)\";"); private Pattern woInternPat = Pattern.compile("(.+) = (.+)\\.@(\\w+);"); private Pattern normPat = Pattern.compile("(.+) = (.+);"); private Pattern appPat = Pattern.compile("(.+) = application\\.(.+);"); private Pattern sesPat = Pattern.compile("(.+) = session\\.(.+);"); private Pattern commentPat = Pattern.compile("//TODO Not used: (.*)\n"); /** * Constructor. * * @param javaProject */ public MethodSearch(IJavaProject javaProject) { this.javaProject = javaProject; unusedMethods = new Hashtable<String, List<String>>(); declaredMethods = new Hashtable<String, List<String>>(); usedMethods = new Hashtable<String, List<String>>(); publicClassVariables = new Hashtable<String, List<String>>(); possibleWodMethods = new Hashtable<String, String>(); unusedClassVariables = new Hashtable<String, List<String>>(); classDependencies = new Hashtable<String, String>(); this.monitor = new NullProgressMonitor(); } /** * Method is started when IProgressMonitorDialog.run() is called. */ public void run(IProgressMonitor progressMonitor) throws InvocationTargetException, InterruptedException { this.taskMonitor = progressMonitor; taskMonitor.beginTask("Search for unused WO code", MAX_RUN); if (!searchForMethods()) { throw new InterruptedException(); } taskMonitor.subTask("checking declared methods against vocated methods"); if (!fillUnusedMethodsHash()) { throw new InterruptedException(); } if (!checkSettersAndGettersFromClassVariables()) { throw new InterruptedException(); } if (taskMonitor.isCanceled()) { throw new InterruptedException(); } if (!checkWodMethods()) { throw new InterruptedException(); } taskMonitor.done(); } /** * Method searches all .java and .wod files and fills the hashtables for * later use. * * @return true if taskMonitor is not canceled * @throws InvocationTargetException */ private boolean searchForMethods() throws InvocationTargetException { // System.out.println("############### SearchForMethods() // #################"); // System.out.println("############### Java #################"); try { // project source folder: java files IPackageFragmentRoot[] packageFragmentRoots = javaProject.getAllPackageFragmentRoots(); IPackageFragmentRoot scr = null; for (int i = 0; i < packageFragmentRoots.length; i++) { IPackageFragmentRoot root = packageFragmentRoots[i]; if (root.getHandleIdentifier().equals("=" + javaProject.getElementName() + "/" + SOURCE_FOLDER)) scr = root; } if (scr != null) { // IPackageFragments: IJavaElement[] IPackageFragments = scr.getChildren(); for (int i = 0; i < IPackageFragments.length; i++) { IPackageFragment packageFragment = (IPackageFragment) IPackageFragments[i]; // ICompilationUnits: ICompilationUnit[] iCompUnits = packageFragment.getCompilationUnits(); for (int j = 0; j < iCompUnits.length; j++) { ICompilationUnit iComp = iCompUnits[j]; String packageName = ""; IPackageDeclaration[] packageBindings = iComp.getPackageDeclarations(); for (int k = 0; k < packageBindings.length; k++) { packageName += packageBindings[k]; } // exclusive stubs if (!iComp.getElementName().startsWith("_")) { parser = ASTParser.newParser(AST.JLS3); parser.setResolveBindings(true); parser.setSource(iComp); CompilationUnit astRoot = (CompilationUnit) parser.createAST(monitor); // //System.out.println(" - // "+iComp.getElementName()); taskMonitor.subTask("searching " + iComp.getElementName()); // check the compilation unit for used and declared // methods and for class variables ASTVisitor astVisitor = new ASTMethodExplorer(usedMethods, declaredMethods, publicClassVariables, classDependencies, iComp); astRoot.accept(astVisitor); } } taskMonitor.worked(1); if (taskMonitor.isCanceled()) return false; } } // Components files: .wod // System.out.println("############### WOD #################"); Object[] nonJava = javaProject.getNonJavaResources(); for (int i = 0; i < nonJava.length; i++) { Object object = nonJava[i]; if (object instanceof IFolder) { IFolder folder = (IFolder) object; // Components if (folder.getName().equals(COMPONENTS_FOLDER)) { IResource[] res = folder.members(); for (int j = 0; j < res.length; j++) { // .wo if (res[j] instanceof IFolder) { IFolder wo = (IFolder) res[j]; // .wod String wodName = wo.getName().substring(0, wo.getName().indexOf(".")) + ".wod"; IFile wod = wo.getFile(wodName); if (wod != null) { // //System.out.println(" - // "+wod.getName()); taskMonitor.subTask("searching " + wod.getName()); checkWOD(wod); } } } } } taskMonitor.worked(1); if (taskMonitor.isCanceled()) return false; } } catch (Exception e) { e.printStackTrace(); throw new InvocationTargetException(e, e.toString()); } return true; } /** * Method checks a .wod file for method or variable calls * * @param wod * @throws CoreException * @throws IOException * @return true if taskMonitor is not canceled */ private boolean checkWOD(IFile wod) throws CoreException, IOException { String wodName = wod.getName().substring(0, wod.getName().indexOf(".")); BufferedReader in = new BufferedReader(new InputStreamReader(wod.getContents())); String line; while ((line = in.readLine()) != null) { Matcher intMat = intPat.matcher(line); Matcher woInternMat = woInternPat.matcher(line); Matcher booleanMat = booleanPat.matcher(line); Matcher stringMat = stringPat.matcher(line); Matcher normMat = normPat.matcher(line); Matcher appMat = appPat.matcher(line); Matcher sesMat = sesPat.matcher(line); if (intMat.find()) { // do nothing } else if (woInternMat.find()) { // do nothing } else if (booleanMat.find()) { // do nothing } else if (stringMat.find()) { // do nothing } else if (appMat.find()) { // application methods possibleWodMethods.put("Application." + appMat.group(2), "null"); } else if (sesMat.find()) { // session methods possibleWodMethods.put("Session." + sesMat.group(2), "null"); } else if (normMat.find()) { // possible method or variable calls possibleWodMethods.put(wodName + "." + normMat.group(2), "null"); } if (taskMonitor.isCanceled()) { in.close(); return false; } } in.close(); return true; } /** * Method checks the declared methods against the used methods and fills the * unusedMethods-Hashtable. * * @return true if taskMonitor is not canceled */ private boolean fillUnusedMethodsHash() { // System.out.println("################## fillUnusedMethodsHash() " + // declaredMethods.size() + " ##################"); Enumeration keysEnum = declaredMethods.keys(); while (keysEnum.hasMoreElements()) { String key = (String) keysEnum.nextElement(); if (!usedMethods.containsKey(key)) { List<String> value = declaredMethods.get(key); String skip = value.get(3); if (skip.equals("false")) { unusedMethods.put(key, value); } } } taskMonitor.worked(1); if (taskMonitor.isCanceled()) return false; return true; } /** * Method removes the setters and getters for the classVariables (WO * specific). * * @return true if taskMonitor is not canceled */ private boolean checkSettersAndGettersFromClassVariables() { // System.out.println("################ Check Setter/Getter // ##################"); Enumeration keyEnum = publicClassVariables.keys(); while (keyEnum.hasMoreElements()) { String classVariable = (String) keyEnum.nextElement(); String[] comps = classVariable.split("\\."); String className = comps[0]; String methodName = comps[1]; methodName = Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1); String key; // setter ArrayList value = (ArrayList) publicClassVariables.get(classVariable); String arg = (String) value.get(1); key = className + ".set" + methodName + "(" + arg + ")"; unusedMethods.remove(key); // getter key = className + ".get" + methodName + "()"; unusedMethods.remove(key); } taskMonitor.worked(1); if (taskMonitor.isCanceled()) return false; return true; } /** * Method checks the possibleWodMethods against the unusedClassVaraibles and * unusedMethods * * @return true if taskMonitor is not canceled */ private boolean checkWodMethods() { // System.out.println("###################### checkWodMethods() " + // possibleWodMethods.size() + " ######################"); unusedClassVariables = new Hashtable<String, List<String>>(publicClassVariables); Enumeration keyEnum = possibleWodMethods.keys(); while (keyEnum.hasMoreElements()) { String call = (String) keyEnum.nextElement(); // //System.out.println("call: "+call); ArrayList<String> splitList = new ArrayList<String>(); String[] split = call.split("\\."); for (int i = 0; i < split.length; i++) { splitList.add(split[i]); } String className = splitList.remove(0); // direct class recursiveMethodFinder(className, new ArrayList<String>(splitList)); // extended by recursiveMethodFinder(classDependencies.get(className), splitList); } taskMonitor.worked(1); if (taskMonitor.isCanceled()) return false; return true; } /** * Recursive method for finding the appropriate methods and class variables * in java from the possibleWodMethods * * @param className * @param splitList */ private void recursiveMethodFinder(String className, List<String> splitList) { try { String local = splitList.remove(0); String key = className + "." + local; // key = "className.call" // local method call // declaredClassName.methodName(paramterTypes) String key2 = key + "()"; if (unusedMethods.containsKey(key2)) { unusedMethods.remove(key2); } // class variable in component if (publicClassVariables.containsKey(key)) { unusedClassVariables.remove(key); if (splitList.size() > 0) { // method of local variable => // recursion List<String> value = publicClassVariables.get(key); String newClass = value.get(1); recursiveMethodFinder(newClass, splitList); } else { // local variable // //System.out.println("local variable: "+className+" // "+local); } } // direct method call if (declaredMethods.containsKey(key2)) { if (splitList.size() > 0) { // return type of method => // recursion ArrayList value = (ArrayList) declaredMethods.get(key2); String newClass = (String) value.get(2); recursiveMethodFinder(newClass, splitList); } } // no method or variable call or method from stubs else { // //System.out.println("no method or variable: "+key); } } catch (Exception e) { e.printStackTrace(); } } /** * Method sets the comments for unused code at the beginnig of the * appropiate java file. * * @return true if taskMonitor is not canceled * @throws InvocationTargetException */ public boolean setComments() throws InvocationTargetException { // System.out.println("################### setComments() // ####################"); // methods Enumeration keysEnum = unusedMethods.keys(); while (keysEnum.hasMoreElements()) { String key = (String) keysEnum.nextElement(); ArrayList value = (ArrayList) unusedMethods.get(key); String handleID = (String) value.get(0); String name = key.split("\\.")[1]; try { ICompilationUnit iCompUnit = (ICompilationUnit) JavaCore.create(handleID); String source = iCompUnit.getBuffer().getContents(); String comment = "//TODO Not used: Method " + name + "\n"; if (checkComment(iCompUnit, comment)) { iCompUnit.getBuffer().setContents(comment + source); iCompUnit.save(monitor, true); } taskMonitor.worked(1); if (taskMonitor.isCanceled()) { return false; } } catch (Exception e) { throw new InvocationTargetException(e, e.toString()); } } // class variables keysEnum = unusedClassVariables.keys(); while (keysEnum.hasMoreElements()) { String key = (String) keysEnum.nextElement(); ArrayList value = (ArrayList) unusedClassVariables.get(key); String handleID = (String) value.get(0); String name = key.split("\\.")[1]; try { ICompilationUnit iCompUnit = (ICompilationUnit) JavaCore.create(handleID); String source = iCompUnit.getBuffer().getContents(); String comment = "//TODO Not used: Class variable " + name + "\n"; if (checkComment(iCompUnit, comment)) { iCompUnit.getBuffer().setContents(comment + source); iCompUnit.save(monitor, true); } taskMonitor.worked(1); if (taskMonitor.isCanceled()) { return false; } } catch (Exception e) { throw new InvocationTargetException(e, e.toString()); } } return true; } /** * Method checks if this comment is already present in the java file. * * @param iCompUnit * @param comment * @return true if comment is not already present * @throws Exception */ private boolean checkComment(ICompilationUnit iCompUnit, String comment) throws Exception { String source = iCompUnit.getBuffer().getContents(); Matcher mat = commentPat.matcher(source); while (mat.find()) { if (mat.group().equals(comment)) { return false; } } return true; } /** * Method writes a text file with a list of all unused code to the given * output file. * * @param outputFile */ public void writePossiblyUnusedMethodsToFile(File outputFile) throws InvocationTargetException { try { NameComparator comparator = new NameComparator(); FileWriter writer = new FileWriter(outputFile); writer.write("############ unused methods #############\n\n"); String[] keyArray = unusedMethods.keySet().toArray(new String[0]); Arrays.sort(keyArray, comparator); for (int i = 0; i < keyArray.length; i++) { String key = keyArray[i]; writer.write(key + "\n"); } writer.write("\n########## unused class variables ##########\n\n"); keyArray = unusedClassVariables.keySet().toArray(new String[0]); Arrays.sort(keyArray, comparator); for (int i = 0; i < keyArray.length; i++) { String key = keyArray[i]; writer.write(key + "\n"); } writer.close(); } catch (IOException e) { e.printStackTrace(); throw new InvocationTargetException(e, e.toString()); } } }