package org.rubypeople.rdt.internal.core.builder; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.jruby.ast.Node; import org.rubypeople.rdt.core.IRubyModelMarker; import org.rubypeople.rdt.core.IRubyProject; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.core.compiler.BuildContext; import org.rubypeople.rdt.core.compiler.CategorizedProblem; import org.rubypeople.rdt.core.compiler.CompilationParticipant; import org.rubypeople.rdt.core.compiler.ReconcileContext; import org.rubypeople.rdt.core.parser.warnings.RubyLintVisitor; import org.rubypeople.rdt.internal.core.parser.warnings.ConstantReassignmentVisitor; import org.rubypeople.rdt.internal.core.parser.warnings.CoreClassReOpening; import org.rubypeople.rdt.internal.core.parser.warnings.EmptyStatementVisitor; import org.rubypeople.rdt.internal.core.parser.warnings.Ruby19HashCommaSyntax; import org.rubypeople.rdt.internal.core.parser.warnings.Ruby19WhenStatements; public class RubyCodeAnalyzer extends CompilationParticipant { private Map<String, Long> timings; @Override public boolean isActive(IRubyProject project) { return true; } @Override public void buildStarting(BuildContext[] files, boolean isBatch, IProgressMonitor monitor) { timings = new HashMap<String, Long>(); SubMonitor sub = SubMonitor.convert(monitor, files.length); for (BuildContext context : files) { sub.subTask("Parsing and analyzing " + context.getFile().getLocation().toPortableString()); String contents = new String(context.getContents()); IRubyScript script = RubyCore.create(context.getFile()); long start = System.currentTimeMillis(); Node ast = context.getAST(); addTiming("AST Generation", System.currentTimeMillis() - start); List<CategorizedProblem> problems = parse(script, contents, ast); context.recordNewProblems(problems.toArray(new CategorizedProblem[problems.size()])); sub.worked(1); } if (RubyBuilder.DEBUG) { for (Map.Entry<String, Long> timing : timings.entrySet()) { System.out.println(timing.getKey() + " took " + timing.getValue() + "ms"); } timings.clear(); } sub.done(); } private List<CategorizedProblem> parse(IRubyScript script, String contents, Node ast) { if (ast == null) return Collections.emptyList(); List<CategorizedProblem> problems = new ArrayList<CategorizedProblem>(); long lintVTime = System.currentTimeMillis(); List<RubyLintVisitor> visitors = getLintVisitors(script, contents); addTiming("RubyLintVisitor creation", System.currentTimeMillis() - lintVTime); for (RubyLintVisitor rubyLintVisitor : visitors) { long start = System.currentTimeMillis(); rubyLintVisitor.acceptNode(ast); if (RubyBuilder.DEBUG) { addTiming(rubyLintVisitor.getClass().getSimpleName(), System.currentTimeMillis() - start); } problems.addAll(rubyLintVisitor.getProblems()); } return problems; } private void addTiming(String simpleName, long length) { if (timings == null) timings = new HashMap<String, Long>(); Long existingValue = timings.get(simpleName); if (existingValue == null) existingValue = 0L; timings.put(simpleName, existingValue + length); } private List<RubyLintVisitor> getLintVisitors(IRubyScript script, String contents) { List<RubyLintVisitor> visitors = new ArrayList<RubyLintVisitor>(); visitors.add(new EmptyStatementVisitor(contents)); visitors.add(new ConstantReassignmentVisitor(contents)); if (script != null) { visitors.add(new CoreClassReOpening(script, contents)); } visitors.add(new Ruby19WhenStatements(contents)); visitors.add(new Ruby19HashCommaSyntax(contents)); List<RubyLintVisitor> filtered = new ArrayList<RubyLintVisitor>(); for (RubyLintVisitor visitor : visitors) { if (visitor.isIgnored()) continue; filtered.add(visitor); } return filtered; } @Override public void reconcile(ReconcileContext context) { try { List<CategorizedProblem> problems = parse(context.getWorkingCopy(), context.getWorkingCopy().getSource(), context.getAST()); addProblems(context, IRubyModelMarker.RUBY_MODEL_PROBLEM_MARKER, problems); } catch (RubyModelException e) { RubyCore.log(e); } } }