/*
* 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.AnalysisEngine;
import com.google.dart.engine.context.AnalysisContextStatistics.PartitionData;
import com.google.dart.engine.internal.context.AnalysisContextStatisticsImpl;
import com.google.dart.engine.internal.context.InternalAnalysisContext;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.utilities.collection.MapIterator;
import com.google.dart.engine.utilities.collection.MultipleMapIterator;
import java.util.Map;
/**
* Instances of the class {@code AnalysisCache} implement an LRU cache of information related to
* analysis.
*/
public class AnalysisCache {
/**
* An array containing the partitions of which this cache is comprised.
*/
private CachePartition[] partitions;
/**
* A flag used to control whether trace information should be produced when the content of the
* cache is modified.
*/
private static final boolean TRACE_CHANGES = false;
/**
* Initialize a newly created cache to have the given partitions. The partitions will be searched
* in the order in which they appear in the array, so the most specific partition (usually an
* {@link SdkCachePartition}) should be first and the most general (usually a
* {@link UniversalCachePartition}) last.
*
* @param partitions the partitions for the newly created cache
*/
public AnalysisCache(CachePartition[] partitions) {
this.partitions = partitions;
}
/**
* Record that the AST associated with the given source was just read from the cache.
*
* @param source the source whose AST was accessed
*/
public void accessedAst(Source source) {
int count = partitions.length;
for (int i = 0; i < count; i++) {
if (partitions[i].contains(source)) {
partitions[i].accessedAst(source);
return;
}
}
}
/**
* Return the entry associated with the given source.
*
* @param source the source whose entry is to be returned
* @return the entry associated with the given source
*/
public SourceEntry get(Source source) {
int count = partitions.length;
for (int i = 0; i < count; i++) {
if (partitions[i].contains(source)) {
return partitions[i].get(source);
}
}
//
// We should never get to this point because the last partition should always be a universal
// partition, except in the case of the SDK context, in which case the source should always be
// part of the SDK.
//
return null;
}
/**
* Return the number of entries in this cache that have an AST associated with them.
*
* @return the number of entries in this cache that have an AST associated with them
*/
public int getAstSize() {
return partitions[partitions.length - 1].getAstSize();
}
/**
* Return context that owns the given source.
*
* @param source the source whose context is to be returned
* @return the context that owns the partition that contains the source
*/
public InternalAnalysisContext getContextFor(Source source) {
int count = partitions.length;
for (int i = 0; i < count; i++) {
if (partitions[i].contains(source)) {
return partitions[i].getContext();
}
}
//
// We should never get to this point because the last partition should always be a universal
// partition, except in the case of the SDK context, in which case the source should always be
// part of the SDK.
//
AnalysisEngine.getInstance().getLogger().logInformation(
"Could not find context for " + source.getFullName(),
new Exception());
return null;
}
/**
* Return information about each of the partitions in this cache.
*
* @return information about each of the partitions in this cache
*/
public PartitionData[] getPartitionData() {
int count = partitions.length;
PartitionData[] data = new PartitionData[count];
for (int i = 0; i < count; i++) {
CachePartition partition = partitions[i];
data[i] = new AnalysisContextStatisticsImpl.PartitionDataImpl(
partition.getAstSize(),
partition.getMap().size());
}
return data;
}
/**
* Return an iterator returning all of the map entries mapping sources to cache entries.
*
* @return an iterator returning all of the map entries mapping sources to cache entries
*/
@SuppressWarnings("unchecked")
public MapIterator<Source, SourceEntry> iterator() {
int count = partitions.length;
Map<Source, SourceEntry>[] maps = new Map[count];
for (int i = 0; i < count; i++) {
maps[i] = partitions[i].getMap();
}
return new MultipleMapIterator<Source, SourceEntry>(maps);
}
/**
* Associate the given entry with the given source.
*
* @param source the source with which the entry is to be associated
* @param entry the entry to be associated with the source
*/
public void put(Source source, SourceEntry entry) {
((SourceEntryImpl) entry).fixExceptionState();
int count = partitions.length;
for (int i = 0; i < count; i++) {
if (partitions[i].contains(source)) {
if (TRACE_CHANGES) {
try {
SourceEntry oldEntry = partitions[i].get(source);
if (oldEntry == null) {
AnalysisEngine.getInstance().getLogger().logInformation(
"Added a cache entry for '" + source.getFullName() + "'.");
} else {
AnalysisEngine.getInstance().getLogger().logInformation(
"Modified the cache entry for " + source.getFullName() + "'. Diff = "
+ ((SourceEntryImpl) entry).getDiff(oldEntry));
}
} catch (Throwable exception) {
// Ignored
System.currentTimeMillis();
}
}
partitions[i].put(source, entry);
return;
}
}
}
/**
* Remove all information related to the given source from this cache.
*
* @param source the source to be removed
*/
public void remove(Source source) {
int count = partitions.length;
for (int i = 0; i < count; i++) {
if (partitions[i].contains(source)) {
if (TRACE_CHANGES) {
try {
AnalysisEngine.getInstance().getLogger().logInformation(
"Removed the cache entry for " + source.getFullName() + "'.");
} catch (Throwable exception) {
// Ignored
System.currentTimeMillis();
}
}
partitions[i].remove(source);
return;
}
}
}
/**
* Record that the AST associated with the given source was just removed from the cache.
*
* @param source the source whose AST was removed
*/
public void removedAst(Source source) {
int count = partitions.length;
for (int i = 0; i < count; i++) {
if (partitions[i].contains(source)) {
partitions[i].removedAst(source);
return;
}
}
}
/**
* Return the number of sources that are mapped to cache entries.
*
* @return the number of sources that are mapped to cache entries
*/
public int size() {
int size = 0;
int count = partitions.length;
for (int i = 0; i < count; i++) {
size += partitions[i].size();
}
return size;
}
/**
* Record that the AST associated with the given source was just stored to the cache.
*
* @param source the source whose AST was stored
*/
public void storedAst(Source source) {
int count = partitions.length;
for (int i = 0; i < count; i++) {
if (partitions[i].contains(source)) {
partitions[i].storedAst(source);
return;
}
}
}
}