001: // wikiCode.java
002: // -------------------------------------
003: // part of YACY
004: //
005: // (C) 2005, 2006 by Alexander Schier
006: // Marc Nause, Franz Brausze
007: //
008: //
009: // last change: $LastChangedDate: $ by $LastChangedBy: $
010: //
011: // This program is free software; you can redistribute it and/or modify
012: // it under the terms of the GNU General Public License as published by
013: // the Free Software Foundation; either version 2 of the License, or
014: // (at your option) any later version.
015: //
016: // This program is distributed in the hope that it will be useful,
017: // but WITHOUT ANY WARRANTY; without even the implied warranty of
018: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019: // GNU General Public License for more details.
020: //
021: // You should have received a copy of the GNU General Public License
022: // along with this program; if not, write to the Free Software
023: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024: //
025: // Using this software in any meaning (reading, learning, copying, compiling,
026: // running) means that you agree that the Author(s) is (are) not responsible
027: // for cost, loss of data or any harm that may be caused directly or indirectly
028: // by usage of this softare or this documentation. The usage of this software
029: // is on your own risk. The installation and usage (starting/running) of this
030: // software may allow other people or application to access your computer and
031: // any attached devices and is highly dependent on the configuration of the
032: // software which must be done by the user of the software; the author(s) is
033: // (are) also not responsible for proper configuration and usage of the
034: // software, even if provoked by documentation provided together with
035: // the software.
036: //
037: // Any changes to this file according to the GPL as documented in the file
038: // gpl.txt aside this file in the shipment you received can be done to the
039: // lines that follows this copyright notice here, but changes must not be
040: // done inside the copyright notive above. A re-distribution must contain
041: // the intact and unchanged copyright notice.
042: // Contributions and changes to the program code must be marked as such.
043:
044: // Contains contributions from Alexander Schier [AS]
045: // Franz Brausze [FB] and Marc Nause [MN]
046:
047: package de.anomic.data;
048:
049: import java.io.BufferedReader;
050: import java.io.IOException;
051: import java.util.ArrayList;
052: import java.util.Arrays;
053: import java.util.HashMap;
054:
055: import de.anomic.data.wiki.abstractWikiParser;
056: import de.anomic.data.wiki.wikiParser;
057: import de.anomic.plasma.plasmaSwitchboard;
058: import de.anomic.server.serverCore;
059:
060: /** This class provides methods to handle texts that have been posted in the yacyWiki or other
061: * parts of YaCy that use this class, like the blog or the profile.
062: */
063: public class wikiCode extends abstractWikiParser implements wikiParser {
064:
065: /* Table properties */
066: private static final String[] tps = { "rowspan", "colspan",
067: "vspace", "hspace", "cellspacing", "cellpadding", "border" };
068: private static final HashMap/* <String,String[]> */<String, String[]> ps = new HashMap<String, String[]>();
069: static {
070: Arrays.sort(tps);
071: String[] array;
072: Arrays.sort(array = new String[] { "void", "above", "below",
073: "hsides", "lhs", "rhs", "vsides", "box", "border" });
074: ps.put("frame", array);
075: Arrays.sort(array = new String[] { "none", "groups", "rows",
076: "cols", "all" });
077: ps.put("rules", array);
078: Arrays.sort(array = new String[] { "top", "middle", "bottom",
079: "baseline" });
080: ps.put("valign", array);
081: Arrays.sort(array = new String[] { "left", "right", "center" });
082: ps.put("align", array);
083: }
084:
085: private String numListLevel = "";
086: private String ListLevel = "";
087: private String defListLevel = "";
088: private boolean cellprocessing = false; //needed for prevention of double-execution of replaceHTML
089: private boolean defList = false; //needed for definition lists
090: private boolean escape = false; //needed for escape
091: private boolean escaped = false; //needed for <pre> not getting in the way
092: private boolean escapeSpan = false; //needed for escape symbols [= and =] spanning over several lines
093: private boolean newrowstart = false; //needed for the first row not to be empty
094: private boolean nolist = false; //needed for handling of [= and <pre> in lists
095: private boolean preformatted = false; //needed for preformatted text
096: private boolean preformattedSpan = false; //needed for <pre> and </pre> spanning over several lines
097: private boolean replacedHTML = false; //indicates if method replaceHTML has been used with line already
098: private boolean table = false; //needed for tables, because they reach over several lines
099: private int preindented = 0; //needed for indented <pre>s
100: private int escindented = 0; //needed for indented [=s
101: private int headlines = 0; //number of headlines in page
102: private ArrayList<String> dirElements = new ArrayList<String>(); //list of headlines used to create diectory of page
103:
104: /** Constructor of the class wikiCode */
105: public wikiCode(plasmaSwitchboard switchboard) {
106: super (switchboard);
107: }
108:
109: protected String transform(BufferedReader reader, int length,
110: String publicAddress, plasmaSwitchboard switchboard)
111: throws IOException {
112: StringBuffer out = new StringBuffer(length);
113: String line;
114: while ((line = reader.readLine()) != null)
115: out.append(transformLine(line, publicAddress, switchboard))
116: .append(serverCore.CRLF_STRING);
117: return out.insert(0, directory()).toString();
118: }
119:
120: /** This method processes tables in the wiki code.
121: * @param a string that might contain parts of a table
122: * @return a string with wiki code of parts of table replaced by HTML code for table
123: */
124: //[FB], changes by [MN]
125: private String processTable(String result,
126: plasmaSwitchboard switchboard) {
127: //some variables that make it easier to change codes for the table
128: String line = "";
129: String tableStart = "{|"; // {|
130: String newLine = "|-"; // |-
131: String cellDivider = "||"; // ||
132: String tableEnd = "|}"; // |}
133: String attribDivider = "|"; // |
134: int lenTableStart = tableStart.length();
135: int lenCellDivider = cellDivider.length();
136: int lenTableEnd = tableEnd.length();
137: int lenAttribDivider = attribDivider.length();
138:
139: if ((result.startsWith(tableStart)) && (!table)) {
140: table = true;
141: newrowstart = true;
142: line = "<table";
143: if (result.trim().length() > lenTableStart) {
144: line += parseTableProperties(
145: result.substring(lenTableStart).trim())
146: .toString();
147: }
148: line += ">";
149: result = line;
150: } else if (result.startsWith(newLine) && (table)) { // new row
151: if (!newrowstart) {
152: line += "\t</tr>\n";
153: } else {
154: newrowstart = false;
155: }
156: line = line + "\t<tr>";
157: result = line;
158: } else if ((result.startsWith(cellDivider)) && (table)) {
159: line += "\t\t<td";
160: int cellEnd = (result.indexOf(cellDivider, lenCellDivider) > 0) ? (result
161: .indexOf(cellDivider, lenCellDivider))
162: : (result.length());
163: int propEnd = result.indexOf(attribDivider, lenCellDivider);
164: int occImage = result.indexOf("[[Image:", lenCellDivider);
165: int occEscape = result.indexOf("[=", lenCellDivider);
166: //If resultOf("[[Image:") is less than propEnd, that means that there is no
167: //property for this cell, only an image. Without this, YaCy could get confused
168: //by a | in [[Image:picture.png|alt-text]] or [[Image:picture.png|alt-text]]
169: //Same for [= (part of [= =])
170: if ((propEnd > lenCellDivider)
171: && ((occImage > propEnd) || (occImage < 0))
172: && ((occEscape > propEnd) || (occEscape < 0))) {
173: propEnd = result.indexOf(attribDivider, lenCellDivider)
174: + lenAttribDivider;
175: } else {
176: propEnd = cellEnd;
177: }
178: // both point at same place => new line
179: if (propEnd == cellEnd) {
180: propEnd = lenCellDivider;
181: } else {
182: line += parseTableProperties(
183: result.substring(lenCellDivider,
184: propEnd - lenAttribDivider).trim())
185: .toString();
186: }
187: // quick&dirty fix [MN]
188: if (propEnd > cellEnd) {
189: propEnd = lenCellDivider;
190: }
191: table = false;
192: cellprocessing = true;
193: line += ">"
194: + processTable(result.substring(propEnd, cellEnd)
195: .trim(), switchboard) + "</td>";
196: table = true;
197: cellprocessing = false;
198: if (cellEnd < result.length()) {
199: line += "\n"
200: + processTable(result.substring(cellEnd),
201: switchboard);
202: }
203: result = line;
204: } else if (result.startsWith(tableEnd) && (table)) { // Table end
205: table = false;
206: line += "\t</tr>\n</table>" + result.substring(lenTableEnd);
207: result = line;
208: }
209: return result;
210: }
211:
212: // contributed by [MN], changes by [FB]
213: /** This method takes possible table properties and tests if they are valid.
214: * Valid in this case means if they are a property for the table, tr or td
215: * tag as stated in the HTML Pocket Reference by Jennifer Niederst (1st edition)
216: * The method is important to avoid XSS attacks on the wiki via table properties.
217: * @param properties A string that may contain several table properties and/or junk.
218: * @return A string that only contains table properties.
219: */
220: private static StringBuffer parseTableProperties(
221: final String properties) {
222: final String[] values = properties.replaceAll(""", "")
223: .split("[= ]"); //splitting the string at = and blanks
224: final StringBuffer sb = new StringBuffer(properties.length());
225: String key, value;
226: String[] posVals;
227: final int numberofvalues = values.length;
228: for (int i = 0; i < numberofvalues; i++) {
229: key = values[i].trim();
230: if (key.equals("nowrap")) {
231: addPair("nowrap", "nowrap", sb);
232: } else if (i + 1 < numberofvalues) {
233: value = values[++i].trim();
234: if ((key.equals("summary"))
235: || (key.equals("bgcolor") && value
236: .matches("#{0,1}[0-9a-fA-F]{1,6}|[a-zA-Z]{3,}"))
237: || ((key.equals("width") || key
238: .equals("height")) && value
239: .matches("\\d+%{0,1}"))
240: || ((posVals = ps.get(key)) != null && Arrays
241: .binarySearch(posVals, value) >= 0)
242: || (Arrays.binarySearch(tps, key) >= 0 && value
243: .matches("\\d+"))) {
244: addPair(key, value, sb);
245: }
246: }
247: }
248: return sb;
249: }
250:
251: private static StringBuffer addPair(String key, String value,
252: StringBuffer sb) {
253: return sb.append(" ").append(key).append("=\"").append(value)
254: .append("\"");
255: }
256:
257: /** This method processes ordered lists.
258: */
259: private String orderedList(String result) {
260: if (!nolist) { //lists only get processed if not forbidden (see code for [= and <pre>). [MN]
261: int p0 = 0;
262: int p1 = 0;
263: //# sorted Lists contributed by [AS]
264: //## Sublist
265: if (result.startsWith(numListLevel + "#")) { //more #
266: p0 = result.indexOf(numListLevel);
267: p1 = result.length();
268: result = "<ol>"
269: + serverCore.CRLF_STRING
270: + "<li>"
271: + result.substring(numListLevel.length() + 1,
272: p1) + "</li>";
273: numListLevel += "#";
274: } else if (numListLevel.length() > 0
275: && result.startsWith(numListLevel)) { //equal number of #
276: p0 = result.indexOf(numListLevel);
277: p1 = result.length();
278: result = "<li>"
279: + result.substring(numListLevel.length(), p1)
280: + "</li>";
281: } else if (numListLevel.length() > 0) { //less #
282: int i = numListLevel.length();
283: String tmp = "";
284:
285: while (!result.startsWith(numListLevel.substring(0, i))) {
286: tmp += "</ol>";
287: i--;
288: }
289: numListLevel = numListLevel.substring(0, i);
290: p0 = numListLevel.length();
291: p1 = result.length();
292:
293: if (numListLevel.length() > 0) {
294: result = tmp + "<li>" + result.substring(p0, p1)
295: + "</li>";
296: } else {
297: result = tmp + result.substring(p0, p1);
298: }
299: }
300: // end contrib [AS]
301: }
302: return result;
303: }
304:
305: /** This method processes unordered lists.
306: */
307: //contributed by [AS] put into it's own method by [MN]
308: private String unorderedList(String result) {
309: if (!nolist) { //lists only get processed if not forbidden (see code for [= and <pre>). [MN]
310: int p0 = 0;
311: int p1 = 0;
312: //contributed by [AS]
313: if (result.startsWith(ListLevel + "*")) { //more stars
314: p0 = result.indexOf(ListLevel);
315: p1 = result.length();
316: result = "<ul>" + serverCore.CRLF_STRING + "<li>"
317: + result.substring(ListLevel.length() + 1, p1)
318: + "</li>";
319: ListLevel += "*";
320: } else if (ListLevel.length() > 0
321: && result.startsWith(ListLevel)) { //equal number of stars
322: p0 = result.indexOf(ListLevel);
323: p1 = result.length();
324: result = "<li>"
325: + result.substring(ListLevel.length(), p1)
326: + "</li>";
327: } else if (ListLevel.length() > 0) { //less stars
328: int i = ListLevel.length();
329: String tmp = "";
330:
331: while (!result.startsWith(ListLevel.substring(0, i))) {
332: tmp += "</ul>";
333: i--;
334: }
335: ListLevel = ListLevel.substring(0, i);
336: p0 = ListLevel.length();
337: p1 = result.length();
338:
339: if (ListLevel.length() > 0) {
340: result = tmp + "<li>" + result.substring(p0, p1)
341: + "</li>";
342: } else {
343: result = tmp + result.substring(p0, p1);
344: }
345: }
346: //end contrib [AS]
347: }
348: return result;
349: }
350:
351: /** This method processes definition lists.
352: */
353: //contributed by [MN] based on unordered list code by [AS]
354: private String definitionList(String result) {
355: if (!nolist) { //lists only get processed if not forbidden (see code for [= and <pre>). [MN]
356: int p0 = 0;
357: int p1 = 0;
358: if (result.startsWith(defListLevel + ";")) { //more semicolons
359: String dt = "";
360: String dd = "";
361: p0 = result.indexOf(defListLevel);
362: p1 = result.length();
363: String resultCopy = result.substring(defListLevel
364: .length() + 1, p1);
365: if ((p0 = resultCopy.indexOf(":")) > 0) {
366: dt = resultCopy.substring(0, p0);
367: dd = resultCopy.substring(p0 + 1);
368: result = "<dl>" + "<dt>" + dt + "</dt>" + "<dd>"
369: + dd;
370: defList = true;
371: }
372: defListLevel += ";";
373: } else if (defListLevel.length() > 0
374: && result.startsWith(defListLevel)) { //equal number of semicolons
375: String dt = "";
376: String dd = "";
377: p0 = result.indexOf(defListLevel);
378: p1 = result.length();
379: String resultCopy = result.substring(defListLevel
380: .length(), p1);
381: if ((p0 = resultCopy.indexOf(":")) > 0) {
382: dt = resultCopy.substring(0, p0);
383: dd = resultCopy.substring(p0 + 1);
384: result = "<dt>" + dt + "</dt>" + "<dd>" + dd;
385: defList = true;
386: }
387: } else if (defListLevel.length() > 0) { //less semicolons
388: String dt = "";
389: String dd = "";
390: int i = defListLevel.length();
391: String tmp = "";
392: while (!result.startsWith(defListLevel.substring(0, i))) {
393: tmp += "</dd></dl>";
394: i--;
395: }
396: defListLevel = defListLevel.substring(0, i);
397: p0 = defListLevel.length();
398: p1 = result.length();
399: if (defListLevel.length() > 0) {
400: String resultCopy = result.substring(p0, p1);
401: if ((p0 = resultCopy.indexOf(":")) > 0) {
402: dt = resultCopy.substring(0, p0);
403: dd = resultCopy.substring(p0 + 1);
404: result = tmp + "<dt>" + dt + "</dt>" + "<dd>"
405: + dd;
406: defList = true;
407: }
408: } else {
409: result = tmp + result.substring(p0, p1);
410: }
411: }
412: }
413: return result;
414: }
415:
416: /** This method processes links and images.
417: */
418: //contributed by [AS] except where stated otherwise
419: private String linksAndImages(String result, String publicAddress,
420: plasmaSwitchboard switchboard) {
421:
422: // create links
423: String kl, kv, alt, align;
424: int p;
425: int p0 = 0;
426: int p1 = 0;
427: // internal links and images
428: while ((p0 = result.indexOf("[[")) >= 0) {
429: p1 = result.indexOf("]]", p0 + 2);
430: if (p1 <= p0)
431: break;
432: kl = result.substring(p0 + 2, p1);
433:
434: // this is the part of the code that's responsible for images
435: // contributed by [MN]
436: if (kl.startsWith("Image:")) {
437: alt = "";
438: align = "";
439: kv = "";
440: kl = kl.substring(6);
441:
442: // are there any arguments for the image?
443: if ((p = kl.indexOf("|")) > 0) {
444: kv = kl.substring(p + 6);
445: kl = kl.substring(0, p);
446: // if there are 2 arguments, write them into ALIGN and ALT
447: if ((p = kv.indexOf("|")) > 0) {
448: align = kv.substring(0, p);
449: //checking validity of value for align. Only non browser specific
450: //values get supported. Not supported: absmiddle, baseline, texttop
451: if ((align.equals("bottom"))
452: || (align.equals("center"))
453: || (align.equals("left"))
454: || (align.equals("middle"))
455: || (align.equals("right"))
456: || (align.equals("top"))) {
457: align = " align=\"" + align + "\"";
458: } else
459: align = "";
460: alt = " alt=\"" + kv.substring(p + 6) + "\"";
461: }
462: // if there is just one, put it into ALT
463: else
464: alt = " alt=\"" + kv + "\"";
465: }
466:
467: // replace incomplete URLs and make them point to http://peerip:port/...
468: // with this feature you can access an image in DATA/HTDOCS/share/yacy.gif
469: // using the wikicode [[Image:share/yacy.gif]]
470: // or an image DATA/HTDOCS/grafics/kaskelix.jpg with [[Image:grafics/kaskelix.jpg]]
471: // you are free to use other sub-paths of DATA/HTDOCS
472: if (kl.indexOf("://") < 1) {
473: kl = "http://" + publicAddress.trim() + "/" + kl;
474: }
475:
476: result = result.substring(0, p0) + "<img src=\"" + kl
477: + "\"" + align + alt + ">"
478: + result.substring(p1 + 2);
479: }
480: // end contrib [MN]
481:
482: // if it's no image, it might be an internal link
483: else {
484: if ((p = kl.indexOf("|")) > 0) {
485: kv = kl.substring(p + 6);
486: kl = kl.substring(0, p);
487: } else {
488: kv = kl;
489: }
490: if (switchboard != null
491: && switchboard.wikiDB.read(kl) != null) {
492: result = result.substring(0, p0)
493: + "<a class=\"known\" href=\"Wiki.html?page="
494: + kl + "\">" + kv + "</a>"
495: + result.substring(p1 + 2);
496: } else {
497: result = result.substring(0, p0)
498: + "<a class=\"unknown\" href=\"Wiki.html?page="
499: + kl + "&edit=Edit\">" + kv + "</a>"
500: + result.substring(p1 + 2);
501: }
502: }
503: }
504:
505: // external links
506: while ((p0 = result.indexOf("[")) >= 0) {
507: p1 = result.indexOf("]", p0 + 1);
508: if (p1 <= p0)
509: break;
510: kl = result.substring(p0 + 1, p1);
511: if ((p = kl.indexOf(" ")) > 0) {
512: kv = kl.substring(p + 1);
513: kl = kl.substring(0, p);
514: }
515: // No text for the link? -> <a href="http://www.url.com/">http://www.url.com/</a>
516: else {
517: kv = kl;
518: }
519: // replace incomplete URLs and make them point to http://peerip:port/...
520: // with this feature you can access a file at DATA/HTDOCS/share/page.html
521: // using the wikicode [share/page.html]
522: // or a file DATA/HTDOCS/www/page.html with [www/page.html]
523: // you are free to use other sub-paths of DATA/HTDOCS
524: if (kl.indexOf("://") < 1) {
525: kl = "http://" + publicAddress.trim() + "/" + kl;
526: }
527: result = result.substring(0, p0)
528: + "<a class=\"extern\" href=\"" + kl + "\">" + kv
529: + "</a>" + result.substring(p1 + 1);
530: }
531: return result;
532: }
533:
534: /** This method handles the escape tags [= =] */
535: //contributed by [MN]
536: private String escapeTag(String result, String publicAddress,
537: plasmaSwitchboard switchboard) {
538: int p0 = 0;
539: int p1 = 0;
540: //both [= and =] in the same line
541: if (((p0 = result.indexOf("[=")) >= 0)
542: && ((p1 = result.indexOf("=]")) > 0)
543: && (!(preformatted))) {
544: if (p0 < p1) {
545: String escapeText = result.substring(p0 + 2, p1);
546: escapeText = escapeText.replaceAll("!esc!", "!esc!!");
547: result = transformLine(result.substring(0, p0)
548: .replaceAll("!esc!", "!esc!!")
549: + "!esc!txt!"
550: + result.substring(p1 + 2).replaceAll("!esc!",
551: "!esc!!"), publicAddress, switchboard);
552: result = result.replaceAll("!esc!txt!", escapeText);
553: result = result.replaceAll("!esc!!", "!esc!");
554: }
555: //handles cases like [=[= =]=] [= =] that would cause an exception otherwise
556: else {
557: escape = true;
558: String temp1 = transformLine(result
559: .substring(0, p0 - 1).replaceAll("!tmp!",
560: "!tmp!!")
561: + "!tmp!txt!", publicAddress, switchboard);
562: nolist = true;
563: String temp2 = transformLine(result.substring(p0),
564: publicAddress, switchboard);
565: nolist = false;
566: result = temp1.replaceAll("!tmp!txt!", temp2);
567: result = result.replaceAll("!tmp!!", "!tmp!");
568: escape = false;
569: }
570: }
571:
572: //start [=
573: else if (((p0 = result.indexOf("[=")) >= 0) && (!escapeSpan)
574: && (!preformatted)) {
575: escape = true; //prevent surplus line breaks
576: escaped = true; //prevents <pre> being parsed
577: String bq = ""; //gets filled with <blockquote>s as needed
578: String escapeText = result.substring(p0 + 2);
579: escapeText = escapeText.replaceAll("!esc!", "!esc!!");
580: //taking care of indented lines
581: while (result.substring(escindented, p0).startsWith(":")) {
582: escindented++;
583: bq = bq + "<blockquote>";
584: }
585: result = transformLine(result.substring(escindented, p0)
586: .replaceAll("!esc!", "!esc!!")
587: + "!esc!txt!", publicAddress, switchboard);
588: result = bq + result.replaceAll("!esc!txt!", escapeText);
589: result = result.replaceAll("!esc!!", "!esc!");
590: escape = false;
591: escapeSpan = true;
592: }
593:
594: //end =]
595: else if (((p0 = result.indexOf("=]")) >= 0) && (escapeSpan)
596: && (!preformatted)) {
597: escapeSpan = false;
598: String bq = ""; //gets filled with </blockquote>s as needed
599: String escapeText = result.substring(0, p0);
600: escapeText = escapeText.replaceAll("!esc!", "!esc!!");
601: //taking care of indented lines
602: while (escindented > 0) {
603: bq = bq + "</blockquote>";
604: escindented--;
605: }
606: result = transformLine("!esc!txt!"
607: + result.substring(p0 + 2).replaceAll("!esc!",
608: "!esc!!"), publicAddress, switchboard);
609: result = result.replaceAll("!esc!txt!", escapeText) + bq;
610: result = result.replaceAll("!esc!!", "!esc!");
611: escaped = false;
612: }
613: //Getting rid of surplus =]
614: else if (((p0 = result.indexOf("=]")) >= 0) && (!escapeSpan)
615: && (!preformatted)) {
616: while ((p0 = result.indexOf("=]")) >= 0) {
617: result = result.substring(0, p0)
618: + result.substring(p0 + 2);
619: }
620: result = transformLine(result, publicAddress, switchboard);
621: }
622: return result;
623: }
624:
625: /** This method handles the preformatted tags <pre> </pre> */
626: //contributed by [MN]
627: private String preformattedTag(String result, String publicAddress,
628: plasmaSwitchboard switchboard) {
629: int p0 = 0;
630: int p1 = 0;
631: //implementation very similar to escape code (see above)
632: //both <pre> and </pre> in the same line
633: if (((p0 = result.indexOf("<pre>")) >= 0)
634: && ((p1 = result.indexOf("</pre>")) > 0)
635: && (!(escaped))) {
636: if (p0 < p1) {
637: String preformattedText = "<pre style=\"border:dotted;border-width:thin\">"
638: + result.substring(p0 + 11, p1) + "</pre>";
639: preformattedText = preformattedText.replaceAll("!pre!",
640: "!pre!!");
641: result = transformLine(result.substring(0, p0)
642: .replaceAll("!pre!", "!pre!!")
643: + "!pre!txt!"
644: + result.substring(p1 + 12).replaceAll("!pre!",
645: "!pre!!"), publicAddress, switchboard);
646: result = result.replaceAll("!pre!txt!",
647: preformattedText);
648: result = result.replaceAll("!pre!!", "!pre!");
649: }
650: //handles cases like <pre><pre> </pre></pre> <pre> </pre> that would cause an exception otherwise
651: else {
652: preformatted = true;
653: String temp1 = transformLine(result
654: .substring(0, p0 - 1).replaceAll("!tmp!",
655: "!tmp!!")
656: + "!tmp!txt!", publicAddress, switchboard);
657: nolist = true;
658: String temp2 = transformLine(result.substring(p0),
659: publicAddress, switchboard);
660: nolist = false;
661: result = temp1.replaceAll("!tmp!txt!", temp2);
662: result = result.replaceAll("!tmp!!", "!tmp!");
663: preformatted = false;
664: }
665: }
666:
667: //start <pre>
668: else if (((p0 = result.indexOf("<pre>")) >= 0)
669: && (!preformattedSpan) && (!escaped)) {
670: preformatted = true; //prevent surplus line breaks
671: String bq = ""; //gets filled with <blockquote>s as needed
672: String preformattedText = "<pre style=\"border:dotted;border-width:thin\">"
673: + result.substring(p0 + 11);
674: preformattedText = preformattedText.replaceAll("!pre!",
675: "!pre!!");
676: //taking care of indented lines
677: while (result.substring(preindented, p0).startsWith(":")) {
678: preindented++;
679: bq = bq + "<blockquote>";
680: }
681: result = transformLine(result.substring(preindented, p0)
682: .replaceAll("!pre!", "!pre!!")
683: + "!pre!txt!", publicAddress, switchboard);
684: result = bq
685: + result.replaceAll("!pre!txt!", preformattedText);
686: result = result.replaceAll("!pre!!", "!pre!");
687: preformattedSpan = true;
688: }
689:
690: //end </pre>
691: else if (((p0 = result.indexOf("</pre>")) >= 0)
692: && (preformattedSpan) && (!escaped)) {
693: preformattedSpan = false;
694: String bq = ""; //gets filled with </blockquote>s as needed
695: String preformattedText = result.substring(0, p0)
696: + "</pre>";
697: preformattedText = preformattedText.replaceAll("!pre!",
698: "!pre!!");
699: //taking care of indented lines
700: while (preindented > 0) {
701: bq = bq + "</blockquote>";
702: preindented--;
703: }
704: result = transformLine("!pre!txt!"
705: + result.substring(p0 + 12).replaceAll("!pre!",
706: "!pre!!"), publicAddress, switchboard);
707: result = result.replaceAll("!pre!txt!", preformattedText)
708: + bq;
709: result = result.replaceAll("!pre!!", "!pre!");
710: preformatted = false;
711: }
712: //Getting rid of surplus </pre>
713: else if (((p0 = result.indexOf("</pre>")) >= 0)
714: && (!preformattedSpan) && (!escaped)) {
715: while ((p0 = result.indexOf("</pre>")) >= 0) {
716: result = result.substring(0, p0)
717: + result.substring(p0 + 12);
718: }
719: result = transformLine(result, publicAddress, switchboard);
720: }
721: return result;
722: }
723:
724: /** This method creates a directory for a wiki page.
725: * @return directory of the wiki
726: */
727: //method contributed by [MN]
728: private String directory() {
729: String directory = "";
730: String element;
731: int s = 0;
732: int level = 1;
733: int level1 = 0;
734: int level2 = 0;
735: int level3 = 0;
736: int doubles = 0;
737: String anchorext = "";
738: if ((s = dirElements.size()) > 2) {
739: for (int i = 0; i < s; i++) {
740: element = dirElements.get(i).toString();
741: //counting double headlines
742: doubles = 0;
743: for (int j = 0; j < i; j++) {
744: if (dirElements.get(j).toString().substring(1)
745: .replaceAll(" ", "_").replaceAll(
746: "[^a-zA-Z0-9_]", "").equals(
747: element.substring(1).replaceAll(
748: " ", "_").replaceAll(
749: "[^a-zA-Z0-9_]", ""))) {
750: doubles++;
751: }
752: }
753: //if there are doubles, create anchorextension
754: if (doubles > 0) {
755: anchorext = "_" + (doubles + 1);
756: }
757:
758: if (element.startsWith("3")) {
759: if (level < 3) {
760: level = 3;
761: level3 = 0;
762: }
763: level3++;
764: String temp = element.substring(1);
765: element = level1 + "." + level2 + "." + level3
766: + " " + temp;
767: directory = directory
768: + " <a href=\"#"
769: + temp.replaceAll(" ", "_").replaceAll(
770: "[^a-zA-Z0-9_]", "") + anchorext
771: + "\" class=\"WikiTOC\">" + element
772: + "</a><br />\n";
773: } else if (element.startsWith("2")) {
774: if (level == 1) {
775: level2 = 0;
776: level = 2;
777: }
778: if (level == 3) {
779: level = 2;
780: }
781: level2++;
782: String temp = element.substring(1);
783: element = level1 + "." + level2 + " " + temp;
784: directory = directory
785: + " <a href=\"#"
786: + temp.replaceAll(" ", "_").replaceAll(
787: "[^a-zA-Z0-9_]", "") + anchorext
788: + "\" class=\"WikiTOC\">" + element
789: + "</a><br />\n";
790: } else if (element.startsWith("1")) {
791: if (level > 1) {
792: level = 1;
793: level2 = 0;
794: level3 = 0;
795: }
796: level1++;
797: String temp = element.substring(1);
798: element = level1 + ". " + temp;
799: directory = directory
800: + "<a href=\"#"
801: + temp.replaceAll(" ", "_").replaceAll(
802: "[^a-zA-Z0-9_]", "") + anchorext
803: + "\" class=\"WikiTOC\">" + element
804: + "</a><br />\n";
805: }
806: anchorext = "";
807: }
808: directory = "<table><tr><td><div class=\"WikiTOCBox\">\n"
809: + directory + "</div></td></tr></table>\n";
810: }
811: // [MN]
812: if (!dirElements.isEmpty()) {
813: dirElements.clear();
814: headlines = 0;
815: }
816: return directory;
817: }
818:
819: /** Replaces two occurences of a substring in a string by a pair of strings if
820: * that substring occurs twice in the string. This method is not greedy! You'll
821: * have to run it in a loop if you want to replace all occurences of the substring.
822: * This method provides special treatment for headlines.
823: * @param input the string that something is to be replaced in
824: * @param pat substring to be replaced
825: * @param repl1 string substring gets replaced by on uneven occurences
826: * @param repl2 string substring gets replaced by on even occurences
827: */
828: //[MN]
829: private String pairReplace(String input, String pat, String repl1,
830: String repl2) {
831: String direlem = ""; //string to keep headlines until they get added to List dirElements
832: int p0 = 0;
833: int p1 = 0;
834: int l = pat.length();
835: //replace pattern if a pair of the pattern can be found in the line
836: if (((p0 = input.indexOf(pat)) >= 0)
837: && ((p1 = input.indexOf(pat, p0 + l)) >= 0)) {
838: //extra treatment for headlines
839: if ((pat.equals("====")) || (pat.equals("==="))
840: || (pat.equals("=="))) {
841: //add anchor and create headline
842: direlem = input.substring(p0 + l, p1);
843: //counting double headlines
844: int doubles = 0;
845: for (int i = 0; i < headlines; i++) {
846: if (dirElements.get(i).toString().substring(1)
847: .equals(direlem)) {
848: doubles++;
849: }
850: }
851: String anchor = direlem.replaceAll(" ", "_")
852: .replaceAll("[^a-zA-Z0-9_]", ""); //replace blanks with underscores and delete everything thats not a regular character, a number or _
853: //if there are doubles, add underscore and number of doubles plus one
854: if (doubles > 0) {
855: anchor = anchor + "_" + (doubles + 1);
856: }
857: input = input.substring(0, p0) + "<a name=\"" + anchor
858: + "\"></a>" + repl1 + direlem + repl2
859: + input.substring(p1 + l);
860: //add headlines to list of headlines (so TOC can be created)
861: if (pat.equals("===="))
862: dirElements.add("3" + direlem);
863: else if (pat.equals("==="))
864: dirElements.add("2" + direlem);
865: else if (pat.equals("=="))
866: dirElements.add("1" + direlem);
867: headlines++;
868: } else {
869: input = input.substring(0, p0) + repl1
870: + (direlem = input.substring(p0 + l, p1))
871: + repl2 + input.substring(p1 + l);
872: }
873: }
874: //recursion if a pair of the pattern can still be found in the line
875: if (((p0 = input.indexOf(pat)) >= 0)
876: && (input.indexOf(pat, p0 + l) >= 0)) {
877: input = pairReplace(input, pat, repl1, repl2);
878: }
879: return input;
880: }
881:
882: /** Replaces wiki tags with HTML tags.
883: * @param result a line of text
884: * @param switchboard
885: * @return the line of text with HTML tags instead of wiki tags
886: */
887: public String transformLine(String result, String publicAddress,
888: plasmaSwitchboard switchboard) {
889: //If HTML has not bee replaced yet (can happen if method gets called in recursion), replace now!
890: if (!replacedHTML || preformattedSpan) {
891: result = htmlTools.encodeUnicode2html(result, true);
892: replacedHTML = true;
893: }
894:
895: //check if line contains escape symbols([= =]) or if we are in an escape sequence already.
896: if ((result.indexOf("[=") >= 0) || (result.indexOf("=]") >= 0)
897: || (escapeSpan)) {
898: result = escapeTag(result, publicAddress, switchboard);
899: }
900:
901: //check if line contains preformatted symbols or if we are in a preformatted sequence already.
902: else if ((result.indexOf("<pre>") >= 0)
903: || (result.indexOf("</pre>") >= 0)
904: || (preformattedSpan)) {
905: result = preformattedTag(result, publicAddress, switchboard);
906: }
907:
908: //transform page as usual
909: else {
910:
911: //tables first -> wiki-tags in cells can be treated after that
912: result = processTable(result, switchboard);
913:
914: // format lines
915: if (result.startsWith(" "))
916: result = "<tt>" + result.substring(1) + "</tt>";
917: if (result.startsWith("----"))
918: result = "<hr />";
919:
920: // citings contributed by [MN]
921: if (result.startsWith(":")) {
922: String head = "";
923: String tail = "";
924: while (result.startsWith(":")) {
925: head = head + "<blockquote>";
926: tail = tail + "</blockquote>";
927: result = result.substring(1);
928: }
929: result = head + result + tail;
930: }
931: // end contrib [MN]
932:
933: // format headers
934: result = pairReplace(result, "====", "<h4>", "</h4>");
935: result = pairReplace(result, "===", "<h3>", "</h3>");
936: result = pairReplace(result, "==", "<h2>", "</h2>");
937:
938: result = pairReplace(result, "'''''", "<b><i>", "</i></b>");
939: result = pairReplace(result, "'''", "<b>", "</b>");
940: result = pairReplace(result, "''", "<i>", "</i>");
941:
942: result = unorderedList(result);
943: result = orderedList(result);
944: result = definitionList(result);
945:
946: result = linksAndImages(result, publicAddress, switchboard);
947:
948: }
949:
950: if (!preformatted)
951: replacedHTML = false;
952: if ((result.endsWith("</li>")) || (defList) || (escape)
953: || (preformatted) || (table) || (cellprocessing))
954: return result;
955: return result + "<br />";
956: }
957: }
|