public static int validateCompilationUnits()

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);
	}