| /* |
| * |
| * 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; |
| } |
| |
| |
| } |