package com.google.dart.tools.core.internal.builder;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.constant.DeclaredVariables;
import com.google.dart.engine.context.AnalysisContext;
import com.google.dart.engine.context.AnalysisContextStatistics;
import com.google.dart.engine.context.AnalysisDelta;
import com.google.dart.engine.context.AnalysisDelta.AnalysisLevel;
import com.google.dart.engine.context.AnalysisErrorInfo;
import com.google.dart.engine.context.AnalysisException;
import com.google.dart.engine.context.AnalysisListener;
import com.google.dart.engine.context.AnalysisOptions;
import com.google.dart.engine.context.AnalysisResult;
import com.google.dart.engine.context.ChangeSet;
import com.google.dart.engine.element.CompilationUnitElement;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ElementLocation;
import com.google.dart.engine.element.HtmlElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.error.AnalysisError;
import com.google.dart.engine.html.ast.HtmlUnit;
import com.google.dart.engine.internal.cache.SourceEntry;
import com.google.dart.engine.internal.context.AnalysisErrorInfoImpl;
import com.google.dart.engine.internal.context.AnalysisOptionsImpl;
import com.google.dart.engine.internal.context.InternalAnalysisContext;
import com.google.dart.engine.internal.context.ResolvableCompilationUnit;
import com.google.dart.engine.internal.context.TimestampedData;
import com.google.dart.engine.internal.element.angular.AngularApplication;
import com.google.dart.engine.internal.resolver.TypeProvider;
import com.google.dart.engine.internal.scope.Namespace;
import com.google.dart.engine.source.ContentCache;
import com.google.dart.engine.source.DirectoryBasedSourceContainer;
import com.google.dart.engine.source.FileBasedSource;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.source.Source.ContentReceiver;
import com.google.dart.engine.source.SourceContainer;
import com.google.dart.engine.source.SourceFactory;
import com.google.dart.engine.source.SourceKind;
import com.google.dart.engine.utilities.io.PrintStringWriter;
import com.google.dart.engine.utilities.source.LineInfo;
import com.google.dart.tools.core.CallList;
import com.google.dart.tools.core.CallList.Call;
import com.google.dart.tools.core.mock.MockContainer;
import com.google.dart.tools.core.mock.MockFile;
import junit.framework.TestCase;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Mock {@link AnalysisContext} that validates calls and returns Mocks rather than performing the
* requested analysis.
*/
public class MockContext implements InternalAnalysisContext {
private final class ChangedCall extends Call {
private ChangedCall(AnalysisContext target, ChangeSet expected) {
super(target, APPLY_CHANGES, expected);
}
@Override
protected boolean equalArguments(Object[] otherArgs) {
if (otherArgs.length != 1 || !(otherArgs[0] instanceof ChangeSet)) {
return false;
}
ChangeSet changes = (ChangeSet) args[0];
ChangeSet otherChanges = (ChangeSet) otherArgs[0];
return equalArgument(changes.getAddedSources(), otherChanges.getAddedSources())
&& equalArgument(changes.getChangedSources(), otherChanges.getChangedSources())
&& equalArgument(changes.getRemovedSources(), otherChanges.getRemovedSources())
&& equalArgument(changes.getRemovedContainers(), otherChanges.getRemovedContainers());
}
@Override
protected void printArguments(PrintStringWriter writer, String indent, Object[] args) {
ChangeSet changes = (ChangeSet) args[0];
writer.print(indent);
writer.println("ChangeSet");
printCollection(writer, indent, "added", changes.getAddedSources());
printCollection(writer, indent, "changed", changes.getChangedSources());
printCollection(writer, indent, "removed", changes.getRemovedSources());
printCollection(writer, indent, "removedContainers", changes.getRemovedContainers());
}
protected void printCollection(PrintStringWriter writer, String indent, String name,
Collection<?> collection) {
writer.print(indent);
writer.print(" ");
writer.print(name);
writer.println(": ");
for (Object object : collection) {
writer.print(indent);
writer.print(" ");
writer.println(object != null ? object.toString() : "null");
}
}
private boolean equalArgument(Collection<?> list1, Collection<?> list2) {
ArrayList<Object> copy = new ArrayList<Object>(list2);
for (Object object : list1) {
if (!copy.remove(object)) {
return false;
}
}
return copy.isEmpty();
}
}
private AnalysisOptions options = new AnalysisOptionsImpl();
/**
* The set of declared variables used when computing constant values.
*/
private DeclaredVariables declaredVariables = new DeclaredVariables();
private static final String APPLY_CHANGES = "applyChanges";
private static final String EXTRACT_CONTEXT = "extractContext";
private static final String MERGE_CONTEXT = "mergeContext";
private static final String SOURCE_CHANGED = "sourceChanged";
private static final String SOURCE_DELETED = "sourceDeleted";
private final CallList calls = new CallList();
private final ContentCache contentCache = new ContentCache();
private SourceFactory factory = new SourceFactory();
private Map<Source, AnalysisLevel> analysisLevels = new HashMap<Source, AnalysisDelta.AnalysisLevel>();
@Override
public void addListener(AnalysisListener listener) {
throw new UnsupportedOperationException();
}
@Override
public void addSourceInfo(Source source, SourceEntry info) {
throw new UnsupportedOperationException();
}
@Override
public void applyAnalysisDelta(AnalysisDelta delta) {
for (Entry<Source, AnalysisLevel> entry : delta.getAnalysisLevels().entrySet()) {
TestCase.assertNotNull(entry.getKey());
TestCase.assertNotNull(entry.getValue());
}
analysisLevels.putAll(delta.getAnalysisLevels());
}
@Override
public void applyChanges(ChangeSet changes) {
calls.add(new ChangedCall(this, changes));
}
public void assertAnalysisLevel(MockContainer container, AnalysisLevel expected) {
for (IResource res : container.getAllDartAndHtmlFiles()) {
MockFile file = (MockFile) res;
assertAnalysisLevel(file, expected);
}
}
public void assertAnalysisLevel(MockFile file, AnalysisLevel expected) {
assertAnalysisLevel(file.asSource(), expected);
}
public void assertAnalysisLevel(Source source, AnalysisLevel expected) {
TestCase.assertNotNull(source);
AnalysisLevel actual = analysisLevels.remove(source);
if (actual != expected) {
TestCase.failNotEquals("Analysis level for " + source + "\n ", expected, actual);
}
TestCase.assertEquals(expected, actual);
}
public void assertChanged(ChangeSet expected) {
calls.assertCall(new ChangedCall(this, expected));
}
public void assertChanged(File[] added, File[] changed, File[] removedFiles, File[] removedDirs) {
final ChangeSet expected = new ChangeSet();
if (added != null) {
for (File file : added) {
expected.addedSource(new FileBasedSource(file));
}
}
if (changed != null) {
for (File file : changed) {
expected.changedSource(new FileBasedSource(file));
}
}
if (removedFiles != null) {
for (File file : removedFiles) {
expected.removedSource(new FileBasedSource(file));
}
}
if (removedDirs != null) {
for (File dir : removedDirs) {
expected.removedContainer(new DirectoryBasedSourceContainer(dir));
}
}
assertChanged(expected);
}
public void assertChanged(IResource[] added, IResource[] changed, IResource[] removed) {
assertChanged(
asFiles(added),
asFiles(changed),
asFiles(filesOnly(removed)),
asFiles(containersOnly(removed)));
}
public void assertExtracted(IContainer expectedContainer) {
if (expectedContainer != null) {
calls.assertCall(this, EXTRACT_CONTEXT, new DirectoryBasedSourceContainer(
expectedContainer.getLocation().toFile()));
} else {
calls.assertNoCall(new Call(this, EXTRACT_CONTEXT) {
@Override
protected boolean equalArguments(Object[] otherArgs) {
return true;
}
});
}
}
public void assertMergedContext(AnalysisContext expectedContext) {
if (expectedContext != null) {
calls.assertCall(this, MERGE_CONTEXT, expectedContext);
} else {
calls.assertNoCall(new Call(this, MERGE_CONTEXT) {
@Override
protected boolean equalArguments(Object[] otherArgs) {
return true;
}
});
}
}
public void assertNoCalls() {
calls.assertNoCalls();
if (!analysisLevels.isEmpty()) {
@SuppressWarnings("resource")
PrintStringWriter writer = new PrintStringWriter();
writer.print("Expected no more calls to updateAnalysis, but found ");
for (Entry<Source, AnalysisLevel> entry : analysisLevels.entrySet()) {
writer.println();
writer.print(" ");
writer.print(entry.getValue());
writer.print(" : ");
writer.print(entry.getKey());
}
TestCase.fail(writer.toString());
}
}
public void assertSourcesChanged(IResource... expected) {
for (IResource resource : expected) {
Source source = new FileBasedSource(resource.getLocation().toFile());
calls.assertCall(this, SOURCE_CHANGED, source);
}
}
public void assertSourcesDeleted(IResource... expected) {
for (IResource resource : expected) {
File file = resource.getLocation().toFile();
if (resource.getType() == IResource.FILE) {
Source source = new FileBasedSource(file);
calls.assertCall(this, SOURCE_DELETED, source);
} else {
SourceContainer sourceContainer = new DirectoryBasedSourceContainer(file);
calls.assertCall(this, SOURCE_DELETED, sourceContainer);
}
}
}
public void clearCalls() {
calls.clear();
analysisLevels.clear();
}
@Override
public String computeDocumentationComment(Element element) throws AnalysisException {
return null;
}
@Override
public AnalysisError[] computeErrors(Source source) throws AnalysisException {
return AnalysisError.NO_ERRORS;
}
@Override
public Source[] computeExportedLibraries(Source source) throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public HtmlElement computeHtmlElement(Source source) throws AnalysisException {
return null;
}
@Override
public Source[] computeImportedLibraries(Source source) throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public SourceKind computeKindOf(Source source) {
return SourceKind.UNKNOWN;
}
@Override
public LibraryElement computeLibraryElement(Source source) throws AnalysisException {
return null;
}
@Override
public LineInfo computeLineInfo(Source source) throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public ResolvableCompilationUnit computeResolvableCompilationUnit(Source source)
throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public void dispose() {
}
@Override
public boolean exists(Source source) {
if (source == null) {
return false;
}
if (contentCache.getContents(source) != null) {
return true;
}
return source.exists();
}
@Override
public AnalysisContext extractContext(SourceContainer container) {
calls.add(this, EXTRACT_CONTEXT, container);
return new MockContext();
}
@Override
public InternalAnalysisContext extractContextInto(SourceContainer container,
InternalAnalysisContext newContext) {
return newContext;
}
@Override
public AnalysisOptions getAnalysisOptions() {
return options;
}
@Override
public AngularApplication getAngularApplicationWithHtml(Source htmlSource) {
throw new UnsupportedOperationException();
}
@Override
public CompilationUnitElement getCompilationUnitElement(Source unitSource, Source librarySource) {
return null;
}
@Override
public TimestampedData<CharSequence> getContents(Source source) throws Exception {
String contents = contentCache.getContents(source);
if (contents != null) {
return new TimestampedData<CharSequence>(contentCache.getModificationStamp(source), contents);
}
return source.getContents();
}
@Override
@SuppressWarnings("deprecation")
public void getContentsToReceiver(Source source, ContentReceiver receiver) throws Exception {
String contents = contentCache.getContents(source);
if (contents != null) {
receiver.accept(contents, contentCache.getModificationStamp(source));
} else {
source.getContentsToReceiver(receiver);
}
}
@Override
public InternalAnalysisContext getContextFor(Source source) {
throw new UnsupportedOperationException();
}
@Override
public DeclaredVariables getDeclaredVariables() {
return declaredVariables;
}
@Override
public Element getElement(ElementLocation location) {
return null;
}
@Override
public AnalysisErrorInfo getErrors(Source source) {
return new AnalysisErrorInfoImpl(AnalysisError.NO_ERRORS, null);
}
@Override
public HtmlElement getHtmlElement(Source source) {
return null;
}
@Override
public Source[] getHtmlFilesReferencing(Source source) {
return Source.EMPTY_ARRAY;
}
@Override
public Source[] getHtmlSources() {
return Source.EMPTY_ARRAY;
}
@Override
public SourceKind getKindOf(Source source) {
throw new UnsupportedOperationException();
}
@Override
public Source[] getLaunchableClientLibrarySources() {
return Source.EMPTY_ARRAY;
}
@Override
public Source[] getLaunchableServerLibrarySources() {
return Source.EMPTY_ARRAY;
}
@Override
public Source[] getLibrariesContaining(Source source) {
throw new UnsupportedOperationException();
}
@Override
public Source[] getLibrariesDependingOn(Source librarySource) {
throw new UnsupportedOperationException();
}
@Override
public Source[] getLibrariesReferencedFromHtml(Source htmlSource) {
throw new UnsupportedOperationException();
}
@Override
public LibraryElement getLibraryElement(Source source) {
return null;
}
@Override
public Source[] getLibrarySources() {
return Source.EMPTY_ARRAY;
}
@Override
public LineInfo getLineInfo(Source source) {
return null;
}
@Override
public long getModificationStamp(Source source) {
Long stamp = contentCache.getModificationStamp(source);
if (stamp != null) {
return stamp.longValue();
}
return source.getModificationStamp();
}
@Override
public Source[] getPrioritySources() {
throw new UnsupportedOperationException();
}
@Override
public Namespace getPublicNamespace(LibraryElement library) {
throw new UnsupportedOperationException();
}
@Override
public Source[] getRefactoringUnsafeSources() {
return Source.EMPTY_ARRAY;
}
@Override
public CompilationUnit getResolvedCompilationUnit(Source unitSource, LibraryElement library) {
return null;
}
@Override
public CompilationUnit getResolvedCompilationUnit(Source unitSource, Source librarySource) {
return null;
}
@Override
public HtmlUnit getResolvedHtmlUnit(Source htmlSource) {
return null;
}
@Override
public SourceFactory getSourceFactory() {
return factory;
}
@Override
public AnalysisContextStatistics getStatistics() {
throw new UnsupportedOperationException();
}
@Override
public TypeProvider getTypeProvider() throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public boolean isClientLibrary(Source librarySource) {
return false;
}
@Override
public boolean isDisposed() {
throw new UnsupportedOperationException();
}
@Override
public boolean isServerLibrary(Source librarySource) {
return false;
}
@Override
public void mergeContext(AnalysisContext context) {
calls.add(this, MERGE_CONTEXT, context);
}
@Override
public CompilationUnit parseCompilationUnit(Source source) throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public HtmlUnit parseHtmlUnit(Source source) throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public AnalysisResult performAnalysisTask() {
// indicate no analysis to be performed
return new AnalysisResult(null, 0L, null, -1L);
}
@Override
public void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
throw new UnsupportedOperationException();
}
@Override
public void removeListener(AnalysisListener listener) {
throw new UnsupportedOperationException();
}
@Override
public CompilationUnit resolveCompilationUnit(Source source, LibraryElement library)
throws AnalysisException {
return null;
}
@Override
public CompilationUnit resolveCompilationUnit(Source librarySource, Source unitSource)
throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public HtmlUnit resolveHtmlUnit(Source htmlSource) throws AnalysisException {
throw new UnsupportedOperationException();
}
@Override
public void setAnalysisOptions(AnalysisOptions options) {
this.options = options;
}
@Override
public void setAnalysisPriorityOrder(List<Source> sources) {
throw new UnsupportedOperationException();
}
@Override
public void setChangedContents(Source source, String contents, int offset, int oldLength,
int newLength) {
throw new UnsupportedOperationException();
}
@Override
public void setContents(Source source, String contents) {
throw new UnsupportedOperationException();
}
@Override
public void setSourceFactory(SourceFactory sourceFactory) {
factory = sourceFactory;
}
private File[] asFiles(IResource[] resources) {
if (resources == null) {
return null;
}
File[] files = new File[resources.length];
for (int i = 0; i < resources.length; i++) {
files[i] = resources[i].getLocation().toFile();
}
return files;
}
private IResource[] containersOnly(IResource[] resources) {
if (resources == null) {
return null;
}
ArrayList<IResource> result = new ArrayList<IResource>();
for (IResource res : resources) {
if (res instanceof IContainer) {
result.add(res);
}
}
return result.toArray(new IResource[result.size()]);
}
private IResource[] filesOnly(IResource[] resources) {
if (resources == null) {
return null;
}
ArrayList<IResource> result = new ArrayList<IResource>();
for (IResource res : resources) {
if (!(res instanceof IContainer)) {
result.add(res);
}
}
return result.toArray(new IResource[result.size()]);
}
}