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