blob: 92524d545e426d085ff07db5049caaa276bfac8d [file] [log] [blame]
/*
*
* 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 org.apache.royale.compiler.internal.css;
import static org.apache.royale.compiler.internal.css.semantics.CSSSemanticAnalyzer.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FilenameUtils;
import org.apache.royale.compiler.config.Configuration;
import org.apache.royale.compiler.css.ICSSDocument;
import org.apache.royale.compiler.css.ICSSManager;
import org.apache.royale.compiler.css.ICSSRule;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.filespecs.IFileSpecification;
import org.apache.royale.compiler.internal.caches.CSSDocumentCache;
import org.apache.royale.compiler.internal.caches.CacheStoreKeyBase;
import org.apache.royale.compiler.internal.css.codegen.CSSCompilationSession;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.scopes.ASProjectScope;
import org.apache.royale.compiler.internal.units.EmbedCompilationUnit;
import org.apache.royale.compiler.problems.CSSUnresolvedClassReferenceProblem;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.targets.ITargetSettings;
import org.apache.royale.compiler.units.ICompilationUnit;
import org.apache.royale.swc.ISWC;
import org.apache.royale.swc.ISWCFileEntry;
import org.apache.royale.swc.ISWCManager;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* Project-level CSS manager.
*/
public class CSSManager implements ICSSManager
{
/**
* Find all the dependency class definitions and compilation units
* introduced by the given CSS rules. This method is basically an
* aggregation of {@link CSSSemanticAnalyzer#resolveDependencies()}.
*
* @param session CSS compilation session that stores resolved embed
* compilation units.
* @param royaleProject Flex project.
* @param cssRules Resolve class dependencies in these CSS rules.
* @param problems Problem collection.
*/
static void getClassDefinitionDependencies(
final CSSCompilationSession session,
final RoyaleProject royaleProject,
final ImmutableSet<ICSSRule> cssRules,
final Set<IClassDefinition> classReferences,
final Set<EmbedCompilationUnit> embedCompilationUnits,
final Collection<ICompilerProblem> problems)
{
for (final ICSSRule matchedRule : cssRules)
{
resolveDependencies(
session.resolvedEmbedProperties,
matchedRule,
royaleProject,
classReferences,
embedCompilationUnits,
problems);
}
}
/**
* Find compilation units for the given definition set. A definition might
* not have a compilation unit, but a compiler problem must already be
* logged for it in {@code problems} before this method is called.
*
* @param royaleProject Flex project.
* @param classDefinitions Class definitions.
* @param problems Problems collection.
* @return A set of compilation unit for the given class definitions.
*/
static ImmutableSet<ICompilationUnit> getCompilationUnitsForDefinitions(
final RoyaleProject royaleProject,
final Set<IClassDefinition> classDefinitions,
final Collection<ICompilerProblem> problems)
{
final ImmutableSet.Builder<ICompilationUnit> builder = new ImmutableSet.Builder<ICompilationUnit>();
for (final IClassDefinition classDefinition : classDefinitions)
{
final ASProjectScope scope = royaleProject.getScope();
final ICompilationUnit compilationUnit = scope.getCompilationUnitForDefinition(classDefinition);
if (compilationUnit != null)
builder.add(compilationUnit);
else
assert problemCreatedForUnresolvedClassReference(problems, classDefinition) : "Can't find compilation unit for class '" + classDefinition.getQualifiedName() + "'. Expected a 'CSSUnresolvedClassReference'.";
}
return builder.build();
}
/**
* Check that a {@link CSSUnresolvedClassReferenceProblem} problem is created for
* the given class definition.
*
* @param problems Problems collection.
* @param classDefinition {@link IClassDefinition} definition of the unresolved class.
* @return True if there's a problem for the unresolved class definition.
*/
static boolean problemCreatedForUnresolvedClassReference(
final Collection<ICompilerProblem> problems,
final IClassDefinition classDefinition)
{
for (final ICompilerProblem problem : problems)
{
if (problem instanceof CSSUnresolvedClassReferenceProblem)
{
final CSSUnresolvedClassReferenceProblem unresolved = (CSSUnresolvedClassReferenceProblem)problem;
if (unresolved.qname.equals(classDefinition.getQualifiedName()))
return true;
}
}
return false;
}
/** Owner project. */
private final RoyaleProject royaleProject;
/**
* Initialize a CSS manager.
*
* @param royaleProject Owner project.
*/
public CSSManager(final RoyaleProject royaleProject)
{
assert royaleProject != null;
this.royaleProject = royaleProject;
}
@Override
public Collection<ICSSDocument> getCSSFromStyleModules()
{
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<ICSSDocument> getCSSFromSWCDefaultStyle()
{
final Collection<ICSSDocument> result = new ArrayList<ICSSDocument>();
for (final ISWC swc : royaleProject.getLibraries())
{
final ICSSDocument css = getDefaultCSS(swc.getSWCFile());
if (css != null)
result.add(css);
}
return result;
}
@Override
public ICSSDocument getDefaultCSS(final File swcFile)
{
final ISWCManager swcManager = royaleProject.getWorkspace().getSWCManager();
final CSSDocumentCache cache = (CSSDocumentCache)swcManager.getCSSDocumentCache();
final ISWC swc = swcManager.get(swcFile);
ITargetSettings ts = royaleProject.getTargetSettings();
List<String> excludedCSSFiles = (ts != null) ? ts.getExcludeDefaultsCSSFiles() : null;
String defaultsCSS = swcFile.getName() + ":" + "defaults.css";
if (excludedCSSFiles != null && excludedCSSFiles.contains(defaultsCSS))
return null;
return cache.getDefaultsCSS(swc, royaleProject.getCompatibilityVersion());
}
@Override
public Collection<ICompilationUnit> getDependentCompilationUnitsFromCSS(
final CSSCompilationSession session,
final ICSSDocument cssDocument,
final Collection<IDefinition> definitions,
final Collection<ICompilerProblem> problems)
{
assert cssDocument != null : "Expected CSS document to get CSS dependencies.";
assert definitions != null : "Expected a set of definitions to activate CSS rules.";
assert problems != null : "Expected problems collection. Do not ignore problems.";
final ImmutableSet<String> qnames = buildQNameToDefinitionMap(getClassDefinitionSet(definitions)).keySet();
// IFilter (ICSSRule[], IClassDefinition[]) -> (ICSSRule[] applies to IClassDefinition[])
final ImmutableSet<ICSSRule> matchedRules = getMatchedRules(session, royaleProject, cssDocument, qnames, problems);
// Find IClassDefinition(ClassReference) and CompilationUnit(Embed) from the matched ICSSRule[]
final Set<IClassDefinition> classReferences = new HashSet<IClassDefinition>();
final Set<EmbedCompilationUnit> embedCompilationUnits = new HashSet<EmbedCompilationUnit>();
getClassDefinitionDependencies(
session,
royaleProject,
matchedRules,
classReferences, // output
embedCompilationUnits, // output
problems);
// Find ICompilationUnit[] for IClassDefinition(ClassReference)[]
final ImmutableSet<ICompilationUnit> classReferenceCompilationUnits =
getCompilationUnitsForDefinitions(royaleProject, classReferences, problems);
// Only "activated" rules are included in code generation.
for (final ICSSRule rule : matchedRules)
{
session.activatedRules.add(rule);
}
// Compilation units from ClassReference() and Embed().
return new ImmutableSet.Builder<ICompilationUnit>()
.addAll(classReferenceCompilationUnits)
.addAll(embedCompilationUnits)
.build();
}
@Override
public Collection<ICSSDocument> getCSSFromThemes(final Collection<ICompilerProblem> problems)
{
final ImmutableList.Builder<ICSSDocument> builder = new ImmutableList.Builder<ICSSDocument>();
final ISWCManager swcManager = royaleProject.getWorkspace().getSWCManager();
final CSSDocumentCache cssCache = (CSSDocumentCache)swcManager.getCSSDocumentCache();
ITargetSettings ts = royaleProject.getTargetSettings();
List<String> excludedCSSFiles = (ts != null) ? ts.getExcludeDefaultsCSSFiles() : null;
for (final IFileSpecification themeFile : royaleProject.getThemeFiles())
{
if (excludedCSSFiles != null && excludedCSSFiles.contains(themeFile.getPath()))
continue;
try
{
final ICSSDocument css;
final String extension = FilenameUtils.getExtension(themeFile.getPath());
if ("swc".equalsIgnoreCase(extension))
{
final ISWC swc = swcManager.get(new File(themeFile.getPath()));
css = cssCache.getDefaultsCSS(
swc,
royaleProject.getCompatibilityVersion());
}
else if ("css".equalsIgnoreCase(extension))
{
final CacheStoreKeyBase key = CSSDocumentCache.createKey(themeFile.getPath());
css = cssCache.get(key);
}
else
{
continue;
}
// Ignore theme file without a defaults CSS.
if (css != null && css != CSSDocumentCache.EMPTY_CSS_DOCUMENT)
builder.add(css);
if ("swc".equalsIgnoreCase(extension))
{
final ISWC swc = swcManager.get(new File(themeFile.getPath()));
// add other css files.
Map<String, ISWCFileEntry> files = swc.getFiles();
Set<String> fileNames = files.keySet();
for (String fileName : fileNames)
{
String suffix = FilenameUtils.getExtension(fileName);
if ("css".equalsIgnoreCase(suffix) && !fileName.contains("default"))
{
final CacheStoreKeyBase key = CSSDocumentCache.createKey(swc, fileName);
final ICSSDocument extracss = cssCache.get(key);
builder.add(extracss);
}
}
}
}
catch (CSSDocumentCache.ProblemParsingCSSRuntimeException cssError)
{
problems.addAll(cssError.cssParserProblems);
}
}
return builder.build();
}
@Override
public boolean isFlex3CSS()
{
final Integer compatibilityVersion = royaleProject.getCompatibilityVersion();
if (compatibilityVersion == null)
return false;
else if (compatibilityVersion > Configuration.MXML_VERSION_3_0)
return false;
else
return true;
}
@Override
public ICSSDocument getCSS(String cssFilename)
{
final CSSDocumentCache cache = (CSSDocumentCache)royaleProject.getWorkspace().getSWCManager().getCSSDocumentCache();
final CacheStoreKeyBase key = CSSDocumentCache.createKey(cssFilename);
final ICSSDocument css = cache.get(key);
if(CSSDocumentCache.EMPTY_CSS_DOCUMENT == css)
return null;
else
return css;
}
}