/* * * 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 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.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; 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.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<Source> sources, SimpleMovie movie, Configuration configuration, SourceList sourceList, String configurationReport, List<Message> messages) { this(sources, movie, configuration, sourceList, configurationReport, messages, null); } public OEMReport(List<Source> sources, SimpleMovie movie, Configuration configuration, SourceList sourceList, String configurationReport, List<Message> messages, Map<String, VirtualFile> archiveFiles) { if (sourceList != null) { sourceListPaths = sourceList.getPaths(); } init(sources, /* null,*/ configuration == null ? null : configuration.getResourceBundles(), archiveFiles, configuration); //processFrames(movie); processMessages(messages); this.frameCount = 1 ; 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<Source> sources, /* List<CompilationUnit> exportedUnits,*/ Set<String> resourceBundles, Map<String, VirtualFile> archiveFiles, Configuration configuration) { TreeSet<String> sourceNames = new TreeSet<String>(); TreeSet<String> assetNames = new TreeSet<String>(); TreeSet<String> libraryNames = new TreeSet<String>(); data = new HashMap<String, Data>(); locations = new HashMap<String, String>(); if (sources != null) { //processSources(sources, sourceNames, assetNames, libraryNames, data, locations); } timestamps = new HashMap<String, Long>(); // 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<RslPathInfo> 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) { // (SDK-30367) Timestamp caching was added after Flex 3 but it causes // performance issues as it also checks files under hidden directories, // e.g. .svn. Skipping checking the timestamp of hidden files and // folders helps reduce the time spent in this method. if (path.isHidden()) return; 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<String, Data> data; private Map<String, String> locations; private Map<String, Long> 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<File> sourceListPaths; public boolean contentUpdated() { // AJH for now, just return true to force another build. Someday be smarter about what sources // we have and what their time stamps are. return true; /* for (Iterator<String> 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 new String[0]; //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 new String[0]; //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<CompilationUnit> units, TreeSet<String> sourceNames, TreeSet<String> assetNames, TreeSet<String> 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<Source> sources, TreeSet<String> sourceNames, TreeSet<String> assetNames, TreeSet<String> libraryNames, Map<String, Data> data, Map<String, String> 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<Name> nameSet = new HashSet<Name>(); 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<CompilationUnit> units = movie.getExportedUnitsByFrame(f); List<String> aList = new ArrayList<String>(), dList = new ArrayList<String>(); 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<Message> messages) { if (messages != null && messages.size() > 0) { List<Message> filtered = new ArrayList<Message>(); 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<String> 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<Name> nameSet, Set[] sets, Map locations) { TreeSet<String> set = new TreeSet<String>(); 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; } }