//////////////////////////////////////////////////////////////////////////////// // // ADOBE SYSTEMS INCORPORATED // Copyright 2006-2007 Adobe Systems Incorporated // All Rights Reserved. // // NOTICE: Adobe permits you to use, modify, and distribute this file // in accordance with the terms of the license agreement accompanying it. // //////////////////////////////////////////////////////////////////////////////// package flex2.tools.oem.internal; import java.io.File; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import flash.swf.Frame; import flex2.compiler.AssetInfo; import flex2.compiler.CompilationUnit; import flex2.compiler.Source; import flex2.compiler.SourceList; import flex2.compiler.common.Configuration; import flex2.compiler.common.Configuration.RslPathInfo; import flex2.compiler.io.FileUtil; import flex2.compiler.io.VirtualFile; import flex2.compiler.swc.SwcScript; import flex2.compiler.util.Name; import flex2.compiler.util.QName; import flex2.compiler.util.QNameList; import flex2.linker.SimpleMovie; import flex2.tools.VersionInfo; import flex2.tools.oem.Message; import flex2.tools.oem.Report; /** * A Report implementation. * * @version 2.0.1 * @author Clement Wong */ public class OEMReport implements Report { public OEMReport(List sources, SimpleMovie movie, Configuration configuration, SourceList sourceList, String configurationReport, List messages) { this(sources, movie, configuration, sourceList, configurationReport, messages, null); } public OEMReport(List sources, SimpleMovie movie, Configuration configuration, SourceList sourceList, String configurationReport, List messages, Map archiveFiles) { if (sourceList != null) { sourceListPaths = sourceList.getPaths(); } init(sources, movie == null ? null : movie.getExportedUnits(), configuration == null ? null : configuration.getResourceBundles(), archiveFiles, configuration); processFrames(movie); processMessages(messages); this.frameCount = movie == null ? 0 : movie.frames.size(); this.configurationReport = configurationReport; defaultWidth = configuration != null ? configuration.defaultWidth() : 0; defaultHeight = configuration != null ? configuration.defaultHeight() : 0; width = 0; height = 0; widthPercent = 0.0; heightPercent = 0.0; if (movie != null) { linkReport = movie.getLinkReport(); sizeReport = movie.getSizeReport(); bgColor = movie.bgcolor.color; pageTitle = movie.pageTitle; if (movie.userSpecifiedWidth) { width = movie.width; } else if (configuration != null) { String percent = configuration.widthPercent(); if (percent != null) { percent = percent.trim(); // Percent character is expected but allow it to be optional. if (percent.length() >= 1 && percent.charAt(percent.length() - 1) == '%') { percent = percent.substring(0, percent.length() - 1); } try { widthPercent = Double.parseDouble(percent) / 100; } catch(NumberFormatException ex) {} } } if (movie.userSpecifiedHeight) { height = movie.height; } else if (configuration != null) { String percent = configuration.heightPercent(); if (percent != null) { percent = percent.trim(); // Percent character is expected but allow it to be optional. if (percent.length() >= 1 && percent.charAt(percent.length() - 1) == '%') { percent = percent.substring(0, percent.length() - 1); } try { heightPercent = Double.parseDouble(percent) / 100; } catch(NumberFormatException ex) {} } } } else { linkReport = null; sizeReport = null; bgColor = 0; pageTitle = null; } } private void init(List sources, List exportedUnits, Set resourceBundles, Map archiveFiles, Configuration configuration) { TreeSet sourceNames = new TreeSet(); TreeSet assetNames = new TreeSet(); TreeSet libraryNames = new TreeSet(); data = new HashMap(); locations = new HashMap(); if (sources != null) { processSources(sources, sourceNames, assetNames, libraryNames, data, locations); } timestamps = new HashMap(); // Store timestamps for each path in the SourceList, so // contentUpdated() can report true if new sources are added // to the SourceList. if (sourceListPaths != null) { for (File path : sourceListPaths) { storeTimestamps(path); } } compiler_SourceNames = toArray(sourceNames); storeTimestamps(compiler_SourceNames); compiler_AssetNames = toArray(assetNames); storeTimestamps(compiler_AssetNames); compiler_LibraryNames = toArray(libraryNames); storeTimestamps(compiler_LibraryNames); resourceBundleNames = toArray(resourceBundles); sourceNames.clear(); assetNames.clear(); libraryNames.clear(); processCompilationUnits(exportedUnits, sourceNames, assetNames, libraryNames); // Add files to the assets set so they are included in the timestamps. // FB calls the OEMReports.contentUpdated() method to determine if a // file has changed and if so it will recompile. This code was added // for the specific case where a default.css file was modified but FB // didn't think it needed to recompile because default.css wasn't in // the timestamps map. if (archiveFiles != null) { for (String fileName: archiveFiles.keySet()) { assetNames.add(fileName); } } linker_SourceNames = toArray(sourceNames); storeTimestamps(linker_SourceNames); linker_AssetNames = toArray(assetNames); storeTimestamps(linker_AssetNames); linker_LibraryNames = toArray(libraryNames); storeTimestamps(linker_LibraryNames); // Add libraries linked as RSLs to the timestamp map. // Saving the digests of the libraries would be better than a // timestamp since we would know if the digest changed, not // just something in the library. We can't compare digests because // contentUpdated() does not have access to the swcs we will // compile with. Timestamps may cause us to recompile an app when // the RSL has not been modified but that should be pretty rare. if (configuration != null) { List rslPathInfoList = configuration.getRslPathInfo(); if (rslPathInfoList.size() > 0) { String[] rslPaths = new String[rslPathInfoList.size()]; int i = 0; for (RslPathInfo rslPathInfo : rslPathInfoList) { rslPaths[i++] = rslPathInfo.getSwcVirtualFile().getName(); } storeTimestamps(rslPaths); } } } private void storeTimestamps(File path) { timestamps.put(FileUtil.getCanonicalPath(path), path.lastModified()); for (File file : path.listFiles()) { if (file.isDirectory()) { storeTimestamps(file); } } } private void storeTimestamps(String[] a) { if (a != null) { for (String fileName : a) { if ((fileName != null) && !timestamps.containsKey(fileName)) { timestamps.put(fileName, new File(fileName).lastModified()); } } } } private String[] compiler_SourceNames, compiler_AssetNames, compiler_LibraryNames; private String[] linker_SourceNames, linker_AssetNames, linker_LibraryNames; private String[] resourceBundleNames; private Map data; private Map locations; private Map timestamps; private int frameCount; private int bgColor, defaultWidth, defaultHeight, width, height; private String pageTitle; private double widthPercent, heightPercent; private String linkReport, sizeReport, configurationReport; private Message[] messages; private String[][] assetNames, definitionNames; private List sourceListPaths; public boolean contentUpdated() { for (Iterator i = timestamps.keySet().iterator(); i.hasNext(); ) { String path = i.next(); Long ts = timestamps.get(path); File f = new File(path); if (!f.exists() || f.lastModified() != ts.longValue()) { return true; } } return false; } public String[] getSourceNames(Object report) { return (COMPILER.equals(report)) ? compiler_SourceNames : (LINKER.equals(report)) ? linker_SourceNames : null; } public String[] getAssetNames(int frame) { return assetNames[frame - 1]; } public String[] getAssetNames(Object report) { return (COMPILER.equals(report)) ? compiler_AssetNames : (LINKER.equals(report)) ? linker_AssetNames : null; } public String[] getLibraryNames(Object report) { return (COMPILER.equals(report)) ? compiler_LibraryNames : (LINKER.equals(report)) ? linker_LibraryNames : null; } public String[] getResourceBundleNames() { return resourceBundleNames; } public String[] getDefinitionNames(int frame) { return definitionNames[frame - 1]; } public String[] getDefinitionNames(String sourceName) { Data d = data.get(sourceName); return d == null ? null : d.definitions; } public String getLocation(String definition) { return locations.get(definition); } public String[] getDependencies(String definition) { String location = getLocation(definition); if (location != null) { Data d = data.get(location); return d == null ? null : d.dependencies; } else { return null; } } public String[] getPrerequisites(String definition) { String location = getLocation(definition); if (location != null) { Data d = data.get(location); return d == null ? null : d.prerequisites; } else { return null; } } public long writeLinkReport(Writer out) throws IOException { long size = 0; if (linkReport != null) { out.write(linkReport); out.flush(); size = linkReport.length(); } return size; } public long writeSizeReport(Writer out) throws IOException { long size = 0; if (sizeReport != null) { out.write(sizeReport); out.flush(); size = sizeReport.length(); } return size; } public long writeConfigurationReport(Writer out) throws IOException { long size = 0; if (configurationReport != null) { out.write(configurationReport); out.flush(); size = configurationReport.length(); } return size; } public int getBackgroundColor() { return bgColor; } public String getPageTitle() { return pageTitle; } public int getDefaultWidth() { return defaultWidth; } public int getDefaultHeight() { return defaultHeight; } public int getWidth() { return width; } public int getHeight() { return height; } public double getWidthPercent() { return widthPercent; } public double getHeightPercent() { return heightPercent; } public String getCompilerVersion() { return VersionInfo.buildMessage(); } public Message[] getMessages() { return messages; } public int getFrameCount() { return frameCount; } private void processCompilationUnits(List units, TreeSet sourceNames, TreeSet assetNames, TreeSet libraryNames) { for (int i = 0, length = units == null ? 0 : units.size(); i < length; i++) { CompilationUnit u = units.get(i); Source s = (u == null) ? null : u.getSource(); if (s == null) { continue; } if (s.isFileSpecOwner() || s.isSourceListOwner() || s.isSourcePathOwner() || s.isResourceBundlePathOwner()) { sourceNames.add(s.getName()); for (Iterator j = s.getFileIncludes(); j.hasNext(); ) { VirtualFile f = (VirtualFile) j.next(); sourceNames.add(f.getName()); } if (u.hasAssets()) { for (Iterator j = u.getAssets().iterator(); j.hasNext(); ) { Map.Entry e = (Map.Entry) j.next(); AssetInfo assetInfo = (AssetInfo) e.getValue(); VirtualFile path = assetInfo.getPath(); if (path != null) { assetNames.add(path.getName()); } } } } else if (s.isSwcScriptOwner()) { String location = ((SwcScript) s.getOwner()).getLibrary().getSwcLocation(); libraryNames.add(location); } } } private void processSources(List sources, TreeSet sourceNames, TreeSet assetNames, TreeSet libraryNames, Map data, Map locations) { for (Source s : sources) { CompilationUnit u = (s == null) ? null : s.getCompilationUnit(); if (s == null) { continue; } if (s.isFileSpecOwner() || s.isSourceListOwner() || s.isSourcePathOwner() || s.isResourceBundlePathOwner()) { sourceNames.add(s.getName()); for (Iterator j = s.getFileIncludes(); j.hasNext(); ) { VirtualFile f = (VirtualFile) j.next(); sourceNames.add(f.getName()); } if (u.hasAssets()) { for (Iterator j = u.getAssets().iterator(); j.hasNext(); ) { Map.Entry e = (Map.Entry) j.next(); AssetInfo assetInfo = (AssetInfo) e.getValue(); VirtualFile path = assetInfo.getPath(); if (path != null) { assetNames.add(assetInfo.getPath().getName()); } } } if (locations != null) { for (int j = 0, size = u.topLevelDefinitions.size(); j < size; j++) { locations.put(u.topLevelDefinitions.get(j).toString(), s.getName()); } } } else if (s.isSwcScriptOwner()) { String location = ((SwcScript) s.getOwner()).getLibrary().getSwcLocation(); libraryNames.add(location); if (locations != null) { for (int j = 0, size = u.topLevelDefinitions.size(); j < size; j++) { locations.put(u.topLevelDefinitions.get(j).toString(), location); } } } } for (Source s : sources) { CompilationUnit u = (s == null) ? null : s.getCompilationUnit(); if (s == null) { continue; } if (s.isFileSpecOwner() || s.isSourceListOwner() || s.isSourcePathOwner() || s.isResourceBundlePathOwner()) { Data d = new Data(); d.definitions = toArray(u.topLevelDefinitions); d.prerequisites = toArray(u.inheritance, null, locations); Set nameSet = new HashSet(); nameSet.addAll(u.namespaces); nameSet.addAll(u.types); nameSet.addAll(u.expressions); d.dependencies = toArray(nameSet, new Set[] { u.extraClasses, u.resourceBundleHistory }, locations); data.put(s.getName(), d); } } } private void processFrames(SimpleMovie movie) { int count = movie == null ? 0 : movie.frames.size(); assetNames = new String[count][]; definitionNames = new String[count][]; for (int i = 0; i < count; i++) { Frame f = movie.frames.get(i); List units = movie.getExportedUnitsByFrame(f); List aList = new ArrayList(), dList = new ArrayList(); for (int j = 0, size = units == null ? 0 : units.size(); j < size; j++) { CompilationUnit u = units.get(j); Source s = u.getSource(); if (u.hasAssets()) { for (Iterator k = u.getAssets().iterator(); k.hasNext(); ) { Map.Entry e = (Map.Entry) k.next(); AssetInfo assetInfo = (AssetInfo) e.getValue(); VirtualFile path = assetInfo.getPath(); if (path != null) { String assetName = path.getName(); if (!aList.contains(assetName)) { aList.add(assetName); } } } } if (s.isFileSpecOwner() || s.isResourceBundlePathOwner() || s.isSourceListOwner() || s.isSourcePathOwner() || s.isSwcScriptOwner()) { for (Iterator k = u.topLevelDefinitions.iterator(); k.hasNext(); ) { String definitionName = k.next().toString(); dList.add(definitionName); } } } if (aList.size() > 0) { assetNames[i] = new String[aList.size()]; aList.toArray(assetNames[i]); } if (dList.size() > 0) { definitionNames[i] = new String[dList.size()]; dList.toArray(definitionNames[i]); } } } private void processMessages(List messages) { if (messages != null && messages.size() > 0) { List filtered = new ArrayList(); for (int i = 0, length = messages.size(); i < length; i++) { Message m = messages.get(i); if (m != null && !Message.INFO.equals(m.getLevel())) { filtered.add(m); } } messages = filtered; } if (messages != null && messages.size() > 0) { this.messages = new Message[messages.size()]; for (int i = 0, length = this.messages.length; i < length; i++) { this.messages[i] = new GenericMessage(messages.get(i)); } } else { this.messages = null; } } private String[] toArray(Set set) { String[] a = new String[set == null ? 0 : set.size()]; int j = 0; if (set != null) { for (String source : set) { a[j++] = source; } } return a.length == 0 ? null : a; } private String[] toArray(QNameList definitions) { String[] a = new String[definitions == null ? 0 : definitions.size()]; for (int i = 0; i < a.length; i++) { a[i] = definitions.get(i).toString(); } return a.length == 0 ? null : a; } private String[] toArray(Set nameSet, Set[] sets, Map locations) { TreeSet set = new TreeSet(); for (Name name : nameSet) { String qName = null; if (name instanceof QName && (locations == null || locations.containsKey(qName = name.toString()))) { set.add(qName); } } for (int i = 0, length = sets == null ? 0 : sets.length; i < length; i++) { if (sets[i] != null) { for (Object obj : sets[i]) { if ((obj instanceof String) && (locations == null || locations.containsKey(obj))) { set.add((String)obj); } } } } return toArray(set); } static class Data { String[] definitions; String[] prerequisites; String[] dependencies; } }