/* ====================================================================
*
* 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());
}
}
}