in flex/tools/idea-flex-compiler-fix/src/flex2/compiler/API.java [1492:2038]
public static int validateCompilationUnits(FileSpec fileSpec, SourceList sourceList, SourcePath sourcePath,
ResourceBundlePath bundlePath, ResourceContainer resources,
CompilerSwcContext swcContext, ContextStatics perCompileData,
boolean recompile, Configuration configuration)
{
final LocalizationManager l10n = ThreadLocalToolkit.getLocalizationManager();
final boolean strict = configuration.getCompilerConfiguration().strict();
final Map updated = new HashMap(), // VirtualFile.getName(), Source
updatedWithStableSignature = new HashMap(), // VirtualFile.getName(), Source
affected = new HashMap(), // VirtualFile.getName(), Source
deleted = new HashMap(), // VirtualFile.getName(), Source
reasons = new HashMap(), // VirtualFile.getName(), String
qnames = new HashMap(); // QName, VirtualFile.getName()
final Set includeUpdated = new HashSet(), // VirtualFile.getName()
resourceDelegates = new HashSet(), // VirtualFile.getNameForReporting()
namespaces = new HashSet();
// put all the Source objects together
final List sources = new ArrayList();
{
if (fileSpec != null)
sources.addAll(fileSpec.sources());
if (sourceList != null)
sources.addAll(sourceList.sources());
if (sourcePath != null)
sources.addAll(sourcePath.sources().values());
if (bundlePath != null)
sources.addAll(bundlePath.sources().values());
}
// if any of the Source objects in ResourceContainer is bad, obsolete the originating Source.
for (Iterator i = resources.sources().iterator(); i.hasNext(); )
{
Source s = (Source) i.next();
if (s != null)
{
CompilationUnit u = (s != null) ? s.getCompilationUnit() : null;
if (s.hasError()
|| (u != null && !u.isDone())
|| s.isUpdated()
|| (u != null && u.getAssets().isUpdated()))
{
resourceDelegates.add(s.getNameForReporting());
s.removeCompilationUnit();
}
}
}
// collect the names of all the update file includes...
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
for (Iterator j = s.getUpdatedFileIncludes(); j != null && j.hasNext();)
{
VirtualFile f = (VirtualFile) j.next();
includeUpdated.add(f.getNameForReporting());
}
}
// if any one of the files in the filespec has changed, the entire app must be recompiled...
// no need to null out ResourceContainer here because stuff in ResourceContainer will be replaced later.
// - ForceRecompilation
// - SourceFileChanged
if (recompile || (fileSpec != null && fileSpec.isUpdated()))
{
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
affected.put(s.getName(), s);
if (recompile)
{
reasons.put(s.getName(), l10n.getLocalizedTextString(new ForceRecompilation()));
}
else
{
reasons.put(s.getName(), l10n.getLocalizedTextString(new SourceFileChanged()));
}
}
// optimization -- if this block runs, all sources are invalidated anyways;
// we get through the rest of the function a little faster if clear()ed
sources.clear();
}
final DependencyGraph graph = new DependencyGraph();
// build a dependency graph
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
if (s == null) continue;
CompilationUnit u = s.getCompilationUnit();
if (u == null) continue;
graph.put(s.getName(), u);
if (!graph.containsVertex(s.getName()))
{
graph.addVertex(new Vertex(s.getName()));
}
// register QName --> VirtualFile.getName()
for (Iterator j = u.topLevelDefinitions.iterator(); j.hasNext();)
{
qnames.put(j.next(), s.getName());
}
}
// setup inheritance-based dependencies...
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
if (s == null) continue;
CompilationUnit u = s.getCompilationUnit();
if (u == null) continue;
String head = s.getName();
for (Iterator k = u.inheritance.iterator(); k.hasNext();)
{
Object obj = k.next();
if (obj instanceof QName)
{
QName qname = (QName) obj;
String tail = (String) qnames.get(qname);
if (tail != null && !head.equals(tail) && !graph.dependencyExists(head, tail))
{
graph.addDependency(head, tail);
}
}
}
}
// identify obsolete CompilationUnit
// - NotFullyCompiled
// - SourceNoLongerExists
// - SourceFileUpdated
// - AssedUpdated
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
if (s == null) continue;
CompilationUnit u = s.getCompilationUnit();
if (s.hasError() || (u != null && !u.isDone()) || resourceDelegates.contains(s.getName()))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new NotFullyCompiled()));
sources.set(i, null);
}
else if (!s.exists())
{
updated.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new SourceNoLongerExists()));
deleted.put(s.getName(), s);
if (u != null)
{
for (Iterator j = u.topLevelDefinitions.iterator(); j.hasNext(); )
{
QName qName = (QName) j.next();
namespaces.add(qName.toString());
}
}
sources.set(i, null);
}
else if (s.isUpdated())
{
// signature optimization:
// read the old signature from the incremental cache
// generate a new signature from the current source
// compare -- if stable, we don't have to recompile dependencies
boolean signatureIsStable = false;
if ((u != null) && (!configuration.getCompilerConfiguration()
.getDisableIncrementalOptimizations())
// skip MXML sources:
// MXML is too complicated to parse/codegen at this point in
// order to generate and compare a new checksum
&& (!s.getMimeType().equals(MimeMappings.MXML)))
{
final Long persistedCRC = u.getSignatureChecksum();
if (persistedCRC != null)
{
assert (s.getMimeType().equals(MimeMappings.ABC) ||
s.getMimeType().equals(MimeMappings.AS));
//TODO if we calculate a new checksum that does not match,
// can we store this checksum and not recompute it later?
final Long currentCRC = computeSignatureChecksum(configuration, s);
signatureIsStable = (currentCRC != null) &&
(persistedCRC.compareTo(currentCRC) == 0);
// if (SignatureExtension.debug)
// {
// final String name = u.getSource().getName();
// SignatureExtension.debug("*** FILE UPDATED: Signature "
// + (signatureIsStable ? "IS" : "IS NOT")
// + " stable ***");
// SignatureExtension.debug("PERSISTED CRC32: " + persistedCRC + "\t--> " + name);
// SignatureExtension.debug("CURRENT CRC32: " + currentCRC + "\t--> " + name);
// }
}
}
// if the class signature is stable (it has not changed since the last compile)
// then we can invalidate and recompile the updated unit alone
// otherwise we default to a chain reaction, invalidating _all_ dependent units
if (signatureIsStable)
{
updatedWithStableSignature.put(s.getName(), s);
}
else
{
updated.put(s.getName(), s);
}
reasons.put(s.getName(), l10n.getLocalizedTextString(new SourceFileUpdated()));
sources.set(i, null);
}
else if (u != null && u.getAssets().isUpdated())
{
updated.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new AssetUpdated()));
sources.set(i, null);
}
}
// permanently remove the deleted Source objects from SourcePath
//
// Note: this step is currently necessary because the location-updating loop that follows iterates over
// 'sources', which has had deleted entries (among others) set to null in the loop above. So here we iterate
// directly over the deleted entries. (Note also that 'reasons' already has an entry for this source.)
//
for (Iterator i = deleted.values().iterator(); i.hasNext(); )
{
Source s = (Source) i.next();
if (s.isSourcePathOwner())
{
SourcePath sp = (SourcePath) s.getOwner();
sp.removeSource(s);
}
}
// Examine each Source object in SourcePath or ResourceBundlePath...
// if a Source object in SourcePath or ResourceBundlePath is no longer the
// first choice according to findFile, it should be removed... i.e. look for ambiguous sources
// - NotSourcePathFirstPreference
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
if (s != null && (s.isSourcePathOwner() || s.isResourceBundlePathOwner()))
{
SourcePathBase sp = (SourcePathBase) s.getOwner();
if (!sp.checkPreference(s))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new NotSourcePathFirstPreference()));
sources.set(i, null);
}
}
}
// invalidate the compilation unit if its dependencies are deleted
// - DependentFileNoLongerExists
// - InvalidImportStatement
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
if (s == null) continue;
CompilationUnit u = s.getCompilationUnit();
if (u == null) continue;
MultiNameSet[] dependencies = new MultiNameSet[]{u.inheritance,
u.namespaces,
u.expressions,
u.types};
boolean valid = true;
// if deleted is an empty set, we don't even need to do this
if (deleted.size() > 0)
{
for (int j = 0; j < 4; j++)
{
for (Iterator k = dependencies[j].iterator(); k.hasNext();)
{
Object obj = k.next();
if (obj instanceof QName)
{
String location = (String) qnames.get(obj);
if (location != null && deleted.containsKey(location))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new DependentFileNoLongerExists(location)));
sources.set(i, null);
valid = false;
break;
}
}
}
}
}
// only check the following when strict is enabled.
valid = valid && strict;
for (Iterator k = u.importPackageStatements.iterator(); valid && k.hasNext();)
{
String packageName = (String) k.next();
if (!hasPackage(sourcePath, swcContext, packageName))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new InvalidImportStatement(packageName)));
sources.set(i, null);
namespaces.add(packageName);
valid = false;
break;
}
}
for (Iterator k = u.importDefinitionStatements.iterator(); valid && k.hasNext();)
{
QName defName = (QName) k.next();
if (!hasDefinition(sourcePath, swcContext, defName))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new InvalidImportStatement(defName.toString())));
sources.set(i, null);
namespaces.add(defName.toString());
valid = false;
break;
}
}
}
// - SuperclassUpdated
// - QNameSourceUpdated
Algorithms.topologicalSort(graph, new Visitor()
{
public void visit(Object v)
{
String name = (String) ((Vertex) v).getWeight();
CompilationUnit u = (CompilationUnit) graph.get(name);
if (u != null)
{
checkInheritance(u);
checkNamespaces(u);
}
}
private void checkInheritance(CompilationUnit u)
{
for (Iterator j = u.inheritance.iterator(); j.hasNext();)
{
Object mn = j.next();
if (mn instanceof QName)
{
QName qname = (QName) mn;
String sourceName = (String) qnames.get(qname);
Source s = u.getSource();
if (sourceName != null && (updated.containsKey(sourceName) || affected.containsKey(sourceName)))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new SuperclassUpdated(sourceName, qname)));
int index = sources.indexOf(s);
if (index != -1)
{
sources.set(index, null);
}
}
}
}
}
private void checkNamespaces(CompilationUnit u)
{
for (Iterator j = u.namespaces.iterator(); j.hasNext();)
{
Object mn = j.next();
if (mn instanceof QName)
{
QName qname = (QName) mn;
String sourceName = (String) qnames.get(qname);
Source s = u.getSource();
if (sourceName != null && updated.containsKey(sourceName))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new QNameSourceUpdated(sourceName, qname)));
int index = sources.indexOf(s);
if (index != -1)
{
sources.set(index, null);
}
}
}
}
}
});
// - DependentFileModified
if (strict)
{
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
if (s == null) continue;
CompilationUnit u = s.getCompilationUnit();
if (u == null) continue;
MultiNameSet[] dependencies = new MultiNameSet[]{u.expressions, u.types};
for (int j = 0; j < 2; j++)
{
for (Iterator k = dependencies[j].iterator(); k.hasNext();)
{
Object obj = k.next();
if (obj instanceof QName)
{
String location = (String) qnames.get(obj);
if (location != null && (updated.containsKey(location) || affected.containsKey(location)))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new DependentFileModified(location)));
sources.set(i, null);
break;
}
}
}
}
}
}
for (Iterator i = includeUpdated.iterator(); i.hasNext();)
{
ThreadLocalToolkit.getLogger().includedFileUpdated((String) i.next());
}
int affectedCount = affected.size();
logReasonAndRemoveCompilationUnit(affected, reasons, includeUpdated);
logReasonAndRemoveCompilationUnit(updated, reasons, includeUpdated);
logReasonAndRemoveCompilationUnit(updatedWithStableSignature, reasons, includeUpdated);
// if a compilation unit becomes obsolete, its satellite compilation units in ResourceContainer
// must go away too.
for (Iterator i = resources.sources().iterator(); i.hasNext(); )
{
Source s = (Source) i.next();
if (s != null)
{
String name = s.getNameForReporting();
if (affected.containsKey(name) || updated.containsKey(name))
{
s.removeCompilationUnit();
}
}
}
affected.clear();
// validate multinames
// - MultiNameMeaningChanged
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
if (s == null) continue;
CompilationUnit u = s.getCompilationUnit();
if (u == null) continue;
for (Iterator j = u.inheritanceHistory.keySet().iterator(); j.hasNext();)
{
MultiName multiName = (MultiName) j.next();
QName qName = (QName) u.inheritanceHistory.get(multiName);
try
{
if (!validateMultiName(multiName, qName, sourcePath))
{
affected.put(s.getName(), s);
reasons.put(s.getName(), l10n.getLocalizedTextString(new MultiNameMeaningChanged(multiName, qName)));
sources.set(i, null);
}
}
catch (CompilerException ex)
{
affected.put(s.getName(), s);
reasons.put(s.getName(), ex.getMessage());
sources.set(i, null);
}
}
}
affectedCount += affected.size();
// remove CompilationUnits from affected Map
logReasonAndRemoveCompilationUnit(affected, reasons, includeUpdated);
// if a compilation unit becomes obsolete, its satellite compilation units in ResourceContainer
// must go away too.
for (Iterator i = resources.sources().iterator(); i.hasNext(); )
{
Source s = (Source) i.next();
if (s != null)
{
String name = s.getNameForReporting();
if (affected.containsKey(name))
{
s.removeCompilationUnit();
}
}
}
// refresh the state of ResourceContainer
resources.refresh();
// finally, remove the deleted namespaces from SymbolTable...
if (perCompileData != null)
{
for (Iterator i = namespaces.iterator(); i.hasNext(); )
{
String ns = (String) i.next();
perCompileData.removeNamespace(ns);
}
}
final int updateCount = updated.size() + updatedWithStableSignature.size();
if (updateCount + affectedCount > 0)
{
ThreadLocalToolkit.log(new FilesChangedAffected(updateCount, affectedCount));
}
return (updateCount + affectedCount);
}