View Javadoc

1   /*
2    * Copyright 2004-2006 the Seasar Foundation and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.seasar.tuigwaa.cms.core.wiki.base;
17  
18  import java.net.URL;
19  import java.util.Iterator;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.seasar.tuigwaa.cms.core.CmsRequest;
24  import org.seasar.tuigwaa.cms.core.CmsResponse;
25  import org.seasar.tuigwaa.cms.core.Resource;
26  import org.seasar.tuigwaa.cms.core.wiki.WikiContext;
27  import org.seasar.tuigwaa.cms.core.wiki.engine.SimpleNode;
28  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiAlias;
29  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiAlign;
30  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiAnnotation;
31  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiAnyOther;
32  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiArgs;
33  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiBlockPlugin;
34  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiCSVTable;
35  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiDefineList;
36  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiDefinedWord;
37  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiDeleteline;
38  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiErrors;
39  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiExcerpt;
40  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiExplanationWord;
41  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiFloatAlign;
42  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiGenerateTree;
43  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiHeading;
44  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiHorizontalline;
45  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiInlinePlugin;
46  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiInterwiki;
47  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiLetters;
48  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiLink;
49  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiList;
50  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiListMember;
51  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiPagename;
52  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiParagraph;
53  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiParserVisitor;
54  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiPreshaped;
55  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiSkipToNewline;
56  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiStrongItalic;
57  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiSyntaxError;
58  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiTable;
59  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiTablecolumn;
60  import org.seasar.tuigwaa.cms.core.wiki.engine.WikiTablemember;
61  import org.seasar.tuigwaa.plugin.Plugin;
62  import org.seasar.tuigwaa.plugin.PluginException;
63  import org.seasar.tuigwaa.plugin.PluginRequest;
64  import org.seasar.tuigwaa.system.TgwSecurityException;
65  
66  import com.isenshi.util.TextStringBuffer;
67  
68  /***
69   * @author someda
70   */
71  public class TextWikiVisitor implements WikiParserVisitor {
72  
73  	private static final int LINESIZE = 80;
74  
75  	TextStringBuffer buf;
76  
77  	private CmsRequest request;
78  
79  	private CmsResponse response;
80  
81  	private WikiConfiguration config;
82  
83  	private WikiContext ctx;
84  	
85  	private int[] listIndex = { 0, 0, 0 };
86  
87  	private int[] headerIndex = { 0, 0, 0 };
88  
89  	private static final String INDENT = " ";
90  
91  	private Log log = LogFactory.getLog(getClass());
92  
93  	public TextWikiVisitor(CmsRequest request, CmsResponse response,
94  			WikiConfiguration config) {
95  		this.request = request;
96  		this.response = response;
97  		this.config = config;
98  		this.ctx = config.getWikiContext();
99  		buf = new TextStringBuffer(LINESIZE);
100 	}
101 
102 	public Object visit(SimpleNode node, Object data) {
103 		return null;
104 	}
105 
106 	public Object visit(WikiSyntaxError node, Object data) {
107 		appendError(node.letter);
108 		return null;
109 	}
110 
111 	public Object visit(WikiSkipToNewline node, Object data) {
112 		buf.append(node.letter);
113 		return null;
114 	}
115 
116 	public Object visit(WikiGenerateTree node, Object data) {
117 		for (int i = 0; i < node.jjtGetNumChildren(); i++)
118 			node.jjtGetChild(i).jjtAccept(this, data);
119 
120 		if (node.annotation.size() > 0) {
121 			buf.appendNewline();
122 			buf.appendRepeatString("-", LINESIZE / 3);
123 
124 			Iterator itr = node.annotation.iterator();
125 			int idx = 1;
126 			while (itr.hasNext()) {
127 				buf.appendNewline();
128 				buf.append("(*" + idx + ")");
129 				SimpleNode n = (SimpleNode) itr.next();
130 				for (int i = 0; i < n.jjtGetNumChildren(); i++) {
131 					n.jjtGetChild(i).jjtAccept(this, data);
132 				}
133 				idx++;
134 			}
135 		}
136 		return buf.toString();
137 	}
138 
139 	public Object visit(WikiParagraph node, Object data) {
140 
141 		buf.appendNewline();
142 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
143 			node.jjtGetChild(i).jjtAccept(this, data);
144 		}
145 		return null;
146 	}
147 
148 	public Object visit(WikiExcerpt node, Object data) {
149 
150 		boolean isTagNeed = VisitorUtils.isExcerptStartNeeded(node);
151 
152 		if (isTagNeed) {
153 			String current = buf.getNewlineHeading();
154 			buf.setNewlineHeading(">" + current);
155 		}
156 		buf.appendNewline();
157 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
158 			if (node.jjtGetChild(i) instanceof WikiParagraph) {
159 				WikiParagraph p = (WikiParagraph) node.jjtGetChild(i);
160 				for (int j = 0; j < p.jjtGetNumChildren(); j++)
161 					p.jjtGetChild(j).jjtAccept(this, data);
162 			} else {
163 				node.jjtGetChild(i).jjtAccept(this, data);
164 			}
165 		}
166 
167 		buf.setNewlineHeading("");
168 		if (node.level == 1 && isTagNeed) {
169 			buf.appendNewline();
170 		}
171 
172 		return null;
173 	}
174 
175 	public Object visit(WikiList node, Object data) {
176 
177 		buf.appendNewline();
178 
179 		int curtype = 0;
180 		int curlevel = 0;
181 		int pretype = 0;
182 		int prelevel = 0;
183 		int[] level = { 0, 0, 0 };
184 
185 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
186 
187 			if (node.jjtGetChild(i) instanceof WikiListMember) {
188 				WikiListMember child = (WikiListMember) node.jjtGetChild(i);
189 
190 				curtype = child.type;
191 				curlevel = child.level;
192 
193 				// upward level change
194 				if (curlevel > prelevel)
195 					startList(curtype, curlevel);
196 
197 				// downward level change
198 				if (curlevel < prelevel) {
199 					for (int j = curlevel; j < prelevel; j++) {
200 						if (level[j] != 0) {
201 							endList(pretype, prelevel);
202 							level[j] = 0;
203 						}
204 					}
205 					if (level[curlevel - 1] != curtype)
206 						startList(curtype, curlevel);
207 				}
208 
209 				// not level change but type change
210 				if (curlevel == prelevel && curtype != pretype) {
211 					endList(pretype, prelevel);
212 					startList(curtype, curlevel);
213 				}
214 
215 				// change state
216 				if (curlevel != prelevel || curtype != pretype) {
217 					level[curlevel - 1] = curtype;
218 					pretype = curtype;
219 					prelevel = curlevel;
220 				}
221 				child.jjtAccept(this, data);
222 			}
223 		}
224 
225 		endAllList();
226 		return null;
227 	}
228 
229 	public Object visit(WikiListMember node, Object data) {
230 
231 		buf.setNewlineHeading(getRepeatedString(INDENT, node.level * 2));
232 		buf.appendNewline();
233 		if (node.type == WikiHelper.LIST_TYPE_NUMERICAL) {
234 			for (int i = 0; i < node.level - 1; i++) {
235 				if (listIndex[i] != 0)
236 					buf.append(listIndex[i] + ".");
237 			}
238 			buf.append((++listIndex[node.level - 1]) + ".");
239 		} else {
240 			buf.append("-");
241 		}
242 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
243 			node.jjtGetChild(i).jjtAccept(this, data);
244 		}
245 		return null;
246 	}
247 
248 	public Object visit(WikiDefineList node, Object data) {
249 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
250 			node.jjtGetChild(i).jjtAccept(this, data);
251 		}
252 		return null;
253 	}
254 
255 	public Object visit(WikiDefinedWord node, Object data) {
256 		if (node.jjtGetNumChildren() > 0) {
257 			buf.appendNewline();
258 			buf.append("[[");
259 			for (int i = 0; i < node.jjtGetNumChildren(); i++) {
260 				node.jjtGetChild(i).jjtAccept(this, data);
261 			}
262 			buf.append("]]");
263 		}
264 		return null;
265 	}
266 
267 	public Object visit(WikiExplanationWord node, Object data) {
268 		buf.setNewlineHeading("\t");
269 		buf.appendNewline();
270 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
271 			node.jjtGetChild(i).jjtAccept(this, data);
272 		}
273 		buf.setNewlineHeading("");
274 		return null;
275 	}
276 
277 	public Object visit(WikiPreshaped node, Object data) {
278 
279 		buf.appendNewline();
280 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
281 			buf.append("\t");
282 			node.jjtGetChild(i).jjtAccept(this, data);
283 			buf.appendNewline();
284 		}
285 		return null;
286 	}
287 
288 	public Object visit(WikiTable node, Object data) {
289 		processTable(node, data);
290 		return null;
291 	}
292 
293 	public Object visit(WikiTablecolumn node, Object data) {
294 
295 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
296 			node.jjtGetChild(i).jjtAccept(this, data);
297 		}
298 		buf.append("|");
299 		return null;
300 	}
301 
302 	public Object visit(WikiTablemember node, Object data) {
303 		buf.appendNewline();
304 		buf.append("|");
305 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
306 			node.jjtGetChild(i).jjtAccept(this, data);
307 		}
308 		return null;
309 	}
310 
311 	public Object visit(WikiCSVTable node, Object data) {
312 		processTable(node, data);
313 		return null;
314 	}
315 
316 	public Object visit(WikiHeading node, Object data) {
317 
318 		buf.appendNewline();
319 
320 		buf.append("(");
321 		for (int i = 0; i < node.level - 1; i++) {
322 			buf.append(headerIndex[i] + "-");
323 		}
324 		buf.append((++headerIndex[node.level - 1]) + ")");
325 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
326 			node.jjtGetChild(i).jjtAccept(this, data);
327 		}
328 
329 		for (int i = node.level; i < headerIndex.length; i++) {
330 			headerIndex[i] = 0;
331 		}
332 		return null;
333 	}
334 
335 	public Object visit(WikiAlign node, Object data) {
336 		buf.appendNewline();
337 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
338 			node.jjtGetChild(i).jjtAccept(this, data);
339 		}
340 		return null;
341 	}
342 
343 	// do-nothing
344 	public Object visit(WikiFloatAlign node, Object data) {
345 		buf.appendNewline();
346 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
347 			node.jjtGetChild(i).jjtAccept(this, data);
348 		}
349 		return null;
350 	}
351 
352 	public Object visit(WikiHorizontalline node, Object data) {
353 		buf.appendNewline();
354 		buf.appendRepeatString("-", LINESIZE / 2);
355 		return null;
356 	}
357 
358 	// block plugin not executed for TextVisitor
359 	public Object visit(WikiBlockPlugin node, Object data) {
360 		buf.appendNewline();
361 		PluginRequest prequest = VisitorUtils.createPluginRequest(node);
362 		buf.append("#" + prequest.toString());
363 		return null;
364 	}
365 
366 	public Object visit(WikiLetters node, Object data) {
367 
368 		if (node.isEmail) {
369 			buf.append(node.letter);
370 		} else if (node.isURL) {
371 			buf.append(node.letter);
372 		} else if (node.isAnchor) {
373 			// do-nothing
374 		} else if (node.isWikiname) {
375 			processSecurityLink(node.letter, node.letter, null);
376 		} else if (node.isNewline) {
377 			buf.appendNewline();
378 		} else {
379 			buf.append(node.letter);
380 		}
381 		return null;
382 	}
383 
384 	public Object visit(WikiAnyOther node, Object data) {
385 		buf.append(node.letter);
386 		return null;
387 	}
388 
389 	public Object visit(WikiStrongItalic node, Object data) {
390 		buf.append("''");
391 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
392 			node.jjtGetChild(i).jjtAccept(this, data);
393 		}
394 		buf.append("''");
395 		return null;
396 	}
397 
398 	public Object visit(WikiDeleteline node, Object data) {
399 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
400 			node.jjtGetChild(i).jjtAccept(this, data);
401 		}
402 		return null;
403 	}
404 
405 	public Object visit(WikiAnnotation node, Object data) {
406 		buf.append("(*" + node.num + ")");
407 		return null;
408 	}
409 
410 	public Object visit(WikiInterwiki node, Object data) {
411 		buf.append("[[" + node.image + "]]");
412 		return null;
413 	}
414 
415 	public Object visit(WikiLink node, Object data) {
416 
417 		String[] s = WikiHelper.split(node.image, WikiHelper.LINK_DELIMITER);
418 		buf.appendLink(s[0], s[1]);
419 		return null;
420 	}
421 
422 	public Object visit(WikiAlias node, Object data) {
423 		String[] s = WikiHelper.split(node.image, WikiHelper.ALIAS_DELIMITER);
424 
425 		if (node.islink) {
426 			buf.appendLink(s[0], s[1]);
427 		} else {
428 			String[] t = WikiHelper.split(s[1], WikiHelper.ANCHOR_MARK);
429 			if (t[0] == null || t[0].equals("")) {
430 				buf.appendLink(s[0], WikiHelper.ANCHOR_MARK + t[1]);
431 			} else {
432 				processSecurityLink(t[0], s[0], t[1]);
433 			}
434 		}
435 		return null;
436 	}
437 
438 	public Object visit(WikiPagename node, Object data) {
439 		String[] s = WikiHelper.split(node.image, WikiHelper.ANCHOR_MARK);
440 		processSecurityLink(s[0], s[0], s[1]);
441 		return null;
442 	}
443 
444 	public Object visit(WikiInlinePlugin node, Object data) {
445 
446 		String name = node.name;
447 
448 		boolean ispluginused = false;
449 		int inlinestart = 0;
450 		PluginRequest prequest = VisitorUtils.createPluginRequest(node);
451 		if (prequest.getArgs() != null)
452 			inlinestart = 1;
453 
454 		String childstr = getChildString(node, data, inlinestart);
455 		prequest.setChild(childstr);
456 
457 		if (config.isPluginLoaded(name)) {
458 			Plugin plugin = config.getPlugin(name);
459 			try {
460 				buf
461 						.append((String) plugin.service(request, response,
462 								prequest));
463 				ispluginused = true;
464 			} catch (PluginException pe) {
465 				log.error(pe.getMessage());
466 			} catch (Exception e) {
467 				e.printStackTrace();
468 				// some exception handling framework needed
469 			}
470 		}
471 
472 		if (!ispluginused) {
473 			buf.append("&" + prequest.toString() + ";");
474 		}
475 		return null;
476 	}
477 
478 	public Object visit(WikiArgs node, Object data) {
479 		return null;
480 	}
481 
482 	public Object visit(WikiErrors node, Object data) {
483 		appendError(node.letter);
484 		return null;
485 	}
486 
487 	private void appendError(String str) {
488 		buf.append(" *(" + str + ")* ");
489 	}
490 
491 	private void processSecurityLink(String pagename, String body, String anchor) {
492 		try {
493 			if (ctx.isPageExist(pagename, request)) {
494 				Resource resource = ctx.getResource(request, pagename);
495 				if (resource.isFolder()) {
496 					if (!pagename.endsWith("/"))
497 						pagename = pagename + "/";
498 				}
499 				URL url = ctx.getURLByName(pagename, request);
500 				if (anchor == null || "".equals(anchor)) {
501 					buf.appendLink(body, url.toString());
502 				} else {
503 					buf.appendLink(body, url.toString()
504 							+ WikiHelper.ANCHOR_MARK + anchor);
505 				}
506 			} else {
507 				URL url = ctx.getCreatePageURL(pagename, request);
508 				buf.appendLink("?" + body, url.toString());
509 			}
510 		} catch (TgwSecurityException tse) {
511 			buf.append(body);
512 		}
513 	}
514 
515 	private void startList(int type, int level) {
516 		// buf.appendNewline();
517 	}
518 
519 	private void endList(int type, int level) {
520 		if (type == WikiHelper.LIST_TYPE_NUMERICAL) {
521 			listIndex[level - 1] = 0;
522 		}
523 	}
524 
525 	private void endAllList() {
526 		for (int i = 0; i < listIndex.length; i++) {
527 			listIndex[i] = 0;
528 		}
529 		buf.setNewlineHeading("");
530 	}
531 
532 	private String getChildString(SimpleNode node, Object data, int idx) {
533 		int start = buf.nextIndex();
534 		String childstr = null;
535 		for (int i = idx; i < node.jjtGetNumChildren(); i++) {
536 			node.jjtGetChild(i).jjtAccept(this, data);
537 		}
538 		int end = buf.nextIndex();
539 		if (start != end)
540 			childstr = buf.cut(start, end);
541 		return childstr;
542 	}
543 
544 	private void processTable(SimpleNode node, Object data) {
545 
546 		VisitorUtils.prepareWikiTable(node, data);
547 		int prenum = 0;
548 		int start = buf.nextIndex();
549 		int end = buf.nextIndex();
550 		for (int i = 0; i < node.jjtGetNumChildren(); i++) {
551 			if (node.jjtGetChild(i) instanceof WikiTablemember) {
552 				if (node.jjtGetChild(i).jjtGetNumChildren() != prenum) {
553 					if (prenum != 0) {
554 						adjust(start, end);
555 					}
556 					start = buf.nextIndex();
557 
558 				}
559 			} else { // in case WikiError
560 				adjust(start, end);
561 				prenum = 0;
562 				continue;
563 			}
564 			prenum = node.jjtGetChild(i).jjtGetNumChildren();
565 			node.jjtGetChild(i).jjtAccept(this, data);
566 			end = buf.nextIndex();
567 		}
568 		adjust(start, end);
569 	}
570 
571 	private void adjust(int start, int end) {
572 
573 		if (end > start) {
574 			String str = buf.cut(start, end);
575 			String[] lines = str.split("\n");
576 			int maxlength = getMaxlength(lines);
577 			int[] maxcolsize = getMaxcolsize(lines);
578 
579 			String decorator = getRepeatedString("-", maxlength);
580 
581 			buf.appendNewline();
582 			buf.append(decorator);
583 			buf.appendNewline();
584 			for (int i = 0; i < lines.length; i++) {
585 				if (!lines[i].equals("")) {
586 					String[] cols = lines[i].split("//|");
587 					for (int j = 0; j < cols.length; j++) {
588 						String col = cols[j];
589 						buf.append(col);
590 						buf.appendRepeatString(" ", maxcolsize[j]
591 								- col.getBytes().length);
592 						buf.append("|");
593 					}
594 					buf.appendNewline();
595 					buf.append(decorator);
596 					buf.appendNewline();
597 				}
598 			}
599 		}
600 	}
601 
602 	private int getMaxlength(String[] lines) {
603 		int maxlength = 0;
604 		for (int i = 0; i < lines.length; i++) {
605 			int size = lines[i].getBytes().length;
606 			if (maxlength < size) {
607 				maxlength = size;
608 			}
609 		}
610 		return maxlength;
611 	}
612 
613 	private int[] getMaxcolsize(String[] lines) {
614 
615 		int[] maxcolsize = null;
616 		boolean first = true;
617 		for (int i = 0; i < lines.length; i++) {
618 			if (!"".equals(lines[i])) {
619 				String[] cols = lines[i].split("//|");
620 				if (first) {
621 					maxcolsize = new int[cols.length];
622 					first = false;
623 				}
624 
625 				for (int j = 0; j < cols.length; j++) {
626 					int size = cols[j].getBytes().length;
627 					if (maxcolsize[j] < size) {
628 						maxcolsize[j] = size;
629 					}
630 				}
631 			}
632 		}
633 		return maxcolsize;
634 	}
635 
636 	private String getRepeatedString(String str, int num) {
637 		StringBuffer buf = new StringBuffer();
638 		for (int i = 0; i < num; i++) {
639 			buf.append(str);
640 		}
641 		return buf.toString();
642 	}
643 }