All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.royale.compiler.internal.targets.SWCTarget Maven / Gradle / Ivy

There is a newer version: 0.9.10
Show newest version
/*
 *
 *  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.targets;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.common.DependencyTypeSet;
import org.apache.royale.compiler.common.ISourceLocation;
import org.apache.royale.compiler.common.VersionInfo;
import org.apache.royale.compiler.common.XMLName;
import org.apache.royale.compiler.config.CompilerDiagnosticsConstants;
import org.apache.royale.compiler.constants.IMetaAttributeConstants;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.metadata.IMetaTag;
import org.apache.royale.compiler.definitions.references.IResolvedQualifiersReference;
import org.apache.royale.compiler.definitions.references.ReferenceFactory;
import org.apache.royale.compiler.exceptions.BuildCanceledException;
import org.apache.royale.compiler.filespecs.IBinaryFileSpecification;
import org.apache.royale.compiler.internal.config.QNameNormalization;
import org.apache.royale.compiler.internal.filespecs.SWCFileSpecification;
import org.apache.royale.compiler.internal.projects.DependencyGraph;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.projects.LibraryPathManager;
import org.apache.royale.compiler.internal.projects.ResourceBundleSourceFileHandler;
import org.apache.royale.compiler.internal.projects.SourcePathManager;
import org.apache.royale.compiler.internal.units.ResourceBundleCompilationUnit;
import org.apache.royale.compiler.problems.DuplicateScriptProblem;
import org.apache.royale.compiler.problems.FileNotFoundProblem;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.NoCompilationUnitForDefinitionProblem;
import org.apache.royale.compiler.problems.NoSourceForClassInNamespaceProblem;
import org.apache.royale.compiler.problems.NoSourceForClassProblem;
import org.apache.royale.compiler.targets.ISWCTarget;
import org.apache.royale.compiler.targets.ISWFTarget;
import org.apache.royale.compiler.targets.ITargetProgressMonitor;
import org.apache.royale.compiler.targets.ITargetReport;
import org.apache.royale.compiler.targets.ITargetSettings;
import org.apache.royale.compiler.units.ICompilationUnit;
import org.apache.royale.compiler.units.ICompilationUnit.UnitType;
import org.apache.royale.compiler.units.requests.IFileScopeRequestResult;
import org.apache.royale.swc.ISWC;
import org.apache.royale.swc.ISWCFileEntry;
import org.apache.royale.swc.ISWCLibrary;
import org.apache.royale.swc.ISWCManager;
import org.apache.royale.swc.ISWCVersion;
import org.apache.royale.swc.SWC;
import org.apache.royale.swc.SWCComponent;
import org.apache.royale.swc.SWCLibrary;
import org.apache.royale.swc.SWCScript;
import org.apache.royale.swf.ISWF;
import org.apache.royale.utils.DAByteArrayOutputStream;
import org.apache.royale.utils.FileID;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

/**
 * Compilation target for SWC library.
 */
public class SWCTarget extends Target implements ISWCTarget
{
    private static final String LIBRARY_SWF = "library.swf";

    public SWCTarget(final RoyaleProject project, ITargetSettings targetSettings, ITargetProgressMonitor progressMonitor)
    {
        super(project, targetSettings, progressMonitor);
        swc = new SWC(targetSettings.getOutput());
        royaleProject = project;
    }

    private final SWC swc;
    private final RoyaleProject royaleProject;
    private ILibrarySWFTarget librarySWFTarget;
    
    private RootedCompilationUnits rootedCompilationUnits;

    @Override
    public ISWC build(final Collection problems)
    {
        buildStarted();
        try
        {
            Iterable fatalProblems = getFatalProblems();
            if (!Iterables.isEmpty(fatalProblems))
            {
                Iterables.addAll(problems, fatalProblems);
                return null;
            }

            setVersionInfo();
            final HashSet definitions = new HashSet();
            buildLibrarySWF(definitions, problems);
            addComponents(definitions);
            addFileEntriesToSWC(problems);

            return swc;
        }
        catch (BuildCanceledException bce)
        {
            return null;
        }
        catch (InterruptedException ie)
        {
            return null;
        }
        finally
        {
            buildFinished();
        }
    }
    
