/* * 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.cache; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.context.AnalysisException; import com.google.dart.engine.element.LibraryElement; import com.google.dart.engine.error.AnalysisError; import com.google.dart.engine.internal.scope.Namespace; import com.google.dart.engine.scanner.Token; import com.google.dart.engine.source.Source; import com.google.dart.engine.source.SourceKind; import com.google.dart.engine.utilities.ast.AstCloner; import com.google.dart.engine.utilities.collection.ListUtilities; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * Instances of the class {@code DartEntryImpl} implement a {@link DartEntry}. * * @coverage dart.engine */ public class DartEntryImpl extends SourceEntryImpl implements DartEntry { /** * Instances of the class {@code ResolutionState} represent the information produced by resolving * a compilation unit as part of a specific library. */ private static class ResolutionState { /** * The next resolution state or {@code null} if none. */ private ResolutionState nextState; /** * The source for the defining compilation unit of the library that contains this unit. If this * unit is the defining compilation unit for it's library, then this will be the source for this * unit. */ private Source librarySource; /** * The state of the cached compilation unit that contains references to the built element model. */ private CacheState builtUnitState = CacheState.INVALID; /** * The compilation unit that contains references to the built element model, or {@code null} if * that compilation unit is not currently cached. */ private CompilationUnit builtUnit; /** * The state of the cached errors reported while building an element model. */ private CacheState buildElementErrorsState = CacheState.INVALID; /** * The errors produced while building an element model, or an empty array if the errors are not * currently cached. */ private AnalysisError[] buildElementErrors = AnalysisError.NO_ERRORS; /** * The state of the cached resolved compilation unit. */ private CacheState resolvedUnitState = CacheState.INVALID; /** * The resolved compilation unit, or {@code null} if the resolved compilation unit is not * currently cached. */ private CompilationUnit resolvedUnit; /** * The state of the cached resolution errors. */ private CacheState resolutionErrorsState = CacheState.INVALID; /** * The errors produced while resolving the compilation unit, or an empty array if the errors are * not currently cached. */ private AnalysisError[] resolutionErrors = AnalysisError.NO_ERRORS; /** * The state of the cached verification errors. */ private CacheState verificationErrorsState = CacheState.INVALID; /** * The errors produced while verifying the compilation unit, or an empty array if the errors are * not currently cached. */ private AnalysisError[] verificationErrors = AnalysisError.NO_ERRORS; /** * The state of the cached hints. */ private CacheState hintsState = CacheState.INVALID; /** * The hints produced while auditing the compilation unit, or an empty array if the hints are * not currently cached. */ private AnalysisError[] hints = AnalysisError.NO_ERRORS; /** * Initialize a newly created resolution state. */ public ResolutionState() { super(); } /** * Set this state to be exactly like the given state, recursively copying the next state as * necessary. * * @param other the state to be copied */ public void copyFrom(ResolutionState other) { librarySource = other.librarySource; builtUnitState = other.builtUnitState; builtUnit = other.builtUnit; buildElementErrorsState = other.buildElementErrorsState; buildElementErrors = other.buildElementErrors; resolvedUnitState = other.resolvedUnitState; resolvedUnit = other.resolvedUnit; resolutionErrorsState = other.resolutionErrorsState; resolutionErrors = other.resolutionErrors; verificationErrorsState = other.verificationErrorsState; verificationErrors = other.verificationErrors; hintsState = other.hintsState; hints = other.hints; if (other.nextState != null) { nextState = new ResolutionState(); nextState.copyFrom(other.nextState); } } /** * Flush any AST structures being maintained by this state. */ public void flushAstStructures() { if (builtUnitState == CacheState.VALID) { builtUnitState = CacheState.FLUSHED; builtUnit = null; } if (resolvedUnitState == CacheState.VALID) { resolvedUnitState = CacheState.FLUSHED; resolvedUnit = null; } if (nextState != null) { nextState.flushAstStructures(); } } public boolean hasErrorState() { return builtUnitState == CacheState.ERROR || buildElementErrorsState == CacheState.ERROR || resolvedUnitState == CacheState.ERROR || resolutionErrorsState == CacheState.ERROR || verificationErrorsState == CacheState.ERROR || hintsState == CacheState.ERROR || (nextState != null && nextState.hasErrorState()); } /** * Invalidate all of the resolution information associated with the compilation unit. */ public void invalidateAllResolutionInformation() { nextState = null; librarySource = null; builtUnitState = CacheState.INVALID; builtUnit = null; buildElementErrorsState = CacheState.INVALID; buildElementErrors = AnalysisError.NO_ERRORS; resolvedUnitState = CacheState.INVALID; resolvedUnit = null; resolutionErrorsState = CacheState.INVALID; resolutionErrors = AnalysisError.NO_ERRORS; verificationErrorsState = CacheState.INVALID; verificationErrors = AnalysisError.NO_ERRORS; hintsState = CacheState.INVALID; hints = AnalysisError.NO_ERRORS; } /** * Record that an error occurred while attempting to build the element model for the source * represented by this state. */ public void recordBuildElementError() { builtUnitState = CacheState.ERROR; builtUnit = null; buildElementErrorsState = CacheState.ERROR; buildElementErrors = AnalysisError.NO_ERRORS; recordResolutionError(); } /** * Record that an error occurred while attempting to generate hints for the source represented * by this entry. This will set the state of all verification information as being in error. */ public void recordHintError() { hints = AnalysisError.NO_ERRORS; hintsState = CacheState.ERROR; } /** * Record that an error occurred while attempting to resolve the source represented by this * state. */ public void recordResolutionError() { resolvedUnitState = CacheState.ERROR; resolvedUnit = null; resolutionErrorsState = CacheState.ERROR; resolutionErrors = AnalysisError.NO_ERRORS; recordVerificationError(); } /** * Record that an error occurred while attempting to scan or parse the entry represented by this * entry. This will set the state of all resolution-based information as being in error, but * will not change the state of any parse results. */ public void recordResolutionErrorsInAllLibraries() { builtUnitState = CacheState.ERROR; builtUnit = null; buildElementErrorsState = CacheState.ERROR; buildElementErrors = AnalysisError.NO_ERRORS; resolvedUnitState = CacheState.ERROR; resolvedUnit = null; resolutionErrorsState = CacheState.ERROR; resolutionErrors = AnalysisError.NO_ERRORS; recordVerificationError(); if (nextState != null) { nextState.recordResolutionErrorsInAllLibraries(); } } /** * Record that an in-process parse has stopped without recording results because the results * were invalidated before they could be recorded. */ public void recordResolutionNotInProcess() { if (resolvedUnitState == CacheState.IN_PROCESS) { resolvedUnitState = CacheState.INVALID; } if (resolutionErrorsState == CacheState.IN_PROCESS) { resolutionErrorsState = CacheState.INVALID; } if (verificationErrorsState == CacheState.IN_PROCESS) { verificationErrorsState = CacheState.INVALID; } if (hintsState == CacheState.IN_PROCESS) { hintsState = CacheState.INVALID; } if (nextState != null) { nextState.recordResolutionNotInProcess(); } } /** * Record that an error occurred while attempting to generate errors and warnings for the source * represented by this entry. This will set the state of all verification information as being * in error. */ public void recordVerificationError() { verificationErrors = AnalysisError.NO_ERRORS; verificationErrorsState = CacheState.ERROR; recordHintError(); } /** * Write a textual representation of the difference between the old entry and this entry to the * given string builder. * * @param builder the string builder to which the difference is to be written * @param oldEntry the entry that was replaced by this entry * @return {@code true} if some difference was written */ protected boolean writeDiffOn(StringBuilder builder, boolean needsSeparator, DartEntry oldEntry) { needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, RESOLVED_UNIT, resolvedUnitState, "resolvedUnit"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, RESOLUTION_ERRORS, resolutionErrorsState, "resolutionErrors"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, VERIFICATION_ERRORS, verificationErrorsState, "verificationErrors"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, HINTS, hintsState, "hints"); return needsSeparator; } /** * Write a textual representation of this state to the given builder. The result will only be * used for debugging purposes. * * @param builder the builder to which the text should be written */ protected void writeOn(StringBuilder builder) { if (librarySource != null) { builder.append("; builtUnit = "); builder.append(builtUnitState); builder.append("; buildElementErrors = "); builder.append(buildElementErrorsState); builder.append("; resolvedUnit = "); builder.append(resolvedUnitState); builder.append("; resolutionErrors = "); builder.append(resolutionErrorsState); builder.append("; verificationErrors = "); builder.append(verificationErrorsState); builder.append("; hints = "); builder.append(hintsState); if (nextState != null) { nextState.writeOn(builder); } } } /** * Write a textual representation of the difference between the state of the specified data * between the old entry and this entry to the given string builder. * * @param builder the string builder to which the difference is to be written * @param needsSeparator {@code true} if any data that is written * @param oldEntry the entry that was replaced by this entry * @param descriptor the descriptor defining the data whose state is being compared * @param label the label used to describe the state * @return {@code true} if some difference was written */ protected boolean writeStateDiffOn(StringBuilder builder, boolean needsSeparator, SourceEntry oldEntry, DataDescriptor<?> descriptor, CacheState newState, String label) { CacheState oldState = ((DartEntryImpl) oldEntry).getStateInLibrary(descriptor, librarySource); if (oldState != newState) { if (needsSeparator) { builder.append("; "); } builder.append(label); builder.append(" = "); builder.append(oldState); builder.append(" -> "); builder.append(newState); return true; } return needsSeparator; } } /** * The state of the cached token stream. */ private CacheState tokenStreamState = CacheState.INVALID; /** * The head of the token stream, or {@code null} if the token stream is not currently cached. */ private Token tokenStream; /** * The state of the cached scan errors. */ private CacheState scanErrorsState = CacheState.INVALID; /** * The errors produced while scanning the compilation unit, or an empty array if the errors are * not currently cached. */ private AnalysisError[] scanErrors = AnalysisError.NO_ERRORS; /** * The state of the cached source kind. */ private CacheState sourceKindState = CacheState.INVALID; /** * The kind of this source. */ private SourceKind sourceKind = SourceKind.UNKNOWN; /** * The state of the cached parsed compilation unit. */ private CacheState parsedUnitState = CacheState.INVALID; /** * A flag indicating whether the parsed AST structure has been accessed since it was set. This is * used to determine whether the structure needs to be copied before it is resolved. */ private boolean parsedUnitAccessed = false; /** * The parsed compilation unit, or {@code null} if the parsed compilation unit is not currently * cached. */ private CompilationUnit parsedUnit; /** * The state of the cached parse errors. */ private CacheState parseErrorsState = CacheState.INVALID; /** * The errors produced while parsing the compilation unit, or an empty array if the errors are not * currently cached. */ private AnalysisError[] parseErrors = AnalysisError.NO_ERRORS; /** * The state of the cached list of imported libraries. */ private CacheState importedLibrariesState = CacheState.INVALID; /** * The list of libraries imported by the library, or an empty array if the list is not currently * cached. The list will be empty if the Dart file is a part rather than a library. */ private Source[] importedLibraries = Source.EMPTY_ARRAY; /** * The state of the cached list of exported libraries. */ private CacheState exportedLibrariesState = CacheState.INVALID; /** * The list of libraries exported by the library, or an empty array if the list is not currently * cached. The list will be empty if the Dart file is a part rather than a library. */ private Source[] exportedLibraries = Source.EMPTY_ARRAY; /** * The state of the cached list of included parts. */ private CacheState includedPartsState = CacheState.INVALID; /** * The list of parts included in the library, or an empty array if the list is not currently * cached. The list will be empty if the Dart file is a part rather than a library. */ private Source[] includedParts = Source.EMPTY_ARRAY; /** * The list of libraries that contain this compilation unit. The list will be empty if there are * no known libraries that contain this compilation unit. */ private ArrayList<Source> containingLibraries = new ArrayList<Source>(); /** * The information known as a result of resolving this compilation unit as part of the library * that contains this unit. This field will never be {@code null}. */ private ResolutionState resolutionState = new ResolutionState(); /** * The state of the cached library element. */ private CacheState elementState = CacheState.INVALID; /** * The element representing the library, or {@code null} if the element is not currently cached. */ private LibraryElement element; /** * The state of the cached public namespace. */ private CacheState publicNamespaceState = CacheState.INVALID; /** * The public namespace of the library, or {@code null} if the namespace is not currently cached. */ private Namespace publicNamespace; /** * The state of the cached client/ server flag. */ private CacheState clientServerState = CacheState.INVALID; /** * The state of the cached launchable flag. */ private CacheState launchableState = CacheState.INVALID; /** * The error produced while performing Angular resolution, or an empty array if there are no * errors if the error are not currently cached. */ private AnalysisError[] angularErrors = AnalysisError.NO_ERRORS; /** * The index of the flag indicating whether this library is launchable (whether the file has a * main method). */ private static final int LAUNCHABLE_INDEX = 1; /** * The index of the flag indicating whether the library is client code (whether the library * depends on the html library). If the library is not "client code", then it is referred to as * "server code". */ private static final int CLIENT_CODE_INDEX = 2; /** * Initialize a newly created cache entry to be empty. */ public DartEntryImpl() { super(); } /** * Add the given library to the list of libraries that contain this part. This method should only * be invoked on entries that represent a part. * * @param librarySource the source of the library to be added */ public void addContainingLibrary(Source librarySource) { containingLibraries.add(librarySource); } /** * Flush any AST structures being maintained by this entry. */ public void flushAstStructures() { if (tokenStreamState == CacheState.VALID) { tokenStreamState = CacheState.FLUSHED; tokenStream = null; } if (parsedUnitState == CacheState.VALID) { parsedUnitState = CacheState.FLUSHED; parsedUnitAccessed = false; parsedUnit = null; } resolutionState.flushAstStructures(); } @Override public AnalysisError[] getAllErrors() { ArrayList<AnalysisError> errors = new ArrayList<AnalysisError>(); ListUtilities.addAll(errors, scanErrors); ListUtilities.addAll(errors, parseErrors); ResolutionState state = resolutionState; while (state != null) { ListUtilities.addAll(errors, state.buildElementErrors); ListUtilities.addAll(errors, state.resolutionErrors); ListUtilities.addAll(errors, state.verificationErrors); ListUtilities.addAll(errors, state.hints); state = state.nextState; } ListUtilities.addAll(errors, angularErrors); if (errors.size() == 0) { return AnalysisError.NO_ERRORS; } return errors.toArray(new AnalysisError[errors.size()]); } @Override public CompilationUnit getAnyParsedCompilationUnit() { if (parsedUnitState == CacheState.VALID) { parsedUnitAccessed = true; return parsedUnit; } ResolutionState state = resolutionState; while (state != null) { if (state.builtUnitState == CacheState.VALID) { return state.builtUnit; } state = state.nextState; }; return getAnyResolvedCompilationUnit(); } @Override public CompilationUnit getAnyResolvedCompilationUnit() { ResolutionState state = resolutionState; while (state != null) { if (state.resolvedUnitState == CacheState.VALID) { return state.resolvedUnit; } state = state.nextState; }; return null; } /** * Return a list containing the libraries that are known to contain this part. * * @return a list containing the libraries that are known to contain this part */ public List<Source> getContainingLibraries() { return containingLibraries; } @Override public SourceKind getKind() { return sourceKind; } /** * Answer an array of library sources containing the receiver's source. */ public Source[] getLibrariesContaining() { ResolutionState state = resolutionState; ArrayList<Source> result = new ArrayList<Source>(); while (state != null) { if (state.librarySource != null) { result.add(state.librarySource); } state = state.nextState; } return result.toArray(new Source[result.size()]); } /** * Return a compilation unit that has not been accessed by any other client and can therefore * safely be modified by the reconciler, or {@code null} if the source has not been parsed. * * @return a compilation unit that can be modified by the reconciler */ public CompilationUnit getResolvableCompilationUnit() { if (parsedUnitState == CacheState.VALID) { if (parsedUnitAccessed) { return (CompilationUnit) parsedUnit.accept(new AstCloner()); } CompilationUnit unit = parsedUnit; parsedUnitState = CacheState.FLUSHED; parsedUnitAccessed = false; parsedUnit = null; return unit; } ResolutionState state = resolutionState; while (state != null) { if (state.builtUnitState == CacheState.VALID) { // TODO(brianwilkerson) We're cloning the structure to remove any previous resolution data, // but I'm not sure that's necessary. return (CompilationUnit) state.builtUnit.accept(new AstCloner()); } if (state.resolvedUnitState == CacheState.VALID) { return (CompilationUnit) state.resolvedUnit.accept(new AstCloner()); } state = state.nextState; }; return null; } @Override public CacheState getState(DataDescriptor<?> descriptor) { if (descriptor == ELEMENT) { return elementState; } else if (descriptor == EXPORTED_LIBRARIES) { return exportedLibrariesState; } else if (descriptor == IMPORTED_LIBRARIES) { return importedLibrariesState; } else if (descriptor == INCLUDED_PARTS) { return includedPartsState; } else if (descriptor == IS_CLIENT) { return clientServerState; } else if (descriptor == IS_LAUNCHABLE) { return launchableState; } else if (descriptor == PARSE_ERRORS) { return parseErrorsState; } else if (descriptor == PARSED_UNIT) { return parsedUnitState; } else if (descriptor == PUBLIC_NAMESPACE) { return publicNamespaceState; } else if (descriptor == SCAN_ERRORS) { return scanErrorsState; } else if (descriptor == SOURCE_KIND) { return sourceKindState; } else if (descriptor == TOKEN_STREAM) { return tokenStreamState; } else { return super.getState(descriptor); } } @Override public CacheState getStateInLibrary(DataDescriptor<?> descriptor, Source librarySource) { ResolutionState state = resolutionState; while (state != null) { if (librarySource.equals(state.librarySource)) { if (descriptor == RESOLUTION_ERRORS) { return state.resolutionErrorsState; } else if (descriptor == RESOLVED_UNIT) { return state.resolvedUnitState; } else if (descriptor == VERIFICATION_ERRORS) { return state.verificationErrorsState; } else if (descriptor == HINTS) { return state.hintsState; } else { throw new IllegalArgumentException("Invalid descriptor: " + descriptor); } } state = state.nextState; }; if (descriptor == RESOLUTION_ERRORS || descriptor == RESOLVED_UNIT || descriptor == VERIFICATION_ERRORS || descriptor == HINTS) { return CacheState.INVALID; } else { throw new IllegalArgumentException("Invalid descriptor: " + descriptor); } } @Override @SuppressWarnings("unchecked") public <E> E getValue(DataDescriptor<E> descriptor) { if (descriptor == ANGULAR_ERRORS) { return (E) angularErrors; } else if (descriptor == CONTAINING_LIBRARIES) { return (E) containingLibraries.toArray(new Source[containingLibraries.size()]); } else if (descriptor == ELEMENT) { return (E) element; } else if (descriptor == EXPORTED_LIBRARIES) { return (E) exportedLibraries; } else if (descriptor == IMPORTED_LIBRARIES) { return (E) importedLibraries; } else if (descriptor == INCLUDED_PARTS) { return (E) includedParts; } else if (descriptor == IS_CLIENT) { return (E) (Boolean) getFlag(CLIENT_CODE_INDEX); } else if (descriptor == IS_LAUNCHABLE) { return (E) (Boolean) getFlag(LAUNCHABLE_INDEX); } else if (descriptor == PARSE_ERRORS) { return (E) parseErrors; } else if (descriptor == PARSED_UNIT) { parsedUnitAccessed = true; return (E) parsedUnit; } else if (descriptor == PUBLIC_NAMESPACE) { return (E) publicNamespace; } else if (descriptor == SCAN_ERRORS) { return (E) scanErrors; } else if (descriptor == SOURCE_KIND) { return (E) sourceKind; } else if (descriptor == TOKEN_STREAM) { return (E) tokenStream; } return super.getValue(descriptor); } @Override @SuppressWarnings("unchecked") public <E> E getValueInLibrary(DataDescriptor<E> descriptor, Source librarySource) { ResolutionState state = resolutionState; while (state != null) { if (librarySource.equals(state.librarySource)) { if (descriptor == RESOLUTION_ERRORS) { return (E) state.resolutionErrors; } else if (descriptor == RESOLVED_UNIT) { return (E) state.resolvedUnit; } else if (descriptor == VERIFICATION_ERRORS) { return (E) state.verificationErrors; } else if (descriptor == HINTS) { return (E) state.hints; } else { throw new IllegalArgumentException("Invalid descriptor: " + descriptor); } } state = state.nextState; }; if (descriptor == RESOLUTION_ERRORS || descriptor == VERIFICATION_ERRORS || descriptor == HINTS) { return (E) AnalysisError.NO_ERRORS; } else if (descriptor == RESOLVED_UNIT) { return null; } else { throw new IllegalArgumentException("Invalid descriptor: " + descriptor); } } @Override public DartEntryImpl getWritableCopy() { DartEntryImpl copy = new DartEntryImpl(); copy.copyFrom(this); return copy; } @Override public boolean hasInvalidData(DataDescriptor<?> descriptor) { if (descriptor == ELEMENT) { return elementState == CacheState.INVALID; } else if (descriptor == EXPORTED_LIBRARIES) { return exportedLibrariesState == CacheState.INVALID; } else if (descriptor == IMPORTED_LIBRARIES) { return importedLibrariesState == CacheState.INVALID; } else if (descriptor == INCLUDED_PARTS) { return includedPartsState == CacheState.INVALID; } else if (descriptor == IS_CLIENT) { return clientServerState == CacheState.INVALID; } else if (descriptor == IS_LAUNCHABLE) { return launchableState == CacheState.INVALID; } else if (descriptor == PARSE_ERRORS) { return parseErrorsState == CacheState.INVALID; } else if (descriptor == PARSED_UNIT) { return parsedUnitState == CacheState.INVALID; } else if (descriptor == PUBLIC_NAMESPACE) { return publicNamespaceState == CacheState.INVALID; } else if (descriptor == SCAN_ERRORS) { return scanErrorsState == CacheState.INVALID; } else if (descriptor == SOURCE_KIND) { return sourceKindState == CacheState.INVALID; } else if (descriptor == TOKEN_STREAM) { return tokenStreamState == CacheState.INVALID; } else if (descriptor == RESOLUTION_ERRORS || descriptor == RESOLVED_UNIT || descriptor == VERIFICATION_ERRORS || descriptor == HINTS) { ResolutionState state = resolutionState; while (state != null) { if (descriptor == RESOLUTION_ERRORS) { return state.resolutionErrorsState == CacheState.INVALID; } else if (descriptor == RESOLVED_UNIT) { return state.resolvedUnitState == CacheState.INVALID; } else if (descriptor == VERIFICATION_ERRORS) { return state.verificationErrorsState == CacheState.INVALID; } else if (descriptor == HINTS) { return state.hintsState == CacheState.INVALID; } } return false; } else { return super.getState(descriptor) == CacheState.INVALID; } } @Override public boolean hasResolvableCompilationUnit() { if (parsedUnitState == CacheState.VALID) { return true; } ResolutionState state = resolutionState; while (state != null) { if (state.builtUnitState == CacheState.VALID || state.resolvedUnitState == CacheState.VALID) { return true; } state = state.nextState; }; return false; } @Override public void invalidateAllInformation() { super.invalidateAllInformation(); scanErrors = AnalysisError.NO_ERRORS; scanErrorsState = CacheState.INVALID; tokenStream = null; tokenStreamState = CacheState.INVALID; sourceKind = SourceKind.UNKNOWN; sourceKindState = CacheState.INVALID; parseErrors = AnalysisError.NO_ERRORS; parseErrorsState = CacheState.INVALID; parsedUnit = null; parsedUnitAccessed = false; parsedUnitState = CacheState.INVALID; discardCachedResolutionInformation(true); } /** * Invalidate all of the resolution information associated with the compilation unit. * * @param invalidateUris true if the cached results of converting URIs to source files should also * be invalidated. */ public void invalidateAllResolutionInformation(boolean invalidateUris) { if (parsedUnitState == CacheState.FLUSHED) { ResolutionState state = resolutionState; while (state != null) { if (state.builtUnitState == CacheState.VALID) { parsedUnit = state.builtUnit; parsedUnitAccessed = true; parsedUnitState = CacheState.VALID; break; } else if (state.resolvedUnitState == CacheState.VALID) { parsedUnit = state.resolvedUnit; parsedUnitAccessed = true; parsedUnitState = CacheState.VALID; break; } state = state.nextState; } } discardCachedResolutionInformation(invalidateUris); } @Override public boolean isRefactoringSafe() { ResolutionState state = resolutionState; while (state != null) { CacheState resolvedState = state.resolvedUnitState; if (resolvedState != CacheState.VALID && resolvedState != CacheState.FLUSHED) { return false; } state = state.nextState; } return true; } /** * Record that an error occurred while attempting to build the element model for the source * represented by this entry. This will set the state of all resolution-based information as being * in error, but will not change the state of any parse results. * * @param librarySource the source of the library in which the element model was being built * @param exception the exception that shows where the error occurred */ public void recordBuildElementErrorInLibrary(Source librarySource, AnalysisException exception) { setException(exception); element = null; elementState = CacheState.ERROR; clearFlags(LAUNCHABLE_INDEX, CLIENT_CODE_INDEX); clientServerState = CacheState.ERROR; launchableState = CacheState.ERROR; ResolutionState state = getOrCreateResolutionState(librarySource); state.recordBuildElementError(); } /** * Record that an in-process model build has stopped without recording results because the results * were invalidated before they could be recorded. */ public void recordBuildElementNotInProcess() { if (elementState == CacheState.IN_PROCESS) { elementState = CacheState.INVALID; } if (clientServerState == CacheState.IN_PROCESS) { clientServerState = CacheState.INVALID; } if (launchableState == CacheState.IN_PROCESS) { launchableState = CacheState.INVALID; } } @Override public void recordContentError(AnalysisException exception) { super.recordContentError(exception); recordScanError(exception); } /** * Record that an error occurred while attempting to generate hints for the source represented by * this entry. This will set the state of all verification information as being in error. * * @param librarySource the source of the library in which hints were being generated * @param exception the exception that shows where the error occurred */ public void recordHintErrorInLibrary(Source librarySource, AnalysisException exception) { setException(exception); ResolutionState state = getOrCreateResolutionState(librarySource); state.recordHintError(); } /** * Record that an error occurred while attempting to scan or parse the entry represented by this * entry. This will set the state of all information, including any resolution-based information, * as being in error. * * @param exception the exception that shows where the error occurred */ public void recordParseError(AnalysisException exception) { sourceKind = SourceKind.UNKNOWN; sourceKindState = CacheState.ERROR; parseErrors = AnalysisError.NO_ERRORS; parseErrorsState = CacheState.ERROR; parsedUnit = null; parsedUnitAccessed = false; parsedUnitState = CacheState.ERROR; exportedLibraries = Source.EMPTY_ARRAY; exportedLibrariesState = CacheState.ERROR; importedLibraries = Source.EMPTY_ARRAY; importedLibrariesState = CacheState.ERROR; includedParts = Source.EMPTY_ARRAY; includedPartsState = CacheState.ERROR; recordResolutionError(exception); } /** * Record that the parse-related information for the associated source is about to be computed by * the current thread. */ public void recordParseInProcess() { if (sourceKindState != CacheState.VALID) { sourceKindState = CacheState.IN_PROCESS; } if (parseErrorsState != CacheState.VALID) { parseErrorsState = CacheState.IN_PROCESS; } if (parsedUnitState != CacheState.VALID) { parsedUnitState = CacheState.IN_PROCESS; } if (exportedLibrariesState != CacheState.VALID) { exportedLibrariesState = CacheState.IN_PROCESS; } if (importedLibrariesState != CacheState.VALID) { importedLibrariesState = CacheState.IN_PROCESS; } if (includedPartsState != CacheState.VALID) { includedPartsState = CacheState.IN_PROCESS; } } /** * Record that an in-process parse has stopped without recording results because the results were * invalidated before they could be recorded. */ public void recordParseNotInProcess() { if (getState(LINE_INFO) == CacheState.IN_PROCESS) { setState(LINE_INFO, CacheState.INVALID); } if (sourceKindState == CacheState.IN_PROCESS) { sourceKindState = CacheState.INVALID; } if (parseErrorsState == CacheState.IN_PROCESS) { parseErrorsState = CacheState.INVALID; } if (parsedUnitState == CacheState.IN_PROCESS) { parsedUnitState = CacheState.INVALID; } if (exportedLibrariesState == CacheState.IN_PROCESS) { exportedLibrariesState = CacheState.INVALID; } if (importedLibrariesState == CacheState.IN_PROCESS) { importedLibrariesState = CacheState.INVALID; } if (includedPartsState == CacheState.IN_PROCESS) { includedPartsState = CacheState.INVALID; } } /** * Record that an error occurred while attempting to resolve the source represented by this entry. * This will set the state of all resolution-based information as being in error, but will not * change the state of any parse results. * * @param exception the exception that shows where the error occurred */ public void recordResolutionError(AnalysisException exception) { setException(exception); element = null; elementState = CacheState.ERROR; clearFlags(LAUNCHABLE_INDEX, CLIENT_CODE_INDEX); clientServerState = CacheState.ERROR; launchableState = CacheState.ERROR; // TODO(brianwilkerson) Remove the code above this line after resolution and element building // are separated. publicNamespace = null; publicNamespaceState = CacheState.ERROR; resolutionState.recordResolutionErrorsInAllLibraries(); } /** * Record that an error occurred while attempting to resolve the source represented by this entry. * This will set the state of all resolution-based information as being in error, but will not * change the state of any parse results. * * @param librarySource the source of the library in which resolution was being performed * @param exception the exception that shows where the error occurred */ public void recordResolutionErrorInLibrary(Source librarySource, AnalysisException exception) { setException(exception); element = null; elementState = CacheState.ERROR; clearFlags(LAUNCHABLE_INDEX, CLIENT_CODE_INDEX); clientServerState = CacheState.ERROR; launchableState = CacheState.ERROR; // TODO(brianwilkerson) Remove the code above this line after resolution and element building // are separated. publicNamespace = null; publicNamespaceState = CacheState.ERROR; ResolutionState state = getOrCreateResolutionState(librarySource); state.recordResolutionError(); } /** * Record that an in-process resolution has stopped without recording results because the results * were invalidated before they could be recorded. */ public void recordResolutionNotInProcess() { if (elementState == CacheState.IN_PROCESS) { elementState = CacheState.INVALID; } if (clientServerState == CacheState.IN_PROCESS) { clientServerState = CacheState.INVALID; } if (launchableState == CacheState.IN_PROCESS) { launchableState = CacheState.INVALID; } // TODO(brianwilkerson) Remove the code above this line after resolution and element building // are separated. if (publicNamespaceState == CacheState.IN_PROCESS) { publicNamespaceState = CacheState.INVALID; } resolutionState.recordResolutionNotInProcess(); } /** * Record that an error occurred while attempting to scan or parse the entry represented by this * entry. This will set the state of all information, including any resolution-based information, * as being in error. * * @param exception the exception that shows where the error occurred */ @Override public void recordScanError(AnalysisException exception) { super.recordScanError(exception); scanErrors = AnalysisError.NO_ERRORS; scanErrorsState = CacheState.ERROR; tokenStream = null; tokenStreamState = CacheState.ERROR; recordParseError(exception); } /** * Record that the scan-related information for the associated source is about to be computed by * the current thread. */ public void recordScanInProcess() { if (getState(LINE_INFO) != CacheState.VALID) { setState(LINE_INFO, CacheState.IN_PROCESS); } if (scanErrorsState != CacheState.VALID) { scanErrorsState = CacheState.IN_PROCESS; } if (tokenStreamState != CacheState.VALID) { tokenStreamState = CacheState.IN_PROCESS; } } /** * Record that an in-process scan has stopped without recording results because the results were * invalidated before they could be recorded. */ public void recordScanNotInProcess() { if (getState(LINE_INFO) == CacheState.IN_PROCESS) { setState(LINE_INFO, CacheState.INVALID); } if (scanErrorsState == CacheState.IN_PROCESS) { scanErrorsState = CacheState.INVALID; } if (tokenStreamState == CacheState.IN_PROCESS) { tokenStreamState = CacheState.INVALID; } } /** * Record that an error occurred while attempting to generate errors and warnings for the source * represented by this entry. This will set the state of all verification information as being in * error. * * @param librarySource the source of the library in which verification was being performed * @param exception the exception that shows where the error occurred */ public void recordVerificationErrorInLibrary(Source librarySource, AnalysisException exception) { setException(exception); ResolutionState state = getOrCreateResolutionState(librarySource); state.recordVerificationError(); } /** * Remove the given library from the list of libraries that contain this part. This method should * only be invoked on entries that represent a part. * * @param librarySource the source of the library to be removed */ public void removeContainingLibrary(Source librarySource) { containingLibraries.remove(librarySource); } /** * Remove any resolution information associated with this compilation unit being part of the given * library, presumably because it is no longer part of the library. * * @param librarySource the source of the defining compilation unit of the library that used to * contain this part but no longer does */ public void removeResolution(Source librarySource) { if (librarySource != null) { if (librarySource.equals(resolutionState.librarySource)) { if (resolutionState.nextState == null) { resolutionState.invalidateAllResolutionInformation(); } else { resolutionState = resolutionState.nextState; } } else { ResolutionState priorState = resolutionState; ResolutionState state = resolutionState.nextState; while (state != null) { if (librarySource.equals(state.librarySource)) { priorState.nextState = state.nextState; break; } priorState = state; state = state.nextState; } } } } /** * Set the list of libraries that contain this compilation unit to contain only the given source. * This method should only be invoked on entries that represent a library. * * @param librarySource the source of the single library that the list should contain */ public void setContainingLibrary(Source librarySource) { containingLibraries.clear(); containingLibraries.add(librarySource); } @Override public void setState(DataDescriptor<?> descriptor, CacheState state) { if (descriptor == ELEMENT) { element = updatedValue(state, element, null); elementState = state; } else if (descriptor == EXPORTED_LIBRARIES) { exportedLibraries = updatedValue(state, exportedLibraries, Source.EMPTY_ARRAY); exportedLibrariesState = state; } else if (descriptor == IMPORTED_LIBRARIES) { importedLibraries = updatedValue(state, importedLibraries, Source.EMPTY_ARRAY); importedLibrariesState = state; } else if (descriptor == INCLUDED_PARTS) { includedParts = updatedValue(state, includedParts, Source.EMPTY_ARRAY); includedPartsState = state; } else if (descriptor == IS_CLIENT) { updateValueOfFlag(CLIENT_CODE_INDEX, state); clientServerState = state; } else if (descriptor == IS_LAUNCHABLE) { updateValueOfFlag(LAUNCHABLE_INDEX, state); launchableState = state; } else if (descriptor == PARSE_ERRORS) { parseErrors = updatedValue(state, parseErrors, AnalysisError.NO_ERRORS); parseErrorsState = state; } else if (descriptor == PARSED_UNIT) { CompilationUnit newUnit = updatedValue(state, parsedUnit, null); if (newUnit != parsedUnit) { parsedUnitAccessed = false; } parsedUnit = newUnit; parsedUnitState = state; } else if (descriptor == PUBLIC_NAMESPACE) { publicNamespace = updatedValue(state, publicNamespace, null); publicNamespaceState = state; } else if (descriptor == SCAN_ERRORS) { scanErrors = updatedValue(state, scanErrors, AnalysisError.NO_ERRORS); scanErrorsState = state; } else if (descriptor == SOURCE_KIND) { sourceKind = updatedValue(state, sourceKind, SourceKind.UNKNOWN); sourceKindState = state; } else if (descriptor == TOKEN_STREAM) { tokenStream = updatedValue(state, tokenStream, null); tokenStreamState = state; } else { super.setState(descriptor, state); } } /** * Set the state of the data represented by the given descriptor in the context of the given * library to the given state. * * @param descriptor the descriptor representing the data whose state is to be set * @param librarySource the source of the defining compilation unit of the library that is the * context for the data * @param cacheState the new state of the data represented by the given descriptor */ public void setStateInLibrary(DataDescriptor<?> descriptor, Source librarySource, CacheState cacheState) { ResolutionState state = getOrCreateResolutionState(librarySource); if (descriptor == RESOLUTION_ERRORS) { state.resolutionErrors = updatedValue( cacheState, state.resolutionErrors, AnalysisError.NO_ERRORS); state.resolutionErrorsState = cacheState; } else if (descriptor == RESOLVED_UNIT) { state.resolvedUnit = updatedValue(cacheState, state.resolvedUnit, null); state.resolvedUnitState = cacheState; } else if (descriptor == VERIFICATION_ERRORS) { state.verificationErrors = updatedValue( cacheState, state.verificationErrors, AnalysisError.NO_ERRORS); state.verificationErrorsState = cacheState; } else if (descriptor == HINTS) { state.hints = updatedValue(cacheState, state.hints, AnalysisError.NO_ERRORS); state.hintsState = cacheState; } else { throw new IllegalArgumentException("Invalid descriptor: " + descriptor); } } @Override public <E> void setValue(DataDescriptor<E> descriptor, E value) { if (descriptor == ANGULAR_ERRORS) { angularErrors = value == null ? AnalysisError.NO_ERRORS : (AnalysisError[]) value; } else if (descriptor == ELEMENT) { countTransitionToValid(descriptor, elementState); element = (LibraryElement) value; elementState = CacheState.VALID; } else if (descriptor == EXPORTED_LIBRARIES) { countTransitionToValid(descriptor, exportedLibrariesState); exportedLibraries = value == null ? Source.EMPTY_ARRAY : (Source[]) value; exportedLibrariesState = CacheState.VALID; } else if (descriptor == IMPORTED_LIBRARIES) { countTransitionToValid(descriptor, importedLibrariesState); importedLibraries = value == null ? Source.EMPTY_ARRAY : (Source[]) value; importedLibrariesState = CacheState.VALID; } else if (descriptor == INCLUDED_PARTS) { countTransitionToValid(descriptor, includedPartsState); includedParts = value == null ? Source.EMPTY_ARRAY : (Source[]) value; includedPartsState = CacheState.VALID; } else if (descriptor == IS_CLIENT) { countTransitionToValid(descriptor, clientServerState); setFlag(CLIENT_CODE_INDEX, ((Boolean) value).booleanValue()); clientServerState = CacheState.VALID; } else if (descriptor == IS_LAUNCHABLE) { countTransitionToValid(descriptor, launchableState); setFlag(LAUNCHABLE_INDEX, ((Boolean) value).booleanValue()); launchableState = CacheState.VALID; } else if (descriptor == PARSE_ERRORS) { countTransitionToValid(descriptor, parseErrorsState); parseErrors = value == null ? AnalysisError.NO_ERRORS : (AnalysisError[]) value; parseErrorsState = CacheState.VALID; } else if (descriptor == PARSED_UNIT) { countTransitionToValid(descriptor, parsedUnitState); parsedUnit = (CompilationUnit) value; parsedUnitAccessed = false; parsedUnitState = CacheState.VALID; } else if (descriptor == PUBLIC_NAMESPACE) { countTransitionToValid(descriptor, publicNamespaceState); publicNamespace = (Namespace) value; publicNamespaceState = CacheState.VALID; } else if (descriptor == SCAN_ERRORS) { countTransitionToValid(descriptor, scanErrorsState); scanErrors = value == null ? AnalysisError.NO_ERRORS : (AnalysisError[]) value; scanErrorsState = CacheState.VALID; } else if (descriptor == SOURCE_KIND) { countTransitionToValid(descriptor, sourceKindState); sourceKind = (SourceKind) value; sourceKindState = CacheState.VALID; } else if (descriptor == TOKEN_STREAM) { countTransitionToValid(descriptor, tokenStreamState); tokenStream = (Token) value; tokenStreamState = CacheState.VALID; } else { super.setValue(descriptor, value); } } /** * Set the value of the data represented by the given descriptor in the context of the given * library to the given value, and set the state of that data to {@link CacheState#VALID}. * * @param descriptor the descriptor representing which data is to have its value set * @param librarySource the source of the defining compilation unit of the library that is the * context for the data * @param value the new value of the data represented by the given descriptor and library */ public <E> void setValueInLibrary(DataDescriptor<E> descriptor, Source librarySource, E value) { ResolutionState state = getOrCreateResolutionState(librarySource); if (descriptor == RESOLUTION_ERRORS) { countTransitionToValid(descriptor, state.resolutionErrorsState); state.resolutionErrors = value == null ? AnalysisError.NO_ERRORS : (AnalysisError[]) value; state.resolutionErrorsState = CacheState.VALID; } else if (descriptor == RESOLVED_UNIT) { countTransitionToValid(descriptor, state.resolvedUnitState); state.resolvedUnit = (CompilationUnit) value; state.resolvedUnitState = CacheState.VALID; } else if (descriptor == VERIFICATION_ERRORS) { countTransitionToValid(descriptor, state.verificationErrorsState); state.verificationErrors = value == null ? AnalysisError.NO_ERRORS : (AnalysisError[]) value; state.verificationErrorsState = CacheState.VALID; } else if (descriptor == HINTS) { countTransitionToValid(descriptor, state.hintsState); state.hints = value == null ? AnalysisError.NO_ERRORS : (AnalysisError[]) value; state.hintsState = CacheState.VALID; } } @Override protected void copyFrom(SourceEntryImpl entry) { super.copyFrom(entry); DartEntryImpl other = (DartEntryImpl) entry; scanErrorsState = other.scanErrorsState; scanErrors = other.scanErrors; tokenStreamState = other.tokenStreamState; tokenStream = other.tokenStream; sourceKindState = other.sourceKindState; sourceKind = other.sourceKind; parsedUnitState = other.parsedUnitState; parsedUnit = other.parsedUnit; parsedUnitAccessed = other.parsedUnitAccessed; parseErrorsState = other.parseErrorsState; parseErrors = other.parseErrors; includedPartsState = other.includedPartsState; includedParts = other.includedParts; exportedLibrariesState = other.exportedLibrariesState; exportedLibraries = other.exportedLibraries; importedLibrariesState = other.importedLibrariesState; importedLibraries = other.importedLibraries; containingLibraries = new ArrayList<Source>(other.containingLibraries); resolutionState.copyFrom(other.resolutionState); elementState = other.elementState; element = other.element; publicNamespaceState = other.publicNamespaceState; publicNamespace = other.publicNamespace; clientServerState = other.clientServerState; launchableState = other.launchableState; angularErrors = other.angularErrors; } @Override protected boolean hasErrorState() { return super.hasErrorState() || scanErrorsState == CacheState.ERROR || tokenStreamState == CacheState.ERROR || sourceKindState == CacheState.ERROR || parsedUnitState == CacheState.ERROR || parseErrorsState == CacheState.ERROR || importedLibrariesState == CacheState.ERROR || exportedLibrariesState == CacheState.ERROR || includedPartsState == CacheState.ERROR || elementState == CacheState.ERROR || publicNamespaceState == CacheState.ERROR || clientServerState == CacheState.ERROR || launchableState == CacheState.ERROR || resolutionState.hasErrorState(); } @Override protected boolean writeDiffOn(StringBuilder builder, SourceEntry oldEntry) { boolean needsSeparator = super.writeDiffOn(builder, oldEntry); if (!(oldEntry instanceof DartEntryImpl)) { if (needsSeparator) { builder.append("; "); } builder.append("entry type changed; was " + oldEntry.getClass().getName()); return true; } needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, TOKEN_STREAM, "tokenStream"); needsSeparator = writeStateDiffOn(builder, needsSeparator, oldEntry, SCAN_ERRORS, "scanErrors"); needsSeparator = writeStateDiffOn(builder, needsSeparator, oldEntry, SOURCE_KIND, "sourceKind"); needsSeparator = writeStateDiffOn(builder, needsSeparator, oldEntry, PARSED_UNIT, "parsedUnit"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, PARSE_ERRORS, "parseErrors"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, IMPORTED_LIBRARIES, "importedLibraries"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, EXPORTED_LIBRARIES, "exportedLibraries"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, INCLUDED_PARTS, "includedParts"); needsSeparator = writeStateDiffOn(builder, needsSeparator, oldEntry, ELEMENT, "element"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, PUBLIC_NAMESPACE, "publicNamespace"); needsSeparator = writeStateDiffOn(builder, needsSeparator, oldEntry, IS_CLIENT, "clientServer"); needsSeparator = writeStateDiffOn( builder, needsSeparator, oldEntry, IS_LAUNCHABLE, "launchable"); // TODO(brianwilkerson) Add better support for containingLibraries. It would be nice to be able // to report on size-preserving changes. int oldLibraryCount = ((DartEntryImpl) oldEntry).containingLibraries.size(); int libraryCount = containingLibraries.size(); if (oldLibraryCount != libraryCount) { if (needsSeparator) { builder.append("; "); } builder.append("containingLibraryCount = "); builder.append(oldLibraryCount); builder.append(" -> "); builder.append(libraryCount); needsSeparator = true; } // // Report change to the per-library state. // HashMap<Source, ResolutionState> oldStateMap = new HashMap<Source, ResolutionState>(); ResolutionState state = ((DartEntryImpl) oldEntry).resolutionState; while (state != null) { Source librarySource = state.librarySource; if (librarySource != null) { oldStateMap.put(librarySource, state); } state = state.nextState; } state = resolutionState; while (state != null) { Source librarySource = state.librarySource; if (librarySource != null) { ResolutionState oldState = oldStateMap.remove(librarySource); if (oldState == null) { if (needsSeparator) { builder.append("; "); } builder.append("added resolution for "); builder.append(librarySource.getFullName()); needsSeparator = true; } else { needsSeparator = oldState.writeDiffOn(builder, needsSeparator, (DartEntry) oldEntry); } } state = state.nextState; } for (Source librarySource : oldStateMap.keySet()) { if (needsSeparator) { builder.append("; "); } builder.append("removed resolution for "); builder.append(librarySource.getFullName()); needsSeparator = true; } return needsSeparator; } @Override protected void writeOn(StringBuilder builder) { builder.append("Dart: "); super.writeOn(builder); builder.append("; tokenStream = "); builder.append(tokenStreamState); builder.append("; scanErrors = "); builder.append(scanErrorsState); builder.append("; sourceKind = "); builder.append(sourceKindState); builder.append("; parsedUnit = "); builder.append(parsedUnitState); builder.append(" ("); builder.append(parsedUnitAccessed ? "T" : "F"); builder.append("); parseErrors = "); builder.append(parseErrorsState); builder.append("; exportedLibraries = "); builder.append(exportedLibrariesState); builder.append("; importedLibraries = "); builder.append(importedLibrariesState); builder.append("; includedParts = "); builder.append(includedPartsState); builder.append("; element = "); builder.append(elementState); builder.append("; publicNamespace = "); builder.append(publicNamespaceState); builder.append("; clientServer = "); builder.append(clientServerState); builder.append("; launchable = "); builder.append(launchableState); // builder.append("; angularElements = "); resolutionState.writeOn(builder); } /** * Invalidate all of the resolution information associated with the compilation unit. * * @param invalidateUris true if the cached results of converting URIs to source files should also * be invalidated. */ private void discardCachedResolutionInformation(boolean invalidateUris) { element = null; elementState = CacheState.INVALID; clearFlags(LAUNCHABLE_INDEX, CLIENT_CODE_INDEX); clientServerState = CacheState.INVALID; launchableState = CacheState.INVALID; publicNamespace = null; publicNamespaceState = CacheState.INVALID; resolutionState.invalidateAllResolutionInformation(); if (invalidateUris) { importedLibraries = Source.EMPTY_ARRAY; importedLibrariesState = CacheState.INVALID; exportedLibraries = Source.EMPTY_ARRAY; exportedLibrariesState = CacheState.INVALID; includedParts = Source.EMPTY_ARRAY; includedPartsState = CacheState.INVALID; } } /** * Return a resolution state for the specified library, creating one as necessary. * * @param librarySource the library source (not {@code null}) * @return the resolution state (not {@code null}) */ private ResolutionState getOrCreateResolutionState(Source librarySource) { ResolutionState state = resolutionState; if (state.librarySource == null) { state.librarySource = librarySource; return state; } while (!state.librarySource.equals(librarySource)) { if (state.nextState == null) { ResolutionState newState = new ResolutionState(); newState.librarySource = librarySource; state.nextState = newState; return newState; } state = state.nextState; } return state; } /** * Given that the specified flag is being transitioned to the given state, set the value of the * flag to the value that should be kept in the cache. * * @param index the index of the flag whose state is being set * @param state the state to which the value is being transitioned */ private void updateValueOfFlag(int index, CacheState state) { if (state == CacheState.VALID) { throw new IllegalArgumentException("Use setValue() to set the state to VALID"); } else if (state != CacheState.IN_PROCESS) { // // If the value is in process, we can leave the current value in the cache for any 'get' // methods to access. // setFlag(index, false); } } }