/* * Copyright (c) 2013, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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 com.google.dart.engine.internal.task; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.context.AnalysisException; import com.google.dart.engine.element.CompilationUnitElement; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.error.AnalysisErrorListener; import com.google.dart.engine.error.BooleanErrorListener; import com.google.dart.engine.internal.context.IncrementalAnalysisCache; import com.google.dart.engine.internal.context.InternalAnalysisContext; import com.google.dart.engine.internal.resolver.IncrementalResolver; import com.google.dart.engine.internal.resolver.TypeProvider; import com.google.dart.engine.parser.IncrementalParser; import com.google.dart.engine.scanner.CharSequenceReader; import com.google.dart.engine.scanner.CharacterReader; import com.google.dart.engine.scanner.IncrementalScanner; import com.google.dart.engine.source.Source; /** * Instances of the class {@code IncrementalAnalysisTask} incrementally update existing analysis. */ public class IncrementalAnalysisTask extends AnalysisTask { /** * The information used to perform incremental analysis. */ private final IncrementalAnalysisCache cache; /** * The compilation unit that was produced by incrementally updating the existing unit. */ private CompilationUnit updatedUnit; /** * Initialize a newly created task to perform analysis within the given context. * * @param context the context in which the task is to be performed * @param cache the incremental analysis cache used to perform the analysis */ public IncrementalAnalysisTask(InternalAnalysisContext context, IncrementalAnalysisCache cache) { super(context); this.cache = cache; } @Override public <E> E accept(AnalysisTaskVisitor<E> visitor) throws AnalysisException { return visitor.visitIncrementalAnalysisTask(this); } /** * Return the incremental cache used for analysis * * @return the cache */ public IncrementalAnalysisCache getCache() { return cache; } /** * Return the compilation unit that was produced by incrementally updating the existing * compilation unit, or {@code null} if the task has not yet been performed, could not be * performed, or if an exception occurred. * * @return the compilation unit */ public CompilationUnit getCompilationUnit() { return updatedUnit; } /** * Return the source that is to be incrementally analyzed. * * @return the source */ public Source getSource() { return cache != null ? cache.getSource() : null; } @Override protected String getTaskDescription() { return "incremental analysis " + (cache != null ? cache.getSource() : "null"); } @Override protected void internalPerform() throws AnalysisException { if (cache == null) { return; } // Only handle small changes if (cache.getOldLength() > 0 || cache.getNewLength() > 30) { return; } // Produce an updated token stream CharacterReader reader = new CharSequenceReader(cache.getNewContents()); BooleanErrorListener errorListener = new BooleanErrorListener(); IncrementalScanner scanner = new IncrementalScanner(cache.getSource(), reader, errorListener); scanner.rescan( cache.getResolvedUnit().getBeginToken(), cache.getOffset(), cache.getOldLength(), cache.getNewLength()); if (errorListener.getErrorReported()) { return; } // Produce an updated AST IncrementalParser parser = new IncrementalParser( cache.getSource(), scanner.getTokenMap(), AnalysisErrorListener.NULL_LISTENER); updatedUnit = parser.reparse( cache.getResolvedUnit(), scanner.getLeftToken(), scanner.getRightToken(), cache.getOffset(), cache.getOffset() + cache.getOldLength()); // Update the resolution TypeProvider typeProvider = getTypeProvider(); if (updatedUnit != null && typeProvider != null) { CompilationUnitElement element = updatedUnit.getElement(); if (element != null) { LibraryElement library = element.getLibrary(); if (library != null) { IncrementalResolver resolver = new IncrementalResolver( library, cache.getSource(), typeProvider, errorListener); resolver.resolve(parser.getUpdatedNode()); } } } } /** * Return the type provider used for incremental resolution. * * @return the type provider (or {@code null} if an exception occurs) */ private TypeProvider getTypeProvider() { try { return getContext().getTypeProvider(); } catch (AnalysisException exception) { return null; } } }