blob: 5e523673ee7068edc0682a0d4e4d19fe79de6940 [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.flex.compiler.internal.targets;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.flex.compiler.common.XMLName;
import org.apache.flex.compiler.definitions.IDefinition;
import org.apache.flex.compiler.definitions.references.IResolvedQualifiersReference;
import org.apache.flex.compiler.definitions.references.ReferenceFactory;
import org.apache.flex.compiler.internal.projects.FlexJSProject;
import org.apache.flex.compiler.internal.projects.SourcePathManager;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.problems.NoCompilationUnitForDefinitionProblem;
import org.apache.flex.compiler.problems.NoSourceForClassInNamespaceProblem;
import org.apache.flex.compiler.problems.NoSourceForClassProblem;
import org.apache.flex.compiler.projects.IASProject;
import org.apache.flex.compiler.targets.IJSTarget;
import org.apache.flex.compiler.targets.ITargetProgressMonitor;
import org.apache.flex.compiler.targets.ITargetSettings;
import org.apache.flex.compiler.units.ICompilationUnit;
import org.apache.flex.compiler.units.ICompilationUnit.UnitType;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
public class FlexJSSWCTarget extends JSTarget implements IJSTarget
{
protected ICompilationUnit mainCU;
protected RootedCompilationUnits rootedCompilationUnits;
/**
* Initialize a JS target with the owner project and root compilation units.
*
* @param project the owner project
*/
public FlexJSSWCTarget(FlexJSProject project, ITargetSettings targetSettings,
ITargetProgressMonitor progressMonitor)
{
super(project, targetSettings, progressMonitor);
flexProject = (FlexJSProject)project;
}
private FlexJSProject flexProject;
@Override
protected Target.RootedCompilationUnits computeRootedCompilationUnits() throws InterruptedException
{
final Set<ICompilationUnit> rootCompilationUnits = new HashSet<ICompilationUnit>();
final Collection<File> includedSourceFiles = targetSettings.getIncludeSources();
final Set<String> includeClassNameSet = ImmutableSet.copyOf(targetSettings.getIncludeClasses());
final Set<String> includedNamespaces = ImmutableSet.copyOf(targetSettings.getIncludeNamespaces());
final ArrayList<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
// Select definitions according to configurations.
// include-namespace
final Collection<ICompilationUnit> includeNamespaceUnits =
getCompilationUnitsForIncludedNamespaces(includedNamespaces, problems);
rootCompilationUnits.addAll(includeNamespaceUnits);
// include-class + include-namespace
rootCompilationUnits.addAll(getCompilationUnitsFromClassNames(null, includeClassNameSet, problems));
// include-source
for (final File includedSourceFileName : includedSourceFiles)
{
// Get all the compilation units in the project that reference the specified file.
Collection<ICompilationUnit> compilationUnitsForFile = project.getWorkspace().getCompilationUnits(includedSourceFileName.getAbsolutePath(), project);
// For compilation units with that differ by qname, choose the compilation that
// appears first on the source-path.
if (compilationUnitsForFile.size() > 1)
{
compilationUnitsForFile = filterUnitsBasedOnSourcePath(compilationUnitsForFile);
}
for (ICompilationUnit cu : compilationUnitsForFile)
{
// IFilter out any compilation unit in the list where the specified file is not the root
// source file compiled by the compilation unit.
if (cu.getAbsoluteFilename().equals(includedSourceFileName.getAbsolutePath()))
rootCompilationUnits.add(cu);
}
}
//Add compilation units for included resource bundles
for (ICompilationUnit rbCompUnit : getIncludedResourceBundlesCompilationUnits(problems))
rootCompilationUnits.add(rbCompUnit);
// -include and -include-libraries
rootCompilationUnits.addAll(getIncludesCompilationUnits());
rootCompilationUnits.addAll(getIncludeLibrariesCompilationUnits());
return new Target.RootedCompilationUnits(rootCompilationUnits, problems);
}
/**
* For compilation units with the same absolute source path, filter based on
* the source path. The compilation unit found on the highest priority
* source path wins. The rest of the compilation units with qnames are
* discared. If a unit is not on the source path or does not have a qname or
* more than one qname, then let it thru the filter.
*
* @param compilationUnitsForFile list of compilation units to filter.
* @return filtered compilation units.
* @throws InterruptedException
*/
private Collection<ICompilationUnit> filterUnitsBasedOnSourcePath(Collection<ICompilationUnit> compilationUnitsForFile) throws InterruptedException
{
List<ICompilationUnit> sourcePathUnits = new ArrayList<ICompilationUnit>(compilationUnitsForFile);
boolean foundHighestPriorityUnit = false;
for (File sourcePath : flexProject.getSourcePath())
{
for (ICompilationUnit unit : sourcePathUnits)
{
// We only care about filtering units on the source path
// that follow the single definition rule.
UnitType unitType = unit.getCompilationUnitType();
if (unitType == UnitType.AS_UNIT || unitType == UnitType.FXG_UNIT ||
unitType == UnitType.MXML_UNIT || unitType == UnitType.CSS_UNIT)
{
Collection<String> qnames = unit.getQualifiedNames();
if (qnames.size() > 1)
continue;
String unitQname = qnames.isEmpty() ? "" : qnames.iterator().next();
String computedQname = SourcePathManager.computeQName(sourcePath, new File(unit.getAbsoluteFilename()));
if (unitQname.equals(computedQname))
{
// We found a unit on the source path. Only keep the
// first unit found on the source path and remove the
// others.
if (foundHighestPriorityUnit)
compilationUnitsForFile.remove(unit);
foundHighestPriorityUnit = true;
break; // should only be one compilation unit on a source path
}
}
}
}
return compilationUnitsForFile;
}
/**
* Get the compilation units for the given included namespaces. Also perform error
* checking.
*
* @param namespaces the namespaces included in this swc target.
* @param problems A collection where detected problems are added.
* @return A collection of compilation units.
* @throws InterruptedException
*/
private Collection<ICompilationUnit> getCompilationUnitsForIncludedNamespaces(
Collection<String> namespaces,
Collection<ICompilerProblem> problems) throws InterruptedException
{
final Collection<ICompilationUnit> allUnits = new HashSet<ICompilationUnit>();
for (String namespace : namespaces)
{
// For each namespace get the set of classes.
// From the classes get the the compilation units.
// Validate the compilation units are resolved to source
// files unless there are lookupOnly entries.
final Collection<String> includeNamespaceQualifiedNames =
flexProject.getQualifiedClassNamesForManifestNamespaces(
Collections.singleton(namespace));
final Collection<ICompilationUnit> units =
getCompilationUnitsFromClassNames(namespace, includeNamespaceQualifiedNames, problems);
validateIncludeNamespaceEntries(namespace, units, problems);
allUnits.addAll(units);
}
return allUnits;
}
/**
* Validate that the manifest entries in the included namespaces resolve to
* source files, not classes from other SWCs. The exception is for entries
* that are "lookupOnly".
*
* @param namespace The target namespace.
* @param units The compilation units found in that namespace.
* @param problems detected problems are added to this list.
* @throws InterruptedException
*/
private void validateIncludeNamespaceEntries(String namespace,
Collection<ICompilationUnit> units,
Collection<ICompilerProblem> problems) throws InterruptedException
{
for (ICompilationUnit unit : units)
{
List<String> classNames = unit.getQualifiedNames();
String className = classNames.get(classNames.size() - 1);
Collection<XMLName> xmlNames = flexProject.getTagNamesForClass(className);
for (XMLName xmlName : xmlNames)
{
if (namespace.equals(xmlName.getXMLNamespace()))
{
if (!flexProject.isManifestComponentLookupOnly(xmlName) &&
unit.getCompilationUnitType() == UnitType.SWC_UNIT)
{
problems.add(new NoSourceForClassInNamespaceProblem(namespace, className));
}
break;
}
}
}
}
/**
* Return a collection of compilation units for a collection of class names.
*
* @param namespace the namespace of the classes. Null if there is no namespace.
* @param classNames
* @param problems detected problems are added to this list.
* @return a collection of compilation units.
*/
private Collection<ICompilationUnit> getCompilationUnitsFromClassNames(String namespace,
Collection<String> classNames,
final Collection<ICompilerProblem> problems)
{
Collection<String> compilableClassNames = new ArrayList<String>();
for (String className : classNames)
{
Collection<XMLName> tagNames = flexProject.getTagNamesForClass(className);
boolean okToAdd = true;
for (XMLName tagName : tagNames)
{
if (flexProject.isManifestComponentLookupOnly(tagName))
okToAdd = false;
}
if (okToAdd)
compilableClassNames.add(className);
}
// Class names are turned into references and then info compilation units.
final Iterable<IResolvedQualifiersReference> references =
Iterables.transform(compilableClassNames, new Function<String, IResolvedQualifiersReference>()
{
@Override
public IResolvedQualifiersReference apply(String qualifiedName)
{
return ReferenceFactory.packageQualifiedReference(project.getWorkspace(), qualifiedName, true);
}
});
Collection<ICompilationUnit> units = new LinkedList<ICompilationUnit>();
for (IResolvedQualifiersReference reference : references)
{
IDefinition def = reference.resolve(flexProject);
if (def == null)
{
if (namespace == null)
problems.add(new NoSourceForClassProblem(reference.getDisplayString()));
else
problems.add(new NoSourceForClassInNamespaceProblem(namespace, reference.getDisplayString()));
}
else
{
ICompilationUnit defCU = project.getScope().getCompilationUnitForDefinition(def);
if (defCU == null)
problems.add(new NoCompilationUnitForDefinitionProblem(def.getBaseName()));
else
units.add(defCU);
}
}
return units;
}
}