| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.royale.compiler.clients.problems; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| import org.apache.royale.compiler.config.ICompilerProblemSettings; |
| import org.apache.royale.compiler.problems.AbstractSemanticProblem; |
| import org.apache.royale.compiler.problems.CodegenInternalProblem; |
| import org.apache.royale.compiler.problems.CodegenProblem; |
| import org.apache.royale.compiler.problems.CompilerProblemSeverity; |
| import org.apache.royale.compiler.problems.ICompilerProblem; |
| import org.apache.royale.compiler.problems.IParserProblem; |
| import org.apache.royale.compiler.problems.SemanticWarningProblem; |
| import org.apache.royale.compiler.problems.StrictSemanticsProblem; |
| import org.apache.royale.compiler.problems.UnfoundPropertyProblem; |
| import org.apache.royale.compiler.problems.collections.CompositeProblemFilter; |
| import org.apache.royale.compiler.problems.collections.FilteredIterator; |
| import com.google.common.collect.Iterables; |
| |
| /** |
| * A ProblemQuery presents a higher-level view of a problem |
| * stream; it offers facilities to enable or disable diagnostics |
| * and presents filtered and (optionally) sorted views of the |
| * underlying problem stream. |
| */ |
| public class ProblemQuery |
| { |
| /** |
| * Construct a ProblemQuery. |
| */ |
| public ProblemQuery() |
| { |
| this(null); |
| } |
| |
| /** |
| * Construct a ProblemQuery with settings to control how compiler problems |
| * are reported. |
| * |
| * @param problemSettings configuration settings to control how compiler |
| * problems are reported. |
| */ |
| public ProblemQuery(ICompilerProblemSettings problemSettings) |
| { |
| this.problems = new ArrayList<ICompilerProblem>(); |
| |
| // An empty problem set is sorted. |
| this.sorted = true; |
| |
| // Start with no restrictions on the problems presented. |
| this.problemFilter = new ProblemFilterClassCriteria(); |
| |
| this.problemSettings = problemSettings; |
| } |
| |
| /** The underlying problem collection. */ |
| private ArrayList<ICompilerProblem> problems; |
| |
| /** |
| * Set to true when the problem collection |
| * is known to be in sorted order. |
| */ |
| private boolean sorted; |
| |
| /** |
| * Problems being rejected as a class. |
| */ |
| private ProblemFilterClassCriteria problemFilter; |
| |
| /** |
| * Configuration settings that control how errors are |
| * reported. |
| */ |
| private ICompilerProblemSettings problemSettings; |
| |
| /** |
| * Gets the list of compiler problems, with no filtering or sorting. |
| */ |
| public List<ICompilerProblem> getProblems() |
| { |
| return problems; |
| } |
| |
| /** |
| * Enable or disable strict semantics mode diagnostics. |
| * @param isStrict - if true, strict semantics mode |
| * diagnostics will appear in the filtered diagnostics. |
| */ |
| public void setShowStrictSemantics(boolean isStrict) |
| { |
| setShowProblemByClass(StrictSemanticsProblem.class, isStrict); |
| } |
| |
| /** |
| * Enable or disable semantic warnings. |
| * @param showWarnings - if true, semantic warnings |
| * will appear in the filtered diagnostics. |
| */ |
| public void setShowWarnings(boolean showWarnings) |
| { |
| setShowProblemByClass(SemanticWarningProblem.class, showWarnings); |
| } |
| |
| /** |
| * Enable or disable internal error display. |
| * @param showInternalErrors - if true, internal error |
| * diagnostics will appear in the filtered diagnostics. |
| */ |
| public void setShowInternalErrors(boolean showInternalErrors) |
| { |
| setShowProblemByClass(CodegenInternalProblem.class,showInternalErrors); |
| } |
| |
| /** |
| * Enable or disable display of "problems" that |
| * are used for compiler internal processing but |
| * have no relevance to the user. |
| * @param showIrrelevantProblems - if true, non-user-relevant |
| * diagnostics will appear in the filtered diagnostics. |
| */ |
| public void setShowIrrelevantProblems(boolean showIrrelevantProblems) |
| { |
| setShowProblemByClass(UnfoundPropertyProblem.class,showIrrelevantProblems); |
| } |
| |
| /** |
| * Enable or disable display of a specific problem class or superclass. |
| * @param problemClass - the problem class/superclass of interest. |
| * @param enable - if true, instances of this problem class |
| * will appear in the filtered diagnostics. |
| */ |
| public void setShowProblemByClass(Class<? extends ICompilerProblem> problemClass, final boolean enable) |
| { |
| if ( enable ) |
| { |
| problemFilter.removeRejectedClass(problemClass); |
| } |
| else |
| { |
| problemFilter.addRejectedClass(problemClass); |
| } |
| } |
| |
| /** |
| * Add an array of compiler problems to the problems collection. |
| * @param newProblems - the problems to add. |
| * @post the problems collection is marked "not sorted." |
| */ |
| public void addAll(ICompilerProblem[] newProblems) |
| { |
| addAll(Arrays.asList(newProblems)); |
| } |
| |
| /** |
| * Add a collection of compiler problems to the problems collection. |
| * @param newProblems - the problems to add. |
| * @post the problems collection is marked "not sorted." |
| */ |
| public void addAll(Iterable<ICompilerProblem> newProblems) |
| { |
| this.sorted = false; |
| Iterables.addAll(this.problems, newProblems); |
| } |
| |
| /** |
| * Add a single problem to the problems collection. |
| * @param problem - the problem to add. |
| * @post the problems collection is marked "not sorted." |
| */ |
| public void add(ICompilerProblem problem) |
| { |
| this.sorted = false; |
| this.problems.add(problem); |
| } |
| |
| /** |
| * Clear the underlying collection of problems. |
| */ |
| public void clear() |
| { |
| this.sorted = true; |
| this.problems.clear(); |
| } |
| |
| /** |
| * Get an iterator over the set of problems that |
| * are to be reported based on the current settings. |
| * Problem categorization is built into this filter so that problems |
| * categorized as "ignore" are filtered out. |
| * |
| * @return an Iterable<ICompilerProblem> over the |
| * subset of problems that are of interest to the client. |
| */ |
| public Iterable<ICompilerProblem> getFilteredProblems() |
| { |
| IProblemFilter filter = CompositeProblemFilter.and(this.problemFilter, new SkipSemanticCascadesFilter()); |
| CompilerProblemCategorizer categorizer = new CompilerProblemCategorizer(problemSettings); |
| |
| CodeGenErrorFilter cgef = new CodeGenErrorFilter(); |
| if (cgef.hasOtherErrors(problems)) |
| { |
| filter = CompositeProblemFilter.and(filter, cgef); |
| } |
| |
| return getFilteredProblems(CompositeProblemFilter.and(filter, new ErrorsAndWarningsFilter(categorizer))); |
| } |
| |
| /** |
| * Used internally as a base filter without any categorization built in. |
| * This is useful for clients that want to do the categorization themselves. |
| * Gets an iterator over the set of problems that are to be reported based |
| * on the current settings. |
| * |
| * @return an Iterable<ICompilerProblem> over the subset of problems |
| * that are of interest to the client. |
| */ |
| private Iterable<ICompilerProblem> getFilteredProblemsUsingBaseFilter() |
| { |
| return getFilteredProblems(CompositeProblemFilter.and(this.problemFilter, new SkipSemanticCascadesFilter())); |
| } |
| |
| /** |
| * Get an iterator over the set of problems that |
| * are to be reported based on the current settings. |
| * |
| * @param filter - the filter to apply. |
| * |
| * @return an Iterable<ICompilerProblem> over the |
| * subset of problems that are of interest to the client. |
| */ |
| private Iterable<ICompilerProblem> getFilteredProblems(IProblemFilter filter) |
| { |
| if (problemSettings != null) |
| filter = CompositeProblemFilter.and(filter, new ProblemSettingsFilter(problemSettings)); |
| |
| // Sort the problems so that the semantic cascade |
| // filter can find semantic problems that occur |
| // on the same line as parser problems. |
| sortProblems(); |
| return getProblemView(filter); |
| } |
| |
| /** |
| * Any problems to report after filtering? |
| * @return true if there are filtered problems. |
| */ |
| public boolean hasFilteredProblems() |
| { |
| return getFilteredProblems().iterator().hasNext(); |
| } |
| |
| /** |
| * Do any problems match the given filter? |
| * @param filter - the problem filter of interest. |
| */ |
| public boolean hasFilteredProblems(final IProblemFilter filter) |
| { |
| return getProblemView(filter).iterator().hasNext(); |
| } |
| |
| /** |
| * Get a filtered view of the underlying problems. |
| * @param filter - the filter to apply. |
| * @return an Iterable that supplies a filtered iterator of the problems. |
| */ |
| public Iterable<ICompilerProblem> getProblemView(final IProblemFilter filter) |
| { |
| return FilteredIterator.getFilteredIterable(problems, filter); |
| } |
| |
| /** |
| * Get an iterator over all internal errors. |
| */ |
| public Iterable<ICompilerProblem> getInternalErrors() |
| { |
| return getProblemView(new ProblemFilterClassCriteria(CodegenInternalProblem.class)); |
| } |
| |
| /** |
| * Sort the captured collection of problems so that we can filter out |
| * semantic problems that occur on the same line as parser problems. |
| */ |
| public void sortProblems() |
| { |
| if ( ! this.sorted ) |
| { |
| Collections.sort( this.problems, compareByPositionAndPhase); |
| } |
| |
| this.sorted = true; |
| } |
| |
| /** |
| * Categorize the compiler problems into two bins, errors and warning. |
| * |
| * @param errors the collection where the errors are added. |
| * @param warnings the collection where the warnings are added. |
| */ |
| public void getErrorsAndWarnings(Collection<ICompilerProblem> errors, |
| Collection<ICompilerProblem> warnings) |
| { |
| CompilerProblemCategorizer categorizer = new CompilerProblemCategorizer(problemSettings); |
| |
| // Get the filtered problems and classify the problems as either errors or |
| // warnings. |
| for (ICompilerProblem problem : getFilteredProblemsUsingBaseFilter()) |
| { |
| CompilerProblemSeverity severity = categorizer.getProblemSeverity(problem); |
| if (severity == CompilerProblemSeverity.ERROR) |
| errors.add(problem); |
| else if (severity == CompilerProblemSeverity.WARNING) |
| warnings.add(problem); |
| } |
| |
| } |
| |
| /** |
| * Test if any of the problems are errors. |
| * |
| * return true if any of the problems are errors, false otherwise. |
| */ |
| public boolean hasErrors() |
| { |
| CompilerProblemCategorizer categorizer = new CompilerProblemCategorizer(problemSettings); |
| |
| for (ICompilerProblem problem : getFilteredProblemsUsingBaseFilter()) |
| { |
| CompilerProblemSeverity severity = categorizer.getProblemSeverity(problem); |
| if (severity == CompilerProblemSeverity.ERROR) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * This Comparator compares problems based on three criteria: |
| * <li> file path |
| * <li> line number |
| * <li> problem class - IParserProblems are "less than" semantic problems. |
| */ |
| public static final Comparator<ICompilerProblem> compareByPositionAndPhase = |
| new Comparator<ICompilerProblem>() |
| { |
| @Override |
| public int compare(ICompilerProblem p1, ICompilerProblem p2) |
| { |
| int result = compareStrings( p1.getSourcePath(), p2.getSourcePath()); |
| |
| if ( result == 0 ) |
| result = p1.getLine() - p2.getLine(); |
| |
| if ( result == 0 ) |
| result = compareProblemClasses(p1, p2); |
| |
| return result; |
| } |
| |
| /** |
| * Look for configurations of problem classes that |
| * imply a definite arrangement; problems from the |
| * syntax analysis phase get ordered before problems |
| * from the semantic analysis phase so that the latter |
| * can be elided. |
| */ |
| private int compareProblemClasses(ICompilerProblem p1, ICompilerProblem p2) |
| { |
| if ( p1 instanceof IParserProblem ) |
| { |
| if ( !(p2 instanceof IParserProblem) ) |
| return -1; |
| } |
| else if ( p2 instanceof IParserProblem ) |
| { |
| if ( !(p1 instanceof IParserProblem) ) |
| return 1; |
| } |
| |
| return 0; |
| } |
| }; |
| |
| |
| /** |
| * SkipSemanticCascadesFilter accepts any problem except a |
| * SemanticProblem or CodegenProblem on a line that has already |
| * reported some kind of parser problem. |
| */ |
| private class SkipSemanticCascadesFilter implements IProblemFilter |
| { |
| private int lineNumber = -1; |
| private String fileName = null; |
| private boolean parserProblemOnLine = false; |
| |
| @Override |
| public boolean accept(ICompilerProblem problem) |
| { |
| if ( problem.getLine() != lineNumber || compareStrings(this.fileName, problem.getSourcePath()) != 0 ) |
| { |
| this.lineNumber = problem.getLine(); |
| this.fileName = problem.getSourcePath(); |
| } |
| else if ( parserProblemOnLine && (problem instanceof AbstractSemanticProblem || problem instanceof CodegenProblem) ) |
| { |
| // Skip this problem. |
| return false; |
| } |
| |
| this.parserProblemOnLine = problem instanceof IParserProblem; |
| return true; |
| } |
| |
| } |
| |
| /** |
| * ErrorsAndWarningsFilter accepts any problem that has a severity |
| * of error or warning. |
| */ |
| private class ErrorsAndWarningsFilter implements IProblemFilter |
| { |
| |
| ErrorsAndWarningsFilter(CompilerProblemCategorizer categorizer) |
| { |
| this.categorizer = categorizer; |
| } |
| |
| private final CompilerProblemCategorizer categorizer; |
| |
| @Override |
| public boolean accept(ICompilerProblem problem) |
| { |
| CompilerProblemSeverity severity = categorizer.getProblemSeverity(problem); |
| if (severity == CompilerProblemSeverity.ERROR || |
| severity == CompilerProblemSeverity.WARNING) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } |
| |
| /** |
| * Compare two strings, either or both of which may be null. |
| * @param s1 - the first string. |
| * @param s2 - the second string. |
| * @return -1 if s1 is less than s2, either in the string comparison |
| * sense or because s1 is null and s2 is not; 0 if the strings |
| * compare equal; 1 if s1 is greater than s1, by the reasoning above. |
| */ |
| private static int compareStrings(String s1, String s2) |
| { |
| if ( s1 == s2 ) |
| return 0; |
| else if ( s1 != null && s2 != null ) |
| return s1.compareTo(s2); |
| else |
| // One or the other is null, but not both. |
| return s1 == null? -1: 1; |
| } |
| } |