Java tutorial
/* * Copyright 2009 The Closure Compiler Authors. * * Licensed 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 com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.javascript.jscomp.NameReferenceGraph.Name; import com.google.javascript.jscomp.NameReferenceGraph.Reference; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Generate a nice HTML file describing the name reference graph. * For each declaration, list the sites where the declaration's name * is referenced, and list all the names that the declaration references. * For each, name exactly where use occurs in the source code. * * <p>This report should be useful both for internal compiler * developers and for engineers trying to understand running behavior * of their code or who want to understand why the compiler won't * move their code into a new module. * * @author bowdidge@google.com (Robert Bowdidge) */ final class NameReferenceGraphReport { private NameReferenceGraph graph = null; /** * Create a NameReferenceGraphReport object. * * @param g name reference graph to describe in report. */ NameReferenceGraphReport(NameReferenceGraph g) { this.graph = g; } /** * Generate a nice HTML file describing the name reference graph. * For each declaration, list the sites where the declaration's name * is referenced, and list all the names that the declaration references. * For each, name exactly where use occurs in the source code. * * <p>This report should be useful both for internal compiler * developers and for engineers trying to understand running * behavior of their code or who want to understand why * AbstractCompiler won't move their code into a new module. * * @return String containing the entire HTML for the report. */ public String getHtmlReport() { StringBuilder builder = new StringBuilder(); List<DiGraphNode<Name, Reference>> nodes = new ArrayList<>(); Iterables.addAll(nodes, graph.getDirectedGraphNodes()); generateHtmlReportHeader(builder); builder.append("<h1>Name Reference Graph Dump</h1>\n"); builder.append("OVERALL STATS\n"); builder.append("<ul>\n"); builder.append("<li>Total names: " + nodes.size()); builder.append("</ul>\n"); builder.append("ALL NAMES\n"); builder.append("<UL>\n"); // Sort declarations in alphabetical order. Collections.sort(nodes, new DiGraphNodeComparator()); for (DiGraphNode<Name, Reference> n : nodes) { // Generate the HTML describing the declaration itself. generateDeclarationReport(builder, n); // Next, list the places where this name is used (REFERS TO), and the // names that this declaration refers to (REFERENCED BY). List<DiGraphEdge<Name, Reference>> outEdges = graph.getOutEdges(n.getValue()); List<DiGraphEdge<Name, Reference>> inEdges = graph.getInEdges(n.getValue()); // Don't bother to create the dotted list if we don't have anything to // put in it. if (!outEdges.isEmpty() || !inEdges.isEmpty()) { builder.append("<ul>"); if (!outEdges.isEmpty()) { builder.append("<li>REFERS TO:<br>\n"); builder.append("<ul>"); for (DiGraphEdge<Name, Reference> edge : outEdges) { generateEdgeReport(builder, edge.getDestination().getValue(), edge); } builder.append("</ul>\n"); } if (!inEdges.isEmpty()) { builder.append("<li>REFERENCED BY:<br>\n"); builder.append("<ul>"); for (DiGraphEdge<Name, Reference> edge : inEdges) { generateEdgeReport(builder, edge.getSource().getValue(), edge); } builder.append("</ul>"); } builder.append("</ul>\n"); } } builder.append("</ul>\n"); generateHtmlReportFooter(builder); return builder.toString(); } /** * Given a node, find the name of the containing source file. * * @param node Parse tree node whose filename is requested * @return String containing name of source file, or empty string if name * cannot be identified. */ private static String getSourceFile(Node node) { String filename = node.getSourceFileName(); if (filename == null) { return ""; } return filename; } /** * Generate the HTML for describing a specific declaration. * @param builder contents of report to be generated * @param declarationNode declaration to describe */ private void generateDeclarationReport(StringBuilder builder, DiGraphNode<Name, Reference> declarationNode) { // Provide the name and location of declaration, // with an anchor to allow navigation to this declaration. String declName = declarationNode.getValue().getQualifiedName(); JSType declType = declarationNode.getValue().getType(); builder.append("<LI> "); builder.append("<A NAME=\"" + declName + "\">"); builder.append(declName); builder.append("\n"); // Provide the type of the declaration. // This is helpful for debugging. generateType(builder, declType); // List all the definitions of this name that were found in the code. // For each, list List<DefinitionsRemover.Definition> defs = declarationNode.getValue().getDeclarations(); if (defs.isEmpty()) { builder.append("<br>No definitions found<br>"); } else { // Otherwise, provide a list of definitions in a dotted list. // For each definition, print the location where that definition is // found. builder.append("<ul>"); for (DefinitionsRemover.Definition def : defs) { Node fnDef = def.getRValue(); String sourceFileName = getSourceFile(fnDef); builder.append("<li> Defined: "); generateSourceReferenceLink(builder, sourceFileName, fnDef.getLineno(), fnDef.getCharno()); } builder.append("</ul>"); } } /** * Generate the HTML header for the report style. * Borrowed straight from NameAnalyzer's report style. * * @param builder contents of the report to be generated */ private static void generateHtmlReportHeader(StringBuilder builder) { builder.append("<!DOCTYPE html>\n" + "<html>" + "<head>" + "<meta http-equiv=\"Content-Type\" " + "content=\"text/html;charset=utf-8\" >" + "<title>Name Reference Graph Dump</title>" + "<style type=\"text/css\">body, td, "); builder.append("p {font-family: Arial; font-size: 83%} "); builder.append("ul {margin-top:2px; margin-left:0px; padding-left:1em;}"); builder.append("li {margin-top:3px; margin-left:24px;" + "padding-left:0px;padding-bottom: 4px}"); builder.append("</style></head><body>\n"); } /** * Generate the HTML footer for the report style. */ private static void generateHtmlReportFooter(StringBuilder builder) { builder.append("</body></html>"); } /** * Generate a description of a specific edge between two nodes. * For each edge, name the element being linked, the location of the * reference in the source file, and the type of the reference. * * @param builder contents of the report to be generated * @param referencedDecl name of the declaration being referenced * @param edge the graph edge being described */ private void generateEdgeReport(StringBuilder builder, Name referencedDecl, DiGraphEdge<Name, Reference> edge) { String srcDeclName = referencedDecl.getQualifiedName(); builder.append("<li><A HREF=\"#" + srcDeclName + "\">"); builder.append(srcDeclName); builder.append("</a> "); Node def = edge.getValue().getSite(); int lineNumber = def.getLineno(); int columnNumber = def.getCharno(); String sourceFile = getSourceFile(def); generateSourceReferenceLink(builder, sourceFile, lineNumber, columnNumber); JSType defType = edge.getValue().getSite().getJSType(); generateType(builder, defType); } /** * Generate a link and text for a reference to a particular location * in a source file. Selecting the link should take the programmer * to a browsable version of the file. * * @param builder contents of the report to be generated * @param sourceFile Path to the file * @param lineNumber line where the object to view is located * @param columnNumber column where the object to highlight is located. */ private static void generateSourceReferenceLink(StringBuilder builder, String sourceFile, int lineNumber, int columnNumber) { assert (sourceFile != null); builder.append("("); // Print out the text path so the user knows where things come from. builder.append(sourceFile + ":" + lineNumber + "," + columnNumber); builder.append(")"); } /** * Dump a type in a nice, readable way. * * @param builder contents of the report to be generated. * @param defType type to describe */ private static void generateType(StringBuilder builder, JSType defType) { if (defType == null) { builder.append(" (type: null) "); } else if (defType.isUnknownType()) { builder.append(" (type: unknown) "); } else { builder.append(" (type: " + defType + ") "); } } /** * DiGraphNodeComparator gives us a way to generate sorted lists * of DiGraphNodes. It provides a compare function used by the * String class's sort method. */ class DiGraphNodeComparator implements Comparator<DiGraphNode<Name, Reference>> { @Override public int compare(DiGraphNode<Name, Reference> node1, DiGraphNode<Name, Reference> node2) { Preconditions.checkNotNull(node1.getValue()); Preconditions.checkNotNull(node2.getValue()); if ((node1.getValue().getQualifiedName() == null) && (node2.getValue().getQualifiedName() == null)) { return 0; } // Node 1, if null, comes before node 2. if (node1.getValue().getQualifiedName() == null) { return -1; } // Node 2, if null, comes before node 1. if (node2.getValue().getQualifiedName() == null) { return 1; } return node1.getValue().getQualifiedName().compareTo(node2.getValue().getQualifiedName()); } } }