850N/A/*
850N/A * CDDL HEADER START
850N/A *
850N/A * The contents of this file are subject to the terms of the
850N/A * Common Development and Distribution License (the "License").
850N/A * You may not use this file except in compliance with the License.
850N/A *
850N/A * See LICENSE.txt included in this distribution for the specific
850N/A * language governing permissions and limitations under the License.
850N/A *
850N/A * When distributing Covered Code, include this CDDL HEADER in each
850N/A * file and include the License file at LICENSE.txt.
850N/A * If applicable, add the following below this CDDL HEADER, with the
850N/A * fields enclosed by brackets "[]" replaced with your own identifying
850N/A * information: Portions Copyright [yyyy] [name of copyright owner]
850N/A *
850N/A * CDDL HEADER END
850N/A */
850N/A
850N/A/*
1259N/A * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
1370N/A * Portions Copyright 2011, 2012 Jens Elkner.
850N/A */
850N/A
850N/Apackage org.opensolaris.opengrok.analysis;
850N/A
1058N/Aimport java.io.CharArrayReader;
850N/Aimport java.io.IOException;
1058N/Aimport java.io.Reader;
1020N/Aimport java.lang.reflect.Field;
1461N/Aimport java.util.ArrayDeque;
1370N/Aimport java.util.ArrayList;
1145N/Aimport java.util.Comparator;
1145N/Aimport java.util.HashMap;
1145N/Aimport java.util.Map;
943N/Aimport java.util.Set;
1145N/Aimport java.util.SortedSet;
1145N/Aimport java.util.TreeSet;
1370N/Aimport java.util.logging.Logger;
1370N/A
1145N/Aimport org.opensolaris.opengrok.analysis.Definitions.Tag;
1470N/Aimport org.opensolaris.opengrok.configuration.Configuration;
857N/Aimport org.opensolaris.opengrok.configuration.Project;
850N/Aimport org.opensolaris.opengrok.configuration.RuntimeEnvironment;
850N/Aimport org.opensolaris.opengrok.history.Annotation;
1020N/Aimport org.opensolaris.opengrok.web.Util;
850N/A
850N/A/**
1020N/A * Base class for Xref lexers.
850N/A *
850N/A * @author Lubos Kosco
850N/A */
1020N/Apublic abstract class JFlexXref {
1370N/A private static final Logger logger = Logger.getLogger(JFlexXref.class.getName());
1461N/A /** Where to write xref content. */
1384N/A public XrefWriter out;
1461N/A /** URL prefix to use when generating search URLs. Per default this is
1470N/A * {@link Configuration#getUrlPrefix()}.*/
1470N/A public String urlPrefix = RuntimeEnvironment.getConfig().getUrlPrefix();
1461N/A /** Annotation to use when writing out annotation info.*/
1121N/A public Annotation annotation;
1461N/A /** The project, to which this instance is related to. */
1121N/A public Project project;
1461N/A /** symbol definitions to use when writing out related info */
1121N/A protected Definitions defs;
1370N/A /**
1370N/A * A stack of <span ...> CSS class names currently opened.
1370N/A * It is used to close all open spans for a line and re-open them at the
1370N/A * start of the next line to produce wellformed XML. So if a
1370N/A * <span ...> is opened and do not closed on the same line, the class
1370N/A * name used needs to be pushed onto this stack. If the <span ...>
1370N/A * gets closed on a different line, it needs to be popped from this stack.
1370N/A * If properly done, the stack should be empty when the last line has been
1370N/A * written.
1370N/A */
1370N/A @SuppressWarnings("serial")
1370N/A protected class SpanStack extends ArrayList<String> {
1370N/A /**
1370N/A * Create a new empty instance with the default capacity.
1370N/A */
1370N/A public SpanStack() {
1370N/A super();
1370N/A }
1370N/A /**
1370N/A * The preferred method to add a new span class name to this instance.
1370N/A * @param s class name to add.
1370N/A * @see #pop()
1370N/A * @throws IllegalArgumentException if the given argument is {@code null}.
1370N/A */
1370N/A public void push(String s) {
1370N/A if (s == null) {
1370N/A throw new IllegalArgumentException("null is not allowed");
1370N/A }
1370N/A super.add(s);
1370N/A }
1461N/A
1370N/A /**
1370N/A * The preferred method to remove the last element from this stack.
1370N/A * @return {@code null} if the stack is empty, the removed element
1370N/A * otherwise.
1370N/A * @see #push(String)
1370N/A */
1370N/A public String pop() {
1370N/A return isEmpty() ? null : remove(size() - 1);
1370N/A }
1370N/A }
1370N/A
1370N/A /** the {@link SpanStack} for this and only this instance */
1370N/A protected final SpanStack spans;
1121N/A /** EOF value returned by yylex(). */
1121N/A private final int yyeof;
1470N/A /** See {@link Configuration#getUserPage()}. Per default initialized
1190N/A * in the constructor and here to be consistent and avoid lot of
1185N/A * unnecessary lookups.
1185N/A * @see #startNewLine() */
1185N/A protected String userPageLink;
1470N/A /** See {@link Configuration#getUserPageSuffix()}. Per default
1190N/A * initialized in the constructor and here to be consistent and avoid lot of
1185N/A * unnecessary lookups.
1185N/A * @see #startNewLine() */
1461N/A protected String userPageSuffix;
1020N/A
1461N/A /** Stack to remember the order of relevant states for the current parser.
1461N/A * @see #labelStack */
1461N/A protected ArrayDeque<Integer> stateStack = new ArrayDeque<Integer>();
1461N/A /** Stack to remember the order of relevant state related labels for the
1461N/A * current parser. To keep consistency, {@link #stateStack} opertions should
1461N/A * always performed accordingly to this instance (i.e. if you wanna pop,
1461N/A * pop both, etc. ;-))! */
1461N/A protected SpanStack labelStack = new SpanStack();
1439N/A
1145N/A /**
1259N/A * Description of the style to use for a type of definitions.
1145N/A */
1259N/A private static class Style {
1259N/A /** Name of the style definition as given by CTags. */
1259N/A final String name;
1259N/A
1259N/A /** Class name used by the style sheets when rendering the xref. */
1259N/A final String ssClass;
1259N/A
1259N/A /**
1259N/A * The title of the section to which this type belongs, or {@code null}
1259N/A * if this type should not be listed in the navigation panel.
1259N/A */
1259N/A final String title;
1259N/A
1259N/A /** Construct a style description. */
1259N/A Style(String name, String ssClass, String title) {
1259N/A this.name = name;
1259N/A this.ssClass = ssClass;
1259N/A this.title = title;
1259N/A }
1259N/A }
1259N/A
1259N/A /**
1259N/A * Description of styles to use for different types of definitions.
1259N/A */
1259N/A private static final Style[] DEFINITION_STYLES = {
1259N/A new Style("macro", "xm", "Macro"),
1259N/A new Style("argument", "xa", null),
1259N/A new Style("local", "xl", null),
1259N/A new Style("variable", "xv", "Variable"),
1259N/A new Style("class", "xc", "Class"),
1259N/A new Style("package", "xp", "Package"),
1259N/A new Style("interface", "xi", "Interface"),
1259N/A new Style("namespace", "xn", "Namespace"),
1259N/A new Style("enumerator", "xer", null),
1259N/A new Style("enum", "xe", "Enum"),
1259N/A new Style("struct", "xs", "Struct"),
1259N/A new Style("typedefs", "xts", null),
1259N/A new Style("typedef", "xt", "Typedef"),
1259N/A new Style("union", "xu", null),
1259N/A new Style("field", "xfld", null),
1259N/A new Style("member", "xmb", null),
1259N/A new Style("function", "xf", "Function"),
1259N/A new Style("method", "xmt", "Method"),
1259N/A new Style("subroutine", "xsr", "Subroutine"),
1145N/A };
1145N/A
1370N/A /**
1370N/A * Create a new lexer instance. Initializes {@link #userPageLink},
1370N/A * {@link #userPageSuffix} using the runtime environment and creates a new
1370N/A * empty {@link SpanStack}.
1470N/A * @see Configuration#getUserPage()
1470N/A * @see Configuration#getUserPageSuffix()
1370N/A */
1121N/A protected JFlexXref() {
1121N/A try {
1121N/A // TODO when bug #16053 is fixed, we should add a getter to a file
1121N/A // that's included from all the Xref classes so that we avoid the
1121N/A // reflection.
1121N/A Field f = getClass().getField("YYEOF");
1121N/A yyeof = f.getInt(null);
1470N/A userPageLink = RuntimeEnvironment.getConfig().getUserPage();
1185N/A if (userPageLink != null && userPageLink.length() == 0) {
1185N/A userPageLink = null;
1185N/A }
1470N/A userPageSuffix = RuntimeEnvironment.getConfig().getUserPageSuffix();
1185N/A if (userPageSuffix != null && userPageSuffix.length() == 0) {
1185N/A userPageSuffix = null;
1185N/A }
1121N/A } catch (Exception e) {
1121N/A // The auto-generated constructors for the Xref classes don't
1121N/A // expect a checked exception, so wrap it in an AssertionError.
1121N/A // This should never happen, since all the Xref classes will get
1121N/A // a public static YYEOF field from JFlex.
1121N/A AssertionError ae = new AssertionError("Couldn't initialize yyeof");
1121N/A ae.initCause(e);
1121N/A throw ae; // NOPMD (stack trace is preserved by initCause(), but
1121N/A // PMD thinks it's lost)
1121N/A }
1370N/A spans = new SpanStack();
1121N/A }
1020N/A
1121N/A /**
1121N/A * Reinitialize the xref with new contents.
1121N/A *
1121N/A * @param contents a char buffer with text to analyze
1121N/A * @param length the number of characters to use from the char buffer
1121N/A */
1121N/A public void reInit(char[] contents, int length) {
1121N/A yyreset(new CharArrayReader(contents, 0, length));
1121N/A annotation = null;
1121N/A }
1121N/A
1461N/A /**
1461N/A * Set the symbol defintions to use when writing out related info.
1461N/A * @param defs symbol definitions to use. Might be {@code null}.
1461N/A */
1121N/A public void setDefs(Definitions defs) {
1121N/A this.defs = defs;
1121N/A }
1121N/A
1461N/A /**
1461N/A * Write out the URL parameter, which specifies the project to use. Does
1461N/A * nothing if {@link #project} is not set.
1461N/A * @throws IOException
1461N/A */
1121N/A protected void appendProject() throws IOException {
1121N/A if (project != null) {
1121N/A out.write("&amp;project=");
1121N/A out.write(project.getDescription());
1121N/A }
1121N/A }
1121N/A
1461N/A /**
1461N/A * Get the URL parameter, which specifies the project to use.
1461N/A * @return an empty String if {@link #project} is not set, the parameter
1461N/A * including the leading &amp; otherwise.
1461N/A */
1121N/A protected String getProjectPostfix() {
1121N/A return project == null ? "" : ("&amp;project=" + project.getDescription());
1121N/A }
1058N/A
1461N/A /**
1461N/A * Run the scanner to get the next token from the input.
1461N/A * @return {@code true} if a new Token is available/was found.
1461N/A * @throws IOException */
1121N/A public abstract int yylex() throws IOException;
1121N/A
1461N/A /**
1461N/A * Closes the current input stream, and resets the scanner to read from the
1461N/A * given input stream. All internal variables are reset, the old input
1461N/A * stream cannot be reused (content of the internal buffer is discarded and
1461N/A * lost). The lexical state is set to {@code YY_INITIAL}.
1461N/A * @param reader the new input stream to operate on.*/
1121N/A public abstract void yyreset(Reader reader);
1121N/A
1461N/A /**
1461N/A * Get the number of the current line of input (which is usually the same as
1461N/A * {@code yyline}).
1461N/A * @return a value &gt;= 0.
1461N/A */
1121N/A protected abstract int getLineNumber();
1121N/A
1461N/A /**
1461N/A * Set the number of the current line of input (which is usually the same as
1461N/A * {@code yyline}).
1461N/A * @param line line number to set. */
1461N/A protected abstract void setLineNumber(int line);
850N/A
1461N/A /**
1461N/A * Enter the given lexical state.
1461N/A * @param newState state to enter
1461N/A */
1439N/A public abstract void yybegin(int newState);
1439N/A
1461N/A /**
1461N/A * Get the current lexical state of the scanner.
1461N/A * @return a lexical state.
1461N/A */
1439N/A public abstract int yystate();
1439N/A
1121N/A /**
1384N/A * Write the crossfile content to the specified {@code Writer}.
1121N/A *
1121N/A * @param out xref destination
1121N/A * @throws IOException on error when writing the xref
1121N/A */
1384N/A public void write(XrefWriter out) throws IOException {
1121N/A this.out = out;
1145N/A writeSymbolTable();
1121N/A setLineNumber(0);
1370N/A out.write("<div id='lines'\n>");
1121N/A startNewLine();
1121N/A while (yylex() != yyeof) { // NOPMD while statement intentionally empty
1121N/A // nothing to do here, yylex() will do the work
1121N/A }
1370N/A finishLine();
1370N/A out.write("</div\n>");
1384N/A out.setLines(getLineNumber());
1370N/A if (spans.size() != 0) {
1384N/A logger.info("The SpanStack for " + out.getFile()
1384N/A + " is not empty! May be the " + getClass().getSimpleName()
1384N/A + " lexer is not perfect yet!");
1370N/A spans.clear();
1370N/A }
1121N/A }
1020N/A
1121N/A /**
1145N/A * Write a JavaScript function that returns an array with the definitions
1145N/A * to list in the navigation panel. Each element of the array is itself an
1145N/A * array containing the name of the definition type, the CSS class name for
1145N/A * the type, and an array of (symbol, line) pairs for the definitions of
1145N/A * that type.
1145N/A */
1145N/A private void writeSymbolTable() throws IOException {
1145N/A if (defs == null) {
1145N/A // No definitions, no symbol table to write
1145N/A return;
1145N/A }
1145N/A
1145N/A // We want the symbol table to be sorted
1145N/A Comparator<Tag> cmp = new Comparator<Tag>() {
1145N/A @Override
1145N/A public int compare(Tag tag1, Tag tag2) {
1145N/A // Order by symbol name, and then by line number if multiple
1145N/A // definitions use the same symbol name
1145N/A int ret = tag1.symbol.compareTo(tag2.symbol);
1145N/A if (ret == 0) {
1145N/A ret = tag1.line - tag2.line;
1145N/A }
1145N/A return ret;
1145N/A }
1145N/A };
1145N/A
1145N/A Map<String, SortedSet<Tag>> symbols =
1145N/A new HashMap<String, SortedSet<Tag>>();
1145N/A
1145N/A for (Tag tag : defs.getTags()) {
1259N/A Style style = getStyle(tag.type);
1259N/A if (style != null && style.title != null) {
1259N/A SortedSet<Tag> tags = symbols.get(style.name);
1145N/A if (tags == null) {
1145N/A tags = new TreeSet<Tag>(cmp);
1259N/A symbols.put(style.name, tags);
1145N/A }
1145N/A tags.add(tag);
1145N/A }
1145N/A }
1145N/A
1145N/A out.append("<script type=\"text/javascript\">/* <![CDATA[ */\n");
1354N/A out.append("O.symlist = [");
1145N/A
1145N/A boolean first = true;
1259N/A for (Style style : DEFINITION_STYLES) {
1259N/A SortedSet<Tag> tags = symbols.get(style.name);
1145N/A if (tags != null) {
1145N/A if (!first) {
1145N/A out.append(',');
1145N/A }
1145N/A out.append("[\"");
1259N/A out.append(style.title);
1145N/A out.append("\",\"");
1259N/A out.append(style.ssClass);
1145N/A out.append("\",[");
1145N/A
1145N/A boolean firstTag = true;
1145N/A for (Tag tag : tags) {
1145N/A if (!firstTag) {
1145N/A out.append(',');
1145N/A }
1145N/A out.append('[');
1145N/A out.append(Util.jsStringLiteral(tag.symbol));
1145N/A out.append(',');
1145N/A out.append(Integer.toString(tag.line));
1145N/A out.append(']');
1145N/A firstTag = false;
1145N/A }
1145N/A out.append("]]");
1145N/A first = false;
1145N/A }
1145N/A }
1185N/A /* no LF intentionally - xml is whitespace aware ... */
1354N/A out.append("]; /* ]]> */</script>");
1145N/A }
1145N/A
1145N/A /**
1145N/A * Get the style description for a definition type.
1145N/A *
1145N/A * @param type the definition type
1259N/A * @return the style of a definition type, or {@code null} if no style is
1259N/A * defined for the type
1145N/A * @see #DEFINITION_STYLES
1145N/A */
1370N/A private static Style getStyle(String type) {
1259N/A for (Style style : DEFINITION_STYLES) {
1259N/A if (type.startsWith(style.name)) {
1145N/A return style;
1145N/A }
1145N/A }
1259N/A return null;
1145N/A }
1145N/A
1145N/A /**
1370N/A * Write out annotation infos for the given line.
1370N/A *
1370N/A * @param num linenumber to print
1370N/A * @throws IOException depends on the destination (<var>out</var>).
1370N/A */
1370N/A private final void writeAnnotationInfos(int num) throws IOException {
1370N/A String r = annotation.getRevision(num);
1370N/A out.write("<span class='blame'>");
1384N/A out.write("<a class='r' href=\"");
1469N/A out.write(Util.uriEncodePath(annotation.getFilename()));
1384N/A out.write("?a=true&amp;r=");
1469N/A out.write(r);
1384N/A String msg = annotation.getDesc(r);
1384N/A if (msg != null) {
1384N/A out.write("\" title=\"");
1469N/A out.write(Util.formQuoteEscape(msg));
1370N/A }
1384N/A out.write("\">");
1469N/A out.write(r);
1384N/A out.write("</a>");
1370N/A String a = annotation.getAuthor(num);
1370N/A if (userPageLink == null) {
1370N/A out.write("<span class='a'>");
1469N/A out.write(Util.htmlize(a));
1370N/A out.write("</span>");
1370N/A } else {
1469N/A out.write("<a class='a' href=\"");
1370N/A out.write(userPageLink);
1469N/A out.write(Util.uriEncodePath(a));
1370N/A if (userPageSuffix != null) {
1370N/A out.write(userPageSuffix);
1370N/A }
1469N/A out.write("\">");
1469N/A out.write(Util.htmlize(a));
1370N/A out.write("</a>");
1370N/A }
1370N/A out.write("</span>");
1370N/A }
1370N/A
1370N/A private final void finishLine() throws IOException {
1384N/A if (out.getMark() == out.getCount()) {
1384N/A out.write(' '); // <div></div> doesn't produce a line-height block
1384N/A }
1370N/A if (spans.size() != 0) {
1370N/A for (int i=spans.size()-1; i >= 0; i--) {
1370N/A out.write("</span>");
1370N/A }
1370N/A }
1370N/A out.write("</div\n>");
1370N/A }
1370N/A
1370N/A /**
1121N/A * Terminate the current line and insert preamble for the next line. The
1121N/A * line count will be incremented.
1121N/A *
1121N/A * @throws IOException on error when writing the xref
1121N/A */
1370N/A protected final void startNewLine() throws IOException {
1370N/A int line = getLineNumber();
1370N/A if (line != 0) {
1370N/A finishLine();
1370N/A }
1370N/A setLineNumber(++line);
1384N/A /* <div id="N">...</div>
1384N/A * -> uncompressed size ~6%, compressed size ~26% bigger;
1384N/A * <div id="N"><b>N</b>...</div>
1384N/A * -> uncompressed size ~12%, compressed size ~52% bigger;
1384N/A * <span class="[h]l" id="N">N</span>...
1384N/A * -> is about the same as previous one
1384N/A */
1384N/A out.write("<div>");
1370N/A if (annotation != null) {
1370N/A writeAnnotationInfos(line);
1370N/A }
1370N/A if (spans.size() != 0) {
1370N/A for (String cname : spans) {
1370N/A if (cname.isEmpty()) {
1370N/A out.write("<span>");
1370N/A } else {
1370N/A out.write("<span class='");
1370N/A out.write(cname);
1370N/A out.write("'>");
1370N/A }
1370N/A }
1370N/A }
1384N/A out.mark();
1121N/A }
1370N/A
1121N/A /**
1121N/A * Write a symbol and generate links as appropriate.
1121N/A *
1469N/A * @param symbol the symbol to write.
1121N/A * @param keywords a set of keywords recognized by this analyzer (no links
1121N/A * will be generated if the symbol is a keyword)
1121N/A * @param line the line number on which the symbol appears
1469N/A * @param escape If {@code true} <var>symbol</var> gets escapes for URI paths
1469N/A * and html text. Otherwise it is assumed, that symbol contains no HTML
1469N/A * special characters.
1121N/A * @throws IOException if an error occurs while writing to the stream
1121N/A */
1469N/A protected void writeSymbol(String symbol, Set<String> keywords, int line,
1469N/A boolean escape) throws IOException
1469N/A {
1121N/A String[] strs = new String[1];
1121N/A strs[0] = "";
1121N/A if (keywords != null && keywords.contains(symbol)) {
1469N/A String htmlSymbol = escape ? Util.htmlize(symbol) : symbol;
1121N/A // This is a keyword, so we don't create a link.
1469N/A out.append("<b>").append(htmlSymbol).append("</b>");
1108N/A
1121N/A } else if (defs != null && defs.hasDefinitionAt(symbol, line, strs)) {
1121N/A // This is the definition of the symbol.
1121N/A String type = strs[0];
1121N/A String style_class = "d";
943N/A
1259N/A Style style = getStyle(type);
1145N/A if (style != null) {
1259N/A style_class = style.ssClass;
1121N/A }
1121N/A
1121N/A // 1) Create an anchor for direct links. (Perhaps we should only
1121N/A // do this when there's exactly one definition of the symbol in
1121N/A // this file? Otherwise, we may end up with multiple anchors with
1121N/A // the same name.)
1121N/A out.append("<a class=\"");
1121N/A out.append(style_class);
1121N/A out.append("\" name=\"");
1469N/A out.append(escape ? Util.formQuoteEscape(symbol) : symbol);
1121N/A out.append("\"/>");
1108N/A
1121N/A // 2) Create a link that searches for all references to this symbol.
1121N/A out.append("<a href=\"");
1121N/A out.append(urlPrefix);
1121N/A out.append("refs=");
1469N/A out.append(escape ? Util.uriEncodeQueryValue(symbol) : symbol);
1121N/A appendProject();
1121N/A out.append("\" class=\"");
1121N/A out.append(style_class);
1121N/A out.append("\">");
1469N/A out.append(escape ? Util.htmlize(symbol) : symbol);
1121N/A out.append("</a>");
1121N/A
1121N/A } else if (defs != null && defs.occurrences(symbol) == 1) {
1121N/A // This is a reference to a symbol defined exactly once in this file.
1121N/A String style_class = "d";
1121N/A
1121N/A // Generate a direct link to the symbol definition.
1121N/A out.append("<a class=\"");
1121N/A out.append(style_class);
1121N/A out.append("\" href=\"#");
1469N/A out.append(escape ? Util.uriEncodeQueryValue(symbol) : symbol);
1121N/A out.append("\">");
1469N/A out.append(escape ? Util.htmlize(symbol) : symbol);
1121N/A out.append("</a>");
943N/A
1121N/A } else {
1121N/A // This is a symbol that is not defined in this file, or a symbol
1121N/A // that is defined more than once in this file. In either case, we
1121N/A // can't generate a direct link to the definition, so generate a
1121N/A // link to search for all definitions of that symbol instead.
1121N/A out.append("<a href=\"");
1121N/A out.append(urlPrefix);
1121N/A out.append("defs=");
1469N/A out.append(escape ? Util.uriEncodeQueryValue(symbol) : symbol);
1121N/A appendProject();
1121N/A out.append("\">");
1469N/A out.append(escape ? Util.htmlize(symbol) : symbol);
1121N/A out.append("</a>");
1121N/A }
1121N/A }
943N/A
1121N/A /**
1469N/A * Write out the Unicode character, unless it's an ISO control character
1469N/A * &lt; 0x20, in which case it is ignored.
1121N/A *
1121N/A * @param c the character to write
1121N/A * @throws IOException if an error occurs while writing to the stream
1121N/A */
1121N/A protected void writeUnicodeChar(char c) throws IOException {
1469N/A if (c > 0x20) {
1469N/A out.append(c);
1121N/A }
1121N/A }
1122N/A
1122N/A /**
1123N/A * Write an e-mail address. The address will be obfuscated if
1470N/A * {@link Configuration#isObfuscatingEMailAddresses()} returns
1469N/A * {@code true}. It gets not htmlized because it is assumed to contain no
1469N/A * HTML special characters.
1122N/A *
1122N/A * @param address the address to write
1122N/A * @throws IOException if an error occurs while writing to the stream
1122N/A */
1122N/A protected void writeEMailAddress(String address) throws IOException {
1470N/A if (RuntimeEnvironment.getConfig().isObfuscatingEMailAddresses()) {
1123N/A out.write(address.replace("@", " (at) "));
1123N/A } else {
1123N/A out.write(address);
1123N/A }
1122N/A }
1439N/A
1461N/A /**
1461N/A * Push the current state to the state order stack, the given label to the
1461N/A * {@link #labelStack} and enter the given state.
1461N/A * @param newState new state to enter.
1461N/A * @param label label to push to the {@link #labelStack}. Might be {@code null}.
1461N/A * @see #stateStack
1461N/A * @see #yystate()
1461N/A * @see #yybegin(int)
1461N/A */
1461N/A @SuppressWarnings("boxing")
1461N/A public void yypush(int newState, String label) {
1461N/A stateStack.push(yystate());
1461N/A labelStack.push(label);
1439N/A yybegin(newState);
1439N/A }
1439N/A
1461N/A /**
1461N/A * Pop the last entry from the state order stack and enter it. Also pop the
1461N/A * the last label from the {@link #labelStack} and write it out, if it is
1461N/A * not {@code null}.
1461N/A * @throws IOException
1461N/A * @see #stateStack
1461N/A * @see #yybegin(int)
1461N/A */
1461N/A @SuppressWarnings("boxing")
1439N/A public void yypop() throws IOException {
1461N/A yybegin(stateStack.pop());
1461N/A out.write(labelStack.pop());
1439N/A }
850N/A}