| /* |
| * |
| * 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.graph; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.PrintWriter; |
| import java.nio.charset.Charset; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.commons.io.FileUtils; |
| import org.apache.royale.compiler.clients.problems.ProblemQuery; |
| import org.apache.royale.compiler.common.DependencyType; |
| import org.apache.royale.compiler.common.DependencyTypeSet; |
| import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogEmitterTokens; |
| import org.apache.royale.compiler.internal.driver.js.JSCompilationUnit; |
| import org.apache.royale.compiler.internal.driver.js.goog.JSGoogConfiguration; |
| import org.apache.royale.compiler.internal.projects.CompilerProject; |
| import org.apache.royale.compiler.internal.projects.DefinitionPriority; |
| import org.apache.royale.compiler.internal.projects.DependencyGraph; |
| import org.apache.royale.compiler.internal.projects.RoyaleJSProject; |
| import org.apache.royale.compiler.problems.MainDefinitionQNameProblem; |
| import org.apache.royale.compiler.problems.FileNotFoundProblem; |
| import org.apache.royale.compiler.units.ICompilationUnit; |
| import org.apache.royale.swc.ISWC; |
| import org.apache.royale.swc.ISWCFileEntry; |
| |
| import com.google.common.io.Files; |
| import com.google.debugging.sourcemap.FilePosition; |
| import com.google.debugging.sourcemap.SourceMapConsumerV3; |
| import com.google.debugging.sourcemap.SourceMapGeneratorV3; |
| import com.google.debugging.sourcemap.SourceMapParseException; |
| |
| public class GoogDepsWriter { |
| |
| public GoogDepsWriter(File outputFolder, String mainClassName, JSGoogConfiguration config, List<ISWC> swcs) |
| { |
| this.outputFolderPath = outputFolder.getAbsolutePath(); |
| this.moduleOutput = config.getModuleOutput(); |
| this.mainName = mainClassName; |
| removeCirculars = config.getRemoveCirculars(); |
| otherPaths = config.getSDKJSLib(); |
| otherPaths.add(new File(outputFolder.getParent(), "royale/Royale/src").getPath()); |
| this.swcs = swcs; |
| for (ISWC swc : swcs) |
| { |
| System.out.println("using SWC: " + swc.getSWCFile().getAbsolutePath()); |
| } |
| } |
| |
| private ProblemQuery problems; |
| private String moduleOutput; |
| private String outputFolderPath; |
| private String mainName; |
| private List<String> otherPaths; |
| private List<String> sourceExternFiles; |
| private List<ISWC> swcs; |
| private boolean removeCirculars = false; |
| private ArrayList<GoogDep> dps; |
| private DependencyGraph graph; |
| private CompilerProject project; |
| private ArrayList<String> staticInitializers; |
| |
| private HashMap<String, GoogDep> depMap = new HashMap<String,GoogDep>(); |
| private HashMap<String, ICompilationUnit> requireMap = new HashMap<String, ICompilationUnit>(); |
| private HashMap<ICompilationUnit, String> requireMap2 = new HashMap<ICompilationUnit, String>(); |
| |
| public boolean needCSS = false; |
| |
| public ArrayList<String> getListOfFiles(CompilerProject project, List<String> sourceExternFiles, ProblemQuery problems) |
| { |
| this.project = project; |
| this.problems = problems; |
| this.sourceExternFiles = sourceExternFiles; |
| |
| if (dps == null) |
| { |
| if (!buildDB()) |
| return null; |
| dps = sort(); |
| } |
| visited.clear(); |
| ArrayList<String> files = new ArrayList<String>(); |
| for (GoogDep gd : dps) |
| { |
| if (gd.fileInfo.isExtern) |
| sourceExternFiles.add(gd.filePath); |
| else |
| files.add(gd.filePath); |
| visited.put(gd.className, gd); |
| } |
| if (removeCirculars) |
| { |
| GoogDep mainDep = depMap.get(mainName); |
| visited.put(mainName, mainDep); |
| for (GoogDep gd : depMap.values()) |
| { |
| if (!visited.containsKey(gd.className)) |
| { |
| if (gd.fileInfo.isExtern) |
| sourceExternFiles.add(gd.filePath); |
| else |
| files.add(gd.filePath); |
| } |
| } |
| files.add(mainDep.filePath); |
| } |
| return files; |
| } |
| |
| public String generateDeps(CompilerProject project, ProblemQuery problems) throws FileNotFoundException |
| { |
| this.project = project; |
| this.problems = problems; |
| if (dps == null) |
| { |
| if (!buildDB()) |
| return ""; |
| dps = sort(); |
| } |
| StringBuilder sb = new StringBuilder(); |
| int n = dps.size(); |
| for (int i = n - 1; i >= 0; i--) |
| { |
| GoogDep gd = dps.get(i); |
| if (!isGoogClass(gd.className)) |
| { |
| if (removeCirculars) |
| { |
| ArrayList <String> deps = new ArrayList<String>(); |
| if (gd.fileInfo.impls != null) |
| deps.addAll(gd.fileInfo.impls); |
| if (gd.fileInfo.staticDeps != null) |
| { |
| for (String dep : gd.fileInfo.staticDeps) |
| { |
| if (!deps.contains(dep)) |
| deps.add(dep); |
| } |
| } |
| sb.append("goog.addDependency('").append(relativePath(gd.filePath)).append("', ['") |
| .append(gd.className).append("'], [") |
| .append(getDependencies(deps)) |
| .append("]);\n"); |
| } |
| else |
| sb.append("goog.addDependency('").append(relativePath(gd.filePath)).append("', ['") |
| .append(gd.className).append("'], [") |
| .append(getDependencies(gd.deps)) |
| .append("]);\n"); |
| } |
| } |
| if (removeCirculars) |
| { |
| StringBuilder mainDeps = new StringBuilder(); |
| GoogDep mainDep = depMap.get(mainName); |
| mainDeps.append("goog.addDependency('").append(relativePath(mainDep.filePath)).append("', ['") |
| .append(mainDep.className).append("'], ["); |
| ArrayList<String> restOfDeps = new ArrayList<String>(); |
| restOfDeps.addAll(mainDep.deps); |
| DependencyTypeSet dependencyTypes = DependencyTypeSet.allOf(); |
| // get the list of all units not referenced by other units |
| for (GoogDep gd : depMap.values()) |
| { |
| if (gd.className.equals(mainName)) |
| { |
| if (gd.fileInfo.impls != null) |
| { |
| for (String d : gd.fileInfo.impls) |
| { |
| if (!restOfDeps.contains(d) && !gd.fileInfo.isExtern && !isExternal(d)) |
| restOfDeps.add(d); |
| } |
| } |
| continue; |
| } |
| ICompilationUnit unit = requireMap.get(gd.className); |
| if (unit == null) |
| { |
| if (!restOfDeps.contains(gd.className) && !gd.fileInfo.isExtern && !isExternal(gd.className)) |
| restOfDeps.add(gd.className); |
| continue; |
| } |
| Set<ICompilationUnit> deps = graph.getDirectReverseDependencies(unit, dependencyTypes); |
| if (deps.size() == 0) |
| { |
| if (!restOfDeps.contains(gd.className) && !gd.fileInfo.isExtern && !isExternal(gd.className)) |
| restOfDeps.add(gd.className); |
| } |
| } |
| mainDeps.append(getDependencies(restOfDeps)).append("]);\n"); |
| sb.insert(0, mainDeps); |
| sb.insert(0, "// generated by Royale\n"); |
| for (String dep : restOfDeps) |
| { |
| GoogDep gd = depMap.get(dep); |
| if (gd == null) |
| { |
| //added this to prevent a NullPointerException when the |
| //GoogDep is null. -JT |
| problems.add(new FileNotFoundProblem(dep)); |
| continue; |
| } |
| sb.append("goog.addDependency('").append(relativePath(gd.filePath)).append("', ['") |
| .append(gd.className).append("'], [") |
| .append((gd.fileInfo.impls != null) ? getDependencies(gd.fileInfo.impls) : "") |
| .append("]);\n"); |
| } |
| addRestOfDeps(mainDep, restOfDeps); |
| } |
| return sb.toString(); |
| } |
| |
| private boolean isGoogClass(String className) |
| { |
| return className.startsWith("goog."); |
| } |
| |
| private boolean buildDB() |
| { |
| staticInitializers = new ArrayList<String>(); |
| |
| graph = new DependencyGraph(); |
| if (isGoogClass(mainName)) |
| { |
| problems.add(new MainDefinitionQNameProblem("Google Closure Library", mainName)); |
| return false; |
| } |
| if (isExternal(mainName)) |
| { |
| problems.add(new MainDefinitionQNameProblem("External Libraries", mainName)); |
| return false; |
| } |
| addDeps(mainName); |
| return true; |
| } |
| |
| public ArrayList<String> additionalHTML = new ArrayList<String>(); |
| |
| private HashMap<String, GoogDep> visited = new HashMap<String, GoogDep>(); |
| |
| private ArrayList<GoogDep> sort() |
| { |
| // first, promote all dependencies of classes used in static initializers to |
| // the level of static dependencies since their constructors will be |
| // run early |
| for (String staticClass: staticInitializers) |
| { |
| GoogDep info = depMap.get(staticClass); |
| if (info != null && info.fileInfo != null && info.fileInfo.deps != null) |
| { |
| if (info.fileInfo.staticDeps == null) |
| info.fileInfo.staticDeps = new ArrayList<String>(); |
| for (String dep : info.fileInfo.deps) |
| { |
| if (!info.fileInfo.staticDeps.contains(dep)) |
| info.fileInfo.staticDeps.add(dep); |
| } |
| } |
| } |
| |
| ArrayList<GoogDep> arr = new ArrayList<GoogDep>(); |
| GoogDep current = depMap.get(mainName); |
| sortFunction(current, arr); |
| if (removeCirculars) |
| { |
| ICompilationUnit mainUnit = requireMap.get(mainName); |
| ArrayList<ICompilationUnit> roots = new ArrayList<ICompilationUnit>(); |
| roots.add(mainUnit); |
| requireMap.remove(mainName); |
| |
| List<ICompilationUnit> order = graph.topologicalSort(requireMap.values()); |
| ArrayList<GoogDep> depsInOrder = new ArrayList<GoogDep>(); |
| for (ICompilationUnit unit : order) |
| { |
| String name = requireMap2.get(unit); |
| if (isGoogClass(name)) continue; |
| GoogDep dep = depMap.get(name); |
| if (dep == null) |
| { |
| System.out.println("No GoogDep for " + name); |
| //added this to prevent a NullPointerException when the |
| //GoogDep is null. -JT |
| problems.add(new FileNotFoundProblem(name)); |
| continue; |
| } |
| depsInOrder.add(dep); |
| } |
| return depsInOrder; |
| } |
| return arr; |
| } |
| |
| private void sortFunction(GoogDep current, List<GoogDep> arr) |
| { |
| visited.put(current.className, current); |
| |
| if (removeCirculars) |
| removeRequires(current); |
| System.out.println("Dependencies calculated for '" + current.className + "'"); |
| |
| ICompilationUnit unit = null; |
| |
| if (removeCirculars) |
| if (current.fileInfo.deps == null) |
| return; |
| |
| if (removeCirculars) |
| { |
| unit = requireMap.get(current.className); |
| if (unit == null) |
| { |
| unit = new JSCompilationUnit(project, current.filePath, DefinitionPriority.BasePriority.SOURCE_LIST, current.className); |
| graph.addCompilationUnit(unit); |
| requireMap.put(current.className, unit); |
| requireMap2.put(unit, current.className); |
| } |
| if (current.fileInfo.staticDeps != null && removeCirculars) |
| { |
| for (String staticDep : current.fileInfo.staticDeps) |
| { |
| ICompilationUnit base = requireMap.get(staticDep); |
| if (base == null) |
| { |
| base = new JSCompilationUnit(project, staticDep, DefinitionPriority.BasePriority.SOURCE_LIST, staticDep); |
| graph.addCompilationUnit(base); |
| requireMap.put(staticDep, base); |
| requireMap2.put(base, staticDep); |
| } |
| System.out.println(current.className + " static initialization depends on " + staticDep); |
| graph.addDependency(unit, base, DependencyType.INHERITANCE); |
| |
| } |
| } |
| } |
| ArrayList<String> impls = current.fileInfo.impls != null ? current.fileInfo.impls : null; |
| if (impls != null) |
| { |
| for (String className : impls) |
| { |
| if (!isGoogClass(className)) |
| { |
| GoogDep gd = depMap.get(className); |
| if (gd != null) |
| { |
| if (removeCirculars) |
| { |
| ICompilationUnit base = requireMap.get(className); |
| if (base == null) |
| { |
| base = new JSCompilationUnit(project, className, DefinitionPriority.BasePriority.SOURCE_LIST, className); |
| graph.addCompilationUnit(base); |
| requireMap.put(className, base); |
| requireMap2.put(base, className); |
| } |
| System.out.println(current.className + " depends on " + className); |
| graph.addDependency(unit, base, DependencyType.INHERITANCE); |
| } |
| if (!visited.containsKey(className)) |
| { |
| sortFunction(gd, arr); |
| } |
| } |
| } |
| } |
| } |
| ArrayList<String> deps = current.deps; |
| for (String className : deps) |
| { |
| if (!isGoogClass(className)) |
| { |
| GoogDep gd = depMap.get(className); |
| if (gd == null) |
| { |
| System.out.println("No GoogDep for " + className); |
| throw new RuntimeException("Unable to find dependency information for class: " + className); |
| } |
| if (!visited.containsKey(className)) |
| { |
| sortFunction(gd, arr); |
| } |
| } |
| } |
| arr.add(current); |
| } |
| |
| private void addRestOfDeps(GoogDep main, List<String> restOfDeps) |
| { |
| List<String> fileLines; |
| try { |
| File mainFile = new File(main.filePath); |
| fileLines = Files.readLines(mainFile, Charset.defaultCharset()); |
| |
| SourceMapConsumerV3 sourceMapConsumer = null; |
| File sourceMapFile = new File(main.filePath + ".map"); |
| if (sourceMapFile.exists()) |
| { |
| String sourceMapContents = FileUtils.readFileToString(sourceMapFile); |
| sourceMapConsumer = new SourceMapConsumerV3(); |
| try |
| { |
| sourceMapConsumer.parse(sourceMapContents); |
| } |
| catch(SourceMapParseException e) |
| { |
| sourceMapConsumer = null; |
| } |
| } |
| |
| int n = restOfDeps.size(); |
| for (int i = n - 1; i >= 0; i--) |
| { |
| String dep = restOfDeps.get(i); |
| //if (!main.deps.contains(dep)) |
| fileLines.add(main.fileInfo.googProvideLine + 1, JSGoogEmitterTokens.GOOG_REQUIRE.getToken() + "('" + dep + "');"); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, mainFile.getName(), main.fileInfo.googProvideLine + 1); |
| } |
| |
| PrintWriter out = new PrintWriter(new FileWriter(mainFile)); |
| for (String s : fileLines) |
| { |
| out.println(s); |
| } |
| out.close(); |
| |
| if (sourceMapConsumer != null) |
| { |
| String newSourceMap = sourceMapConsumerToString(sourceMapConsumer, mainFile.getName()); |
| PrintWriter sourceMapOut = new PrintWriter(new FileWriter(sourceMapFile)); |
| sourceMapOut.print(newSourceMap); |
| sourceMapOut.close(); |
| } |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| |
| } |
| |
| private void addDeps(String className) |
| { |
| if (depMap.containsKey(className) || isGoogClass(className) || isExternal(className)) |
| return; |
| |
| // build goog dependency list |
| GoogDep gd = new GoogDep(); |
| gd.className = className; |
| gd.filePath = getFilePath(className); |
| if(gd.filePath.isEmpty()) { |
| // TODO Send a ICompilerProblem instead. |
| throw new RuntimeException("Unable to find JavaScript filePath for class: " + className); |
| } |
| depMap.put(gd.className, gd); |
| List<String> fileLines; |
| try { |
| fileLines = Files.readLines(new File(gd.filePath), Charset.defaultCharset()); |
| FileInfo fi = getFileInfo(fileLines, className); |
| gd.fileInfo = fi; |
| } catch (IOException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| if (gd.fileInfo.impls != null) |
| { |
| for (String dep : gd.fileInfo.impls) |
| { |
| if (gd.fileInfo.provides != null && |
| gd.fileInfo.provides.contains(dep)) continue; |
| addDeps(dep); |
| } |
| } |
| gd.deps = new ArrayList<String>(); |
| if (gd.fileInfo.deps != null) |
| { |
| for (String dep : gd.fileInfo.deps) |
| { |
| gd.deps.add(dep); |
| if (gd.fileInfo.provides != null && |
| gd.fileInfo.provides.contains(dep)) continue; |
| addDeps(dep); |
| } |
| } |
| } |
| |
| void removeRequires(GoogDep gd) |
| { |
| String className = gd.className; |
| |
| // remove requires that are not base classes and interfaces |
| try |
| { |
| gd = depMap.get(className); |
| File depFile = new File(gd.filePath); |
| List<String> fileLines = Files.readLines(depFile, Charset.defaultCharset()); |
| ArrayList<String> finalLines = new ArrayList<String>(); |
| |
| SourceMapConsumerV3 sourceMapConsumer = null; |
| File sourceMapFile = new File(gd.filePath + ".map"); |
| if (sourceMapFile.exists()) |
| { |
| String sourceMapContents = FileUtils.readFileToString(sourceMapFile); |
| sourceMapConsumer = new SourceMapConsumerV3(); |
| try |
| { |
| sourceMapConsumer.parse(sourceMapContents); |
| } |
| catch(SourceMapParseException e) |
| { |
| sourceMapConsumer = null; |
| } |
| } |
| |
| boolean firstDependency = true; |
| StringBuilder sb = new StringBuilder(); |
| sb.append(JSGoogEmitterTokens.ROYALE_DEPENDENCY_LIST.getToken()); |
| |
| FileInfo fi = gd.fileInfo; |
| int suppressCount = 0; |
| int i = 0; |
| int stopLine = fi.constructorLine; |
| if (fi.constructorLine == -1) // standalone functions |
| stopLine = fi.googProvideLine + 4; // search a few more lines after goog.provide |
| for (String line : fileLines) |
| { |
| if (i < stopLine) |
| { |
| int c = line.indexOf(JSGoogEmitterTokens.ROYALE_DEPENDENCY_LIST.getToken()); |
| if (c > -1) |
| return; // already been processed |
| c = line.indexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); |
| if (c > -1) |
| { |
| int c2 = line.indexOf(")"); |
| String s = line.substring(c + 14, c2 - 1); |
| if ((gd.fileInfo.impls == null || !gd.fileInfo.impls.contains(s)) && |
| (gd.fileInfo.staticDeps == null || !gd.fileInfo.staticDeps.contains(s))) |
| { |
| // don't remove the require if some class needs it at static initialization |
| // time |
| suppressCount++; |
| System.out.println(gd.filePath + " removing require: " + s); |
| if (!firstDependency) |
| sb.append(","); |
| sb.append(s); |
| firstDependency = false; |
| sourceMapConsumer = removeLineFromSourceMap(sourceMapConsumer, depFile.getName(), finalLines.size()); |
| continue; |
| } |
| } |
| } |
| finalLines.add(line); |
| i++; |
| } |
| //if (suppressCount > 0) |
| //{ |
| if (fi.suppressLine > 0) |
| { |
| if (fi.suppressLine < fi.constructorLine || fi.constructorLine == -1) |
| { |
| String line = finalLines.get(fi.suppressLine); |
| int c = line.indexOf("@suppress {"); |
| if (c > -1) |
| { |
| if (!line.contains("missingRequire")) |
| { |
| line = line.substring(0, c) + "@suppress {missingRequire|" + line.substring(c + 11); |
| finalLines.remove(fi.suppressLine); |
| finalLines.add(fi.suppressLine, line); |
| //no need to call addLineToSourceMap or removeLineFromSourceMap here |
| } |
| } |
| else |
| System.out.println("Confused by @suppress in " + className); |
| } |
| else |
| { |
| // the @suppress was for the constructor or some other thing so add a top-level |
| // @suppress |
| if (fi.fileoverviewLine > -1) |
| { |
| // there is already a fileOverview but no @suppress |
| finalLines.add(fi.fileoverviewLine + 1, " * @suppress {missingRequire}"); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.fileoverviewLine + 1); |
| } |
| else if (fi.googProvideLine > -1) |
| { |
| finalLines.add(fi.googProvideLine, " */"); |
| finalLines.add(fi.googProvideLine, " * @suppress {missingRequire}"); |
| finalLines.add(fi.googProvideLine, " * @fileoverview"); |
| finalLines.add(fi.googProvideLine, "/**"); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.googProvideLine); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.googProvideLine); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.googProvideLine); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.googProvideLine); |
| } |
| else |
| { |
| System.out.println("Confused by @suppress in " + className); |
| } |
| } |
| } |
| else |
| { |
| if (fi.fileoverviewLine > -1) |
| { |
| // there is already a fileoverview but no @suppress |
| finalLines.add(fi.fileoverviewLine + 1, " * @suppress {missingRequire}"); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.fileoverviewLine + 1); |
| } |
| else if (fi.googProvideLine > -1) |
| { |
| finalLines.add(fi.googProvideLine, " */"); |
| finalLines.add(fi.googProvideLine, " * @suppress {missingRequire}"); |
| finalLines.add(fi.googProvideLine, " * @fileoverview"); |
| finalLines.add(fi.googProvideLine, "/**"); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.googProvideLine); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.googProvideLine); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.googProvideLine); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), fi.googProvideLine); |
| } |
| else |
| { |
| System.out.println("Confused by @suppress in " + className); |
| } |
| } |
| //} |
| |
| sb.append("*/"); |
| finalLines.add(gd.fileInfo.googProvideLine + 1, sb.toString()); |
| sourceMapConsumer = addLineToSourceMap(sourceMapConsumer, depFile.getName(), gd.fileInfo.googProvideLine + 1); |
| |
| PrintWriter out = new PrintWriter(new FileWriter(depFile)); |
| for (String s : finalLines) |
| { |
| out.println(s); |
| } |
| out.close(); |
| |
| if (sourceMapConsumer != null) |
| { |
| String newSourceMap = sourceMapConsumerToString(sourceMapConsumer, depFile.getName()); |
| PrintWriter sourceMapOut = new PrintWriter(new FileWriter(sourceMapFile)); |
| sourceMapOut.print(newSourceMap); |
| sourceMapOut.close(); |
| } |
| } |
| catch (IOException e) |
| { |
| e.printStackTrace(); |
| } |
| } |
| |
| String sourceMapConsumerToString(SourceMapConsumerV3 consumer, String file) |
| { |
| SourceMapGeneratorV3 generator = sourceMapConsumerToGenerator(consumer); |
| StringBuilder builder = new StringBuilder(); |
| try |
| { |
| generator.appendTo(builder, file); |
| } |
| catch(IOException e) |
| { |
| return ""; |
| } |
| return builder.toString(); |
| } |
| |
| private void appendExtraMappingToGenerator(SourceMapGeneratorV3 generator, |
| String sourceName, |
| String symbolName, |
| FilePosition sourceStartPosition, |
| FilePosition startPosition, |
| FilePosition endPosition) |
| { |
| //add an extra mapping because there seems to be a bug in |
| //SourceMapGeneratorV3's appendTo() that omits the last |
| //entry, for some reason |
| FilePosition newEndPosition = new FilePosition(endPosition.getLine(), endPosition.getColumn() + 1); |
| generator.addMapping(sourceName, null, sourceStartPosition, endPosition, newEndPosition); |
| } |
| |
| private SourceMapGeneratorV3 sourceMapConsumerToGenerator(SourceMapConsumerV3 consumer) |
| { |
| final SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); |
| final SourceMapEntryCounter counter = new SourceMapEntryCounter(); |
| consumer.visitMappings(counter); |
| consumer.visitMappings(new SourceMapConsumerV3.EntryVisitor() |
| { |
| private int index = 0; |
| |
| @Override |
| public void visit(String sourceName, |
| String symbolName, |
| FilePosition sourceStartPosition, |
| FilePosition startPosition, |
| FilePosition endPosition) { |
| generator.addMapping(sourceName, symbolName, sourceStartPosition, startPosition, endPosition); |
| index++; |
| if(index == counter.count) |
| { |
| //add an extra mapping because there seems to be a bug in |
| //SourceMapGeneratorV3's appendTo() that omits the last |
| //entry, for some reason |
| appendExtraMappingToGenerator(generator, sourceName, symbolName, sourceStartPosition, startPosition, endPosition); |
| } |
| } |
| }); |
| return generator; |
| } |
| |
| class SourceMapEntryCounter implements SourceMapConsumerV3.EntryVisitor |
| { |
| private int count = 0; |
| |
| @Override |
| public void visit(String sourceName, |
| String symbolName, |
| FilePosition sourceStartPosition, |
| FilePosition startPosition, |
| FilePosition endPosition) { |
| count++; |
| } |
| } |
| |
| SourceMapConsumerV3 sourceMapGeneratorToConsumer(SourceMapGeneratorV3 generator, String fileName) |
| { |
| StringBuilder builder = new StringBuilder(); |
| try |
| { |
| generator.appendTo(builder, fileName); |
| } |
| catch(IOException e) |
| { |
| return null; |
| } |
| SourceMapConsumerV3 consumer = new SourceMapConsumerV3(); |
| try |
| { |
| consumer.parse(builder.toString()); |
| } |
| catch(SourceMapParseException e) |
| { |
| return null; |
| } |
| return consumer; |
| } |
| |
| SourceMapConsumerV3 addLineToSourceMap(SourceMapConsumerV3 consumer, String sourceFileName, final int lineToAdd) |
| { |
| if (consumer == null) |
| { |
| return null; |
| } |
| final SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); |
| final SourceMapEntryCounter counter = new SourceMapEntryCounter(); |
| consumer.visitMappings(counter); |
| consumer.visitMappings(new SourceMapConsumerV3.EntryVisitor() |
| { |
| private int index = 0; |
| |
| @Override |
| public void visit(String sourceName, |
| String symbolName, |
| FilePosition sourceStartPosition, |
| FilePosition startPosition, |
| FilePosition endPosition) { |
| if(startPosition.getLine() >= lineToAdd) |
| { |
| startPosition = new FilePosition(startPosition.getLine() + 1, startPosition.getColumn()); |
| endPosition = new FilePosition(endPosition.getLine() + 1, endPosition.getColumn()); |
| } |
| generator.addMapping(sourceName, symbolName, sourceStartPosition, startPosition, endPosition); |
| index++; |
| if(index == counter.count) |
| { |
| //add an extra mapping because there seems to be a bug in |
| //SourceMapGeneratorV3's appendTo() that omits the last |
| //entry, for some reason |
| appendExtraMappingToGenerator(generator, sourceName, symbolName, sourceStartPosition, startPosition, endPosition); |
| } |
| } |
| }); |
| return sourceMapGeneratorToConsumer(generator, sourceFileName); |
| } |
| |
| SourceMapConsumerV3 removeLineFromSourceMap(SourceMapConsumerV3 consumer, String sourceFileName, final int lineToRemove) |
| { |
| if (consumer == null) |
| { |
| return null; |
| } |
| final SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); |
| final SourceMapEntryCounter counter = new SourceMapEntryCounter(); |
| consumer.visitMappings(counter); |
| consumer.visitMappings(new SourceMapConsumerV3.EntryVisitor() |
| { |
| private int index = 0; |
| |
| @Override |
| public void visit(String sourceName, |
| String symbolName, |
| FilePosition sourceStartPosition, |
| FilePosition startPosition, |
| FilePosition endPosition) { |
| if(startPosition.getLine() == lineToRemove) |
| { |
| return; |
| } |
| if(startPosition.getLine() > lineToRemove) |
| { |
| startPosition = new FilePosition(startPosition.getLine() - 1, startPosition.getColumn()); |
| } |
| if(endPosition.getLine() > lineToRemove) |
| { |
| endPosition = new FilePosition(endPosition.getLine() - 1, endPosition.getColumn()); |
| } |
| generator.addMapping(sourceName, symbolName, sourceStartPosition, startPosition, endPosition); |
| index++; |
| if(index == counter.count) |
| { |
| //add an extra mapping because there seems to be a bug in |
| //SourceMapGeneratorV3's appendTo() that omits the last |
| //entry, for some reason |
| appendExtraMappingToGenerator(generator, sourceName, symbolName, sourceStartPosition, startPosition, endPosition); |
| } |
| } |
| }); |
| return sourceMapGeneratorToConsumer(generator, sourceFileName); |
| } |
| |
| FileInfo getFileInfo(List<String> lines, String className) |
| { |
| FileInfo fi = new FileInfo(); |
| |
| int numProvides = 0; |
| int constructorCount = 0; |
| int n = lines.size(); |
| fi.constructorLine = -1; |
| fi.suppressLine = -1; |
| fi.fileoverviewLine = -1; |
| fi.googProvideLine = -1; |
| boolean inInjectHTML = false; |
| for (int i = 0; i < n; i++) |
| { |
| String line = lines.get(i); |
| int c2; |
| int c = line.indexOf("*/"); |
| if (c > -1 && constructorCount > 0 && constructorCount == numProvides) |
| { |
| return fi; |
| } |
| else |
| { |
| if (inInjectHTML) |
| { |
| if (line.indexOf("</inject_html>") > -1) |
| { |
| inInjectHTML = false; |
| continue; |
| } |
| line = line.trim(); |
| if (line.startsWith("*")) |
| line = line.substring(1); |
| additionalHTML.add(line); |
| continue; |
| } |
| c = line.indexOf("<inject_html>"); |
| if (c > -1) |
| { |
| inInjectHTML = true; |
| } |
| c = line.indexOf("@constructor"); |
| if (c > -1) |
| { |
| if (fi.constructorLine == -1) |
| fi.constructorLine = i; |
| constructorCount++; |
| |
| } |
| else |
| { |
| c = line.indexOf("@interface"); |
| if (c > -1) |
| fi.constructorLine = i; |
| else |
| { |
| c = line.indexOf("@suppress"); |
| if (c > -1) |
| fi.suppressLine = i; |
| else |
| { |
| c = line.indexOf("@fileoverview"); |
| if (c > -1) |
| fi.fileoverviewLine = i; |
| else |
| { |
| c = line.indexOf("goog.provide"); |
| if (c > -1) |
| { |
| if (fi.googProvideLine == -1) |
| fi.googProvideLine = i; |
| if (numProvides > 0) |
| { |
| if (fi.provides == null) |
| fi.provides = new ArrayList<String>(); |
| c2 = line.indexOf(")", c); |
| String provide = line.substring(c + 14, c2 - 1); |
| fi.provides.add(provide); |
| } |
| numProvides++; |
| } |
| else |
| { |
| c = line.indexOf("@implements"); |
| if (c > -1) |
| { |
| if (fi.impls == null) |
| fi.impls = new ArrayList<String>(); |
| c2 = line.indexOf("}", c); |
| String impl = line.substring(c + 13, c2); |
| fi.impls.add(impl); |
| if (impl.equals("org.apache.royale.core.ICSSImpl")) |
| needCSS = true; |
| } |
| else |
| { |
| c = line.indexOf("@extends"); |
| if (c > -1) |
| { |
| if (fi.impls == null) |
| fi.impls = new ArrayList<String>(); |
| c2 = line.indexOf("}", c); |
| String impl = line.substring(c + 10, c2); |
| fi.impls.add(impl); |
| } |
| else |
| { |
| String token = JSGoogEmitterTokens.ROYALE_STATIC_DEPENDENCY_LIST.getToken(); |
| c = line.indexOf(token); |
| if (c > -1) |
| { |
| c2 = line.indexOf("*/"); |
| line = line.substring(c + token.length(), c2); |
| List<String> staticDeps = Arrays.asList(line.split(",")); |
| fi.staticDeps = new ArrayList<String>(); |
| fi.staticDeps.addAll(staticDeps); |
| for (String staticDep : staticDeps) |
| { |
| if (staticDep.equals(className)) |
| continue; |
| staticInitializers.add(staticDep); |
| } |
| } |
| else |
| { |
| c = line.indexOf("@externs"); |
| if (c > -1) |
| { |
| fi.isExtern = true; |
| } |
| else |
| { |
| token = JSGoogEmitterTokens.ROYALE_DEPENDENCY_LIST.getToken(); |
| c = line.indexOf(token); |
| if (c > -1) |
| { |
| c2 = line.indexOf("*/"); |
| line = line.substring(c + token.length(), c2); |
| fi.deps = new ArrayList<String>(); |
| if (line.length() > 2) // don't add blank or space if no deps |
| fi.deps.addAll(Arrays.asList(line.split(","))); |
| fi.depsLine = i; |
| } |
| else if (fi.depsLine == 0) |
| { |
| token = JSGoogEmitterTokens.GOOG_REQUIRE.getToken(); |
| c = line.indexOf(token); |
| if (c > -1) |
| { |
| c2 = line.indexOf(")"); |
| String s = line.substring(c + 14, c2 - 1); |
| if (fi.deps == null) |
| fi.deps = new ArrayList<String>(); |
| fi.deps.add(s); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return fi; |
| } |
| |
| String getFilePath(String className) |
| { |
| String fn; |
| File destFile; |
| File f; |
| |
| String classPath = className.replace(".", File.separator); |
| |
| fn = outputFolderPath + File.separator + classPath + ".js"; |
| f = new File(fn); |
| if (f.exists()) |
| { |
| return fn; |
| } |
| |
| for (String otherPath : otherPaths) |
| { |
| fn = otherPath + File.separator + classPath + ".js"; |
| f = new File(fn); |
| if (f.exists()) |
| { |
| fn = outputFolderPath + File.separator + classPath + ".js"; |
| destFile = new File(fn); |
| // copy source to output |
| try { |
| FileUtils.copyFile(f, destFile); |
| |
| // (erikdebruin) copy class assets files |
| if (className.contains("org.apache.royale")) |
| { |
| File assetsDir = new File(f.getParentFile(), "assets"); |
| if (assetsDir.exists()) |
| { |
| String nameOfClass = className.substring(className.lastIndexOf('_') + 1); |
| |
| File[] assetsList = assetsDir.listFiles(); |
| assert assetsList != null; |
| for (File assetFile : assetsList) { |
| String assetFileName = assetFile.getName(); |
| |
| if (assetFile.isFile() && assetFileName.indexOf(nameOfClass) == 0) { |
| String pathOfClass; |
| pathOfClass = className.substring(0, className.lastIndexOf('_')); |
| pathOfClass = pathOfClass.replace(".", File.separator); |
| |
| destFile = new File(outputFolderPath + |
| File.separator + pathOfClass + |
| File.separator + "assets" + |
| File.separator + assetFileName); |
| FileUtils.copyFile(assetFile, destFile); |
| |
| destFile = new File(outputFolderPath.replace("js-debug", "js-release") + |
| File.separator + pathOfClass + |
| File.separator + "assets" + |
| File.separator + assetFileName); |
| FileUtils.copyFile(assetFile, destFile); |
| |
| System.out.println("Copied assets of the '" + nameOfClass + "' class"); |
| } |
| } |
| } |
| } |
| } catch (IOException e) { |
| System.out.println("Error copying file for class: " + className); |
| } |
| return fn; |
| } |
| } |
| |
| String fwdClassPath = className.replace(".", "/"); |
| String bckClassPath = className.replace(".", "\\"); |
| for (ISWC swc : swcs) |
| { |
| ISWCFileEntry fileEntry = swc.getFile("js/src/" + fwdClassPath + ".js"); |
| if (fileEntry == null) |
| fileEntry = swc.getFile("js/out/" + fwdClassPath + ".js"); |
| if (fileEntry == null) |
| fileEntry = swc.getFile("js/src/" + bckClassPath + ".js"); |
| if (fileEntry == null) |
| fileEntry = swc.getFile("js/out/" + bckClassPath + ".js"); |
| if (fileEntry == null) |
| fileEntry = swc.getFile("js\\src\\" + bckClassPath + ".js"); |
| if (fileEntry == null) |
| fileEntry = swc.getFile("js\\out\\" + bckClassPath + ".js"); |
| if (fileEntry != null) |
| { |
| fn = outputFolderPath + File.separator + classPath + ".js"; |
| destFile = new File(fn); |
| // copy source to output |
| try { |
| InputStream inStream = fileEntry.createInputStream(); |
| OutputStream outStream = FileUtils.openOutputStream(destFile); |
| byte[] b = new byte[1024 * 1024]; |
| int bytes_read; |
| while ((bytes_read = inStream.read(b)) != -1) |
| { |
| outStream.write(b, 0, bytes_read); |
| } |
| outStream.flush(); |
| outStream.close(); |
| inStream.close(); |
| |
| // (erikdebruin) copy class assets files |
| if (className.contains("org.apache.royale")) |
| { |
| Map<String, ISWCFileEntry> includedfiles = swc.getFiles(); |
| Set<String> includedList = includedfiles.keySet(); |
| for (String included : includedList) |
| { |
| if (included.contains(".png") || |
| included.contains(".gif") || |
| included.contains(".jpg") || |
| included.contains(".jpeg") || |
| included.contains(".svg") || |
| included.contains(".json")) |
| { |
| fileEntry = includedfiles.get(included); |
| String assetName = outputFolderPath + File.separator + included; |
| File assetFile = new File(assetName); |
| inStream = fileEntry.createInputStream(); |
| outStream = FileUtils.openOutputStream(assetFile); |
| b = new byte[inStream.available()]; |
| inStream.read(b); |
| outStream.write(b); |
| inStream.close(); |
| outStream.flush(); |
| outStream.close(); |
| System.out.println("Copied asset " + assetName); |
| } |
| } |
| } |
| } catch (IOException e) { |
| System.out.println("Error copying file for class: " + className); |
| } |
| return fn; |
| } |
| } |
| |
| System.out.println("Could not find file for class: " + className); |
| problems.add(new FileNotFoundProblem(className)); |
| return ""; |
| } |
| |
| /* |
| private ArrayList<String> getDirectDependencies(String fn) |
| { |
| ArrayList<String> deps = new ArrayList<String>(); |
| |
| FileInputStream fis; |
| try { |
| fis = new FileInputStream(fn); |
| Scanner scanner = new Scanner(fis, "UTF-8"); |
| boolean inInjectHTML = false; |
| while (scanner.hasNextLine()) |
| { |
| String s = scanner.nextLine(); |
| if (s.contains("goog.inherits")) |
| break; |
| if (inInjectHTML) |
| { |
| int c = s.indexOf("</inject_html>"); |
| if (c > -1) |
| { |
| inInjectHTML = false; |
| continue; |
| } |
| } |
| if (inInjectHTML) |
| { |
| s = s.trim(); |
| if (s.startsWith("*")) |
| s = s.substring(1); |
| additionalHTML.add(s); |
| continue; |
| } |
| else |
| otherScanning(s); |
| int c = s.indexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); |
| if (c > -1) |
| { |
| int c2 = s.indexOf(")"); |
| s = s.substring(c + 14, c2 - 1); |
| deps.add(s); |
| } |
| c = s.indexOf("<inject_html>"); |
| if (c > -1) |
| { |
| inInjectHTML = true; |
| } |
| } |
| scanner.close(); |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| } |
| return deps; |
| } |
| */ |
| |
| protected void otherScanning(String s) |
| { |
| } |
| |
| private String getDependencies(ArrayList<String> deps) |
| { |
| String s = ""; |
| for (String dep : deps) |
| { |
| if (s.length() > 0) |
| { |
| s += ", "; |
| } |
| s += "'" + dep + "'"; |
| } |
| return s; |
| } |
| |
| String relativePath(String path) |
| { |
| String replacement = "../../.."; |
| if (path.indexOf(outputFolderPath) == 0) |
| { |
| if (moduleOutput != null) |
| { |
| replacement += moduleOutput; |
| if (replacement.endsWith("/")) |
| replacement = replacement.substring(0, replacement.length() - 1); |
| } |
| path = path.replace(outputFolderPath, replacement); |
| } |
| else |
| { |
| for (String otherPath : otherPaths) |
| { |
| if (path.indexOf(otherPath) == 0) |
| { |
| path = path.replace(otherPath, "../../.."); |
| |
| } |
| } |
| } |
| // paths are actually URIs and always have forward slashes |
| path = path.replace('\\', '/'); |
| return path; |
| } |
| |
| boolean isExternal(String className) |
| { |
| ICompilationUnit cu = project.resolveQNameToCompilationUnit(className); |
| if (cu == null) return false; // unit testing |
| |
| return ((RoyaleJSProject)project).isExternalLinkage(cu); |
| } |
| |
| private class GoogDep |
| { |
| public String filePath; |
| public String className; |
| public ArrayList<String> deps; |
| public FileInfo fileInfo; |
| |
| } |
| |
| private class FileInfo |
| { |
| public ArrayList<String> impls; |
| public ArrayList<String> deps; |
| public ArrayList<String> staticDeps; |
| public ArrayList<String> provides; |
| public int constructorLine; |
| public int depsLine; |
| public int suppressLine; |
| public int fileoverviewLine; |
| public int googProvideLine; |
| public boolean isExtern; |
| } |
| } |