    /**
     * Sets version information to be written into the  tag in catalog.xml.
     */
    private void setVersionInfo()
    {
        ISWCVersion swcVersion = swc.getVersion();
        swcVersion.setSWCVersion(VersionInfo.getLibVersion());
        if (royaleProject.isRoyale())
        {
            swcVersion.setRoyaleVersion(VersionInfo.getRoyaleVersion());
            swcVersion.setRoyaleBuild(VersionInfo.getBuild());
            swcVersion.setRoyaleMinSupportedVersion(targetSettings.getRoyaleMinimumSupportedVersion());
        }
        swcVersion.setCompilerName(VersionInfo.getCompilerName());
        swcVersion.setCompilerVersion(VersionInfo.getCompilerVersion());
        swcVersion.setCompilerBuild(VersionInfo.getCompilerBuild());
    }
    
    @Override
    public ISWFTarget getLibrarySWFTarget() throws InterruptedException
    {
        if (librarySWFTarget == null)
        {
            Target.RootedCompilationUnits rootedCompilationUnits = getRootedCompilationUnits();
            if (royaleProject.isRoyale())
            {
                librarySWFTarget =
                    new RoyaleLibrarySWFTarget(royaleProject, targetSettings, rootedCompilationUnits.getUnits());                
            }
            else
            {
                librarySWFTarget = new LibrarySWFTarget(royaleProject, targetSettings,
                        rootedCompilationUnits.getUnits());                
            }
        }
        assert librarySWFTarget != null;
        return librarySWFTarget;
    }

    @Override
    public TargetType getTargetType()
    {
        return TargetType.SWC;
    }

    /**
     * Creating {@code } tags in catalog.xml.
     * 
     * @param definitions All definitions to be linked into the target.
     */
    private void addComponents(final HashSet definitions)
    {
        final Set includedNamespaces = ImmutableSet.copyOf(targetSettings.getIncludeNamespaces());
        
        for (String namespace : includedNamespaces)
        {
            // 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 includeNamespaceQualifiedNames =
                royaleProject.getQualifiedClassNamesForManifestNamespaces(
                        Collections.singleton(namespace));
            for (String qName : includeNamespaceQualifiedNames)
            {
                final Collection tagNames = royaleProject.getTagNamesForClass(qName);
                for (XMLName tagName : tagNames)
                {
                    if (includeComponent(tagName, includedNamespaces))
                    {
                        final SWCComponent component = new SWCComponent();
                        component.setName(tagName.getName());
                        component.setURI(tagName.getXMLNamespace());
                        component.setQName(qName);
                        swc.addComponent(component);                    
                    }
                }                
            }
        }

        /*
        for (final IDefinition def : definitions)
        {
            final String qName = def.getQualifiedName();
            
            final Collection tagNames = royaleProject.getTagNamesForClass(qName);
            for (XMLName tagName : tagNames)
            {
                if (includeComponent(tagName, includedNamespaces))
                {
                    final SWCComponent component = new SWCComponent();
                    component.setName(tagName.getName());
                    component.setURI(tagName.getXMLNamespace());
                    component.setQName(qName);
                    swc.addComponent(component);                    
                }
            }
        }
        */
    }

