/* * 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.context; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.internal.cache.DartEntry; import com.google.dart.engine.internal.cache.DartEntryImpl; import com.google.dart.engine.internal.cache.SourceEntry; import com.google.dart.engine.source.Source; import com.google.dart.engine.utilities.ast.AstComparator; /** * Instances of the class {@code IncrementalAnalysisCache} hold information used to perform * incremental analysis. * * @see AnalysisContextImpl#setChangedContents(Source, String, int, int, int) */ public class IncrementalAnalysisCache { /** * Determine if the incremental analysis result can be cached for the next incremental analysis. * * @param cache the prior incremental analysis cache * @param unit the incrementally updated compilation unit * @return the cache used for incremental analysis or {@code null} if incremental analysis results * cannot be cached for the next incremental analysis */ public static IncrementalAnalysisCache cacheResult(IncrementalAnalysisCache cache, CompilationUnit unit) { if (cache != null && unit != null) { return new IncrementalAnalysisCache( cache.librarySource, cache.source, unit, cache.newContents, cache.newContents, 0, 0, 0); } return null; } /** * Determine if the cache should be cleared. * * @param cache the prior cache or {@code null} if none * @param source the source being updated (not {@code null}) * @return the cache used for incremental analysis or {@code null} if incremental analysis cannot * be performed */ public static IncrementalAnalysisCache clear(IncrementalAnalysisCache cache, Source source) { if (cache == null || cache.getSource().equals(source)) { return null; } return cache; } /** * Determine if incremental analysis can be performed from the given information. * * @param cache the prior cache or {@code null} if none * @param source the source being updated (not {@code null}) * @param oldContents the original source contents prior to this update (may be {@code null}) * @param newContents the new contents after this incremental change (not {@code null}) * @param offset the offset at which the change occurred * @param oldLength the length of the text being replaced * @param newLength the length of the replacement text * @param sourceEntry the cached entry for the given source or {@code null} if none * @return the cache used for incremental analysis or {@code null} if incremental analysis cannot * be performed */ public static IncrementalAnalysisCache update(IncrementalAnalysisCache cache, Source source, String oldContents, String newContents, int offset, int oldLength, int newLength, SourceEntry sourceEntry) { // Determine the cache resolved unit Source librarySource = null; CompilationUnit unit = null; if (sourceEntry instanceof DartEntryImpl) { DartEntryImpl dartEntry = (DartEntryImpl) sourceEntry; Source[] librarySources = dartEntry.getLibrariesContaining(); if (librarySources.length == 1) { librarySource = librarySources[0]; if (librarySource != null) { unit = dartEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource); } } } // Create a new cache if there is not an existing cache or the source is different // or a new resolved compilation unit is available if (cache == null || !cache.getSource().equals(source) || unit != null) { if (unit == null) { return null; } if (oldContents == null) { if (oldLength != 0) { return null; } oldContents = newContents.substring(0, offset) + newContents.substring(offset + newLength); } return new IncrementalAnalysisCache( librarySource, source, unit, oldContents, newContents, offset, oldLength, newLength); } // Update the existing cache if the change is contiguous if (cache.oldLength == 0 && cache.newLength == 0) { cache.offset = offset; cache.oldLength = oldLength; cache.newLength = newLength; } else { if (cache.offset > offset || offset > cache.offset + cache.newLength) { return null; } cache.newLength += newLength - oldLength; } cache.newContents = newContents; return cache; } /** * Verify that the incrementally parsed and resolved unit in the incremental cache is structurally * equivalent to the fully parsed unit. * * @param cache the prior cache or {@code null} if none * @param source the source of the compilation unit that was parsed (not {@code null}) * @param unit the compilation unit that was just parsed * @return the cache used for incremental analysis or {@code null} if incremental analysis results * cannot be cached for the next incremental analysis */ public static IncrementalAnalysisCache verifyStructure(IncrementalAnalysisCache cache, Source source, CompilationUnit unit) { if (cache != null && unit != null && cache.source.equals(source)) { if (!AstComparator.equalNodes(cache.resolvedUnit, unit)) { return null; } } return cache; } private final Source librarySource; private final Source source; private final String oldContents; private final CompilationUnit resolvedUnit; private String newContents; private int offset; private int oldLength; private int newLength; public IncrementalAnalysisCache(Source librarySource, Source source, CompilationUnit resolvedUnit, String oldContents, String newContents, int offset, int oldLength, int newLength) { this.librarySource = librarySource; this.source = source; this.resolvedUnit = resolvedUnit; this.oldContents = oldContents; this.newContents = newContents; this.offset = offset; this.oldLength = oldLength; this.newLength = newLength; } /** * Answer the library source for the incremental analysis to be performed * * @return the source (not {@code null}) */ public Source getLibrarySource() { return librarySource; } /** * Return the current contents for the receiver's source. * * @return the contents (not {@code null}) */ public String getNewContents() { return newContents; } /** * Return the number of characters in the replacement text. * * @return the replacement length (zero or greater) */ public int getNewLength() { return newLength; } /** * Return the character position of the first changed character. * * @return the offset (zero or greater) */ public int getOffset() { return offset; } /** * Return the original contents for the receiver's source. * * @return the contents (not {@code null}) */ public String getOldContents() { return oldContents; } /** * Return the number of characters that were replaced. * * @return the replaced length (zero or greater) */ public int getOldLength() { return oldLength; } /** * Return the resolved compilation unit to be used for incremental analysis * * @return the resolved unit (not {@code null}) */ public CompilationUnit getResolvedUnit() { return resolvedUnit; } /** * Return the source for which incremental analysis is to be performed * * @return the source (not {@code null}) */ public Source getSource() { return source; } /** * Determine if the cache contains source changes that need to be analyzed * * @return {@code true} if the cache contains changes to be analyzed, else {@code false} */ public boolean hasWork() { return oldLength > 0 || newLength > 0; } }