001: package vqwiki.lex;
002:
003: import java.util.Enumeration;
004: import java.util.Vector;
005: import java.util.regex.Matcher;
006: import java.util.regex.Pattern;
007: import org.apache.log4j.Logger;
008: import vqwiki.Environment;
009:
010: /**
011: * Experimental class. This class may be used in two ways:
012: *
013: * The static addTableOfContents(String) method may be called to automatically
014: * adds a table of contents on the right side of an article. This method
015: * works with all lexers, because it parses the HTML for headers. However it
016: * doesn't care where it is. So if you have a header on the TopArea / LeftMenu /
017: * BottomArea, it will also add a TOC there...
018: *
019: * The static addTableOfContents(MakeTableOfContents, StringBuffer) method
020: * may be called to insert a pre-built MakeTableOfContents object into an
021: * article. This method requires that the parser has added all table of
022: * contents headings to the object and included a TOC_INSERT_TAG at the point
023: * where the table of contents should be inserted. It is a bit more flexible
024: * but requires more preperatory work.
025: *
026: * @author studer
027: */
028: public class MakeTableOfContents {
029:
030: private static final Logger logger = Logger
031: .getLogger(MakeTableOfContents.class);
032: public static final int STATUS_TOC_UNINITIALIZED = 0;
033: public static final int STATUS_TOC_INITIALIZED = 1;
034: public static final int STATUS_NO_TOC = 2;
035: public static final String TOC_INSERT_TAG = "__INSERT_TOC__";
036: private int currentLevel = 0;
037: private int minLevel = 4;
038: private Vector entries = new Vector();
039: private int status = STATUS_TOC_UNINITIALIZED;
040:
041: /**
042: * Adds TOC at the beginning as a table on the right side of the page if the
043: * page has any HTML-headers.
044: *
045: * @param text
046: * @return
047: */
048: public static String addTableOfContents(String text) {
049: logger.debug("Start TOC generating...");
050: Pattern p = Pattern
051: .compile("<[Hh][123][^>]*>(.*)</[Hh][123][^>]*>");
052: Matcher m = p.matcher(text);
053: StringBuffer result = new StringBuffer();
054: StringBuffer toc = new StringBuffer();
055: toc.append("<table align=\"right\" class=\"toc\"><tr><td>");
056: int position = 0;
057: while (m.find()) {
058: result.append(text.substring(position, m.start(1)));
059: position = m.start(1);
060: result.append("<a class=\"tocheader\" name=\"" + position
061: + "\" id=\"" + position + "\"></a>");
062: if (m.group().startsWith("<h1")
063: || m.group().startsWith("<H1")) {
064: toc.append("<span class=\"tocheader1\">");
065: } else if (m.group().startsWith("<h2")
066: || m.group().startsWith("<H2")) {
067: toc.append("<span class=\"tocheader2\">");
068: } else {
069: toc.append("<span class=\"tocheader3\">");
070: }
071: toc.append("<li><a href=\"#" + position + "\">"
072: + m.group(1) + "</a></li></span>");
073: result.append(text.substring(position, m.end(1)));
074: position = m.end(1);
075: logger.debug("Adding content: " + m.group(1));
076: }
077: toc.append("</td></tr></table>");
078: result.append(text.substring(position));
079: if (position > 0) {
080: logger.debug("adding TOC at the beginning!");
081: toc.append(result);
082: } else {
083: toc = result;
084: }
085: return toc.toString();
086: }
087:
088: /**
089: * Insert an existing MakeTableOfContents object into formatted HTML
090: * output.
091: *
092: * @param toc A pre-built MakeTableOfContents object.
093: * @param contents The Wiki syntax, which should contain TOC_INSERT_TAG at
094: * the point where the table of contents object is to be inserted.
095: * @return The formatted content containing the table of contents.
096: */
097: public static StringBuffer addTableOfContents(
098: MakeTableOfContents toc, StringBuffer contents) {
099: int pos = contents.indexOf(MakeTableOfContents.TOC_INSERT_TAG);
100: if (pos >= 0) {
101: // FIXME - don't hardcode minimum TOC size
102: if (toc == null
103: || toc.size() <= 3
104: || toc.getStatus() == MakeTableOfContents.STATUS_NO_TOC
105: || !Environment.getInstance().isMakeToc()) {
106: // remove the insert tag
107: contents.delete(pos, pos
108: + MakeTableOfContents.TOC_INSERT_TAG.length());
109: } else {
110: // insert the toc
111: contents.replace(pos, pos
112: + MakeTableOfContents.TOC_INSERT_TAG.length(),
113: toc.toHTML());
114: }
115: }
116: return contents;
117: }
118:
119: /**
120: * Add a new table of contents entry.
121: *
122: * @param name The name of the entry, to be used in the anchor tag name.
123: * @param text The text to display for the table of contents entry.
124: * @param level The level of the entry. If an entry is a sub-heading of
125: * another entry the value should be 2. If there is a sub-heading of that
126: * entry then its value would be 3, and so forth.
127: */
128: public void addEntry(String name, String text, int level) {
129: if (this .status != STATUS_NO_TOC)
130: this .status = STATUS_TOC_INITIALIZED;
131: TableOfContentsEntry entry = new TableOfContentsEntry(name,
132: text, level);
133: entries.add(entry);
134: if (level < minLevel)
135: minLevel = level;
136: }
137:
138: /**
139: * Internal method to close any list tags prior to adding the next entry.
140: */
141: private void closeList(int level, StringBuffer text) {
142: while (level < currentLevel) {
143: // close lists to current level
144: text.append("</li></ol>");
145: currentLevel--;
146: }
147: }
148:
149: /**
150: * Return the current table of contents status, such as "no table of contents
151: * allowed" or "uninitialized".
152: */
153: public int getStatus() {
154: return this .status;
155: }
156:
157: /**
158: * Internal method to open any list tags prior to adding the next entry.
159: */
160: private void openList(int level, StringBuffer text) {
161: if (level == currentLevel) {
162: // same level as previous item, close previous and open new
163: text.append("</li><li>");
164: return;
165: }
166: while (level > currentLevel) {
167: // open lists to current level
168: text.append("<ol><li>");
169: currentLevel++;
170: }
171: }
172:
173: /**
174: * Set the current table of contents status, such as "no table of contents
175: * allowed" or "uninitialized".
176: */
177: public void setStatus(int status) {
178: this .status = status;
179: }
180:
181: /**
182: * Return the number of entries in this TOC object.
183: */
184: public int size() {
185: return this .entries.size();
186: }
187:
188: /**
189: * Return an HTML representation of this table of contents object.
190: */
191: public String toHTML() {
192: Enumeration e = entries.elements();
193: StringBuffer text = new StringBuffer();
194: text.append("<table class=\"toc\"><tr><td>");
195: TableOfContentsEntry entry = null;
196: int adjustedLevel = 0;
197: while (e.hasMoreElements()) {
198: entry = (TableOfContentsEntry) e.nextElement();
199: // adjusted level determines how far to indent the list
200: adjustedLevel = ((entry.level - minLevel) + 1);
201: closeList(adjustedLevel, text);
202: openList(adjustedLevel, text);
203: text.append("<a href=\"#").append(entry.name).append("\">")
204: .append(entry.text).append("</a>");
205: }
206: closeList(0, text);
207: text.append("</td></tr></table>");
208: return text.toString();
209: }
210:
211: /**
212: * Inner class holds TOC entries until they can be processed for display.
213: */
214: class TableOfContentsEntry {
215:
216: int level;
217: String name;
218: String text;
219:
220: /**
221: *
222: */
223: TableOfContentsEntry(String name, String text, int level) {
224: this.name = name;
225: this.text = text;
226: this.level = level;
227: }
228: }
229: }
|