View Javadoc
1   /*
2    * #%L
3    * JRst :: Api
4    * %%
5    * Copyright (C) 2004 - 2010 CodeLutin
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 3 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20   * #L%
21   */
22  
23  /* *
24   * JRSTReader.java
25   *
26   * Created: 27 oct. 06 00:15:34
27   *
28   * @author poussin
29   * @version $Revision$
30   *
31   * Last update: $Date$
32   * by : $Author$
33   */
34  
35  package org.nuiton.jrst.legacy;
36  
37  import static org.nuiton.i18n.I18n.t;
38  import static org.nuiton.jrst.legacy.ReStructuredText.ADDRESS;
39  import static org.nuiton.jrst.legacy.ReStructuredText.ADMONITION;
40  import static org.nuiton.jrst.legacy.ReStructuredText.ATTRIBUTION;
41  import static org.nuiton.jrst.legacy.ReStructuredText.AUTHOR;
42  import static org.nuiton.jrst.legacy.ReStructuredText.AUTHORS;
43  import static org.nuiton.jrst.legacy.ReStructuredText.BLOCK_QUOTE;
44  import static org.nuiton.jrst.legacy.ReStructuredText.BULLET_LIST;
45  import static org.nuiton.jrst.legacy.ReStructuredText.COLSPEC;
46  import static org.nuiton.jrst.legacy.ReStructuredText.COMMENT;
47  import static org.nuiton.jrst.legacy.ReStructuredText.CONTACT;
48  import static org.nuiton.jrst.legacy.ReStructuredText.COPYRIGHT;
49  import static org.nuiton.jrst.legacy.ReStructuredText.DATE;
50  import static org.nuiton.jrst.legacy.ReStructuredText.DECORATION;
51  import static org.nuiton.jrst.legacy.ReStructuredText.DEFINITION;
52  import static org.nuiton.jrst.legacy.ReStructuredText.DEFINITION_LIST;
53  import static org.nuiton.jrst.legacy.ReStructuredText.DEFINITION_LIST_ITEM;
54  import static org.nuiton.jrst.legacy.ReStructuredText.DESCRIPTION;
55  import static org.nuiton.jrst.legacy.ReStructuredText.DOCINFO;
56  import static org.nuiton.jrst.legacy.ReStructuredText.DOCTEST_BLOCK;
57  import static org.nuiton.jrst.legacy.ReStructuredText.DOCUMENT;
58  import static org.nuiton.jrst.legacy.ReStructuredText.EMPHASIS;
59  import static org.nuiton.jrst.legacy.ReStructuredText.ENTRY;
60  import static org.nuiton.jrst.legacy.ReStructuredText.ENUMERATED_LIST;
61  import static org.nuiton.jrst.legacy.ReStructuredText.FIELD;
62  import static org.nuiton.jrst.legacy.ReStructuredText.FIELD_BODY;
63  import static org.nuiton.jrst.legacy.ReStructuredText.FIELD_LIST;
64  import static org.nuiton.jrst.legacy.ReStructuredText.FIELD_NAME;
65  import static org.nuiton.jrst.legacy.ReStructuredText.FOOTER;
66  import static org.nuiton.jrst.legacy.ReStructuredText.FOOTNOTE;
67  import static org.nuiton.jrst.legacy.ReStructuredText.FOOTNOTE_REFERENCE;
68  import static org.nuiton.jrst.legacy.ReStructuredText.FOOTNOTE_SYMBOL;
69  import static org.nuiton.jrst.legacy.ReStructuredText.GENERATED;
70  import static org.nuiton.jrst.legacy.ReStructuredText.HEADER;
71  import static org.nuiton.jrst.legacy.ReStructuredText.IMAGE;
72  import static org.nuiton.jrst.legacy.ReStructuredText.LABEL;
73  import static org.nuiton.jrst.legacy.ReStructuredText.LINE;
74  import static org.nuiton.jrst.legacy.ReStructuredText.LINE_BLOCK;
75  import static org.nuiton.jrst.legacy.ReStructuredText.LIST_ITEM;
76  import static org.nuiton.jrst.legacy.ReStructuredText.LITERAL;
77  import static org.nuiton.jrst.legacy.ReStructuredText.LITERAL_BLOCK;
78  import static org.nuiton.jrst.legacy.ReStructuredText.OPTION;
79  import static org.nuiton.jrst.legacy.ReStructuredText.OPTION_ARGUMENT;
80  import static org.nuiton.jrst.legacy.ReStructuredText.OPTION_GROUP;
81  import static org.nuiton.jrst.legacy.ReStructuredText.OPTION_LIST;
82  import static org.nuiton.jrst.legacy.ReStructuredText.OPTION_LIST_ITEM;
83  import static org.nuiton.jrst.legacy.ReStructuredText.OPTION_STRING;
84  import static org.nuiton.jrst.legacy.ReStructuredText.ORGANIZATION;
85  import static org.nuiton.jrst.legacy.ReStructuredText.PARAGRAPH;
86  import static org.nuiton.jrst.legacy.ReStructuredText.REFERENCE;
87  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_ANONYMOUS_HYPERLINK_REFERENCE;
88  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_EMAIL;
89  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_EMPHASIS;
90  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_FOOTNOTE_REFERENCE;
91  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_HYPERLINK_REFERENCE;
92  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_INLINE_REFERENCE;
93  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_LITERAL;
94  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_REFERENCE;
95  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_STRONG;
96  import static org.nuiton.jrst.legacy.ReStructuredText.REGEX_SUBSTITUTION_REFERENCE;
97  import static org.nuiton.jrst.legacy.ReStructuredText.REVISION;
98  import static org.nuiton.jrst.legacy.ReStructuredText.ROW;
99  import static org.nuiton.jrst.legacy.ReStructuredText.SECTION;
100 import static org.nuiton.jrst.legacy.ReStructuredText.SIDEBAR;
101 import static org.nuiton.jrst.legacy.ReStructuredText.STATUS;
102 import static org.nuiton.jrst.legacy.ReStructuredText.STRONG;
103 import static org.nuiton.jrst.legacy.ReStructuredText.SUBSTITUTION_DEFINITION;
104 import static org.nuiton.jrst.legacy.ReStructuredText.SUBTITLE;
105 import static org.nuiton.jrst.legacy.ReStructuredText.TABLE;
106 import static org.nuiton.jrst.legacy.ReStructuredText.TARGET;
107 import static org.nuiton.jrst.legacy.ReStructuredText.TBODY;
108 import static org.nuiton.jrst.legacy.ReStructuredText.TERM;
109 import static org.nuiton.jrst.legacy.ReStructuredText.TGROUP;
110 import static org.nuiton.jrst.legacy.ReStructuredText.THEAD;
111 import static org.nuiton.jrst.legacy.ReStructuredText.TITLE;
112 import static org.nuiton.jrst.legacy.ReStructuredText.TOPIC;
113 import static org.nuiton.jrst.legacy.ReStructuredText.TRANSITION;
114 import static org.nuiton.jrst.legacy.ReStructuredText.VERSION;
115 
116 
117 import java.io.BufferedReader;
118 import java.io.File;
119 import java.io.FileReader;
120 import java.io.IOException;
121 import java.io.InputStreamReader;
122 import java.io.Reader;
123 import java.io.StringReader;
124 import java.io.UnsupportedEncodingException;
125 import java.net.URL;
126 import java.net.URLEncoder;
127 import java.util.HashMap;
128 import java.util.LinkedList;
129 import java.util.List;
130 import java.util.Map;
131 import java.util.TreeSet;
132 import java.util.regex.Matcher;
133 import java.util.regex.Pattern;
134 import java.util.NoSuchElementException;
135 
136 import org.apache.commons.lang.ObjectUtils;
137 import org.apache.commons.lang.StringEscapeUtils;
138 import org.apache.commons.logging.Log;
139 import org.apache.commons.logging.LogFactory;
140 import org.nuiton.jrst.JRSTDirective;
141 import org.nuiton.jrst.legacy.directive.ContentDirective;
142 import org.nuiton.jrst.legacy.directive.DateDirective;
143 import org.nuiton.jrst.legacy.directive.ImageDirective;
144 import org.nuiton.jrst.legacy.directive.SectnumDirective;
145 import org.nuiton.util.StringUtil;
146 import org.dom4j.Document;
147 import org.dom4j.DocumentException;
148 import org.dom4j.DocumentHelper;
149 import org.dom4j.Element;
150 import org.dom4j.IllegalAddException;
151 import org.dom4j.Node;
152 import org.dom4j.VisitorSupport;
153 
154 /*
155  * 
156  * <pre> +--------------------------------------------------------------------+ |
157  * document [may begin with a title, subtitle, decoration, docinfo] | |
158  * +--------------------------------------+ | | sections [each begins with a
159  * title] |
160  * +-----------------------------+-------------------------+------------+ |
161  * [body elements:] | (sections) | | | - literal | - lists | | - hyperlink
162  * +------------+ | | blocks | - tables | | targets | | para- | - doctest | -
163  * block | foot- | - sub. defs | | graphs | blocks | quotes | notes | - comments |
164  * +---------+-----------+----------+-------+--------------+ | [text]+ | [text] |
165  * (body elements) | [text] | | (inline
166  * +-----------+------------------+--------------+ | markup) | +---------+
167  * </pre>
168  * 
169  * 
170  * Inline support: http://docutils.sourceforge.net/docs/user/rst/quickref.html
171  * 
172  * <li> STRUCTURAL ELEMENTS: document, section, topic, sidebar <li> STRUCTURAL
173  * SUBELEMENTS: title, subtitle, decoration, docinfo, transition <li> docinfo:
174  * address, author, authors, contact, copyright, date, field, organization,
175  * revision, status, version <li> decoration: footer, header <li> BODY ELEMENTS:
176  * admonition, attention, block_quote, bullet_list, caution, citation, comment,
177  * compound, container, danger, definition_list, doctest_block, enumerated_list,
178  * error, field_list, figure, footnote, hint, image, important, line_block,
179  * literal_block, note, option_list, paragraph, pending, raw, rubric,
180  * substitution_definition, system_message, table, target, tip, warning <li>
181  * SIMPLE BODY ELEMENTS: comment, doctest_block, image, literal_block,
182  * paragraph, pending, raw, rubric, substitution_definition, target <li>
183  * COMPOUND BODY ELEMENTS: admonition, attention, block_quote, bullet_list,
184  * caution, citation, compound, container, danger, definition_list,
185  * enumerated_list, error, field_list, figure, footnote, hint, important,
186  * line_block, note, option_list, system_message, table, tip, warning <li> BODY
187  * SUBELEMENTS: attribution, caption, classifier, colspec, field_name, label,
188  * line, option_argument, option_string, term definition, definition_list_item,
189  * description, entry, field, field_body, legend, list_item, option,
190  * option_group, option_list_item, row, tbody, tgroup, thead <li> INLINE
191  * ELEMENTS: abbreviation, acronym, citation_reference, emphasis,
192  * footnote_reference, generated, image, inline, literal, problematic,
193  * reference, strong, subscript, substitution_reference, superscript, target,
194  * title_reference, raw
195  * 
196  * <pre> DOCUMENT :: ( (title, subtitle?)?, decoration?, (docinfo,
197  * transition?)?, STRUCTURE.MODEL; ) decoration :: (header?, footer?) header,
198  * footer, definition, description, attention, caution, danger, error, hint,
199  * important, note, tip, warning :: (BODY.ELEMENTS;)+ transition :: EMPTY
200  * docinfo :: (BIBLIOGRAPHIC.ELEMENTS;)+ BIBLIOGRAPHIC.ELEMENTS :: author |
201  * authors | organization | contact | address | version | revision | status |
202  * date | copyright | field authors :: ( (author)+ ) field :: (field_name,
203  * field_body) field_body, list_item :: (BODY.ELEMENTS;)* STRUCTURE.MODEL :: ( (
204  * (BODY.ELEMENTS; | topic | sidebar)+, transition? )*, ( (section),
205  * (transition?, (section) )* )? ) BODY.ELEMENTS :: paragraph | compound |
206  * container | literal_block | doctest_block | line_block | block_quote | table |
207  * figure | image | footnote | citation | rubric | bullet_list | enumerated_list |
208  * definition_list | field_list | option_list | attention | caution | danger |
209  * error | hint | important | note | tip | warning | admonition | reference |
210  * target | substitution_definition | comment | pending | system_message | raw
211  * topic :: (title?, (BODY.ELEMENTS;)+) sidebar :: (title, subtitle?,
212  * (BODY.ELEMENTS; | topic)+) section :: (title, STRUCTURE.MODEL;) line_block ::
213  * (line | line_block)+ block_quote:: ((BODY.ELEMENTS;)+, attribution?)
214  * bullet_list, enumerated_list :: (list_item +) definition_list ::
215  * (definition_list_item +) definition_list_item :: (term, classifier?,
216  * definition) field_list :: (field +) option_list :: (option_list_item +)
217  * option_list_item :: (option_string, option_argument *, description)
218  * option_string, option_argument :: (#PCDATA) admonition :: (title,
219  * (BODY.ELEMENTS;)+)
220  * 
221  * title, subtitle, author, organization, contact, address, version, revision,
222  * status, date, copyright, field_name, paragraph, compound, container,
223  * literal_block, doctest_block, attribution, line, term, classifier ::
224  * TEXT.MODEL;
225  * 
226  * TEXT.MODEL :: (#PCDATA | INLINE.ELEMENTS;)* INLINE.ELEMENTS :: emphasis |
227  * strong | literal | reference | footnote_reference | citation_reference |
228  * substitution_reference | title_reference | abbreviation | acronym | subscript |
229  * superscript | inline | problematic | generated | target | image | raw
230  * emphasis :: '*' #PCDATA '*' strong :: '**' #PCDATA '**' literal :: '``'
231  * #PCDATA '``' footnote_reference :: '[' ([0-9]+|#) ']' citation_reference ::
232  * '[' [a-zA-Z]+ ']'
233  * 
234  * </pre>
235  */
236 
237 /**
238  * Le principe est d'utiliser les methodes peek* {@link JRSTLexer} pour
239  * prendre l'element que l'on attend, si la methode retourne null ou un autre
240  * element et bien c que ce n'est pas le bon choix, cela veut dire que l'element
241  * courant est fini d'etre lu (plus de paragraphe dans la section par exemple)
242  * ou qu'il y a une erreur dans le fichier en entre.
243  * <p>
244  * On construit un arbre XML representant le RST au fur et a mesure, on peut
245  * ensuite appliquer une feuille de style ou autre chose avec
246  * {@link org.nuiton.jrst.JRSTGenerator}
247  * 
248  * <p>
249  * Tous les elements ont un attribut level qui permet de savoir on il est dans
250  * la hierarchie. Le Document a le level -1001, et les sections/titres on pour
251  * level les valeurs 1000, -999, -998, ...
252  * <p>
253  * de cette facon les methods isUpperLevel et isSameLevel fonctionne pour tous
254  * les elements de la meme facon
255  * 
256  * <pre>
257  *   abbreviation
258  *   acronym
259  *   address (done)
260  *   admonition (done)
261  *   attention (done)
262  *   attribution
263  *   author (done)
264  *   authors (done)
265  *   block_quote (done)
266  *   bullet_list (done)
267  *   caption
268  *   caution (done)
269  *   citation
270  *   citation_reference
271  *   classifier (done)
272  *   colspec (done)
273  *   comment
274  *   compound
275  *   contact (done)
276  *   container
277  *   copyright (done)
278  *   danger (done)
279  *   date (done)
280  *   decoration (done)
281  *   definition (done)
282  *   definition_list (done)
283  *   definition_list_item (done)
284  *   description (done)
285  *   docinfo (done)
286  *   doctest_block (done)
287  *   document (done)
288  *   emphasis (done)
289  *   entry (done)
290  *   enumerated_list (done)
291  *   error (done)
292  *   field (done)
293  *   field_body (done)
294  *   field_list (done)
295  *   field_name (done)
296  *   figure
297  *   footer (done)
298  *   footnote	(done)
299  *   footnote_reference (done)
300  *   generated
301  *   header (done)
302  *   hint (done)
303  *   image (done)
304  *   important (done)
305  *   inline
306  *   label
307  *   legend
308  *   line (done)
309  *   line_block (done)
310  *   list_item (done)
311  *   literal (done)
312  *   literal_block (done)
313  *   note (done)
314  *   option (done)
315  *   option_argument (done)
316  *   option_group (done)
317  *   option_list (done)
318  *   option_list_item (done)
319  *   option_string (done)
320  *   organization (done)
321  *   paragraph (done)
322  *   pending
323  *   problematic
324  *   raw
325  *   reference (done)
326  *   revision (done)
327  *   row (done)
328  *   rubric
329  *   section (done)
330  *   sidebar (done)
331  *   status (done)
332  *   strong (done)
333  *   subscript
334  *   substitution_definition
335  *   substitution_reference
336  *   subtitle (done)
337  *   superscript
338  *   system_message
339  *   table (done)
340  *   target (done)
341  *   tbody (done)
342  *   term (done)
343  *   tgroup (done)
344  *   thead (done)
345  *   tip (done)
346  *   title (done)
347  *   title_reference
348  *   topic (done)
349  *   transition (done)
350  *   version (done)
351  *   warning (done)
352  * </pre>
353  * 
354  * Created: 27 oct. 06 00:15:34
355  *
356  * @author poussin, letellier
357  * @version $Revision$
358  *
359  * Last update: $Date$
360  * by : $Author$
361  */
362 public class JRSTReader {
363 
364     /** to use log facility, just put in your code: log.info(\"...\"); */
365     private static Log log = LogFactory.getLog(JRSTReader.class);
366 
367     protected static final String ANONYMOUS = "anonymous";
368     
369     protected static final String AUTO = "auto";
370     
371     protected static final String AUTONUM = "autoNum";
372     
373     protected static final String AUTONUMLABEL = "autoNumLabel";
374     
375     protected static final String AUTOSYMBOL = "autoSymbol";
376     
377     protected static final String ATTR_REFID = "refid";
378     
379     protected static final String ATTR_INLINE = "inline";
380     
381     protected static final String ATTR_IDS = "ids";
382     
383     protected static final String BACKREFS = "backrefs";
384     
385     protected static final String BULLET = "bullet";
386     
387     protected static final String CLASS = "class";
388     
389     protected static final String CONTENTS = "contents";
390     
391     protected static final String DELIMITER = "delimiter";
392     
393     protected static final String DELIMITEREXISTE ="delimiterExiste";
394     
395     protected static final String ENUMTYPE = "enumtype";
396     
397     protected static final String FOOTNOTES = "footnotes";
398     
399     protected static final String ID = "id";
400 
401     protected static final String INCLUDE = "include";
402     
403     protected static final String LEVEL = "level";
404     
405     protected static final String NAME = "name";
406     
407     protected static final String NAMES = "names";
408     
409     protected static final String NUM = "num";
410     
411     protected static final String REFURI = "refuri";
412     
413     protected static final String PREFIX = "prefix";
414     
415     protected static final String REMOVE = "remove";
416 
417     protected static final String START = "start";
418     
419     protected static final String SECTNUM = "sectnum";
420     
421     protected static final String SUBEXISTE = "subExiste";
422     
423     protected static final String SUFFIX = "suffix";
424     
425     protected static final String TRUE = "true";
426     
427     protected static final String TYPE = "type";
428     
429     protected static final String TARGETANONYMOUS = "targetAnonymous";
430     
431     protected static final String VALUE = "value";
432     
433     protected boolean ERROR_MISSING_ITEM;
434 
435     protected static int MAX_SECTION_DEPTH = -1000;
436 
437     protected static Map<String, JRSTDirective> defaultDirectives;
438 
439     protected Map<String, JRSTDirective> directives = new HashMap<String, JRSTDirective>();
440 
441     private boolean sectnum;
442 
443     private Element footer;
444 
445     private int idMax;
446 
447     private int symbolMax;
448 
449     private int symbolMaxRef;
450 
451     private LinkedList<Integer> lblFootnotes = new LinkedList<Integer>();
452 
453     private LinkedList<Integer> lblFootnotesRef = new LinkedList<Integer>();
454 
455     private LinkedList<Element> eFootnotes = new LinkedList<Element>();
456 
457     private LinkedList<Element> eTarget = new LinkedList<Element>();
458 
459     private LinkedList<Element> eTargetAnonymous = new LinkedList<Element>();
460 
461     private LinkedList<Element> eTargetAnonymousCopy = new LinkedList<Element>();
462 
463     private LinkedList<Element> eTitle = new LinkedList<Element>();
464 
465     static {
466         defaultDirectives = new HashMap<String, JRSTDirective>();
467         defaultDirectives.put(IMAGE, new ImageDirective());
468         defaultDirectives.put(DATE, new DateDirective());
469         defaultDirectives.put("time", new DateDirective());
470         defaultDirectives.put(CONTENTS, new ContentDirective());
471         // defaultDirectives.put("calc", new CalcDirective());
472         defaultDirectives.put(SECTNUM, new SectnumDirective());
473         // TODO put here all other directive
474     }
475 
476     /**
477      * 
478      */
479     public JRSTReader() {
480     }
481 
482     /**
483      * @param name
484      * @return the defaultDirectives
485      */
486     public static JRSTDirective getDefaultDirective(String name) {
487         return defaultDirectives.get(name);
488     }
489 
490     /**
491      * @param name
492      * @param directive the defaultDirectives to set
493      */
494     public static void addDefaultDirectives(String name, JRSTDirective directive) {
495         JRSTReader.defaultDirectives.put(name, directive);
496     }
497 
498     /**
499      * @param name
500      * @return the defaultDirectives
501      */
502     public JRSTDirective getDirective(String name) {
503         return directives.get(name);
504     }
505 
506     /**
507      * @param name 
508      * @param directive the defaultDirectives to set
509      */
510     public void addDirectives(String name, JRSTDirective directive) {
511         directives.put(name, directive);
512     }
513 
514     /**
515      * On commence par decouper tout le document en Element, puis on construit
516      * l'article a partir de ces elements.
517      * 
518      * @param reader
519      * @return le document cree
520      * @throws Exception
521      */
522     public Document read(Reader reader) throws Exception {
523         JRSTLexer lexer = new JRSTLexer(reader);
524         try {
525             Element root = composeDocument(lexer);
526 
527             Document result = DocumentHelper.createDocument();
528             result.setRootElement(root);
529 
530             root.accept(new VisitorSupport() {
531                 @Override
532                 public void visit(Element e) {
533                     // remove all level attribute
534                     e.addAttribute(LEVEL, null);
535                     // Constrution du sommaire
536                     String type = e.attributeValue(TYPE);
537                     if (type != null) {
538                         if (type.equals(CONTENTS)) {
539                             composeContents(e);
540                             e.addAttribute(TYPE, null);
541                         }
542                     }
543 
544                     if (TRUE.equalsIgnoreCase(e.attributeValue(ATTR_INLINE))) {
545                         e.addAttribute(ATTR_INLINE, null);
546                         try {
547                             inline(e);
548                         } catch (DocumentException eee) {
549                             if (log.isWarnEnabled()) {
550                                 log.warn("Can't inline text for " + e, eee);
551                             }
552                         } catch (UnsupportedEncodingException ee) {
553 			    if (log.isWarnEnabled()) {
554 				log.warn("Unsupported encoding " + e, ee);
555 			    }
556 			}
557                     }
558                 }
559             });
560 
561             return result;
562         } catch (Exception eee) {
563             log.error(t("JRST parsing error line %d char %s:\n%s", lexer
564                     .getLineNumber(), lexer.getCharNumber(), lexer
565                     .readNotBlanckLine()));
566             throw eee;
567         }
568     }
569 
570     /**
571      * <p>
572      * exemple :
573      * </p>
574      * 
575      * <pre>
576      * ..contents : Sommaire
577      *   depth: 3
578      * </pre>
579      * 
580      * <p>
581      * depth sert a limiter la profondeur du sommaire
582      * </p>
583      * 
584      * @param Element
585      * 
586      */
587     private void composeContents(Element e) {
588         Element result = DocumentHelper.createElement(TOPIC);
589         String option = e.getText();
590         int depth = -1;
591         // depth: 3
592         Pattern pattern = Pattern.compile("\\s*\\:depth\\:\\s*\\p{Digit}+");
593         Matcher matcher = pattern.matcher(option);
594         if (matcher.matches()) {
595             pattern = Pattern.compile("\\p{Digit}+");
596             matcher = pattern.matcher(matcher.group());
597             if (matcher.find()) {
598                 depth = Integer.parseInt(matcher.group());
599             }
600         }
601         int levelInit = 0;
602         boolean noTitle = false;
603         
604         try {
605             levelInit = Integer.parseInt(eTitle.getFirst().attributeValue(
606             		LEVEL));
607         } catch (NumberFormatException eee) {
608             log.error("Can't parse level in: "
609                                     + eTitle.getFirst().asXML(), eee);
610             return;
611         } catch (NoSuchElementException eee) {
612             noTitle = true;
613         }
614 
615         LinkedList<Element> title = new LinkedList<Element>();
616         // on rajoute les refid
617         for (int i = 0; i < eTitle.size(); i++) {
618             idMax++;
619             eTitle.get(i).addAttribute(ATTR_REFID, ID + idMax);
620         }
621         // on enleve les titres limites par depth
622         for (Element el : eTitle) {
623             int level = Integer.parseInt(el.attributeValue(LEVEL));
624             level = level - levelInit;
625             el.addAttribute(LEVEL, "" + level);
626             if (depth == -1) {
627                 title.add(el);
628             }
629             else {
630                 if (depth > level) {
631                     title.add(el);
632                 }
633             }
634         }
635         e.addAttribute(CLASS, CONTENTS);
636         String titleValue = e.attributeValue(VALUE);
637         e.addAttribute(VALUE, null);
638         String value = titleValue.trim().toLowerCase();
639         // sans titre c "contents" par default
640         if (value.matches("\\s*")) {
641             value = CONTENTS;
642             titleValue = "Contents";
643         }
644         e.addAttribute(ATTR_IDS, value);
645         e.addAttribute(NAMES, value);
646         result.addElement(TITLE).setText(titleValue);
647         // on compose les lignes
648         if (!noTitle) { //Si il y a des titres à lier à la table des matières
649             result.add(composeLineContent(title, ""));
650         }
651         e.setText("");
652         e.appendContent(result);
653     }
654 
655     /**
656      * @param title
657      *            <Element> title, String num
658      * @return Element
659      */
660     private Element composeLineContent(LinkedList<Element> title, String num) {
661         Element result = DocumentHelper.createElement(BULLET_LIST);
662         if (sectnum) {
663             result.addAttribute(CLASS, "auto-toc");
664         }
665         Element item = null;
666         int cnt = 0;
667         while (!title.isEmpty()) {
668 
669             Element e = title.getFirst();
670             int level = Integer.parseInt(e.attributeValue(LEVEL));
671             LinkedList<Element> child = new LinkedList<Element>();
672 
673             if (level <= 0) {
674                 cnt++;
675                 title.removeFirst();
676                 item = result.addElement(LIST_ITEM);
677                 Element para = item.addElement(PARAGRAPH);
678                 Element reference = para.addElement(REFERENCE);
679                 String text = e.getText();
680                 String id = e.attributeValue(ATTR_REFID);
681                 reference.addAttribute(ATTR_IDS, id);
682                 reference.addAttribute(ATTR_REFID, text.replaceAll("\\W+", " ")
683                         .trim().toLowerCase().replaceAll("\\W+", "-"));
684                 // si l'on doit les numeroter
685                 if (sectnum) {
686                     Element generated = reference.addElement(GENERATED)
687                             .addAttribute(CLASS, SECTNUM);
688                     generated.setText(num + cnt + "   ");
689                     for (int i = 0; i < eTitle.size(); i++) {
690                         if (eTitle.get(i).attributeValue(ATTR_REFID).equals(id)) {
691                             Element generatedTitle = eTitle.get(i).addElement(
692                                     GENERATED);
693                             generatedTitle.addAttribute(CLASS, SECTNUM);
694                             generatedTitle.setText(num + cnt + "   ");
695                         }
696 
697                     }
698                 }
699                 
700                 text = text.trim();
701                 text = text.replaceAll("_", "");
702                 
703                 text = REGEX_STRONG.matcher(text).replaceAll(
704                         "<" + STRONG + ">$1</" + STRONG + ">");
705                 text = REGEX_EMPHASIS.matcher(text).replaceAll(
706                         "<" + EMPHASIS + ">$1</" + EMPHASIS + ">");
707                 
708                 try {
709                     Element textElement = DocumentHelper.parseText("<TMP>" + text + "</TMP>").getRootElement();
710                     reference.appendContent(textElement);
711 
712                 } catch (DocumentException eee) {
713                     if (log.isWarnEnabled()) {
714                         log.warn("Can't inline text for " + e, eee);
715                     }
716                 }
717                     
718             } else {
719                 do {
720                     e.addAttribute(LEVEL, "" + (level - 1));
721                     child.add(e);
722                     title.removeFirst();
723                     if (!title.isEmpty()) {
724                         e = title.getFirst();
725                         level = Integer.parseInt(e.attributeValue(LEVEL));
726                     }
727                 } while (!title.isEmpty() && level > 0);
728                 String numTmp = "";
729                 // numerotation
730                 if (sectnum) {
731                     numTmp = num + cnt + ".";
732                 }
733                 if (item != null) {
734                     item.add(composeLineContent(child, numTmp)); // Appel
735                     // recursif
736                 } else {
737                     result.add(composeLineContent(child, numTmp)); // Appel
738                     // recursif
739                 }
740             }
741         }
742         return result;
743     }
744 
745     /**
746      * @param lexer
747      * @return Element
748      * @throws Exception
749      */
750     private Element composeDocument(JRSTLexer lexer) throws Exception {
751         Element result = DocumentHelper.createElement(DOCUMENT);
752         result.addAttribute(LEVEL, String.valueOf(MAX_SECTION_DEPTH - 1));
753 
754         Element item = null;
755 
756         // skip blank line
757         skipBlankLine(lexer);
758 
759         // les liens anonymes
760         LinkedList<Element> items = lexer.refTarget();
761         for (Element e : items) {
762             eTarget.add(e);
763 
764         }
765 
766         // le header
767         item = lexer.peekHeader();
768         if (itemEquals(HEADER, item)) {
769             Element decoration = result.addElement(DECORATION);
770             Element header = decoration.addElement(HEADER);
771             header.addAttribute(ATTR_INLINE, TRUE).setText(item.getText());
772         }
773 
774         // le footer
775         item = lexer.peekFooter();
776         if (itemEquals(FOOTER, item)) {
777             footer = DocumentHelper.createElement(DECORATION);
778             Element header = footer.addElement(FOOTER);
779             header.addAttribute(ATTR_INLINE, TRUE).setText(item.getText());
780         }
781 
782         // les hyperlinks
783         LinkedList<Element> listItem = lexer.peekTargetAnonymous();
784         if (listItem != null) {
785             for (Element e : listItem) {
786                 Element anonym = DocumentHelper.createElement(TARGET);
787                 anonym.addAttribute(ANONYMOUS, "1");
788                 idMax++;
789                 anonym.addAttribute(ATTR_IDS, ID + idMax);
790 
791                 anonym.addAttribute(REFURI, e.attributeValue(REFURI).trim());
792 
793                 eTargetAnonymous.add(anonym);
794                 eTargetAnonymousCopy.add(anonym);
795             }
796         }
797 
798         // les eléments a enlever (deja parser : header, footer...)
799         item = lexer.peekRemove();
800         if (itemEquals(REMOVE, item)) {
801             lexer.remove();
802         }
803 
804         // skip blank line
805         skipBlankLine(lexer);
806         
807         // les commentaires
808         List<Element> comments = lexer.peekAllComment();
809 
810         // skip blank line
811         skipBlankLine(lexer);
812 
813         // le titre du doc
814         item = lexer.peekTitle();
815         if (itemEquals(TITLE, item)) {
816             lexer.remove();
817             Element title = result.addElement(TITLE);
818             String txt = item.getText();
819             result.addAttribute(ATTR_IDS, txt.replaceAll("[(\\W+)_]", " ")
820                     .toLowerCase().trim().replaceAll("\\s+", "-"));
821             result.addAttribute(NAMES, txt.toLowerCase().replaceAll(
822                     "[(\\W+)_&&[^\\:]]+", " ").trim());
823             copyLevel(item, title);
824             title.addAttribute(ATTR_INLINE, TRUE).setText(txt.trim());
825         }
826 
827         // skip blank line
828         skipBlankLine(lexer);
829 
830         // le sous titre du doc
831         item = lexer.peekTitle();
832         if (itemEquals(TITLE, item)) {
833             lexer.remove();
834             Element subtitle = result.addElement(SUBTITLE);
835             String txt = item.getText();
836             subtitle.addAttribute(ATTR_IDS, txt.replaceAll("[(\\W+)_]", " ")
837                     .toLowerCase().trim().replaceAll("\\s+", "-"));
838             subtitle.addAttribute(NAMES, txt.toLowerCase().replaceAll(
839                     "[(\\W+)_]", " ").trim());
840             copyLevel(item, subtitle);
841             DocumentHelper.createElement(FOOTNOTES);
842             subtitle.addAttribute(ATTR_INLINE, TRUE).setText(txt.trim());
843         }
844 
845         // skip blank line
846         skipBlankLine(lexer);
847 
848         // les infos du doc
849         item = lexer.peekDocInfo();
850         Element documentinfo = null;
851         while (itemEquals(DOCINFO, item) || itemEquals(FIELD_LIST, item)) {
852 
853             if (documentinfo == null) {
854                 documentinfo = result.addElement(DOCINFO);
855             }
856             skipBlankLine(lexer);
857             if (itemEquals(FIELD_LIST, item)) {
858                 Element field = composeFieldItemList(lexer);
859                 documentinfo.add(field);
860             } else {
861                 if ("author".equalsIgnoreCase(item.attributeValue(TYPE))) {
862                     documentinfo.addElement(AUTHOR).addAttribute(ATTR_INLINE,
863                             TRUE).setText(item.getText());
864                 } else if ("date".equalsIgnoreCase(item.attributeValue(TYPE))) {
865                     documentinfo.addElement(DATE)
866                             .addAttribute(ATTR_INLINE, TRUE).setText(
867                                     item.getText().trim());
868                 } else if ("organization".equalsIgnoreCase(item
869                         .attributeValue(TYPE))) {
870                     documentinfo.addElement(ORGANIZATION).addAttribute(
871                             ATTR_INLINE, TRUE).setText(item.getText().trim());
872                 } else if ("contact".equalsIgnoreCase(item
873                         .attributeValue(TYPE))) {
874                     documentinfo.addElement(CONTACT).addAttribute(ATTR_INLINE,
875                             TRUE).setText(item.getText().trim());
876                 } else if ("address".equalsIgnoreCase(item
877                         .attributeValue(TYPE))) {
878                     documentinfo.addElement(ADDRESS).addAttribute(ATTR_INLINE,
879                             TRUE).setText(item.getText().trim());
880                 } else if ("version".equalsIgnoreCase(item
881                         .attributeValue(TYPE))) {
882                     documentinfo.addElement(VERSION).addAttribute(ATTR_INLINE,
883                             TRUE).setText(item.getText().trim());
884                 } else if ("revision".equalsIgnoreCase(item
885                         .attributeValue(TYPE))) {
886                     documentinfo.addElement(REVISION).addAttribute(ATTR_INLINE,
887                             TRUE).setText(item.getText().trim());
888                 } else if ("status".equalsIgnoreCase(item
889                         .attributeValue(TYPE))) {
890                     documentinfo.addElement(STATUS).addAttribute(ATTR_INLINE,
891                             TRUE).setText(item.getText().trim());
892                 } else if ("copyright".equalsIgnoreCase(item
893                         .attributeValue(TYPE))) {
894                     documentinfo.addElement(COPYRIGHT).addAttribute(ATTR_INLINE,
895                             TRUE).setText(item.getText().trim());
896                 } else if ("authors".equalsIgnoreCase(item
897                         .attributeValue(TYPE))) {
898                     Element authors = documentinfo.addElement(AUTHORS);
899                     int t = 0;
900                     String line = item.getText();
901                     for (int i = 0; i < line.length(); i++) {
902                         if (line.charAt(i) == ';' || line.charAt(i) == ',') {
903                             authors.addElement(AUTHOR).addAttribute(ATTR_INLINE,
904                                     TRUE)
905                                     .setText(line.substring(t, i).trim());
906                             t = i + 1;
907                         }
908 
909                     }
910                     authors.addElement(AUTHOR).addAttribute(ATTR_INLINE, TRUE)
911                             .setText(line.substring(t, line.length()).trim());
912                 }
913                 lexer.remove();
914             }
915             // skip blank line
916             // skipBlankLine(lexer);
917             item = lexer.peekDocInfo();
918 
919         }
920 
921          // Ajout des commentaires
922     //        System.out.println(comment.asXML());               
923         for (Element comment : comments){
924             result.add(composeComment(comment));
925         }
926 
927         // l'abstract du doc
928         item = lexer.peekTitle();
929         while (itemNotEquals(TITLE, item) && !lexer.eof()) {
930             composeBody(lexer, result);
931             item = lexer.peekTitle();
932         }
933 
934         // les sections
935         item = lexer.peekTitle();
936         while (itemEquals(TITLE, item, true, lexer.eof())) {
937             Element section = composeSection(lexer);
938             result.add(section);
939             item = lexer.peekTitle();
940         }
941 
942         // on ajoute le footer a la fin
943         if (footer != null) {
944             result.add(footer);
945         }
946 
947         return result;
948     }
949 
950     /**
951      * <p>
952      * skip blank line
953      * </p>
954      * 
955      * @param lexer
956      * @throws DocumentException
957      * @throws IOException
958      */
959     private void skipBlankLine(JRSTLexer lexer) throws IOException,
960             DocumentException {
961         Element item = lexer.peekBlankLine();
962         // skip blank line
963         while (itemEquals(JRSTLexer.BLANK_LINE, item)) {
964             // go to the next element
965             lexer.remove();
966             item = lexer.peekBlankLine();
967         }
968     }
969 
970     /**
971      * *
972      * <p>
973      * Corps du document
974      * </p>
975      * 
976      * @param lexer
977      * @return Element
978      * @throws DocumentException
979      * @throws IOException
980      */
981     private Element composeBody(JRSTLexer lexer, Element parent)
982             throws Exception {
983 
984         Element item = lexer.peekTitleOrBodyElement();
985         if (item == null && !lexer.eof()) {
986             item = lexer.peekTitleOrBodyElement();
987         }
988 
989         while (!lexer.eof() && itemNotEquals(TITLE, item)
990                 && isUpperLevel(item, parent)) {
991             if (itemEquals(JRSTLexer.BLANK_LINE, item)) {
992                 // go to the next element
993                 lexer.remove();
994             } else if (itemEquals(REMOVE, item)) {
995                 lexer.remove();
996             } else if (itemEquals(INCLUDE, item)) {
997                 lexer.remove();
998                 Element list = composeInclude(item);
999                 parent.add(list);
1000             } else if (itemEquals(DOCTEST_BLOCK, item)) {
1001                 lexer.remove();
1002                 Element list = composeDoctestBlock(item);
1003                 parent.add(list);
1004             } else if (itemEquals(ADMONITION, item)) {
1005                 lexer.remove();
1006                 Element list = composeAdmonition(item);
1007                 parent.add(list);
1008             } else if (itemEquals(SIDEBAR, item)) {
1009                 lexer.remove();
1010                 Element list = composeSidebar(item);
1011                 parent.add(list);
1012             } else if (itemEquals(TOPIC, item)) {
1013                 lexer.remove();
1014                 Element list = composeTopic(item);
1015                 parent.add(list);
1016             } else if (itemEquals(TRANSITION, item)) {
1017                 lexer.remove();
1018                 Element para = parent.addElement(TRANSITION);
1019                 copyLevel(item, para);
1020             } else if (itemEquals(PARAGRAPH, item)) {
1021                 lexer.remove();
1022                 Element para = parent.addElement(PARAGRAPH);
1023                 copyLevel(item, para);
1024                 para.addAttribute(ATTR_INLINE, TRUE).setText(item.getText());
1025             } else if (itemEquals(JRSTLexer.DIRECTIVE, item)) {
1026                 lexer.remove();
1027                 Node directive = composeDirective(item);
1028                 parent.add(directive);
1029             } else if (itemEquals(SUBSTITUTION_DEFINITION, item)) {
1030                 lexer.remove();
1031                 Element subst = composeSubstitutionDefinition(item);
1032                 parent.add(subst);
1033             } else if (itemEquals(LITERAL_BLOCK, item)) {
1034                 lexer.remove();
1035                 Element para = parent.addElement(LITERAL_BLOCK);
1036                 copyLevel(item, para);
1037                 para.setText(item.getText());
1038             } else if (itemEquals(JRSTLexer.TABLE, item)) {
1039                 lexer.remove();
1040                 Element table = composeTable(item);
1041                 parent.add(table);
1042                 // Element para = parent.addElement(TABLE);
1043                 // copyLevel(item, para);
1044                 // para.setText(item.getText());
1045             } else if (itemEquals(LINE_BLOCK, item)) {
1046                 lexer.remove();
1047                 Element list = composeLineBlock(lexer, item);
1048                 parent.add(list);
1049             } else if (itemEquals(BULLET_LIST, item)) {
1050                 Element list = composeBulletList(lexer);
1051                 parent.add(list);
1052             } else if (itemEquals(ENUMERATED_LIST, item)) {
1053                 Element list = composeEnumeratedList(lexer);
1054                 parent.add(list);
1055             } else if (itemEquals(DEFINITION_LIST, item)) {
1056                 Element list = composeDefinitionList(lexer);
1057                 parent.add(list);
1058             } else if (itemEquals(FIELD_LIST, item)) {
1059                 Element list = composeFieldList(lexer);
1060                 parent.add(list);
1061             } else if (itemEquals(BLOCK_QUOTE, item)) {
1062                 lexer.remove();
1063                 Element list = composeBlockQuote(item);
1064                 parent.add(list);
1065             } else if (itemEquals(OPTION_LIST, item)) {
1066                 Element list = composeOptionList(lexer);
1067                 parent.add(list);
1068             } else if (itemEquals(TARGET, item)) {
1069                 lexer.remove();
1070                 Element list = composeTarget(item);
1071                 if (list != null) {
1072                     try {
1073                 	parent.add(list);
1074                     } catch (IllegalAddException e) {}
1075                 } else
1076                     System.err.println("Unknown target name : \"" + item.attributeValue(ATTR_IDS) + "\"");
1077             } else if (itemEquals(TARGETANONYMOUS, item)) {
1078                 lexer.remove();
1079                 Element list = composeTargetAnonymous(item);
1080                 parent.add(list);
1081             } else if (itemEquals(FOOTNOTES, item)) {
1082                 lexer.remove();
1083                 Element[] list = composeFootnote(item);
1084                 for (Element l : list) {
1085                     parent.add(l);
1086                 }
1087             } else if (itemEquals(COMMENT, item)) {
1088                 lexer.remove();
1089                 Element list = composeComment(item);
1090                 parent.add(list);
1091             }
1092 
1093             else {
1094                 if (ERROR_MISSING_ITEM) {
1095                     throw new DocumentException("Unknow item type: "
1096                             + item.getName());
1097                 } else {
1098                     lexer.remove();
1099                 }
1100             }
1101 
1102             // Pour afficher le "PseudoXML"
1103             // if (item!=null) System.out.println(item.asXML());
1104 
1105             item = lexer.peekTitleOrBodyElement();
1106         }
1107 
1108         return parent;
1109     }
1110 
1111     /**
1112      * <p>
1113      * include un document rst
1114      * </p>
1115      * 
1116      * <pre>
1117      * .. include:: doc.rst
1118      * </pre>
1119      * 
1120      * <p>
1121      * include un document literal (code...)
1122      * </p>
1123      * 
1124      * <pre>
1125      * .. include:: literal
1126      *       doc.rst
1127      * </pre>
1128      * 
1129      * @param item
1130      * @return Element
1131      * @throws Exception
1132      */
1133     private Element composeInclude(Element item) throws Exception {
1134         String option = item.attributeValue(OPTION);
1135         String path = item.getText();
1136         Element result = null;
1137         if (option.equals(LITERAL)) {
1138             result = DocumentHelper.createElement(LITERAL_BLOCK);
1139             FileReader reader = new FileReader(path);
1140             BufferedReader bf = new BufferedReader(reader);
1141             String line = "";
1142             String lineTmp = bf.readLine();
1143             while (lineTmp != null) {
1144                 line += '\n' + lineTmp;
1145                 lineTmp = bf.readLine();
1146             }
1147             result.setText(line);
1148         } else {
1149             File fileIn = new File(path);
1150             URL url = fileIn.toURI().toURL();
1151             Reader in = new InputStreamReader(url.openStream());
1152 
1153             Document doc = newJRSTReader(in);
1154 
1155             result = doc.getRootElement();
1156         }
1157         return result;
1158     }
1159 
1160     /**
1161      * <pre>
1162      * ..
1163      *   So this block is not &quot;lost&quot;,
1164      *   despite its indentation.
1165      * </pre>
1166      * 
1167      * @param item
1168      * @return Element
1169      */
1170     private Element composeComment(Element item) {
1171 
1172         return item;
1173     }
1174 
1175     /**
1176      * <pre>
1177      * __ http://truc.html
1178      * </pre>
1179      * 
1180      * @param item
1181      * @return Element
1182      */
1183     private Element composeTargetAnonymous(Element item) {
1184         Element result = null;
1185         result = eTargetAnonymousCopy.getFirst();
1186         eTargetAnonymousCopy.removeFirst();
1187         return result;
1188     }
1189 
1190     /**
1191      * <pre _ target: target.html </pre>
1192      * 
1193      * @param item
1194      * @return Element
1195      */
1196     private Element composeTarget(Element item) {
1197         Element result = null;
1198         for (Element e : eTarget) {
1199             if (e.attributeValue(ID).equals(item.attributeValue(ID))) {
1200                 result = e;
1201             }
1202         }
1203         return result;
1204     }
1205 
1206     /**
1207      * <pre>
1208      * .. [#] This is a footnote
1209      * </pre>
1210      * 
1211      * @param item
1212      * @return Element
1213      * @throws Exception
1214      */
1215     private Element[] composeFootnote(Element item) throws Exception {
1216         Element[] result = null;
1217         if (itemEquals(FOOTNOTES, item)) {
1218             List<Element> footnotes = (List<Element>) item
1219                     .selectNodes(FOOTNOTE);
1220             result = new Element[footnotes.size()];
1221             int cnt = 0;
1222             for (Element footnote : footnotes) {
1223                 result[cnt] = DocumentHelper.createElement(FOOTNOTE);
1224                 Element efootnote = DocumentHelper.createElement(FOOTNOTE);
1225                 int labelMax = 0;
1226 
1227                 for (int i = 0; i < lblFootnotes.size(); i++) {
1228                     int lbl = lblFootnotes.get(i);
1229                     labelMax = Math.max(lbl, labelMax);
1230                 }
1231 
1232                 boolean[] labels = new boolean[labelMax];
1233                 for (int i = 0; i < labels.length; i++) {
1234                     labels[i] = false;
1235                 }
1236                 for (int i = 0; i < lblFootnotes.size(); i++) {
1237                     labels[lblFootnotes.get(i) - 1] = true;
1238                 }
1239                 idMax++;
1240                 String name = null;
1241                 String id = "";
1242                 String label = null;
1243                 String type = footnote.attributeValue(TYPE);
1244                 if (type.equals(AUTONUM) || type.equals(AUTONUMLABEL)) {
1245                     result[cnt].addAttribute(AUTO, "1");
1246                 }
1247                 if (type.equals(AUTOSYMBOL)) {
1248                     result[cnt].addAttribute(AUTO, "*");
1249                 }
1250                 result[cnt].addAttribute(BACKREFS, ID + idMax);
1251                 efootnote.addAttribute(BACKREFS, ID + idMax);
1252                 if (type.equals(NUM) || type.equals(AUTONUMLABEL)) {
1253                     name = footnote.attributeValue(NAME);
1254                     if (type.equals(AUTONUMLABEL)) {
1255                         id = name;
1256                     }
1257                     else {
1258                         label = name;
1259                     }
1260                 }
1261                 if (type.equals(AUTONUM) || type.equals(AUTONUMLABEL)) {
1262                     boolean done = false;
1263 
1264                     for (int i = 0; i < labels.length && !done; i++) {
1265                         if (!labels[i]) {
1266                             done = true;
1267                             label = "" + (i + 1);
1268                         }
1269                     }
1270                     if (!done) {
1271                         label = "" + (labels.length + 1);
1272                     }
1273                     if (type.equals(AUTONUM)) {
1274                         name = label;
1275                     }
1276                 }
1277                 if (type.equals(AUTOSYMBOL)) {
1278 
1279                     int nb = Math.abs(symbolMax / 10) + 1;
1280                     char symbol = FOOTNOTE_SYMBOL.charAt(symbolMax % 10);
1281                     label = "";
1282                     for (int j = 0; j < nb; j++) {
1283                         label += symbol;
1284                     }
1285                     symbolMax++;
1286 
1287                 }
1288                 result[cnt].addAttribute(ATTR_IDS, "" + id);
1289                 efootnote.addAttribute(ATTR_IDS, "" + id);
1290                 if (!type.equals(AUTOSYMBOL)) {
1291                     result[cnt].addAttribute(NAME, "" + name);
1292                     efootnote.addAttribute(NAME, "" + name);
1293                 }
1294                 result[cnt].addElement(LABEL).setText("" + label);
1295                 efootnote.addAttribute(LABEL, "" + label);
1296                 if (!type.equals(AUTOSYMBOL)) {
1297                     lblFootnotes.add(Integer.parseInt(label));
1298                 }
1299                 efootnote.addAttribute(TYPE, type);
1300                 eFootnotes.add(efootnote);
1301                 String text = footnote.getText();
1302                 Document doc = newJRSTReader(new StringReader(text));
1303                 result[cnt].appendContent(doc.getRootElement());
1304 
1305                 cnt++;
1306             }
1307         }
1308         for (int i = 0; i < result.length; i++) {
1309             if (result[i].attributeValue(ATTR_IDS).equals("")) {
1310                 idMax++;
1311                 result[i].addAttribute(ATTR_IDS, ID + idMax);
1312                 (eFootnotes.get(i)).addAttribute(ATTR_IDS, ID + idMax);
1313             }
1314 
1315         }
1316 
1317         return result;
1318     }
1319 
1320     /**
1321      * <pre>
1322      * -a command-line option &quot;a&quot; -1 file, --one=file, --two file Multiple
1323      * options with arguments.
1324      * </pre>
1325      * 
1326      * @param lexer
1327      * @return Element
1328      * @throws Exception
1329      * @throws DocumentException
1330      */
1331     private Element composeOptionList(JRSTLexer lexer)
1332             throws DocumentException, Exception {
1333         Element item = lexer.peekOption();
1334         Element result = DocumentHelper.createElement(OPTION_LIST);
1335         while (itemEquals(OPTION_LIST, item)) {
1336             lexer.remove();
1337             Element optionListItem = result.addElement(OPTION_LIST_ITEM);
1338             Element optionGroup = optionListItem.addElement(OPTION_GROUP);
1339             List<Element> option = (List<Element>) item.selectNodes(OPTION);
1340             for (Element e : option) {
1341                 Element eOption = optionGroup.addElement(OPTION);
1342                 eOption.addElement(OPTION_STRING).setText(
1343                         e.attributeValue(OPTION_STRING));
1344                 if (e.attributeValue(DELIMITEREXISTE).equals(TRUE)) {
1345                     eOption.addElement(OPTION_ARGUMENT).addAttribute(
1346                             DELIMITER, e.attributeValue(DELIMITER))
1347                             .setText(e.attributeValue(OPTION_ARGUMENT));
1348                 }
1349             }
1350             Element description = optionListItem.addElement(DESCRIPTION);
1351 
1352             String text = item.getText();
1353             Document doc = newJRSTReader(new StringReader(text));
1354             description.appendContent(doc.getRootElement());
1355 
1356             item = lexer.peekOption();
1357         }
1358         return result;
1359     }
1360 
1361     /**
1362      * <pre>
1363      * .. topic:: Title
1364      * 
1365      *    Body.
1366      * </pre>
1367      * 
1368      * @param Element
1369      *            item
1370      * @return Element
1371      * @throws Exception
1372      */
1373 
1374     private Element composeTopic(Element item) throws Exception {
1375         Element result = null;
1376         result = DocumentHelper.createElement(TOPIC);
1377         result.addElement(TITLE).addAttribute(ATTR_INLINE, TRUE).setText(
1378                 item.attributeValue(TITLE));
1379         String text = item.getText();
1380         Document doc = newJRSTReader(new StringReader(text));
1381         result.appendContent(doc.getRootElement());
1382 
1383         return result;
1384     }
1385 
1386     /**
1387      * <pre>
1388      * .. sidebar:: Title
1389      *    :subtitle: If Desired
1390      *    
1391      *    Body.
1392      * </pre>
1393      * 
1394      * @param Element
1395      * @return Element
1396      * @throws Exception
1397      */
1398 
1399     private Element composeSidebar(Element item) throws Exception {
1400         Element result = null;
1401         result = DocumentHelper.createElement(SIDEBAR);
1402         result.addElement(TITLE).addAttribute(ATTR_INLINE, TRUE).setText(
1403                 item.attributeValue(TITLE));
1404         if (item.attributeValue(SUBEXISTE).equals(TRUE)) {
1405             result.addElement(SUBTITLE).addAttribute(ATTR_INLINE, TRUE).setText(
1406                     item.attributeValue(SUBTITLE));
1407         }
1408         
1409         String text = item.getText();
1410         Document doc = newJRSTReader(new StringReader(text));
1411         result.appendContent(doc.getRootElement());
1412 
1413         return result;
1414     }
1415 
1416     /**
1417      * <pre>
1418      * | line block
1419      * |
1420      * |    indent
1421      * </pre>
1422      * 
1423      * @param lexer
1424      * @param item
1425      * @return Element
1426      * @throws Exception
1427      */
1428     private Element composeLineBlock(JRSTLexer lexer, Element item)
1429             throws Exception {
1430         Element result = null;
1431         result = DocumentHelper.createElement(LINE_BLOCK);
1432         List<Element> lines = (List<Element>) item.selectNodes(LINE);
1433         int[] levels = new int[lines.size()];
1434         int cnt = 0;
1435         for (Element l : lines) {
1436             levels[cnt] = Integer.parseInt(l.attributeValue(LEVEL));
1437             cnt++;
1438         }
1439         cnt = 0;
1440         boolean[] lineDone = new boolean[lines.size()];
1441         for (int i = 0; i < lineDone.length; i++) {
1442             lineDone[i] = false;
1443         }
1444         for (Element l : lines) {
1445             if (levels[cnt] == 0) {
1446                 result.addElement(LINE).addAttribute(ATTR_INLINE, TRUE).setText(
1447                         l.getText());
1448             }
1449             else {
1450                 if (!lineDone[cnt]) {
1451                     Element newItem = DocumentHelper.createElement(LINE_BLOCK);
1452                     Boolean done = false;
1453                     for (int i = cnt; i < lines.size() && !done; i++) {
1454                         if (levels[i] > 0) {
1455                             Element eLine = newItem.addElement(LINE);
1456                             eLine.addAttribute(LEVEL, "" + (levels[i] - 1));
1457                             eLine.setText(lines.get(i).getText());
1458                             lineDone[i] = true;
1459                         } else {
1460                             done = true;
1461                         }
1462 
1463                     }
1464                     Element eLineBlock = result.addElement(LINE_BLOCK);
1465                     // Appel recursif
1466                     eLineBlock.appendContent(composeLineBlock(lexer, newItem));
1467                 }
1468             }
1469             cnt++;
1470 
1471         }
1472         return result;
1473     }
1474 
1475     /**
1476      * <pre>
1477      * &gt;&gt;&gt; print 'this is a Doctest block'
1478      * this is a Doctest block
1479      * </pre>
1480      * 
1481      * @param Element
1482      * @return Element
1483      */
1484     private Element composeDoctestBlock(Element item) {
1485         return item;
1486     }
1487 
1488     /**
1489      * <pre>
1490      * As a great paleontologist once said,
1491      * 
1492      *      This theory, that is mine, is mine.
1493      * 
1494      *      -- Anne Elk (Miss)
1495      * </pre>
1496      * 
1497      * @param Element
1498      * @return Element
1499      * @throws Exception
1500      * 
1501      */
1502     private Element composeBlockQuote(Element item) throws Exception {
1503         Element result = null;
1504         result = DocumentHelper.createElement(BLOCK_QUOTE);
1505 
1506         String text = item.getText();
1507         Document doc = newJRSTReader(new StringReader(text));
1508         result.appendContent(doc.getRootElement());
1509         String sAttribution = item.attributeValue(ATTRIBUTION);
1510         if (sAttribution != null) {
1511             Element attribution = result.addElement(ATTRIBUTION);
1512             attribution.setText(sAttribution);
1513             attribution.addAttribute(ATTR_INLINE, TRUE);
1514         }
1515         return result;
1516     }
1517 
1518     /**
1519      * <pre>
1520      * .. admonition:: And, by the way...
1521      * 
1522      *      You can make up your own admonition too.
1523      * </pre>
1524      * 
1525      * @param Element
1526      * @return Element
1527      * @throws Exception
1528      * 
1529      */
1530     private Element composeAdmonition(Element item) throws Exception {
1531         Element result = null;
1532         if (item.attributeValue(TYPE).equalsIgnoreCase(ADMONITION)) {
1533             result = DocumentHelper.createElement(ADMONITION);
1534             String title = item.attributeValue(TITLE);
1535             String admonitionClass = "admonition_" + title;
1536             admonitionClass = admonitionClass.toLowerCase().replaceAll(
1537                     "\\p{Punct}", "");
1538             admonitionClass = admonitionClass.replace(' ', '-');
1539             admonitionClass = admonitionClass.replace('\n', '-');
1540             result.addAttribute(CLASS, admonitionClass);
1541             result.addElement(TITLE).addAttribute(ATTR_INLINE, TRUE).setText(
1542                     title.trim());
1543         } else {
1544             result = DocumentHelper.createElement(item.attributeValue(TYPE)
1545                     .toLowerCase());
1546         }
1547         
1548         String text = item.getText();
1549         Document doc = newJRSTReader(new StringReader(text));
1550         result.appendContent(doc.getRootElement());
1551         return result;
1552     }
1553 
1554     /**
1555      * parse all directives
1556      * 
1557      * @param Element
1558      * @return Node
1559      */
1560     private Node composeDirective(Element item) {
1561         Node result = item;
1562         String type = item.attributeValue(JRSTLexer.DIRECTIVE_TYPE);
1563         if (type.equals(SECTNUM)) {
1564             sectnum = true;
1565         }
1566         JRSTDirective directive = getDirective(type);
1567         if (directive == null) {
1568             directive = getDefaultDirective(type);
1569         }
1570         if (directive != null) {
1571             result = directive.parse(item);
1572         } else {
1573             log.warn("Unknow directive type '" + type + "' in: " + item);
1574         }
1575         return result;
1576     }
1577 
1578     /**
1579      * <pre>
1580      * .. |biohazard| image:: biohazard.png
1581      * </pre>
1582      * 
1583      * @param Element
1584      * @return Element
1585      */
1586     private Element composeSubstitutionDefinition(Element item) {
1587         Element result = item;
1588         Element child = (Element) item.selectSingleNode("*");
1589         Node newChild = composeDirective(child);
1590         result.remove(child); // remove old after composeDirective, because
1591         // directive can be used this parent
1592         result.add(newChild);
1593         return result;
1594     }
1595 
1596     /**
1597      * <p>
1598      * Complexe Table
1599      * </p>
1600      * 
1601      * <pre>
1602      * +------------------------+------------+---------------------+
1603      * | body row 3             | Cells may  | - Table cells       |
1604      * +------------------------+ span rows. | - contain           |
1605      * | body row 4             |            | - body elements.    |
1606      * +------------------------+------------+---------------------+
1607      * </pre>
1608      * 
1609      * <p>
1610      * And simple table
1611      * </p>
1612      * 
1613      * <pre>
1614      * =====  =====  ======
1615      *    Inputs     Output
1616      * ============  ======
1617      *   A      B    A or B
1618      * ------------  ------
1619      *   A      B    A or B
1620      * =====  =====  ======
1621      * </pre>
1622      * 
1623      * @param Element
1624      * @return Element
1625      * 
1626      */
1627     private Element composeTable(Element item) throws Exception {
1628 
1629         Element result = DocumentHelper.createElement(TABLE);
1630 
1631         int tableWidth = Integer.parseInt(item
1632                 .attributeValue(JRSTLexer.TABLE_WIDTH));
1633 
1634         TreeSet<Integer> beginCellList = new TreeSet<Integer>();
1635 
1636         for (Element cell : (List<Element>) item.selectNodes(JRSTLexer.ROW
1637                 + "/" + JRSTLexer.CELL)) {
1638             Integer begin = Integer.valueOf(cell
1639                     .attributeValue(JRSTLexer.CELL_INDEX_START));
1640             beginCellList.add(begin);
1641         }
1642 
1643         int[] beginCell = new int[beginCellList.size() + 1]; // + 1 to put
1644         // table width
1645         // to simulate
1646         // new cell
1647         int[] lengthCell = new int[beginCellList.size()];
1648 
1649         int cellNumber = 0;
1650         for (int b : beginCellList) {
1651             beginCell[cellNumber] = b;
1652             if (cellNumber > 0) {
1653                 lengthCell[cellNumber - 1] = beginCell[cellNumber]
1654                         - beginCell[cellNumber - 1];
1655             }
1656             cellNumber++;
1657         }
1658         beginCell[cellNumber] = tableWidth;
1659         lengthCell[cellNumber - 1] = beginCell[cellNumber]
1660                 - beginCell[cellNumber - 1];
1661 
1662         Element tgroup = result.addElement(TGROUP).addAttribute("cols",
1663                 String.valueOf(cellNumber));
1664         for (int width : lengthCell) {
1665             tgroup.addElement(COLSPEC).addAttribute("colwidth",
1666                     String.valueOf(width));
1667         }
1668 
1669         Element rowList = null;
1670         if (TRUE.equals(item.attributeValue(JRSTLexer.TABLE_HEADER))) {
1671             rowList = tgroup.addElement(THEAD);
1672         } else {
1673             rowList = tgroup.addElement(TBODY);
1674         }
1675         List<Element> rows = (List<Element>) item.selectNodes(JRSTLexer.ROW);
1676         for (int r = 0; r < rows.size(); r++) {
1677             Element row = rowList.addElement(ROW);
1678             List<Element> cells = (List<Element>) rows.get(r).selectNodes(
1679                     JRSTLexer.CELL);
1680             for (int c = 0; c < cells.size(); c++) {
1681                 Element cell = cells.get(c);
1682                 // si la cellule a ete utilise pour un regroupement vertical on
1683                 // la passe
1684                 if (!TRUE.equals(cell.attributeValue("used"))) {
1685                     Element entry = row.addElement(ENTRY);
1686                     String text = "";
1687 
1688                     // on regroupe les cellules verticalement
1689                     int morerows = -1;
1690                     Element tmpCell = null;
1691                     String cellStart = cell
1692                             .attributeValue(JRSTLexer.CELL_INDEX_START);
1693                     do {
1694                         morerows++;
1695                         tmpCell = (Element) rows.get(r + morerows)
1696                                 .selectSingleNode(
1697                                         JRSTLexer.CELL + "[@"
1698                                                 + JRSTLexer.CELL_INDEX_START
1699                                                 + "=" + cellStart + "]");
1700                         text += tmpCell.getText();
1701                         // on marque la cellule comme utilise
1702                         tmpCell.addAttribute("used", TRUE);
1703                     } while (!TRUE.equals(tmpCell
1704                             .attributeValue(JRSTLexer.CELL_END)));
1705 
1706                     if (morerows > 0) {
1707                         entry
1708                                 .addAttribute("morerows", String
1709                                         .valueOf(morerows));
1710                     }
1711 
1712                     // on compte le nombre de cellules regroupees
1713                     // horizontalement
1714                     int morecols = 0;
1715                     tmpCell = cells.get(c + morecols);
1716                     int cellEnd = Integer.parseInt(tmpCell
1717                             .attributeValue(JRSTLexer.CELL_INDEX_END));
1718                     while (cellEnd + 1 != beginCell[c + morecols + 1]) {
1719                         morecols++;
1720                         // tmpCell = cells.get(c + morecols);
1721                         // cellEnd =
1722                         // Integer.parseInt(tmpCell.attributeValue(JRSTLexer.
1723                         // CELL_INDEX_END));
1724                     }
1725                     if (morecols > 0) {
1726                         entry
1727                                 .addAttribute("morecols", String
1728                                         .valueOf(morecols));
1729                     }
1730                     // parse entry text in table
1731                     Document doc = newJRSTReader(new StringReader(text));
1732                     entry.appendContent(doc.getRootElement());
1733                 }
1734             }
1735             if (TRUE.equals(rows.get(r).attributeValue(
1736                     JRSTLexer.ROW_END_HEADER))) {
1737                 rowList = tgroup.addElement(TBODY);
1738             }
1739         }
1740 
1741         return result;
1742     }
1743 
1744     /**
1745      * <p>
1746      * items begin with "-", "+", or "*"
1747      * </p>
1748      * 
1749      * <pre>
1750      * * aaa
1751      *   - bbb
1752      * * ccc
1753      *   - ddd
1754      *      + eee
1755      * </pre>
1756      * 
1757      * @param lexer
1758      * @return Element
1759      * @throws Exception
1760      */
1761     private Element composeBulletList(JRSTLexer lexer) throws Exception {
1762         Element item = lexer.peekBulletList();
1763         Element result = DocumentHelper.createElement(BULLET_LIST);
1764         copyLevel(item, result);
1765         result.addAttribute(BULLET, item.attributeValue(BULLET));
1766         while (itemEquals(BULLET_LIST, item) && isSameLevel(item, result)
1767                 && hasSameAttribute(item, result, BULLET)) {
1768             lexer.remove();
1769             Element bullet = result.addElement(LIST_ITEM);
1770             copyLevel(item, bullet);
1771             bullet.addElement(PARAGRAPH).addAttribute(ATTR_INLINE, TRUE)
1772                     .setText(item.getText());
1773             composeBody(lexer, bullet);
1774 
1775             item = lexer.peekBulletList();
1776         }
1777         return result;
1778     }
1779 
1780     /**
1781      * <pre>
1782      * 3. et meme
1783      * * #. pour voir
1784      * * I) de tout
1785      * (a) pour tout
1786      * (#) vraiment tout
1787      * </pre>
1788      * 
1789      * @param lexer
1790      * @return Element
1791      * @throws Exception
1792      */
1793     private Element composeEnumeratedList(JRSTLexer lexer) throws Exception {
1794         Element item = lexer.peekEnumeratedList();
1795         Element result = DocumentHelper.createElement(ENUMERATED_LIST);
1796         copyLevel(item, result);
1797         String enumType = item.attributeValue(ENUMTYPE);
1798         if (!enumType.equals("arabic")) {
1799             result.addAttribute(START, item.attributeValue(START));
1800         }
1801         result.addAttribute(PREFIX, item.attributeValue(PREFIX));
1802         result.addAttribute(SUFFIX, item.attributeValue(SUFFIX));
1803         result.addAttribute(ENUMTYPE, enumType);
1804         while (itemEquals(ENUMERATED_LIST, item)
1805                 && isSameLevel(item, result)
1806                 && hasSameAttribute(item, result, PREFIX, SUFFIX)
1807                 && (AUTO.equals(item.attributeValue(ENUMTYPE)) || hasSameAttribute(
1808                         item, result, ENUMTYPE))) {
1809             lexer.remove();
1810             Element e = result.addElement(LIST_ITEM);
1811             copyLevel(item, e);
1812             e.addElement(PARAGRAPH).addAttribute(ATTR_INLINE, TRUE).setText(
1813                     item.getText());
1814             composeBody(lexer, e);
1815 
1816             item = lexer.peekEnumeratedList();
1817         }
1818         return result;
1819     }
1820 
1821     /**
1822      * <pre>
1823      * le mot : la classe
1824      *   la definition
1825      * </pre>
1826      * 
1827      * @param lexer
1828      * @return Element
1829      * @throws Exception
1830      */
1831     private Element composeDefinitionList(JRSTLexer lexer) throws Exception {
1832         Element item = lexer.peekBodyElement();
1833         Element result = DocumentHelper.createElement(DEFINITION_LIST);
1834         copyLevel(item, result);
1835         while (itemEquals(DEFINITION_LIST, item) && isSameLevel(item, result)) {
1836             lexer.remove();
1837             Element def = result.addElement(DEFINITION_LIST_ITEM);
1838             copyLevel(item, def);
1839 
1840             Element term = def.addElement(TERM);
1841             copyLevel(item, term);
1842             term.addAttribute(ATTR_INLINE, TRUE).setText(
1843                     item.attributeValue("term"));
1844 
1845             String[] classifiers = StringUtil.split(item
1846                     .attributeValue("classifiers"), " : ");
1847             for (String classifierText : classifiers) {
1848                 Element classifier = def.addElement("classifier");
1849                 copyLevel(item, classifier);
1850                 classifier.addAttribute(ATTR_INLINE, TRUE).setText(
1851                         classifierText);
1852             }
1853 
1854             Element definition = def.addElement(DEFINITION);
1855             definition.addElement(PARAGRAPH).addAttribute(ATTR_INLINE, TRUE)
1856                     .setText(item.getText());
1857             copyLevel(item, definition);
1858 
1859             composeBody(lexer, definition);
1860 
1861             item = lexer.peekBodyElement();
1862         }
1863         return result;
1864     }
1865 
1866     /**
1867      * <pre>
1868      * :un peu: de field
1869      *      ca ne fait pas
1870      *      de mal
1871      * </pre>
1872      * 
1873      * @param lexer
1874      * @return Element
1875      * @throws Exception
1876      */
1877     private Element composeFieldList(JRSTLexer lexer) throws Exception {
1878         Element item = lexer.peekBodyElement();
1879         Element result = DocumentHelper.createElement(FIELD_LIST);
1880         copyLevel(item, result);
1881         while (itemEquals(FIELD_LIST, item) && isSameLevel(item, result)) {
1882             Element field = composeFieldItemList(lexer);
1883             result.add(field);
1884             item = lexer.peekBodyElement();
1885         }
1886         return result;
1887     }
1888 
1889     /**
1890      * <pre>
1891      * :field1: avec un
1892      *    petit texte 
1893      *    - et meme un 
1894      *    - debut 
1895      *    - de list
1896      * </pre>
1897      * 
1898      * @param lexer
1899      * @return Element
1900      * @throws Exception
1901      */
1902     private Element composeFieldItemList(JRSTLexer lexer) throws Exception {
1903         Element item = lexer.peekFieldList();
1904         if (itemEquals(FIELD_LIST, item)) {
1905             lexer.remove();
1906             Element field = DocumentHelper.createElement(FIELD);
1907             copyLevel(item, field);
1908             Element fieldName = field.addElement(FIELD_NAME);
1909             copyLevel(item, fieldName);
1910             fieldName.addAttribute(ATTR_INLINE, TRUE).setText(
1911                     item.attributeValue(NAME));
1912             Element fieldBody = field.addElement(FIELD_BODY);
1913             fieldBody.addElement(PARAGRAPH).addAttribute(ATTR_INLINE, TRUE)
1914                     .setText(item.getText());
1915             copyLevel(item, fieldBody);
1916             composeBody(lexer, fieldBody);
1917 
1918             return field;
1919         } else {
1920             throw new DocumentException("Waiting for " + FIELD_LIST
1921                     + " and found " + item.getName());
1922         }
1923     }
1924 
1925     /**
1926      * <pre>
1927      * DEFINITIONS
1928      * -----------
1929      * </pre>
1930      * 
1931      * @param lexer
1932      * @return Element
1933      * @throws Exception
1934      */
1935     private Element composeSection(JRSTLexer lexer) throws Exception {
1936         Element result = DocumentHelper.createElement(SECTION);
1937         Element firstTitle = null;
1938 
1939         Element item = null;
1940 
1941         // le titre de la section
1942         item = lexer.peekTitle();
1943         if (itemEquals(TITLE, item, true, lexer.eof())) {
1944             lexer.remove();
1945             firstTitle = item;
1946             Element title = result.addElement(TITLE);
1947             copyLevel(item, result);
1948             copyLevel(item, title);
1949             title.addAttribute(ATTR_INLINE, TRUE).setText(item.getText().trim());
1950             result.addAttribute(ATTR_IDS, item.getText().replaceAll("\\W+", " ")
1951                     .trim().toLowerCase().replaceAll("\\W+", "-"));
1952             result.addAttribute(NAME, item.getText().toLowerCase().trim());
1953             eTitle.add(title);
1954         }
1955 
1956         // le contenu de la section
1957         item = lexer.peekTitle();
1958         while (itemNotEquals(TITLE, item) && !lexer.eof()) {
1959             composeBody(lexer, result);
1960             item = lexer.peekTitle();
1961         }
1962 
1963         // les sous sections
1964         item = lexer.peekTitle();
1965         while (itemEquals(TITLE, item) && isUpperLevel(item, firstTitle)) {
1966             Element section = composeSection(lexer);
1967             result.add(section);
1968             item = lexer.peekTitle();
1969         }
1970 
1971         return result;
1972     }
1973 
1974     /**
1975      * Indique si la sous section est bien une sous section, c-a-d dire que son
1976      * level est superieur a celui de la section
1977      * 
1978      * @param item
1979      * @param firstTitle
1980      * @return boolean
1981      * @throws DocumentException
1982      */
1983     private boolean isUpperLevel(Element subSection, Element section)
1984             throws DocumentException {
1985         // if (!(itemEquals(SECTION, subSection) && itemEquals(SECTION,
1986         // section))
1987         // || itemEquals(DOCUMENT, section) || itemEquals(SECTION, section)) {
1988         // // all element is upper than Document or section
1989         // return true;
1990         // }
1991         int subSectionLevel = Integer.parseInt(subSection
1992                 .attributeValue(LEVEL));
1993         int sectionLevel = Integer.parseInt(section.attributeValue(LEVEL));
1994         boolean result = subSectionLevel > sectionLevel;
1995         return result;
1996     }
1997 
1998     /**
1999      * Indique si les deux elements sont au meme niveau
2000      * 
2001      * @param item
2002      * @param firstTitle
2003      * @return boolean
2004      * @throws DocumentException
2005      */
2006     private boolean isSameLevel(Element subSection, Element section)
2007             throws DocumentException {
2008         // if (itemEquals(DOCUMENT, section) || itemEquals(SECTION, section)) {
2009         // // all element is upper than Document or section
2010         // return false;
2011         // }
2012         int subSectionLevel = Integer.parseInt(subSection
2013                 .attributeValue(LEVEL));
2014         int sectionLevel = Integer.parseInt(section.attributeValue(LEVEL));
2015         boolean result = subSectionLevel == sectionLevel;
2016         return result;
2017     }
2018 
2019     /**
2020      * @param Element
2021      *            e1
2022      * @param Element
2023      *            e2
2024      * @param String
2025      *            ... attnames
2026      * @return boolean
2027      */
2028     private boolean hasSameAttribute(Element e1, Element e2, String... attnames) {
2029         boolean result = true;
2030         for (String attname : attnames) {
2031             String a1 = e1.attributeValue(attname);
2032             String a2 = e2.attributeValue(attname);
2033             if (!ObjectUtils.equals(a1, a2)) {
2034                 result = false;
2035                 break;
2036             }
2037         }
2038         return result;
2039     }
2040 
2041     /**
2042      * @param Element
2043      *            from
2044      * @param Element
2045      *            to
2046      * @throws DocumentException
2047      */
2048     private void copyLevel(Element from, Element to) throws DocumentException {
2049         String level = from.attributeValue(LEVEL);
2050         if (level == null) {
2051             throw new DocumentException("Element without level: " + from);
2052         }
2053         to.addAttribute(LEVEL, level);
2054     }
2055 
2056     /**
2057      * @param String
2058      *            name
2059      * @param Element
2060      *            e
2061      * @return boolean
2062      * @throws DocumentException
2063      */
2064     private boolean itemEquals(String name, Element e) throws DocumentException {
2065         boolean result = itemEquals(name, e, false, false);
2066         return result;
2067     }
2068 
2069     /**
2070      * @param String
2071      *            name
2072      * @param Element
2073      *            e
2074      * @param throwError
2075      * @param eof
2076      * @return boolean
2077      * @throws DocumentException
2078      */
2079     private boolean itemEquals(String name, Element e, boolean throwError,
2080             boolean eof) throws DocumentException {
2081         boolean result = e != null && name.equals(e.getName());
2082         if (ERROR_MISSING_ITEM && !result && throwError && !eof) {
2083             throw new DocumentException("Malformed document waiting " + name
2084                     + " and found " + (e != null ? e.getName() : "null"));
2085         }
2086         return result;
2087     }
2088 
2089     /**
2090      * @param String
2091      *            name
2092      * @param Element
2093      *            e
2094      * @return boolean
2095      */
2096     private boolean itemNotEquals(String name, Element e) {
2097         boolean result = e == null || !name.equals(e.getName());
2098         return result;
2099     }
2100 
2101     private Document newJRSTReader(Reader r) throws Exception {
2102         JRSTReader reader = new JRSTReader();
2103         reader.setVariable(idMax, symbolMax, symbolMaxRef, lblFootnotes,
2104                 lblFootnotesRef, eFootnotes, eTarget, eTargetAnonymous,
2105                 eTargetAnonymousCopy);
2106 
2107         return reader.read(r);
2108 
2109     }
2110 
2111     /**
2112      * <p>
2113      * Initialises les variables d'environements par ex, les hyperlinks peuvent
2114      * etre referencer dans tous le document
2115      * </p>
2116      * 
2117      * @param idMax
2118      * @param symbolMax
2119      * @param symbolMaxRef
2120      * @param lblFootnotes
2121      * @param lblFootnotesRef
2122      * @param eFootnotes
2123      * @param eTarget
2124      * @param eTargetAnonymous
2125      * @param eTargetAnonymousCopy
2126      */
2127     public void setVariable(int idMax, int symbolMax, int symbolMaxRef,
2128             LinkedList<Integer> lblFootnotes,
2129             LinkedList<Integer> lblFootnotesRef,
2130             LinkedList<Element> eFootnotes, LinkedList<Element> eTarget,
2131             LinkedList<Element> eTargetAnonymous,
2132             LinkedList<Element> eTargetAnonymousCopy) {
2133         this.idMax = idMax;
2134         this.symbolMax = symbolMax;
2135         this.symbolMaxRef = symbolMaxRef;
2136         this.lblFootnotes = lblFootnotes;
2137         this.lblFootnotesRef = lblFootnotesRef;
2138         this.eFootnotes = eFootnotes;
2139         this.eTarget = eTarget;
2140         this.eTargetAnonymous = eTargetAnonymous;
2141         this.eTargetAnonymousCopy = eTargetAnonymousCopy;
2142     }
2143 
2144     /**
2145      * Parse text in element and replace text with parse result
2146      * 
2147      * @param Element
2148      *            e
2149      * @throws DocumentException
2150      * @throws UnsupportedEncodingException 
2151      */
2152     
2153     private void inline(Element e) throws DocumentException, UnsupportedEncodingException {
2154         String text = e.getText();
2155 
2156         text = StringEscapeUtils.escapeXml(text);
2157         // search all LITERAL and replace it with special mark
2158         // this prevent substitution in literal, example **something** must not
2159         // change in literal
2160         Map<String, String> temporaries = new HashMap<String, String>();
2161         Matcher matcher = REGEX_LITERAL.matcher(text);
2162         int index = 0;
2163         while (matcher.find()) {
2164             int start = matcher.start();
2165             int end = matcher.end();
2166             String literal = "<" + LITERAL + ">" + matcher.group(1) + "</"
2167                     + LITERAL + ">";
2168             String key = LITERAL + index++;
2169             temporaries.put(key, literal);
2170             text = text.substring(0, start) + "<tmp>" + key + "</tmp>"
2171                     + text.substring(end);
2172             matcher = REGEX_LITERAL.matcher(text);
2173         }
2174         // search all REGEX_INLINE_REFERENCE and replace it with special mark
2175         // this prevent substitution of URL with REGEX_REFERENCE. Use same
2176         // mechanisme as literal for that
2177         matcher = REGEX_INLINE_REFERENCE.matcher(text);
2178         index = 0;
2179         while (matcher.find()) {
2180             int start = matcher.start();
2181             int end = matcher.end();
2182             Element ref = DocumentHelper.createElement(REFERENCE);
2183             ref.addAttribute(REFURI, StringEscapeUtils.unescapeXml(matcher.group(2)));
2184             ref.setText(StringEscapeUtils.unescapeXml(matcher.group(1)));
2185             String key = "inlineReference" + index++;
2186             temporaries.put(key, ref.asXML());
2187             text = text.substring(0, start) + "<tmp>" + key + "</tmp>"
2188                     + text.substring(end);
2189             matcher = REGEX_INLINE_REFERENCE.matcher(text);
2190 
2191         }
2192         // do all substitution inline
2193         text = REGEX_EMAIL.matcher(text).replaceAll(
2194                 "$1<" + REFERENCE + " refuri='mailto:$2'>$2</" + REFERENCE
2195                         + ">$3");
2196         text = REGEX_STRONG.matcher(text).replaceAll(
2197                 "<" + STRONG + ">$1</" + STRONG + ">");
2198         text = REGEX_EMPHASIS.matcher(text).replaceAll(
2199                 "<" + EMPHASIS + ">$1</" + EMPHASIS + ">");
2200         text = REGEX_REFERENCE.matcher(text).replaceAll(
2201                 "<" + REFERENCE + " refuri='$1'>$1</" + REFERENCE + ">$2");
2202         // _[#]truc
2203         matcher = REGEX_FOOTNOTE_REFERENCE.matcher(text);
2204         while (matcher.find()) {
2205             String txtDebut = text.substring(0, matcher.start());
2206             String txtFin = text.substring(matcher.end()-1, text.length()-1);
2207             Element footnote = DocumentHelper.createElement(FOOTNOTE_REFERENCE);
2208             String sFootnote = matcher.group();
2209             boolean done = false;
2210             for (int i = 0; i < sFootnote.length() && !done; i++) {
2211                 if (sFootnote.charAt(i) == ']') {
2212                     String id = sFootnote.substring(1, i);
2213                     if (id.equals("*")) {
2214                         int nb = Math.abs(symbolMaxRef / 10) + 1;
2215                         char symbol = FOOTNOTE_SYMBOL.charAt(symbolMaxRef % 10);
2216                         String label = "";
2217                         for (int j = 0; j < nb; j++) {
2218                             label += symbol;
2219                         }
2220                         symbolMaxRef++;
2221                         footnote.addAttribute(AUTO, "*");
2222                         for (int j = 0; j < eFootnotes.size(); j++) {
2223                             Element eFootnote = eFootnotes.get(j);
2224                             if (eFootnote.attributeValue(LABEL).equals(label)) {
2225 
2226                                 footnote.addAttribute(ATTR_IDS, eFootnote
2227                                         .attributeValue(BACKREFS));
2228                                 footnote.addAttribute(ATTR_REFID, eFootnote
2229                                         .attributeValue(ATTR_IDS));
2230 
2231                             }
2232                         }
2233                         footnote.setText(label);
2234 
2235                     } else if (id.matches("[1-9]+")) {
2236 
2237                         for (int j = 0; j < eFootnotes.size(); j++) {
2238                             Element eFootnote = eFootnotes.get(j);
2239                             if (eFootnote.attributeValue(LABEL).equals(id)) {
2240                                 footnote.addAttribute(ATTR_IDS, eFootnote
2241                                         .attributeValue(BACKREFS));
2242                                 footnote.addAttribute(ATTR_REFID, eFootnote
2243                                         .attributeValue(ATTR_IDS));
2244                             }
2245                         }
2246                         footnote.setText(id);
2247                         lblFootnotesRef.add(Integer.parseInt(id));
2248 
2249                     } else if (id.equals("#")) {
2250                         int lblMax = 0;
2251                         for (int j = 0; j < lblFootnotesRef.size(); j++) {
2252                             lblMax = Math.max(lblMax, lblFootnotesRef.get(j));
2253                         }
2254 
2255                         boolean[] lbls = new boolean[lblMax];
2256                         for (int j = 0; j < lbls.length; j++) {
2257                             lbls[j] = false;
2258                         }
2259                         for (int j = 0; j < lblFootnotesRef.size(); j++) {
2260                             lbls[lblFootnotesRef.get(j) - 1] = true;
2261                         }
2262                         boolean valide = false;
2263                         do {
2264                             boolean trouve = false;
2265                             String label = null;
2266                             for (int j = 0; j < lbls.length && !trouve; j++) {
2267 
2268                                 if (!lbls[j]) {
2269                                     trouve = true;
2270                                     label = "" + (j + 1);
2271                                 }
2272                             }
2273                             if (!trouve) {
2274                                 label = "" + (lbls.length + 1);
2275                             }
2276                             footnote.addAttribute(AUTO, "1");
2277                             for (int j = 0; j < eFootnotes.size(); j++) {
2278                                 Element eFootnote = eFootnotes.get(j);
2279                                 if (eFootnote.attributeValue(LABEL).equals(
2280                                         label)) {
2281                                     if (!(eFootnote.attributeValue(TYPE)
2282                                             .equals(AUTONUMLABEL))) {
2283                                         footnote.addAttribute(ATTR_IDS, eFootnote
2284                                                 .attributeValue(BACKREFS));
2285                                         footnote.addAttribute(ATTR_REFID,
2286                                                 eFootnote.attributeValue(ATTR_IDS));
2287                                         footnote.setText(label);
2288                                         lblFootnotesRef.add(Integer
2289                                                 .parseInt(label));
2290                                         valide = true;
2291                                     } else {
2292                                         valide = false;
2293                                         lbls[Integer.parseInt(label) - 1] = true;
2294                                     }
2295                                 }
2296                             }
2297                         } while (!valide);
2298 
2299                     }
2300 
2301                     else {
2302                         footnote.addAttribute(AUTO, "1");
2303 
2304                         String name = id.substring(1);
2305                         boolean trouve = false;
2306                         for (int j = 0; j < eFootnotes.size() && !trouve; j++) {
2307                             Element eFootnote = eFootnotes.get(j);
2308                             if (eFootnote.attributeValue(NAMES).equals(name)) {
2309                                 footnote.addAttribute(ATTR_IDS, eFootnote
2310                                         .attributeValue(BACKREFS));
2311                                 footnote.addAttribute(ATTR_REFID, eFootnote
2312                                         .attributeValue(ATTR_IDS));
2313                                 String label = eFootnote
2314                                         .attributeValue(LABEL);
2315                                 footnote.setText(label);
2316                                 lblFootnotesRef.add(Integer.parseInt(label));
2317                                 trouve = true;
2318                             }
2319                         }
2320 
2321                         footnote.addAttribute(NAMES, name);
2322                     }
2323                     done = true;
2324                 }
2325             }
2326             text = txtDebut + footnote.asXML() + txtFin;
2327             matcher = REGEX_FOOTNOTE_REFERENCE.matcher(text);
2328         }
2329         // .. __http://truc.html
2330         matcher = REGEX_ANONYMOUS_HYPERLINK_REFERENCE.matcher(text);
2331         while (matcher.find()) {
2332             String txtDebut = text.substring(0, matcher.start());
2333             String txtFin = text.substring(matcher.end(), text.length());
2334             String ref = text.substring(matcher.start(), matcher.end() - 2);
2335             ref = ref.replaceAll("`", "");
2336             Element anonym = DocumentHelper.createElement(REFERENCE);
2337             anonym.addAttribute(ANONYMOUS, "1");
2338             anonym.addAttribute(NAME, ref.trim());
2339             if (!eTargetAnonymous.isEmpty()) {
2340                 Element target = eTargetAnonymous.getFirst();
2341                 eTargetAnonymous.removeFirst();
2342                 anonym.addAttribute(REFURI, target.attributeValue(REFURI));
2343             }
2344             anonym.setText(ref);
2345             text = txtDebut + anonym.asXML() + txtFin;
2346             matcher = REGEX_ANONYMOUS_HYPERLINK_REFERENCE.matcher(text);
2347         }
2348         // .. _truc: http://truc.html
2349         matcher = REGEX_HYPERLINK_REFERENCE.matcher(text);
2350         while (matcher.find()) {
2351             String txtDebut = text.substring(0, matcher.start());
2352             String txtFin = text.substring(matcher.end(), text.length());
2353             String ref = text.substring(matcher.start(), matcher.end() - 1);
2354             ref = StringEscapeUtils.unescapeXml(ref);
2355             ref = ref.replaceAll("(&apos;|_)", "");
2356             ref = ref.replaceAll("`", "");
2357             Element hyper = DocumentHelper.createElement(REFERENCE);
2358             hyper.addAttribute(NAME, ref);
2359             boolean trouve = false;
2360             for (int i = 0; i < eTarget.size() && !trouve; i++) {
2361                 Element el = eTarget.get(i);
2362                 String refTmp = URLEncoder.encode(ref.replaceAll("\\s", "-").toLowerCase(), "UTF-8");
2363                 if (el.attributeValue(ID).equalsIgnoreCase((refTmp))) {
2364                     hyper.addAttribute(REFURI, el.attributeValue(REFURI));
2365                     trouve = true;
2366                 }
2367             }
2368             if (!trouve) {
2369                 hyper.addAttribute(ATTR_REFID, ref);
2370             }
2371             hyper.setText(ref);
2372             text = txtDebut + hyper.asXML() + " " + txtFin;
2373             matcher = REGEX_HYPERLINK_REFERENCE.matcher(text);
2374 
2375         }
2376 
2377         // substitution reference
2378         matcher = REGEX_SUBSTITUTION_REFERENCE.matcher(text);
2379         int begin = 0;
2380         while (matcher.find(begin)) {
2381             String start = text.substring(0, matcher.start());
2382             String end = text.substring(matcher.end());
2383             String ref = matcher.group(1);
2384 
2385             Node subst = e.selectSingleNode("//" + SUBSTITUTION_DEFINITION
2386                     + "[@name='" + ref + "']/child::node()");
2387 
2388             if (subst == null) {
2389                 text = start + "|" + ref + "|";
2390             } else {
2391                 text = start + subst.asXML();
2392             }
2393 
2394             begin = text.length();
2395             text += end;
2396             matcher = REGEX_SUBSTITUTION_REFERENCE.matcher(text);
2397 
2398         }
2399         // undo substitution in LITERAL
2400         Pattern p = Pattern.compile("<tmp>([^<>]+)</tmp>");
2401 
2402         matcher = p.matcher(text);
2403         while (matcher.find()) {
2404             String start = text.substring(0, matcher.start());
2405             String end = text.substring(matcher.end());
2406 
2407             String tempKey = matcher.group(1);
2408             text = start + temporaries.get(tempKey) + end;
2409             matcher = p.matcher(text);
2410         }
2411         
2412         String resultElementText = text.trim();
2413         Element result = DocumentHelper.parseText(
2414                 "<TMP>" + resultElementText + "</TMP>").getRootElement();
2415 
2416         e.setText("");
2417         e.appendContent(result);
2418     }
2419 }