    /**
     * Test if a component should be included in a SWC's component list.
     * 

* The rule for including a component in a SWC's component list is that * the component must be in one of the SWC's included namespaces * (meaning those specified by -include-namespace, not -namespace) * and either the component's lookupOnly flag is false * or the option -include-lookup-only is true. * * @param tagName the tag name of the component to check. * @param includedNamespaces the namespaces included in this SWC using the * -include-namespaces option. * @return true if component should be included, false otherwise. */ private boolean includeComponent(XMLName tagName, Set includedNamespaces) { if (includedNamespaces.contains(tagName.getXMLNamespace())) { return !royaleProject.isManifestComponentLookupOnly(tagName) || targetSettings.isIncludeLookupOnlyEnabled(); } return false; } /** * Add asset files to the SWC model. * * @param problems Problem collection. */ private void addFileEntriesToSWC(Collection problems) { // -include Map fileEntries = computeIncludedFiles(); for (Entry entry : fileEntries.entrySet()) { processFileEntry(entry, problems); } // -include-libraries Map includeLibraryFiles = getIncludedLibrariesFiles(); for (ISWCFileEntry fileEntry : includeLibraryFiles.values()) { swc.addFile(fileEntry); } // [IconFile(...)] metadata Map iconFilesMap = computeIconFiles(); for (Entry entry : iconFilesMap.entrySet()) { processFileEntry(entry, problems); } } /** * Helper method used by addFileEntriesToSWC() to process one file. */ private void processFileEntry(Entry entry, Collection problems) { String path = entry.getKey(); IBinaryFileSpecification fileSpec = entry.getValue().getFileSpec(); ISourceLocation sourceLocation = entry.getValue().getSourceLocation(); byte[] contents = getContents(fileSpec); if (contents != null) { long fileDate = 0; String metadataDate = targetSettings.getSWFMetadataDate(); if (metadataDate != null) { String metadataFormat = targetSettings.getSWFMetadataDateFormat(); try { SimpleDateFormat sdf = new SimpleDateFormat(metadataFormat); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); fileDate = sdf.parse(metadataDate).getTime(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e1) { e1.printStackTrace(); } fileSpec.setLastModified(fileDate); } swc.addFile(path, fileSpec.getLastModified(), contents); } else { final ICompilerProblem problem = sourceLocation != null ? new FileNotFoundProblem(sourceLocation, fileSpec.getPath()) : new FileNotFoundProblem(fileSpec.getPath()); problems.add(problem); } } /** * Returns the contents of a binary file as an array of bytes, * or null if the file cannot be read. */ private byte[] getContents(IBinaryFileSpecification fileSpec) { byte[] contents = null; try { final DAByteArrayOutputStream buffer = new DAByteArrayOutputStream(); final InputStream fileInputStream = fileSpec.createInputStream(); IOUtils.copy(fileInputStream, buffer); IOUtils.closeQuietly(buffer); IOUtils.closeQuietly(fileInputStream); if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.DA_BYTEARRAY) == CompilerDiagnosticsConstants.DA_BYTEARRAY) System.out.println("SWCTarget waiting for lock in getContents"); contents = buffer.getDirectByteArray(); if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.DA_BYTEARRAY) == CompilerDiagnosticsConstants.DA_BYTEARRAY) System.out.println("SWCTarget waiting for lock in getContents"); } catch (IOException e) { } return contents; } /** * Build "library.swf" model and add to the SWC model as a * {@link ISWCLibrary}. * * @param definitionsToBuild After this method returns, all the definitions * to be build into the target will be stored on this collection. It is * needed to compute a set of URLs for {@code } tags in * catalog.xml file. * @param problems Compiler problems. * @return the ISWF for the library * @throws InterruptedException Compilation terminated. */ private ISWF buildLibrarySWF(final Collection definitionsToBuild, final Collection problems) throws InterruptedException { getLibrarySWFTarget(); Iterables.addAll(problems, getRootedCompilationUnits().getProblems()); final LinkageChecker externalLinkageChecker = new LinkageChecker( royaleProject, targetSettings); ((Target)librarySWFTarget).setLinkageChecker(externalLinkageChecker); setLinkageChecker(externalLinkageChecker); final ISWF defaultLibrarySWF = librarySWFTarget.build(problems); // make default library model final ISWCLibrary defaultLibrary = new SWCLibrary(LIBRARY_SWF, defaultLibrarySWF); swc.addLibrary(defaultLibrary); // Deal with all the cu's that don't need to be added via addScript // save all the add script ones in cuToWrite for the next phase. Set cuToWrite = new HashSet(); for (final ICompilationUnit cu : librarySWFTarget.getCompilationUnits()) { if (isLinkageExternal(cu, targetSettings)) { // don't do anything with these } //Resource bundles processed uniquely else if (cu instanceof ResourceBundleCompilationUnit) { assert project instanceof RoyaleProject; processResourceBundle((RoyaleProject)project, (ResourceBundleCompilationUnit)cu, swc, problems); } else { // everyone else goes in this list for next step cuToWrite.add(cu); } } // remove duplicates and log resulting problems, then add to library filterCompilationUnits(cuToWrite, problems); for (final ICompilationUnit cu : cuToWrite) { defaultLibrary.addScript(createScript(cu, definitionsToBuild)); } // Add the generated root class and its dependencies to the list of // scripts. if (librarySWFTarget.getRootClassName() != null) { final SWCScript script = new SWCScript(); script.setName(librarySWFTarget.getRootClassName()); script.addDefinition(librarySWFTarget.getRootClassName()); // This class is synthetic and is supposed to be globally // unique, so nobody should care what the time stamp is. // If we set the time stamp to the current time, then // every SWC we make will be slightly different from all other // SWCs. script.setLastModified(1); // add baseclass as an inheritance dependency script.addDependency(librarySWFTarget.getBaseClassQName(), DependencyType.INHERITANCE); defaultLibrary.addScript(script); } if (librarySWFTarget.getASMetadataNames() != null) { for (String name : librarySWFTarget.getASMetadataNames()) { defaultLibrary.addNameToKeepAS3MetadataSet(name); } } return defaultLibrarySWF; } /** * removes compilation units from a list if they are shaddowed by other ones * that try to generate the same script. Logs a problem in this case * * @param cuToWrite * @param problems * @throws InterruptedException */ private void filterCompilationUnits(Set cuToWrite, Collection problems) throws InterruptedException { // group cu's by script name. Map> cuMap = new HashMap>(); Comparator comparator = new Comparator() { @Override public int compare(ICompilationUnit arg0, ICompilationUnit arg1) { // inverted sort on name will give the behavior of the old compiler: // last path in alphabetical order wins return -arg0.getName().compareTo(arg1.getName()); }}; for (ICompilationUnit cu : cuToWrite) { String name = cu.getSWFTagsRequest().get().getDoABCTagName(); Set l = cuMap.get(name); if (l == null) { l = new TreeSet(comparator); cuMap.put(name, l); } l.add(cu); } // now that we have grouped CUs based on script name, we can look for dupes for (Collection cus : cuMap.values()) { assert !cus.isEmpty(); Iterator it = cus.iterator(); ICompilationUnit cuKept = it.next(); // we are keeping the first one ICompilationUnit cuDumped = null; boolean somoneHasVisibleDefinitions = hasExternallyVisibleDefinitions(cuKept); while (it.hasNext()) // remove all the dupes from the list { cuDumped = it.next(); // Remember the last dupe to report it cuToWrite.remove(cuDumped); somoneHasVisibleDefinitions |= hasExternallyVisibleDefinitions(cuDumped); } // report a problem for dupe, unless none of the shadows has anything in it. if (cuDumped != null && somoneHasVisibleDefinitions) { problems.add(new DuplicateScriptProblem(cuKept.getAbsoluteFilename(), cuDumped.getAbsoluteFilename())); } } } private boolean hasExternallyVisibleDefinitions(ICompilationUnit cu) throws InterruptedException { final IFileScopeRequestResult r = cu.getFileScopeRequest().get(); Collection vis = r.getExternallyVisibleDefinitions(); return !vis.isEmpty(); } /** * Create a new script based on the compilation unit. * * @param cu * @param definitionsToBuild * @return * @throws InterruptedException */ private SWCScript createScript(ICompilationUnit cu, Collection definitionsToBuild) throws InterruptedException { // Create a script model per compilation unit. final SWCScript script = new SWCScript(); script.setName(cu.getSWFTagsRequest().get().getDoABCTagName()); // Add all the externally visible definitions to the script model. final IFileScopeRequestResult fsResult = cu.getFileScopeRequest().get(); for (final IDefinition def : fsResult.getExternallyVisibleDefinitions()) { script.addDefinition(def.getQualifiedName()); long mod = cu.getSyntaxTreeRequest().get().getLastModified(); String metadataDate = targetSettings.getSWFMetadataDate(); if (metadataDate != null) { String metadataFormat = targetSettings.getSWFMetadataDateFormat(); try { SimpleDateFormat sdf = new SimpleDateFormat(metadataFormat); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); mod = sdf.parse(metadataDate).getTime(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e1) { e1.printStackTrace(); } } script.setLastModified(mod); if (definitionsToBuild != null) definitionsToBuild.add(def); } final DependencyGraph dependencyGraph = royaleProject.getDependencyGraph(); Set directDependencies = dependencyGraph.getDirectDependencies(cu); for (ICompilationUnit directDependency : directDependencies) { final Map dependenciesMap = dependencyGraph.getDependencySet(cu, directDependency); for (Map.Entry dependencyEntry : dependenciesMap.entrySet()) { for (DependencyType type : dependencyEntry.getValue()) script.addDependency(dependencyEntry.getKey(), type); } } return script; } /** * Process a resource bundle represented by the specified compilation unit. * * @param project active project * @param cu resource bundle compilation unit to process * @param swc target swc * @param problems problems collection */ private void processResourceBundle(RoyaleProject project, ResourceBundleCompilationUnit cu, SWC swc, Collection problems) { Collection locales = null; if (cu.getLocale() == null) { //Create a file entry for each locale since this compilation unit is not locale specific locales = project.getLocales(); } else { //This compilation unit is for a locale specific file, //therefore create a file entry for only the locale comp unit depends on locales = Collections.singletonList(cu.getLocale()); } byte[] fileContent = cu.getFileContent(problems); //get file content if (fileContent == null) return; //Is this the right thing to do here? for (String locale : locales) { //Build the destination path. Destination path for a .properties file //is such as locale/{locale}/foo/bar/x.properties StringBuilder sb = new StringBuilder(); sb.append(ResourceBundleCompilationUnit.LOCALE); sb.append(File.separator); sb.append(locale); sb.append(File.separator); sb.append(QNameNormalization.normalize(cu.getBundleNameInColonSyntax()).replace('.', File.separatorChar)); sb.append(FilenameUtils.EXTENSION_SEPARATOR_STR); sb.append(ResourceBundleSourceFileHandler.EXTENSION); swc.addFile(sb.toString(), cu.getFileLastModified(), fileContent); } } @Override public boolean addToZipOutputStream(ZipOutputStream output, Collection problemCollection) { throw new UnsupportedOperationException(); } @Override protected Target.RootedCompilationUnits computeRootedCompilationUnits() throws InterruptedException { final Set rootCompilationUnits = new HashSet(); final Collection includedSourceFiles = targetSettings.getIncludeSources(); final Set includeClassNameSet = ImmutableSet.copyOf(targetSettings.getIncludeClasses()); final Set includedNamespaces = ImmutableSet.copyOf(targetSettings.getIncludeNamespaces()); final ArrayList problems = new ArrayList(); // Select definitions according to configurations. // include-namespace final Collection 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 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()); Target.RootedCompilationUnits units = new Target.RootedCompilationUnits(rootCompilationUnits, problems); Set unitSet = units.getUnits(); // for (ICompilationUnit cu : unitSet) // System.out.println(cu.getName()); return units; } @Override public RootedCompilationUnits getRootedCompilationUnits() throws InterruptedException { if (this.rootedCompilationUnits == null) rootedCompilationUnits = computeRootedCompilationUnits(); assert rootedCompilationUnits != null; return rootedCompilationUnits; } /** * 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 filterUnitsBasedOnSourcePath(Collection compilationUnitsForFile) throws InterruptedException { List sourcePathUnits = new ArrayList(compilationUnitsForFile); boolean foundHighestPriorityUnit = false; for (File sourcePath : royaleProject.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 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 getCompilationUnitsForIncludedNamespaces( Collection namespaces, Collection problems) throws InterruptedException { final Collection allUnits = new HashSet(); 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 includeNamespaceQualifiedNames = royaleProject.getQualifiedClassNamesForManifestNamespaces( Collections.singleton(namespace)); final Collection 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 units, Collection problems) throws InterruptedException { for (ICompilationUnit unit : units) { List classNames = unit.getQualifiedNames(); String className = classNames.get(classNames.size() - 1); Collection xmlNames = royaleProject.getTagNamesForClass(className); for (XMLName xmlName : xmlNames) { if (namespace.equals(xmlName.getXMLNamespace())) { if (!royaleProject.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 getCompilationUnitsFromClassNames(String namespace, Collection classNames, final Collection problems) { Collection compilableClassNames = new ArrayList(); for (String className : classNames) { Collection tagNames = royaleProject.getTagNamesForClass(className); boolean okToAdd = true; for (XMLName tagName : tagNames) { if (royaleProject.isManifestComponentLookupOnly(tagName)) okToAdd = false; } if (okToAdd) compilableClassNames.add(className); } // Class names are turned into references and then info compilation units. final Iterable references = Iterables.transform(compilableClassNames, new Function() { @Override public IResolvedQualifiersReference apply(String qualifiedName) { return ReferenceFactory.packageQualifiedReference(project.getWorkspace(), qualifiedName, true); } }); Collection units = new LinkedList(); for (IResolvedQualifiersReference reference : references) { IDefinition def = reference.resolve(royaleProject); 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; } /** * @return Expand {@code -include-files} items into normalized file paths. */ private Map computeIncludedFiles() { Map includedFiles = new HashMap(); for (Entry entry : targetSettings.getIncludeFiles().entrySet()) { String filename = entry.getKey(); if (filename.contains("*")) { Collection files = getFiles(entry.getValue()); String basename = entry.getValue().getAbsolutePath(); for (File file : files) { String path = file.getAbsolutePath(); if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE) System.out.println("SWCTarget waiting for lock in getLatestBinaryFileSpecification"); IBinaryFileSpecification fileSpec = project.getWorkspace().getLatestBinaryFileSpecification(path); if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE) System.out.println("SWCTarget done with lock in getLatestBinaryFileSpecification"); if (filename != null && fileSpec != null) { String relativePath = path.substring(basename.length() + 1); relativePath = filename.replace("*", relativePath); FileEntryValue value = new FileEntryValue(fileSpec, null); includedFiles.put(relativePath, value); } } } else { String path = entry.getValue().getAbsolutePath(); if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE) System.out.println("SWCTarget waiting for lock in getLatestBinaryFileSpecification"); IBinaryFileSpecification fileSpec = project.getWorkspace().getLatestBinaryFileSpecification(path); if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE) System.out.println("SWCTarget done with lock in getLatestBinaryFileSpecification"); if (filename != null && fileSpec != null) { FileEntryValue value = new FileEntryValue(fileSpec, null); includedFiles.put(filename, value); } } } return includedFiles; } private Collection getFiles(File file) { String filename = file.getAbsolutePath(); if (filename.endsWith(File.separator)) filename = filename.substring(0, filename.length() - 1); Collection files = FileUtils.listFiles(new File( filename), null, true); return files; } /** * The collection of files from all of the libraries found on the * -include-libraries path. * * @return collection of absolute path names of SWC files. */ private Map getIncludedLibrariesFiles() { Map files = new HashMap(); // Find all of the libraries on the -include-library path Set swcs = LibraryPathManager.discoverSWCFilePaths( targetSettings.getIncludeLibraries().toArray(new File[0])); // For each library, get a compilation unit for every class in the // library. ISWCManager swcManager = project.getWorkspace().getSWCManager(); for (FileID swcPath : swcs) { ISWC swc = swcManager.get(swcPath.getFile()); Map swcFiles = swc.getFiles(); for (Map.Entry file : swcFiles.entrySet()) { ISWCFileEntry currentFileEntry = files.get(file.getKey()); if (currentFileEntry == null || (file.getValue().getLastModified() > currentFileEntry.getLastModified())) { files.put(file.getKey(), file.getValue()); } } } return files; } /** * Loops over all externally visible definitions in all compilation units * for this target, looking for class definitions with [IconFile(...)] metadata. *

* Suppose we find [IconFile("MyComponent.png")] on the class foo.bar.MyComponent. * Then the returned map will have an entry whose key is "foo/bar/MyComponent" * and whose value is a file specification that can be used to read the contents * of this file. A file with the same contents will get created at foo/bar/MyComponent * inside the SWC. */ private Map computeIconFiles() { Map iconFiles = new HashMap(); try { Collection compilationUnits = librarySWFTarget.getCompilationUnits(); for (IDefinition definition : getAllExternallyVisibleDefinitions(compilationUnits)) { if (!(definition instanceof IClassDefinition)) continue; IClassDefinition classDefinition = (IClassDefinition)definition; IMetaTag iconFileMetaTag = classDefinition.getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_ICON_FILE); String iconFilePath = classDefinition.getIconFile(); if (iconFilePath == null) continue; String packageName = classDefinition.getPackageName(); String key = packageName.replaceAll("\\.", "/") + "/" + iconFilePath; ICompilationUnit cu = project.getScope().getCompilationUnitForDefinition(classDefinition); IBinaryFileSpecification fileSpec = null; if (cu.getCompilationUnitType() == UnitType.SWC_UNIT) { ISWC swc = project.getWorkspace().getSWCManager().get(new File(cu.getAbsoluteFilename())); ISWCFileEntry swcFileEntry = swc.getFile(key); fileSpec = new SWCFileSpecification(key, swcFileEntry); } else { String sourcePath = classDefinition.getSourcePath(); iconFilePath = FilenameUtils.getFullPath(sourcePath) + iconFilePath; if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE) System.out.println("SWCTarget waiting for lock in getLatestBinaryFileSpecification"); fileSpec = project.getWorkspace().getLatestBinaryFileSpecification(iconFilePath); if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE) System.out.println("SWCTarget done with lock in getLatestBinaryFileSpecification"); } FileEntryValue value = new FileEntryValue(fileSpec, iconFileMetaTag); iconFiles.put(key, value); } } catch (InterruptedException e) { } return iconFiles; } @Override protected ITargetReport computeTargetReport() throws InterruptedException { assert librarySWFTarget != null : "Must call build before getting the target report!!"; return librarySWFTarget.getTargetReport(); } private static class FileEntryValue { FileEntryValue(IBinaryFileSpecification fileSpec, ISourceLocation sourceLocation) { this.fileSpec = fileSpec; this.sourceLocation = sourceLocation; } private IBinaryFileSpecification fileSpec; private ISourceLocation sourceLocation; IBinaryFileSpecification getFileSpec() { return fileSpec; } ISourceLocation getSourceLocation() { return sourceLocation; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy