/*
* Copyright 2003-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.dsl.checker;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.codehaus.groovy.ast.Comment;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.codehaus.jdt.groovy.model.GroovyNature;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.groovy.core.util.ContentTypeUtils;
import org.eclipse.jdt.groovy.search.TypeInferencingVisitorFactory;
import org.eclipse.jdt.groovy.search.TypeInferencingVisitorWithRequestor;
import org.eclipse.jdt.internal.core.util.Util;
/**
* Performs static checking on all groovy files contained in the resource passed in
* @author andrew
* @created Aug 29, 2011
*/
public class ResourceTypeChecker {
class CheckerVisitor implements IResourceVisitor {
private IProgressMonitor monitor;
CheckerVisitor(IProgressMonitor monitor) {
this.monitor = monitor;
}
public boolean visit(IResource resource) throws CoreException {
if (resource.isDerived()) {
return false;
}
handler.handleResourceStart(resource);
if (resource.getType() == IResource.FILE && ContentTypeUtils.isGroovyLikeFileName(resource.getName())) {
if (Util.isExcluded(resource, includes, excludes)) {
return false;
}
GroovyCompilationUnit unit = (GroovyCompilationUnit) JavaCore.create((IFile) resource);
if (unit != null && unit.isOnBuildPath()) {
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
monitor.subTask(resource.getName());
handler.setResource((IFile) resource);
Map<Integer, String> commentsMap = findComments(unit);
StaticTypeCheckerRequestor requestor = new StaticTypeCheckerRequestor(handler, commentsMap, onlyAssertions);
TypeInferencingVisitorWithRequestor visitor = new TypeInferencingVisitorFactory().createVisitor(unit);
try {
unit.becomeWorkingCopy(monitor);
visitor.visitCompilationUnit(requestor);
} finally {
unit.discardWorkingCopy();
}
}
}
return true;
}
private Map<Integer, String> findComments(GroovyCompilationUnit unit) {
List<Comment> comments = unit.getModuleNode().getContext().getComments();
Map<Integer, String> allComments = new HashMap<Integer, String>(comments.size());
for (Comment comment : comments) {
StringTokenizer stok = new StringTokenizer(comment.toString());
String type = null;
if (stok.hasMoreTokens()) {
// consume the comment start
String val = stok.nextToken();
int typeIndex = val.indexOf("TYPE:");
if (typeIndex > 0) {
type = val.substring(typeIndex + "TYPE:".length());
if (type.length() == 0) {
type = null;
}
}
}
String candidate;
if (stok.hasMoreTokens() && (candidate = stok.nextToken()).startsWith("TYPE:")) {
// may or may not have a space after the colon
if (candidate.equals("TYPE:")) {
if (stok.hasMoreTokens()) {
type = stok.nextToken();
}
} else {
String[] split = candidate.split("\\:");
type = split[1];
}
}
if (type != null) {
allComments.put(comment.sline, type);
}
}
return allComments;
}
}
private final IStaticCheckerHandler handler;
private final List<IResource> resources;
protected boolean onlyAssertions;
protected final char[][] includes;
protected final char[][] excludes;
public ResourceTypeChecker(IStaticCheckerHandler handler, String projectName, char[][] includes, char[][] excludes, boolean onlyAssertions) {
this(handler, createProject(projectName), includes, excludes, onlyAssertions);
}
public ResourceTypeChecker(IStaticCheckerHandler handler, List<IResource> resources, char[][] includes, char[][] excludes, boolean onlyAssertions) {
this.handler = handler;
this.resources = resources;
this.includes = includes;
this.excludes = excludes;
this.onlyAssertions = onlyAssertions;
}
private static List<IResource> createProject(String projectName) {
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
if (!GroovyNature.hasGroovyNature(project)) {
throw new IllegalArgumentException("Invalid project: " + projectName);
}
return Collections.<IResource>singletonList(project);
}
/**
* Performs the tpe checking on the selected resources.
* @param monitor progress monitor, can be null
* @return true iff no type problems were found
* @throws CoreException
*/
public boolean doCheck(IProgressMonitor monitor) throws CoreException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
monitor.beginTask("Static type analysis", resources.size());
for (IResource resource : resources) {
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
CheckerVisitor visitor = new CheckerVisitor(monitor);
resource.accept(visitor);
monitor.worked(1);
}
return handler.finish(null);
}
}