001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * bstefanescu 011 * 012 * $Id$ 013 */ 014 015package org.nuxeo.ecm.platform.rendering.wiki; 016 017import java.io.IOException; 018 019import org.apache.commons.logging.Log; 020import org.apache.commons.logging.LogFactory; 021import org.nuxeo.ecm.platform.rendering.wiki.extensions.WikiBlockWriter; 022import org.wikimodel.wem.PrintListener; 023import org.wikimodel.wem.WikiFormat; 024import org.wikimodel.wem.WikiParameters; 025 026import freemarker.core.Environment; 027import freemarker.template.TemplateException; 028 029/** 030 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 031 */ 032public class WikiSerializerHandler extends PrintListener { 033 034 public static final Log log = LogFactory.getLog(WikiSerializerHandler.class); 035 036 protected static final String LINE_SEP = System.getProperty("line.separator"); 037 038 protected final WikiSerializer engine; 039 040 protected final StringBuilder words = new StringBuilder(); 041 042 protected Environment env; 043 044 protected WikiWriter writer; 045 046 protected int mark = -1; // used to mark the current buffer to be able to retrieve printed text that starts at the 047 // mark 048 049 protected Toc toc; 050 051 public WikiSerializerHandler(WikiSerializer engine) { 052 super(null); // cannot base on the wikiprinter - so we don't use it 053 this.engine = engine; 054 writer = new WikiWriter(); 055 if (engine.macros.containsKey("toc")) { 056 toc = new Toc(); 057 } 058 } 059 060 @Override 061 protected void print(String str) { 062 writer.print(str); 063 } 064 065 @Override 066 protected void println() { 067 writer.println(); 068 } 069 070 @Override 071 protected void println(String str) { 072 writer.println(str); 073 } 074 075 public WikiWriter getWriter() { 076 return writer; 077 } 078 079 public Environment getEnvironment() { 080 if (env == null) { 081 env = Environment.getCurrentEnvironment(); 082 } 083 return env; 084 } 085 086 protected void beginElement() { 087 flushWords(); 088 } 089 090 protected void endElement() { 091 flushWords(); 092 } 093 094 protected void flushWords() { 095 if (words.length() == 0) { 096 return; 097 } 098 String text = words.toString(); 099 words.setLength(0); 100 for (int i = 0, len = engine.filters.size(); i < len; i++) { 101 String result = engine.filters.get(i).apply(text); 102 if (result != null) { 103 print(result); 104 return; 105 } 106 } 107 print(text); 108 } 109 110 @Override 111 public void beginDefinitionDescription() { 112 beginElement(); 113 super.beginDefinitionDescription(); 114 } 115 116 @Override 117 public void beginDefinitionList(WikiParameters parameters) { 118 beginElement(); 119 super.beginDefinitionList(parameters); 120 } 121 122 @Override 123 public void beginDefinitionTerm() { 124 beginElement(); 125 super.beginDefinitionTerm(); 126 } 127 128 @Override 129 public void beginDocument() { 130 beginElement(); 131 super.beginDocument(); 132 } 133 134 @Override 135 public void beginFormat(WikiFormat format) { 136 beginElement(); 137 super.beginFormat(format); 138 } 139 140 @Override 141 public void beginHeader(int level, WikiParameters params) { 142 beginElement(); 143 super.beginHeader(level, params); 144 if (toc != null) { 145 String id = toc.addHeading(null, level); // we don't know the title yet 146 print("<a name=\"heading_" + id + "\">"); 147 mark = writer.getBuffer().length(); 148 } 149 } 150 151 @Override 152 public void beginInfoBlock(char infoType, WikiParameters params) { 153 beginElement(); 154 super.beginInfoBlock(infoType, params); 155 } 156 157 @Override 158 public void beginList(WikiParameters parameters, boolean ordered) { 159 beginElement(); 160 super.beginList(parameters, ordered); 161 } 162 163 @Override 164 public void beginListItem() { 165 beginElement(); 166 super.beginListItem(); 167 } 168 169 @Override 170 public void beginParagraph(WikiParameters params) { 171 beginElement(); 172 super.beginParagraph(params); 173 } 174 175 @Override 176 public void beginPropertyBlock(String propertyUri, boolean doc) { 177 beginElement(); 178 if (propertyUri.startsWith("block:")) { 179 String name = propertyUri.substring(6); 180 WikiBlockWriter bwriter = new WikiBlockWriter(writer, name); 181 writer.writeText(bwriter); 182 writer = bwriter; 183 } else { 184 super.beginPropertyBlock(propertyUri, doc); 185 } 186 } 187 188 @Override 189 public void beginPropertyInline(String str) { 190 beginElement(); 191 super.beginPropertyInline(str); 192 } 193 194 @Override 195 public void beginQuotation(WikiParameters params) { 196 beginElement(); 197 super.beginQuotation(params); 198 } 199 200 @Override 201 public void beginQuotationLine() { 202 beginElement(); 203 super.beginQuotationLine(); 204 } 205 206 @Override 207 public void beginTable(WikiParameters params) { 208 beginElement(); 209 super.beginTable(params); 210 } 211 212 @Override 213 public void beginTableCell(boolean tableHead, WikiParameters params) { 214 beginElement(); 215 super.beginTableCell(tableHead, params); 216 } 217 218 @Override 219 public void beginTableRow(WikiParameters params) { 220 beginElement(); 221 super.beginTableRow(params); 222 } 223 224 @Override 225 public void endDefinitionDescription() { 226 endElement(); 227 super.endDefinitionDescription(); 228 } 229 230 @Override 231 public void endDefinitionList(WikiParameters parameters) { 232 endElement(); 233 super.endDefinitionList(parameters); 234 } 235 236 @Override 237 public void endDefinitionTerm() { 238 endElement(); 239 super.endDefinitionTerm(); 240 } 241 242 @Override 243 public void endDocument() { 244 endElement(); 245 super.endDocument(); 246 } 247 248 @Override 249 public void endFormat(WikiFormat format) { 250 endElement(); 251 super.endFormat(format); 252 } 253 254 @Override 255 public void endHeader(int level, WikiParameters params) { 256 if (toc != null) { 257 if (mark == -1) { 258 throw new IllegalStateException("marker was not set"); 259 } 260 toc.tail.title = writer.getBuffer().substring(mark); 261 mark = -1; 262 print("</a>"); 263 super.endHeader(level, params); 264 } else { 265 super.endHeader(level, params); 266 } 267 endElement(); 268 } 269 270 @Override 271 public void endInfoBlock(char infoType, WikiParameters params) { 272 endElement(); 273 super.endInfoBlock(infoType, params); 274 } 275 276 @Override 277 public void endList(WikiParameters parameters, boolean ordered) { 278 endElement(); 279 super.endList(parameters, ordered); 280 } 281 282 @Override 283 public void endListItem() { 284 endElement(); 285 super.endListItem(); 286 } 287 288 @Override 289 public void endParagraph(WikiParameters params) { 290 endElement(); 291 super.endParagraph(params); 292 } 293 294 @Override 295 public void endPropertyBlock(String propertyUri, boolean doc) { 296 endElement(); 297 if (propertyUri.startsWith("block:")) { 298 writer = writer.getParent(); 299 if (writer == null) { 300 throw new IllegalStateException("block macro underflow"); 301 } 302 } else { 303 super.endPropertyBlock(propertyUri, doc); 304 } 305 } 306 307 @Override 308 public void endPropertyInline(String inlineProperty) { 309 endElement(); 310 super.endPropertyInline(inlineProperty); 311 } 312 313 @Override 314 public void endQuotation(WikiParameters params) { 315 endElement(); 316 super.endQuotation(params); 317 } 318 319 @Override 320 public void endQuotationLine() { 321 endElement(); 322 super.endQuotationLine(); 323 } 324 325 @Override 326 public void endTable(WikiParameters params) { 327 endElement(); 328 super.endTable(params); 329 } 330 331 @Override 332 public void endTableCell(boolean tableHead, WikiParameters params) { 333 endElement(); 334 super.endTableCell(tableHead, params); 335 } 336 337 @Override 338 public void endTableRow(WikiParameters params) { 339 endElement(); 340 super.endTableRow(params); 341 } 342 343 @Override 344 public void onEmptyLines(int count) { 345 flushWords(); 346 super.onEmptyLines(count); 347 } 348 349 @Override 350 public void onHorizontalLine() { 351 flushWords(); 352 super.onHorizontalLine(); 353 } 354 355 @Override 356 public void onLineBreak() { 357 flushWords(); 358 super.onLineBreak(); 359 } 360 361 @Override 362 public void onReference(String ref, boolean explicitLink) { 363 flushWords(); 364 super.onReference(ref, explicitLink); 365 } 366 367 @Override 368 public void onTableCaption(String str) { 369 flushWords(); 370 super.onTableCaption(str); 371 } 372 373 @Override 374 public void onVerbatimBlock(String str) { 375 flushWords(); 376 super.onVerbatimBlock(str); 377 } 378 379 @Override 380 public void onVerbatimInline(String str) { 381 flushWords(); 382 super.onVerbatimInline(str); 383 } 384 385 @Override 386 public void onMacroBlock(String macroName, WikiParameters params, String content) { 387 flushWords(); 388 WikiMacro expression = engine.macros.get(macroName); 389 if (expression != null) { 390 try { 391 expression.eval(params, content, this); 392 } catch (IOException | TemplateException e) { 393 log.error("Failed to eval macro", e); 394 } 395 } else { 396 log.warn("Unknown wiki macro: " + macroName); 397 } 398 } 399 400 @Override 401 public void onMacroInline(String macroName, WikiParameters params, String content) { 402 flushWords(); 403 WikiMacro expression = engine.macros.get(macroName); 404 if (expression != null) { 405 try { 406 expression.evalInline(params, content, this); 407 } catch (IOException | TemplateException e) { 408 log.error("Failed to eval macro", e); 409 } 410 } else { 411 log.warn("Unknown wiki macro: " + macroName); 412 } 413 } 414 415 @Override 416 public void onExtensionBlock(String extensionName, WikiParameters params) { 417 flushWords(); 418 log.warn("Unknown wiki expression: " + extensionName); 419 } 420 421 @Override 422 public void onExtensionInline(String extensionName, WikiParameters params) { 423 flushWords(); 424 log.warn("Unknown wiki expression: " + extensionName); 425 } 426 427 @Override 428 public void onSpecialSymbol(String str) { 429 String entity = getSymbolEntity(str); 430 if (entity != null) { 431 words.append(entity); 432 } else { // do not escape - to be able to use filters on it 433 words.append(str); 434 } 435 } 436 437 @Override 438 public void onSpace(String str) { 439 flushWords(); 440 super.onSpace(str); 441 } 442 443 @Override 444 public void onNewLine() { 445 flushWords(); 446 super.onNewLine(); 447 } 448 449 @Override 450 public void onEscape(String str) { 451 flushWords(); 452 super.onEscape(str); 453 } 454 455 @Override 456 public void onWord(String word) { 457 words.append(word); 458 // writeWord(word); 459 } 460 461 protected void writeWord(String word) { 462 for (int i = 0, len = engine.filters.size(); i < len; i++) { 463 String result = engine.filters.get(i).apply(word); 464 if (result != null) { 465 print(result); 466 return; 467 } 468 } 469 print(word); 470 } 471 472 /** 473 * Returns an HTML/XML entity corresponding to the specified special symbol. Depending on implementation it can be 474 * real entities (like &amp; &lt; &gt; or the corresponding digital codes (like &#38;, 475 * &#&#38; or &#8250;). Digital entity representation is better for generation of XML files. 476 * 477 * @param str the special string to convert to an HTML/XML entity 478 * @return an HTML/XML entity corresponding to the specified special symbol. 479 */ 480 protected String getSymbolEntity(String str) { 481 String entity = null; 482 if (isHtmlEntities()) { 483 entity = WikiEntityUtil.getHtmlSymbol(str); 484 } else { 485 int code = WikiEntityUtil.getHtmlCodeByWikiSymbol(str); 486 if (code > 0) { 487 entity = "#" + Integer.toString(code); 488 } 489 } 490 if (entity != null) { 491 entity = "&" + entity + ";"; 492 if (str.startsWith(" --")) { 493 entity = " " + entity + " "; 494 } 495 } 496 return entity; 497 } 498 499 /** 500 * Returns <code>true</code> if special Wiki entities should be represented as the corresponding HTML entities or 501 * they should be visualized using the corresponding XHTML codes (like &amp; and so on). This method can be 502 * overloaded in subclasses to re-define the visualization style. 503 * 504 * @return <code>true</code> if special Wiki entities should be represented as the corresponding HTML entities or 505 * they should be visualized using the corresponding XHTML codes (like &amp; and so on). 506 */ 507 protected boolean isHtmlEntities() { 508 return true; 509 } 510 511}