JFlexXref.java revision 1185
0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License (the "License").
0N/A * You may not use this file except in compliance with the License.
0N/A *
0N/A * See LICENSE.txt included in this distribution for the specific
0N/A * language governing permissions and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at LICENSE.txt.
0N/A * If applicable, add the following below this CDDL HEADER, with the
0N/A * fields enclosed by brackets "[]" replaced with your own identifying
0N/A * information: Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A */
0N/A
0N/A/*
119N/A * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * Portions Copyright 2011 Jens Elkner.
0N/A */
0N/A
65N/Apackage org.opensolaris.opengrok.analysis;
0N/A
125N/Aimport java.io.CharArrayReader;
125N/Aimport java.io.IOException;
125N/Aimport java.io.Reader;
125N/Aimport java.io.Writer;
58N/Aimport java.lang.reflect.Field;
77N/Aimport java.util.Comparator;
125N/Aimport java.util.HashMap;
125N/Aimport java.util.Map;
125N/Aimport java.util.Set;
125N/Aimport java.util.SortedSet;
261N/Aimport java.util.TreeSet;
261N/Aimport org.opensolaris.opengrok.analysis.Definitions.Tag;
126N/Aimport org.opensolaris.opengrok.configuration.Project;
58N/Aimport org.opensolaris.opengrok.configuration.RuntimeEnvironment;
8N/Aimport org.opensolaris.opengrok.history.Annotation;
30N/Aimport org.opensolaris.opengrok.web.Util;
0N/A
77N/A/**
0N/A * Base class for Xref lexers.
0N/A *
0N/A * @author Lubos Kosco
0N/A */
0N/Apublic abstract class JFlexXref {
0N/A public Writer out;
0N/A public String urlPrefix = RuntimeEnvironment.getInstance().getUrlPrefix();
65N/A public Annotation annotation;
65N/A public Project project;
65N/A protected Definitions defs;
65N/A /** EOF value returned by yylex(). */
0N/A private final int yyeof;
30N/A /** See {@link RuntimeEnvironment#getUserPage()}. Per default initialized
58N/A * in the constructor and here to be consistent and avoid lot of
260N/A * unnecessary lookups.
112N/A * @see #startNewLine() */
0N/A protected String userPageLink;
0N/A /** See {@link RuntimeEnvironment#getUserPageSuffix()}. Per default
0N/A * initialized in the constructor and here to be consistent and avoid lot of
260N/A * unnecessary lookups.
0N/A * @see #startNewLine() */
0N/A protected String userPageSuffix;
0N/A
0N/A /**
0N/A * Description of styles to use for different types of definitions. Each
11N/A * element in the array contains a three-element string array with the
0N/A * following values: (0) the name of the style definition type given by
240N/A * CTags, (1) the class name used by the style sheets when rendering the
58N/A * xref, and (2) the title of the section listing symbols of this type
58N/A * in the xref's navigation panel, or {@code null} if this type should not
58N/A * be included in the navigation panel.
58N/A */
77N/A private static final String[][] DEFINITION_STYLES = {
207N/A {"macro", "xm", "Macro"},
207N/A {"argument", "xa", null},
261N/A {"local", "xl", null},
260N/A {"variable", "xv", "Variable"},
77N/A {"class", "xc", "Class"},
260N/A {"package", "xp", "Package"},
112N/A {"interface", "xi", "Interface"},
77N/A {"namespace", "xn", "Namespace"},
77N/A {"enumerator", "xer", null},
77N/A {"enum", "xe", "Enum"},
77N/A {"struct", "xs", "Struct"},
260N/A {"typedefs", "xts", null},
77N/A {"typedef", "xt", "Typedef"},
77N/A {"union", "xu", null},
77N/A {"field", "xfld", null},
0N/A {"member", "xmb", null},
77N/A {"function", "xf", "Function"},
111N/A {"method", "xmt", "Method"},
111N/A {"subroutine", "xsr", "Subroutine"},
111N/A };
111N/A
111N/A protected JFlexXref() {
111N/A try {
111N/A // TODO when bug #16053 is fixed, we should add a getter to a file
111N/A // that's included from all the Xref classes so that we avoid the
111N/A // reflection.
111N/A Field f = getClass().getField("YYEOF");
111N/A yyeof = f.getInt(null);
111N/A userPageLink = RuntimeEnvironment.getInstance().getUserPage();
77N/A if (userPageLink != null && userPageLink.length() == 0) {
77N/A userPageLink = null;
77N/A }
207N/A userPageSuffix = RuntimeEnvironment.getInstance().getUserPageSuffix();
207N/A if (userPageSuffix != null && userPageSuffix.length() == 0) {
77N/A userPageSuffix = null;
77N/A }
99N/A } catch (Exception e) {
77N/A // The auto-generated constructors for the Xref classes don't
77N/A // expect a checked exception, so wrap it in an AssertionError.
77N/A // This should never happen, since all the Xref classes will get
77N/A // a public static YYEOF field from JFlex.
77N/A AssertionError ae = new AssertionError("Couldn't initialize yyeof");
77N/A ae.initCause(e);
77N/A throw ae; // NOPMD (stack trace is preserved by initCause(), but
77N/A // PMD thinks it's lost)
0N/A }
77N/A }
77N/A
77N/A /**
77N/A * Reinitialize the xref with new contents.
77N/A *
77N/A * @param contents a char buffer with text to analyze
77N/A * @param length the number of characters to use from the char buffer
77N/A */
77N/A public void reInit(char[] contents, int length) {
77N/A yyreset(new CharArrayReader(contents, 0, length));
77N/A annotation = null;
111N/A }
111N/A
77N/A public void setDefs(Definitions defs) {
77N/A this.defs = defs;
77N/A }
240N/A
173N/A protected void appendProject() throws IOException {
173N/A if (project != null) {
173N/A out.write("&project=");
173N/A out.write(project.getDescription());
254N/A }
173N/A }
173N/A
173N/A protected String getProjectPostfix() {
254N/A return project == null ? "" : ("&project=" + project.getDescription());
173N/A }
173N/A
173N/A /** Get the next token from the scanner. */
253N/A public abstract int yylex() throws IOException;
253N/A
253N/A /** Reset the scanner. */
253N/A public abstract void yyreset(Reader reader);
253N/A
253N/A /** Get the value of {@code yyline}. */
253N/A protected abstract int getLineNumber();
253N/A
253N/A /** Set the value of {@code yyline}. */
253N/A protected abstract void setLineNumber(int x);
253N/A
253N/A /**
99N/A * Write xref to the specified {@code Writer}.
77N/A *
77N/A * @param out xref destination
77N/A * @throws IOException on error when writing the xref
77N/A */
77N/A public void write(Writer out) throws IOException {
0N/A this.out = out;
0N/A writeSymbolTable();
77N/A setLineNumber(0);
125N/A startNewLine();
77N/A while (yylex() != yyeof) { // NOPMD while statement intentionally empty
77N/A // nothing to do here, yylex() will do the work
205N/A }
205N/A }
205N/A
112N/A /**
112N/A * Write a JavaScript function that returns an array with the definitions
112N/A * to list in the navigation panel. Each element of the array is itself an
77N/A * array containing the name of the definition type, the CSS class name for
106N/A * the type, and an array of (symbol, line) pairs for the definitions of
119N/A * that type.
106N/A */
119N/A private void writeSymbolTable() throws IOException {
106N/A if (defs == null) {
119N/A // No definitions, no symbol table to write
119N/A return;
119N/A }
119N/A
106N/A // We want the symbol table to be sorted
106N/A Comparator<Tag> cmp = new Comparator<Tag>() {
106N/A @Override
99N/A public int compare(Tag tag1, Tag tag2) {
99N/A // Order by symbol name, and then by line number if multiple
99N/A // definitions use the same symbol name
99N/A int ret = tag1.symbol.compareTo(tag2.symbol);
99N/A if (ret == 0) {
99N/A ret = tag1.line - tag2.line;
99N/A }
99N/A return ret;
99N/A }
125N/A };
125N/A
125N/A Map<String, SortedSet<Tag>> symbols =
125N/A new HashMap<String, SortedSet<Tag>>();
125N/A
125N/A for (Tag tag : defs.getTags()) {
125N/A String[] style = getStyle(tag.type);
125N/A if (style != null && style[2] != null) {
125N/A SortedSet<Tag> tags = symbols.get(style[0]);
126N/A if (tags == null) {
125N/A tags = new TreeSet<Tag>(cmp);
125N/A symbols.put(style[0], tags);
125N/A }
126N/A tags.add(tag);
126N/A }
126N/A }
126N/A
126N/A out.append("<script type=\"text/javascript\">/* <![CDATA[ */\n");
126N/A out.append("function get_sym_list(){return [");
126N/A
126N/A boolean first = true;
126N/A for (String[] style : DEFINITION_STYLES) {
126N/A SortedSet<Tag> tags = symbols.get(style[0]);
126N/A if (tags != null) {
126N/A if (!first) {
126N/A out.append(',');
126N/A }
126N/A out.append("[\"");
126N/A out.append(style[2]);
126N/A out.append("\",\"");
126N/A out.append(style[1]);
126N/A out.append("\",[");
210N/A
210N/A boolean firstTag = true;
210N/A for (Tag tag : tags) {
210N/A if (!firstTag) {
210N/A out.append(',');
210N/A }
210N/A out.append('[');
126N/A out.append(Util.jsStringLiteral(tag.symbol));
126N/A out.append(',');
126N/A out.append(Integer.toString(tag.line));
126N/A out.append(']');
144N/A firstTag = false;
144N/A }
144N/A out.append("]]");
261N/A first = false;
261N/A }
261N/A }
261N/A /* no LF intentionally - xml is whitespace aware ... */
261N/A out.append("];} /* ]]> */</script>");
261N/A }
261N/A
261N/A /**
296N/A * Get the style description for a definition type.
296N/A *
296N/A * @param type the definition type
296N/A * @return a three element string array that describes the style of a
296N/A * definition type (name of type, CSS style class, title in the navigation
296N/A * panel)
296N/A * @see #DEFINITION_STYLES
296N/A */
296N/A private String[] getStyle(String type) {
296N/A for (String[] style : DEFINITION_STYLES) {
296N/A if (type.startsWith(style[0])) {
293N/A return style;
293N/A }
293N/A }
293N/A return null;
293N/A }
77N/A
293N/A /**
0N/A * Terminate the current line and insert preamble for the next line. The
0N/A * line count will be incremented.
0N/A *
77N/A * @throws IOException on error when writing the xref
77N/A */
205N/A protected void startNewLine() throws IOException {
77N/A int line = getLineNumber() + 1;
77N/A setLineNumber(line);
77N/A Util.readableLine(line, out, annotation, userPageLink, userPageSuffix);
77N/A }
77N/A
58N/A /**
58N/A * Write a symbol and generate links as appropriate.
77N/A *
260N/A * @param symbol the symbol to write
0N/A * @param keywords a set of keywords recognized by this analyzer (no links
0N/A * will be generated if the symbol is a keyword)
77N/A * @param line the line number on which the symbol appears
58N/A * @throws IOException if an error occurs while writing to the stream
58N/A */
58N/A protected void writeSymbol(String symbol, Set<String> keywords, int line)
0N/A throws IOException {
0N/A String[] strs = new String[1];
0N/A strs[0] = "";
58N/A
0N/A if (keywords != null && keywords.contains(symbol)) {
0N/A // This is a keyword, so we don't create a link.
0N/A out.append("<b>").append(symbol).append("</b>");
0N/A
58N/A } else if (defs != null && defs.hasDefinitionAt(symbol, line, strs)) {
0N/A // This is the definition of the symbol.
260N/A String type = strs[0];
0N/A String style_class = "d";
0N/A
58N/A String[] style = getStyle(type);
77N/A if (style != null) {
58N/A style_class = style[1];
58N/A }
260N/A
0N/A // 1) Create an anchor for direct links. (Perhaps we should only
0N/A // do this when there's exactly one definition of the symbol in
0N/A // this file? Otherwise, we may end up with multiple anchors with
77N/A // the same name.)
99N/A out.append("<a class=\"");
0N/A out.append(style_class);
0N/A out.append("\" name=\"");
77N/A out.append(symbol);
11N/A out.append("\"/>");
99N/A
99N/A // 2) Create a link that searches for all references to this symbol.
99N/A out.append("<a href=\"");
99N/A out.append(urlPrefix);
11N/A out.append("refs=");
290N/A out.append(symbol);
11N/A appendProject();
99N/A out.append("\" class=\"");
99N/A out.append(style_class);
99N/A out.append("\">");
11N/A out.append(symbol);
77N/A out.append("</a>");
58N/A
77N/A } else if (defs != null && defs.occurrences(symbol) == 1) {
70N/A // This is a reference to a symbol defined exactly once in this file.
70N/A String style_class = "d";
58N/A
77N/A // Generate a direct link to the symbol definition.
70N/A out.append("<a class=\"");
58N/A out.append(style_class);
58N/A out.append("\" href=\"#");
77N/A out.append(symbol);
77N/A out.append("\">");
99N/A out.append(symbol);
99N/A out.append("</a>");
99N/A
99N/A } else {
77N/A // This is a symbol that is not defined in this file, or a symbol
77N/A // that is defined more than once in this file. In either case, we
77N/A // can't generate a direct link to the definition, so generate a
77N/A // link to search for all definitions of that symbol instead.
77N/A out.append("<a href=\"");
77N/A out.append(urlPrefix);
77N/A out.append("defs=");
77N/A out.append(symbol);
77N/A appendProject();
77N/A out.append("\">");
58N/A out.append(symbol);
77N/A out.append("</a>");
77N/A }
77N/A }
77N/A
77N/A /**
77N/A * Write HTML escape sequence for the specified Unicode character, unless
77N/A * it's an ISO control character, in which case it is ignored.
77N/A *
77N/A * @param c the character to write
99N/A * @throws IOException if an error occurs while writing to the stream
58N/A */
99N/A protected void writeUnicodeChar(char c) throws IOException {
99N/A if (!Character.isISOControl(c)) {
99N/A out.append("&#").append(Integer.toString(c)).append(';');
99N/A }
58N/A }
99N/A
99N/A /**
99N/A * Write an e-mail address. The address will be obfuscated if
99N/A * {@link RuntimeEnvironment#isObfuscatingEMailAddresses()} returns
58N/A * {@code true}.
112N/A *
58N/A * @param address the address to write
203N/A * @throws IOException if an error occurs while writing to the stream
240N/A */
240N/A protected void writeEMailAddress(String address) throws IOException {
58N/A if (RuntimeEnvironment.getInstance().isObfuscatingEMailAddresses()) {
58N/A out.write(address.replace("@", " (at) "));
207N/A } else {
207N/A out.write(address);
207N/A }
207N/A }
207N/A}
207N/A