| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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 flex2.tools.oem; |
| |
| import flex2.compiler.CompilationUnit; |
| import flex2.compiler.Source; |
| import flex2.compiler.common.CompilerConfiguration; |
| import flex2.compiler.common.Configuration; |
| import flex2.compiler.io.VirtualFile; |
| import flex2.compiler.util.Name; |
| import flex2.compiler.util.QName; |
| import java.lang.ref.SoftReference; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| import macromedia.asc.embedding.ConfigVar; |
| import macromedia.asc.util.ObjectList; |
| |
| /** |
| * A cache which allows SourceList, SourcePath, and ResourceContainer |
| * based Sources, which are common between compilations, to be shared. |
| * When a Flash Builder "user triggered clean" occurs, |
| * ApplicationCache.clear() should be called. When Flash Builder |
| * calls Builder.clean() after writing out a PersistenceStore cache, |
| * ApplicationCache.clear() should not be called. Otherwise, the |
| * benefit of the application cache would be lost. |
| * |
| * @since 4.5 |
| */ |
| public class ApplicationCache extends CacheBase |
| { |
| private Map<String, Source> sources; |
| private SoftReference<Object> trigger; |
| private int lowestTotalDependentCount; |
| private Configuration configuration; |
| |
| public ApplicationCache() |
| { |
| sources = new HashMap<String, Source>(); |
| } |
| |
| /** |
| * Check that the cache is consistent with <code>configuration</code> |
| * before using it. For example, a cache created with strict |
| * turned on, can't be used in non-strict mode, and vice versa. |
| * |
| * @return true if it's ok to use the cache. |
| */ |
| boolean isConsistent(Configuration configuration) |
| { |
| boolean result = true; |
| |
| if (contextStatics == null) |
| { |
| result = false; |
| } |
| else if (this.configuration != null) |
| { |
| CompilerConfiguration compilerConfiguration = configuration.getCompilerConfiguration(); |
| CompilerConfiguration cacheCompilerConfiguration = this.configuration.getCompilerConfiguration(); |
| |
| if (compilerConfiguration.strict() != cacheCompilerConfiguration.strict()) |
| { |
| result = false; |
| } |
| |
| if (result && (compilerConfiguration.dialect() != cacheCompilerConfiguration.dialect())) |
| { |
| result = false; |
| } |
| |
| if (result && configuration.getTargetPlayerTargetAVM() != this.configuration.getTargetPlayerTargetAVM()) |
| { |
| result = false; |
| } |
| |
| if (result && (compilerConfiguration.debug() != cacheCompilerConfiguration.debug())) |
| { |
| result = false; |
| } |
| |
| if (result && (compilerConfiguration.omitTraceStatements() != cacheCompilerConfiguration.omitTraceStatements())) |
| { |
| result = false; |
| } |
| |
| if (result && (compilerConfiguration.accessible() != cacheCompilerConfiguration.accessible())) |
| { |
| result = false; |
| } |
| |
| ObjectList<ConfigVar> define = compilerConfiguration.getDefine(); |
| ObjectList<ConfigVar> cacheDefine = cacheCompilerConfiguration.getDefine(); |
| |
| if (result && !define.equals(cacheDefine)) |
| { |
| result = false; |
| } |
| |
| if (result && (compilerConfiguration.verboseStacktraces() != cacheCompilerConfiguration.verboseStacktraces())) |
| { |
| result = false; |
| } |
| |
| if (result && !ApplicationCache.<String>equals(compilerConfiguration.getKeepAs3Metadata(), |
| cacheCompilerConfiguration.getKeepAs3Metadata())) |
| { |
| result = false; |
| } |
| |
| if (result && (compilerConfiguration.keepGeneratedActionScript() != cacheCompilerConfiguration.keepGeneratedActionScript())) |
| { |
| result = false; |
| } |
| |
| if (result && (compilerConfiguration.enableRuntimeDesignLayers() != cacheCompilerConfiguration.enableRuntimeDesignLayers())) |
| { |
| result = false; |
| } |
| |
| if (result && !ApplicationCache.<String>equals(compilerConfiguration.getLocales(), |
| cacheCompilerConfiguration.getLocales())) |
| { |
| result = false; |
| } |
| |
| if (result && !compilerConfiguration.getThemeNames().equals(cacheCompilerConfiguration.getThemeNames())) |
| { |
| result = false; |
| } |
| |
| if (result && !ApplicationCache.<VirtualFile>equals(compilerConfiguration.getSourcePath(), |
| cacheCompilerConfiguration.getSourcePath())) |
| { |
| result = false; |
| } |
| } |
| |
| return result; |
| } |
| |
| private static <T> boolean equals(T[] a1, T[] a2) |
| { |
| boolean result = true; |
| |
| if (((a1 == null) && (a2 != null)) || |
| ((a1 != null) && (a2 == null))) |
| { |
| result = false; |
| } |
| else if ((a1 != null) && (a2 != null)) |
| { |
| // Convert the arrays to sets to filter out duplicates. |
| Set<T> a1Set = new HashSet<T>(Arrays.<T>asList(a1)); |
| Set<T> a2Set = new HashSet<T>(Arrays.<T>asList(a2)); |
| |
| if (!a1Set.equals(a2Set)) |
| { |
| result = false; |
| } |
| } |
| |
| return result; |
| } |
| |
| private static void addDependents(Source source, |
| Set<QName> dependents, |
| Map<QName, Source> qNameToSourceMap, |
| Map<Name, Map<String, Source>> dependentMap) |
| { |
| CompilationUnit compilationUnit = source.getCompilationUnit(); |
| |
| if (compilationUnit != null) |
| { |
| for (QName topLevelDefinition : compilationUnit.topLevelDefinitions) |
| { |
| Map<String, Source> topLevelDefinitionDependents = dependentMap.get(topLevelDefinition); |
| |
| if (topLevelDefinitionDependents != null) |
| { |
| for (Source dependentSource : topLevelDefinitionDependents.values()) |
| { |
| boolean foundNewDependent = false; |
| CompilationUnit dependentCompilationUnit = dependentSource.getCompilationUnit(); |
| |
| if (dependentCompilationUnit != null) |
| { |
| for (QName dependent : dependentCompilationUnit.topLevelDefinitions) |
| { |
| if (!dependents.contains(dependent)) |
| { |
| dependents.add(dependent); |
| foundNewDependent = true; |
| } |
| } |
| } |
| |
| if (foundNewDependent) |
| { |
| addDependents(dependentSource, dependents, qNameToSourceMap, dependentMap); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * |
| */ |
| void addSources(Map<String, Source> sources) |
| { |
| this.sources.putAll(sources); |
| |
| Map<QName, Source> qNames = new HashMap<QName, Source>(sources.size()); |
| Map<Name, Map<String, Source>> dependentMap = new HashMap<Name, Map<String, Source>>(sources.size()); |
| |
| for (Source source : this.sources.values()) |
| { |
| CompilationUnit compilationUnit = source.getCompilationUnit(); |
| |
| if (compilationUnit != null) |
| { |
| for (QName qName : compilationUnit.topLevelDefinitions) |
| { |
| qNames.put(qName, source); |
| } |
| } |
| } |
| |
| for (Source source : this.sources.values()) |
| { |
| CompilationUnit compilationUnit = source.getCompilationUnit(); |
| |
| if (compilationUnit != null) |
| { |
| for (Name name : compilationUnit.inheritance) |
| { |
| if (name instanceof QName) |
| { |
| Map<String, Source> inheritanceDependents = dependentMap.get(name); |
| |
| if (inheritanceDependents == null) |
| { |
| inheritanceDependents = new HashMap<String, Source>(); |
| dependentMap.put(name, inheritanceDependents); |
| } |
| |
| inheritanceDependents.put(source.getName(), source); |
| } |
| } |
| |
| for (Name name : compilationUnit.namespaces) |
| { |
| if (name instanceof QName) |
| { |
| Map<String, Source> namespacesDependents = dependentMap.get(name); |
| |
| if (namespacesDependents == null) |
| { |
| namespacesDependents = new HashMap<String, Source>(); |
| dependentMap.put(name, namespacesDependents); |
| } |
| |
| namespacesDependents.put(source.getName(), source); |
| } |
| } |
| |
| for (Name name : compilationUnit.expressions) |
| { |
| if (name instanceof QName) |
| { |
| Map<String, Source> expressionsDependents = dependentMap.get(name); |
| |
| if (expressionsDependents == null) |
| { |
| expressionsDependents = new HashMap<String, Source>(); |
| dependentMap.put(name, expressionsDependents); |
| } |
| |
| expressionsDependents.put(source.getName(), source); |
| } |
| } |
| |
| for (Name name : compilationUnit.types) |
| { |
| if (name instanceof QName) |
| { |
| Map<String, Source> typesDependents = dependentMap.get(name); |
| |
| if (typesDependents == null) |
| { |
| typesDependents = new HashMap<String, Source>(); |
| dependentMap.put(name, typesDependents); |
| } |
| |
| typesDependents.put(source.getName(), source); |
| } |
| } |
| } |
| } |
| |
| lowestTotalDependentCount = Integer.MAX_VALUE; |
| |
| for (Source source : this.sources.values()) |
| { |
| Set<QName> dependents = new HashSet<QName>(); |
| addDependents(source, dependents, qNames, dependentMap); |
| int totalDependentCount = dependents.size(); |
| source.setTotalDependentCount(totalDependentCount); |
| |
| if (totalDependentCount < lowestTotalDependentCount) |
| { |
| lowestTotalDependentCount = totalDependentCount; |
| } |
| } |
| |
| reloadTrigger(); |
| } |
| |
| /** |
| * If available, returns the <code>Source</code> associated with |
| * the <code>className</code>. |
| */ |
| public Source getSource(String className) |
| { |
| return sources.get(className); |
| } |
| |
| private void prune() |
| { |
| Iterator<Source> iterator = sources.values().iterator(); |
| int nextLowestTotalDependentCount = Integer.MAX_VALUE; |
| |
| while (iterator.hasNext()) |
| { |
| Source source = iterator.next(); |
| int totalDependentCount = source.getTotalDependentCount(); |
| |
| if (totalDependentCount == lowestTotalDependentCount) |
| { |
| iterator.remove(); |
| } |
| else if (totalDependentCount < nextLowestTotalDependentCount) |
| { |
| nextLowestTotalDependentCount = totalDependentCount; |
| } |
| } |
| |
| if (nextLowestTotalDependentCount < Integer.MAX_VALUE) |
| { |
| lowestTotalDependentCount = nextLowestTotalDependentCount; |
| reloadTrigger(); |
| } |
| } |
| |
| private void reloadTrigger() |
| { |
| if (trigger == null) |
| { |
| trigger = new SoftReference<Object>(new Object() |
| { |
| protected void finalize() |
| throws Throwable |
| { |
| prune(); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Clears the cache. |
| */ |
| public void clear() |
| { |
| sources.clear(); |
| } |
| |
| /** |
| * Get the configuration used to initialize the cache. |
| * |
| * @return the current configuration. |
| */ |
| Configuration getConfiguration() |
| { |
| return configuration; |
| } |
| |
| /** |
| * Sets the configuration used to initialize the cache. The |
| * previous checksum is overwritten. |
| */ |
| void setConfiguration(Configuration configuration) |
| { |
| this.configuration = configuration; |
| } |
| } |