001package org.apache.maven.doxia.module.twiki.parser; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.maven.doxia.util.ByLineSource; 023import org.apache.maven.doxia.parser.ParseException; 024import org.apache.maven.doxia.sink.Sink; 025 026import java.util.ArrayList; 027import java.util.List; 028import java.util.regex.Matcher; 029import java.util.regex.Pattern; 030 031/** 032 * Generic list parser 033 * 034 * @author Juan F. Codagnone 035 * @version $Id: GenericListBlockParser.html 905940 2014-04-12 16:27:29Z hboutemy $ 036 */ 037public class GenericListBlockParser 038 implements BlockParser 039{ 040 static final String EOL = System.getProperty( "line.separator" ); 041 042 /** 043 * parser used to create text blocks 044 */ 045 private FormatedTextParser formatedTextParser; 046 047 /** 048 * supported patterns 049 */ 050 private final Pattern[] patterns = new Pattern[TYPES.length]; 051 052 /** 053 * Creates the GenericListBlockParser. 054 */ 055 public GenericListBlockParser() 056 { 057 for ( int i = 0; i < TYPES.length; i++ ) 058 { 059 patterns[i] = Pattern.compile( "^(( )+)" + TYPES[i].getItemPattern() + "(.*)$" ); 060 } 061 } 062 063 /** {@inheritDoc} */ 064 public final boolean accept( final String line ) 065 { 066 boolean ret = false; 067 068 for ( int i = 0; !ret && i < patterns.length; i++ ) 069 { 070 ret |= patterns[i].matcher( line ).lookingAt(); 071 } 072 073 return ret; 074 } 075 076 /** 077 * {@inheritDoc} 078 */ 079 public final Block visit( final String line, final ByLineSource source ) 080 throws ParseException 081 { 082 final TreeListBuilder treeListBuilder = new TreeListBuilder( formatedTextParser ); 083 // new TreeListBuilder(formatedTextParser); 084 String l = line; 085 do 086 { 087 if ( !accept( l ) ) 088 { 089 break; 090 } 091 092 for ( int i = 0; i < patterns.length; i++ ) 093 { 094 final Matcher m = patterns[i].matcher( l ); 095 if ( m.lookingAt() ) 096 { 097 final int numberOfSpaces = 3; 098 final int textGroup = 3; 099 assert m.group( 1 ).length() % numberOfSpaces == 0; 100 final int level = m.group( 1 ).length() / numberOfSpaces; 101 treeListBuilder.feedEntry( TYPES[i], level, m.group( textGroup ).trim() ); 102 break; 103 } 104 } 105 } 106 while ( ( l = source.getNextLine() ) != null ); 107 108 if ( l != null ) 109 { 110 source.ungetLine(); 111 } 112 113 return treeListBuilder.getBlock(); 114 } 115 116 /** 117 * Sets the formatTextParser. 118 * 119 * @param textParser <code>FormatedTextParser</code> with the formatTextParser. 120 */ 121 public final void setTextParser( final FormatedTextParser textParser ) 122 { 123 if ( textParser == null ) 124 { 125 throw new IllegalArgumentException( "formatTextParser can't be null" ); 126 } 127 this.formatedTextParser = textParser; 128 } 129 130 interface Type 131 { 132 /** 133 * @return the pattern of the item part of the list regex 134 */ 135 String getItemPattern(); 136 137 /** 138 * @param items children of the new listblock 139 * @return a new ListBlock 140 */ 141 ListBlock createList( final ListItemBlock[] items ); 142 143 } 144 145 /** 146 * unordered list 147 */ 148 private static final Type LIST = new Type() 149 { 150 /** {@inheritDoc} */ 151 public String getItemPattern() 152 { 153 return "[*]"; 154 } 155 156 /** {@inheritDoc} */ 157 public ListBlock createList( final ListItemBlock[] items ) 158 { 159 return new UnorderedListBlock( items ); 160 } 161 }; 162 163 /** 164 * a. 165 */ 166 private static final Type ORDERED_LOWER_ALPHA = new Type() 167 { 168 /** {@inheritDoc} */ 169 public String getItemPattern() 170 { 171 return "[a-hj-z][.]"; 172 } 173 174 /** {@inheritDoc} */ 175 public ListBlock createList( final ListItemBlock[] items ) 176 { 177 return new NumeratedListBlock( Sink.NUMBERING_LOWER_ALPHA, items ); 178 } 179 }; 180 181 /** 182 * A. 183 */ 184 private static final Type ORDERED_UPPER_ALPHA = new Type() 185 { 186 /** {@inheritDoc} */ 187 public String getItemPattern() 188 { 189 return "[A-HJ-Z][.]"; 190 } 191 192 /** {@inheritDoc} */ 193 public ListBlock createList( final ListItemBlock[] items ) 194 { 195 return new NumeratedListBlock( Sink.NUMBERING_UPPER_ALPHA, items ); 196 } 197 }; 198 199 /** 200 * 1. 201 */ 202 private static final Type ORDERERED_DECIMAL = new Type() 203 { 204 /** {@inheritDoc} */ 205 public String getItemPattern() 206 { 207 return "[0123456789][.]"; 208 } 209 210 /** {@inheritDoc} */ 211 public ListBlock createList( final ListItemBlock[] items ) 212 { 213 return new NumeratedListBlock( Sink.NUMBERING_DECIMAL, items ); 214 } 215 }; 216 217 /** 218 * i. 219 */ 220 private static final Type ORDERERED_LOWER_ROMAN = new Type() 221 { 222 /** {@inheritDoc} */ 223 public String getItemPattern() 224 { 225 return "[i][.]"; 226 } 227 228 /** {@inheritDoc} */ 229 public ListBlock createList( final ListItemBlock[] items ) 230 { 231 return new NumeratedListBlock( Sink.NUMBERING_LOWER_ROMAN, items ); 232 } 233 }; 234 235 /** 236 * I. 237 */ 238 private static final Type ORDERERED_UPPER_ROMAN = new Type() 239 { 240 /** {@inheritDoc} */ 241 public String getItemPattern() 242 { 243 return "[I][.]"; 244 } 245 246 /** {@inheritDoc} */ 247 public ListBlock createList( final ListItemBlock[] items ) 248 { 249 return new NumeratedListBlock( Sink.NUMBERING_UPPER_ROMAN, items ); 250 } 251 }; 252 253 private static final Type[] TYPES = 254 { LIST, ORDERED_LOWER_ALPHA, ORDERED_UPPER_ALPHA, ORDERERED_DECIMAL, ORDERERED_LOWER_ROMAN, 255 ORDERERED_UPPER_ROMAN }; 256 257} 258 259/** 260 * It helps to build 261 * 262 * @author Juan F. Codagnone 263 * @version $Id: GenericListBlockParser.html 905940 2014-04-12 16:27:29Z hboutemy $ 264 */ 265class TreeListBuilder 266{ 267 /** 268 * parser that create text blocks 269 */ 270 private final FormatedTextParser textParser; 271 272 /** 273 * tree root 274 */ 275 private final TreeComponent root; 276 277 /** 278 * the current element of the tree 279 */ 280 private TreeComponent current; 281 282 /** 283 * Creates the TreeListBuilder. 284 * 285 * @param formatTextParser parser that create text blocks 286 * @throws IllegalArgumentException if <code>formatTextParser</code> is null 287 */ 288 TreeListBuilder( final FormatedTextParser formatTextParser ) 289 throws IllegalArgumentException 290 { 291 if ( formatTextParser == null ) 292 { 293 throw new IllegalArgumentException( "argument is null" ); 294 } 295 this.textParser = formatTextParser; 296 root = new TreeComponent( null, "root", null ); 297 current = root; 298 } 299 300 /** 301 * recibe un nivel y un texto y armar magicamente (manteniendo estado) 302 * el �rbol 303 * 304 * @param type type of list 305 * @param level indentation level of the item 306 * @param text text of the item 307 */ 308 void feedEntry( final GenericListBlockParser.Type type, final int level, final String text ) 309 { 310 final int currentDepth = current.getDepth(); 311 final int incomingLevel = level - 1; 312 313 if ( incomingLevel == currentDepth ) 314 { 315 // nothing to move 316 } 317 else if ( incomingLevel > currentDepth ) 318 { 319 // el actual ahora es el �ltimo que insert� 320 final TreeComponent[] components = current.getChildren(); 321 if ( components.length == 0 ) 322 { 323 /* for example: 324 * * item1 325 * * item2 326 */ 327 for ( int i = 0, n = incomingLevel - currentDepth; i < n; i++ ) 328 { 329 current = current.addChildren( "", type ); 330 } 331 } 332 else 333 { 334 current = components[components.length - 1]; 335 } 336 337 } 338 else 339 { 340 for ( int i = 0, n = currentDepth - incomingLevel; i < n; i++ ) 341 { 342 current = current.getFather(); 343 if ( current == null ) 344 { 345 throw new IllegalStateException(); 346 } 347 } 348 } 349 current.addChildren( text, type ); 350 } 351 352 /** 353 * @return a Block for the list that we received 354 */ 355 ListBlock getBlock() 356 { 357 return getList( root ); 358 } 359 360 /** 361 * Wrapper 362 * 363 * @param tc tree 364 * @return list Block for this tree 365 */ 366 private ListBlock getList( final TreeComponent tc ) 367 { 368 ListItemBlock[] li = getListItems( tc ).toArray( new ListItemBlock[] {} ); 369 return tc.getChildren()[0].getType().createList( li ); 370 } 371 372 /** 373 * @param tc tree 374 * @return list Block for this tree 375 */ 376 private List<ListItemBlock> getListItems( final TreeComponent tc ) 377 { 378 final List<ListItemBlock> blocks = new ArrayList<ListItemBlock>(); 379 380 for ( int i = 0; i < tc.getChildren().length; i++ ) 381 { 382 final TreeComponent child = tc.getChildren()[i]; 383 384 Block[] text = new Block[] {}; 385 if ( child.getFather() != null ) 386 { 387 text = textParser.parse( child.getText() ); 388 } 389 390 if ( child.getChildren().length != 0 ) 391 { 392 blocks.add( new ListItemBlock( text, getList( child ) ) ); 393 } 394 else 395 { 396 blocks.add( new ListItemBlock( text ) ); 397 } 398 } 399 400 return blocks; 401 } 402 403 /** 404 * A bidirectional tree node 405 * 406 * @author Juan F. Codagnone 407 * @version $Id: GenericListBlockParser.html 905940 2014-04-12 16:27:29Z hboutemy $ 408 */ 409 class TreeComponent 410 { 411 /** 412 * childrens 413 */ 414 private List<TreeComponent> children = new ArrayList<TreeComponent>(); 415 416 /** 417 * node text 418 */ 419 private String text; 420 421 /** 422 * the father 423 */ 424 private TreeComponent father; 425 426 /** 427 * type of the list 428 */ 429 private GenericListBlockParser.Type type; 430 431 /** 432 * Creates the TreeComponent. 433 * 434 * @param father Component father 435 * @param text component text 436 * @param type component type 437 */ 438 TreeComponent( final TreeComponent father, final String text, final GenericListBlockParser.Type type ) 439 { 440 this.text = text; 441 this.father = father; 442 this.type = type; 443 } 444 445 /** 446 * @return my childrens 447 */ 448 TreeComponent[] getChildren() 449 { 450 return (TreeComponent[]) children.toArray( new TreeComponent[] {} ); 451 } 452 453 /** 454 * adds a children node 455 * 456 * @param t text of the children 457 * @param ttype component type 458 * @return the new node created 459 */ 460 TreeComponent addChildren( final String t, final GenericListBlockParser.Type ttype ) 461 { 462 if ( t == null || ttype == null ) 463 { 464 throw new IllegalArgumentException( "argument is null" ); 465 } 466 final TreeComponent ret = new TreeComponent( this, t, ttype ); 467 children.add( ret ); 468 469 return ret; 470 } 471 472 /** 473 * @return the father 474 */ 475 TreeComponent getFather() 476 { 477 return father; 478 } 479 480 /** 481 * @return the node depth in the tree 482 */ 483 int getDepth() 484 { 485 int ret = 0; 486 487 TreeComponent c = this; 488 489 while ( ( c = c.getFather() ) != null ) 490 { 491 ret++; 492 } 493 494 return ret; 495 } 496 497 /** {@inheritDoc} */ 498 public String toString() 499 { 500 return toString( "" ); 501 } 502 503 /** {@inheritDoc} */ 504 public String toString( final String indent ) 505 { 506 final StringBuilder sb = new StringBuilder(); 507 508 if ( father != null ) 509 { 510 sb.append( indent ); 511 sb.append( "- " ); 512 sb.append( text ); 513 sb.append( GenericListBlockParser.EOL ); 514 } 515 for ( TreeComponent lc : children ) 516 { 517 sb.append( lc.toString( indent + " " ) ); 518 } 519 return sb.toString(); 520 } 521 522 /** 523 * Returns the text. 524 * 525 * @return <code>String</code> with the text. 526 */ 527 String getText() 528 { 529 return text; 530 } 531 532 /** 533 * Returns the type. 534 * 535 * @return <code>Type</code> with the text. 536 */ 537 GenericListBlockParser.Type getType() 538 { 539 return type; 540 } 541 } 542}