r693 - in branches/jrst-docutils-jython/jrst: . src/main/java/org/nuiton/jrst src/main/java/org/nuiton/jrst/directive src/main/resources/i18n src/test/java/org/nuiton/jrst src/test/java/org/nuiton/jrst/bugs src/test/resources
Author: jpages Date: 2012-06-01 17:05:38 +0200 (Fri, 01 Jun 2012) New Revision: 693 Url: http://nuiton.org/repositories/revision/jrst/693 Log: R?\195?\169int?\195?\169gration de la g?\195?\169n?\195?\169ration du XML via l'ancien parser (JRST1.5) sous forme d'une option R?\195?\169int?\195?\169gration dans l'application d'un certain nombre de classes supprim?\195?\169es pr?\195?\169c$ Modification des tests pour supporter les deux types de g?\195?\169n?\195?\169ration Modification de l'interface graphique pour permettre la g?\195?\169n?\195?\169ration de XML sans DocUtils Added: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/AdvancedReader.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTLexer.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTReader.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ContentDirective.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/DateDirective.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ImageDirective.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/SectnumDirective.java Modified: branches/jrst-docutils-jython/jrst/pom.xml branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRST.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTConfig.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTConfigOption.java branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTInterface.java branches/jrst-docutils-jython/jrst/src/main/resources/i18n/jrst_en_GB.properties branches/jrst-docutils-jython/jrst/src/main/resources/i18n/jrst_fr_FR.properties branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTAbstractTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTGeneratorTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/AdmonitionTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/DirectiveTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/ErrorsTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/OptionTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TableTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TextTest.java branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TitlesTest.java branches/jrst-docutils-jython/jrst/src/test/resources/test.rst Modified: branches/jrst-docutils-jython/jrst/pom.xml =================================================================== --- branches/jrst-docutils-jython/jrst/pom.xml 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/pom.xml 2012-06-01 15:05:38 UTC (rev 693) @@ -48,8 +48,11 @@ <version>${project.version}</version> <scope>runtime</scope> </dependency> - <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + </dependency> + <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> Added: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/AdvancedReader.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/AdvancedReader.java (rev 0) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/AdvancedReader.java 2012-06-01 15:05:38 UTC (rev 693) @@ -0,0 +1,406 @@ +/* + * #%L + * JRst :: Api + * + * $Id: AdvancedReader.java 601 2011-06-09 16:31:45Z kcardineaud $ + * $HeadURL: http://svn.nuiton.org/svn/jrst/branches/jrst-docutils-jython/jrst/src/main/j... $ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.jrst; + +import org.apache.commons.collections.primitives.ArrayCharList; +import org.apache.commons.collections.primitives.CharList; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.PushbackReader; +import java.io.Reader; +import java.util.ArrayList; + +/** + * Le principe est d'avoir dans cette classe le moyen de lire, et de retourner + * la ou on etait avant la lecture (mark et reset de {@link BufferedReader}). + * <p> + * Mais il faut aussi pouvoir dire qu'en fin de compte on ne souhaite pas lire + * les caracteres que l'on vient de lire ({@link #unread(int)} a peu pres egal a + * {@link PushbackReader}) + * <p> + * Le pointer nextChar pointe toujours sur le prochain caractere qui sera lu + * <p> + * Lorsque l'on appelle la method {@link #mark()} on vide aussi le buffer pour + * liberer de la place, car on a plus le moyen de retourner avant le mark que + * l'on vient de positionner. + * <p> + * On contraire du mark de {@link BufferedReader} ou {@link LineNumberReader} il + * n'y a pas a specifier le nombre de caractere a garder au maximum, seul la + * memoire nous limite. Du coup si l'on utilise cette classe sans mark, au final + * on aura dans le buffer tout le contenu du reader, il faut donc utiliser mark + * avec cette classe + * + * buffer + * + * <pre> + * ######################### + * 0 ˆ ˆ + * | | + * | + nextChar + * + markChar + * </pre> + * + * Created: 27 oct. 06 00:24:57 + * + * @author poussin + * @version $Revision: 601 $ + * + * Last update: $Date: 2011-06-09 18:31:45 +0200 (jeu. 09 juin 2011) $ + * by : $Author: kcardineaud $ + */ +public class AdvancedReader { + + /** le nombre d'espace pour remplacer les tabulations */ + protected static final String TAB = " "; + /** nombre de caractere lu au minimum sur le vrai reader */ + protected static final int READ_AHEAD = 80; + + protected Reader in; + protected CharList buffer; + + protected int charNumber; + protected int charNumberMark; + protected int lineNumber; + protected int lineNumberMark; + + protected int nextChar; + protected int markChar; + + protected int readInMark; + + protected boolean nlTwoCtrlChars = false; + protected boolean noNlAtEOF = false; + + /** + * + * @param in the io reader + */ + public AdvancedReader(Reader in) { + this.in = new LineNumberReader(in); + buffer = new ArrayCharList(); + } + + public void mark() throws IOException { + markChar = nextChar; + charNumberMark = charNumber; + lineNumberMark = lineNumber; + + free(markChar); + } + + public void reset() throws IOException { + nextChar = markChar; + charNumber = charNumberMark; + lineNumber = lineNumberMark; + + } + + public int readSinceMark() { + return nextChar - markChar; + } + + /** + * @return the charNumber + */ + public int getCharNumber() { + return charNumber; + } + + /** + * @return the lineNumber + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * remove number of char in buffer + * + * @param number + * @return the real number of char removed from the head of buffer + * @throws IOException + */ + private int free(int number) throws IOException { + // fill(number); + int result = Math.min(buffer.size(), number); + buffer.subList(0, result).clear(); + + nextChar -= result; + markChar -= result; + + return result; + } + + /** + * ensure that have number char available and not already read + * + * @param number ? + * @throws IOException + */ + private void fill(int number) throws IOException { + int needed = nextChar + number - buffer.size(); + if (needed > 0) { + char[] cbuf = new char[needed + READ_AHEAD]; + int read = in.read(cbuf); + if (read != -1) { + for (int i = 0; i < read; i++) { + buffer.add(cbuf[i]); + } + } + } + } + + public boolean eof() throws IOException { + boolean result = -1 == read(); + if (!result) { + unread(1); + } + return result; + } + + public int skip(int number) throws IOException { + int result = 0; + while (result < number && read() != -1) { + result++; + } + return result; + } + + /** + * Add a character at the current position + * @param character that you want to add + */ + public void add(char character) { + buffer.add(nextChar,character); + } + + + /** + * go left in reading char buffer + * + * @param number + * @return realy unread char number + */ + public int unread(int number) { + int result = Math.min(number, nextChar); + + nextChar -= result; + charNumber -= result; + for (int i = nextChar; i < nextChar + result; i++) { + if (buffer.get(i) == '\n' || (buffer.get(i) == '\r' && i + 1 < nextChar + result && buffer.get(i + 1) != '\n')) { + lineNumber--; + } + } + + return result; + } + + /** + * Unread the line length + * + * @param line + * line used to know the length to unread + * @param addNewLine + * if true then add +1 to unread lenght for not present '\n' + * @return number of unread char + */ + public int unread(String line, boolean addNewLine) { + int result = unread(line.length() + (addNewLine ? (nlTwoCtrlChars ? 2 : 1) : 0)); + return result; + } + + /** + * Unread the line length + * + * @param lines + * lines used to know the length to unread + * @param addNewLine + * if true then add +1 for each line to unread lenght for not + * present '\n' + * @return number of unread char + */ + public int unread(String[] lines, boolean addNewLine) { + int result = 0; + for (String line : lines) { + result += unread(line, addNewLine); + } + return result; + } + + /** + * read one char in buffer + * + * @return the next char + * @throws IOException pour tout pb de lecture + */ + public int read() throws IOException { + fill(1); + int result = -1; + if (nextChar < buffer.size()) { + result = buffer.get(nextChar++); + charNumber++; + if ((char)result == '\n' || ((char)result == '\r' && nextChar < buffer.size() && buffer.get(nextChar) != '\n')) { + lineNumber++; + } + } + return result; + } + + /** + * read one line + * + * @return one line without '\n' or null if end of file + * @throws IOException pour tout pb de lecture + */ + public String readLine() throws IOException { + StringBuffer result = new StringBuffer(READ_AHEAD); + int c = read(); + while (c != -1 && c != '\n' && c != '\r') { + result.append((char) c); + c = read(); + } + nlTwoCtrlChars = false; + if (c == '\r') { + c = read(); + if (c != '\n' && c != -1) { + unread(1); + } else { + nlTwoCtrlChars = true; + } + } + noNlAtEOF = false; + if (c == -1 && result.length() >= 0) { + if (c == -1 && result.length() == 0) { + return null; + } else { + noNlAtEOF = true; + return result.toString(); + } + } else { + return result.toString(); + } + } + + + + + /** + * passe les lignes blanches + * + * @throws IOException + */ + public void skipBlankLines() throws IOException { + readUntil("^\\s*\\S+.*"); + } + + /** + * lit toutes les lignes du fichier + * + * @return toutes les lignes du fichier + * @throws IOException pour tout pb de lecture + */ + String[] readAll() throws IOException { + String[] result = readLines(-1); + return result; + } + + /** + * lit un certain nombre de lignes + * + * @param count + * si negatif lit toutes les lignes + * @return un certain nombre de lignes + * @throws IOException pour tout pb de lecture + */ + public String[] readLines(int count) throws IOException { + ArrayList<String> result = new ArrayList<String>(); + + String tmp = ""; + for (int i = count; tmp != null && i != 0; i--) { + tmp = readLine(); + if (tmp != null) { + result.add(tmp); + } + } + return result.toArray(new String[result.size()]); + } + + /** + * lit les lignes jusqu'a la premiere ligne blanche (non retournée) + * + * @return toutes les ligne jusqu'à la première ligne blanche (non incluse) + * @throws IOException pour tout pb de lecture + */ + public String[] readUntilBlank() throws IOException { + String[] result = readUntil("\\s*"); + return result; + } + + /** + * lit les lignes jusqu'a la ligne qui correspond pas au pattern, cette + * ligne n'est pas mise dans le resultat retourne + * + * @param pattern ? + * @return les lignes jusqu'a la ligne qui correspond pas au pattern, cette + * ligne n'est pas mise dans le resultat retourne + * @throws IOException pour tout pb de lecture + */ + public String[] readUntil(String pattern) throws IOException { + ArrayList<String> result = new ArrayList<String>(); + String tmp = readLine(); + while (tmp != null && !tmp.matches(pattern)) { + result.add(tmp); + tmp = readLine(); + } + if (tmp != null) { + unread(tmp.length() + (!noNlAtEOF ? (nlTwoCtrlChars ? 2 : 1) : 0)); // +1 for '\n' not in line + } + return result.toArray(new String[result.size()]); + } + + /** + * lit les lignes tant que les lignes correspondent au pattern + * + * @param pattern ? + * @return les lignes tant que les lignes correspondent au pattern + * @throws IOException pour tout pb de lecture + */ + public String[] readWhile(String pattern) throws IOException { + ArrayList<String> result = new ArrayList<String>(); + String tmp = readLine(); + while (tmp != null && tmp.matches(pattern)) { + result.add(tmp); + tmp = readLine(); + } + if (tmp != null) { + unread(tmp.length() + (!noNlAtEOF ? (nlTwoCtrlChars ? 2 : 1) : 0)); // +1 for '\n' not in line + } + return result.toArray(new String[result.size()]); + } + +} Modified: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRST.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRST.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRST.java 2012-06-01 15:05:38 UTC (rev 693) @@ -32,8 +32,10 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.Reader; import java.net.URL; import java.util.HashMap; import java.util.List; @@ -118,7 +120,7 @@ /** XSL Stylesheet to transform Docbook into PDF. */ protected static final String DOCBOOK_2_FO = "/docbook/fo/docbook.xsl"; - public static final String PATTERN_TYPE = "xml|xhtml|docbook|html|htmlInnerBody|xdoc|fo|pdf"; + public static final String PATTERN_TYPE = "xml|xhtml|docbook|html|htmlInnerBody|xdoc|rst|fo|pdf|odt|rtf"; /** HTML output format type */ public static final String TYPE_HTML = "html"; @@ -223,8 +225,12 @@ if (JRSTConfigOption.FORCE.getOptionAsBoolean(config)) { overwrite = Overwrite.ALLTIME; } + boolean simpleGeneration = false; + if (JRSTConfigOption.SIMPLE.getOptionAsBoolean(config)) { + simpleGeneration = true; + } - generate(xslList, inputFile, ouputFile, overwrite); + generate(xslList, inputFile, ouputFile, overwrite, simpleGeneration); } private static String[] askOption() throws SecurityException, @@ -260,6 +266,7 @@ return graph.getCmd(); } + /** * Transforms a Restructured Text (ReST) file to another type ( html, xdoc, pdf, etc... ) * @@ -270,7 +277,7 @@ * @throws Exception */ public static void generate(String outputType, File fileIn, - File fileOut, Overwrite overwrite) throws Exception { + File fileOut, Overwrite overwrite, boolean simpleGeneration) throws Exception { if (fileOut != null && fileOut.exists() && (overwrite == Overwrite.NEVER || (overwrite == Overwrite.IFNEWER && FileUtils @@ -279,9 +286,14 @@ log.info("Don't generate file " + fileOut + ", because already exists"); } else { + Document doc = null; - // Transformation to XML - Document doc = generateDocutils(fileIn); + if (simpleGeneration) { + doc = generateSimpleDoc(fileIn, UTF_8); + } else { + // Transformation to XML + doc = generateDocutils(fileIn); + } // Application of xsl stylesheets doc = generateXml(doc, outputType); @@ -295,6 +307,15 @@ } } + public static Document generateSimpleDoc(File fileIn, String encoding) throws Exception { + URL url = fileIn.toURI().toURL(); + Reader in = new InputStreamReader(url.openStream(), encoding); + + // parse rst file + JRSTReader jrst = new JRSTReader(); + return jrst.read(in); + } + /** * Transforms a restructured text file to a XML file using Jython interpreter to execute DocUtils scripts. * Modified: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTConfig.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTConfig.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTConfig.java 2012-06-01 15:05:38 UTC (rev 693) @@ -93,6 +93,8 @@ conf.addAlias("--xslFile", "--option", "xslFile"); conf.addAlias("-x", "--option", "xslFile"); conf.addAlias("-i", "--intermediate", "intermediateFile"); + conf.addAlias("--simple", "--option", "simple", "true"); + conf.addAlias("-s", "--option", "simple", "true"); try { conf.parse(args); Modified: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTConfigOption.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTConfigOption.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTConfigOption.java 2012-06-01 15:05:38 UTC (rev 693) @@ -40,6 +40,7 @@ public enum JRSTConfigOption implements OptionDef { FORCE("force", n_("jrst.option.force"), "false", String.class, true, false), + SIMPLE("simple", n_("jrst.option.simple"), "false", String.class, true, false), OUT_FILE("outFile", n_("jrst.option.outfile"), null, String.class, true, false), OUT_TYPE("outType", n_("jrst.option.outtype"), null, String.class, true, false), XSL_FILE("xslFile", n_("jrst.option.xslfile"), null, String.class, true, false), Modified: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTInterface.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTInterface.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTInterface.java 2012-06-01 15:05:38 UTC (rev 693) @@ -46,6 +46,7 @@ import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; +import javax.swing.JCheckBox; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.nuiton.util.Resource; @@ -86,6 +87,8 @@ private JPanel boutonPanel; + private JPanel simpleModePanel; + private JButton boutonSaveLocation; private JLabel errorLbl; @@ -106,6 +109,8 @@ private JRadioButton xslRadio; + private JCheckBox simpleModeChechBox; + private LinkedList<JPanel> ListAddXslPanel; private LinkedList<JTextField> ListXslText; @@ -114,6 +119,7 @@ private LinkedList<JButton> ListXslBoutonLocation; private boolean ecrase; + private boolean simpleMode; private String[] commande; private ImageIcon open = Resource.getIcon("icone/open.png"); @@ -152,6 +158,7 @@ panelPrincipal.add(getOuvrirPanel()); panelPrincipal.add(getSavePanel()); panelPrincipal.add(getFormatPanel()); + panelPrincipal.add(getSimpleModePanel()); // panelPrincipal.add(getConvertPanel()); panelPrincipal.add(getBoutonPanel()); @@ -178,6 +185,29 @@ return boutonPanel; } + private JCheckBox getSimpleCheckBox() { + if (simpleModeChechBox == null) { + simpleModeChechBox = new JCheckBox("Simple mode"); //simpleMode + simpleModeChechBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setMode(); + } + }); + } + return simpleModeChechBox; + } + + private JPanel getSimpleModePanel() { + + if (simpleModePanel == null) { + simpleModePanel = new JPanel(); + simpleModePanel.setLayout(new FlowLayout(FlowLayout.LEFT)); + simpleModePanel.add(getSimpleCheckBox()); + + } + return simpleModePanel; + } + private JButton getBoutonConvertir() { if (boutonConvertir == null) { boutonConvertir = new JButton(); @@ -518,6 +548,10 @@ System.exit(0); } + private void setMode() { + simpleMode = !simpleMode; + } + private void convert() { boolean exit = false; if (getOpenText().getText().equals("")) { @@ -539,6 +573,9 @@ String cmd = ""; if (ecrase) cmd += "--force "; + if (simpleMode) { + cmd += "--simple "; + } if (getFormat().isSelected()) cmd += "-t " + getFormatList().getSelectedItem(); else { @@ -555,6 +592,7 @@ commande = cmd.trim().split(" "); dispose(); } + } } Added: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTLexer.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTLexer.java (rev 0) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTLexer.java 2012-06-01 15:05:38 UTC (rev 693) @@ -0,0 +1,2514 @@ +/* + * #%L + * JRst :: Api + * + * $Id: JRSTLexer.java 623 2011-10-28 14:10:56Z sletellier $ + * $HeadURL: http://svn.nuiton.org/svn/jrst/branches/jrst-docutils-jython/jrst/src/main/j... $ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.jrst; + +import static org.nuiton.jrst.ReStructuredText.BLOCK_QUOTE; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; + +import java.io.IOException; +import java.io.Reader; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Le principe est de positionner la mark du {@link AdvancedReader} lors du + * debut d'une methode peek*, puis a la fin de la methode de regarder le nombre + * de caractere utilisé pour la methode et de faire un reset. + * <p> + * Le nombre de caractere utilisé servira pour le remove lorsque l'utilisateur + * indiquera qu'il utilise l'element retourné, si l'utilisateur n'appelle pas + * remove alors il peut relire autant de fois qu'il veut le meme element, ou + * essayer d'en lire un autre. + * <p> + * Pour mettre en place ce mecanisme le plus simple est d'utiliser les methodes + * {@link JRSTLexer#beginPeek()} et {@link JRSTLexer#endPeek()} + * + * Created: 28 oct. 06 00:44:20 + * + * @author poussin, letellier + * @version $Revision: 623 $ + * + * Last update: $Date: 2011-10-28 16:10:56 +0200 (ven. 28 oct. 2011) $ + * by : $Author: sletellier $ + */ +public class JRSTLexer { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static Log log = LogFactory.getLog(JRSTLexer.class); + + public static final String BULLET_CHAR = "*" + "+" + "-"/* + * + "\u2022" + + * "\u2023" + + * "\u2043" + */; + + public static final String TITLE_CHAR = "-=-~'`^+:!\"#$%&*,./;|?@\\_[\\]{}<>()"; + + public static final String DOCINFO_ITEM = "author|authors|organization|address|contact|version|revision|status|date|copyright"; + + public static final String ADMONITION_PATTERN = "admonition|attention|caution|danger|error|hint|important|note|tip|warning"; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Title Elements + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public static final String TITLE = "title"; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Bibliographic Elements + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public static final String DOCINFO = "docinfo"; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Decoration Elements + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public static final String DECORATION = "decoration"; + + public static final String HEADER = "header"; + + public static final String FOOTER = "footer"; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Structural Elements + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public static final String TRANSITION = "transition"; + + public static final String SIDEBAR = "sidebar"; + + public static final String TOPIC = "topic"; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Body Elements + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + static final public String LITERAL_BLOCK = "literal_block"; + + static final public String PARAGRAPH = "paragraph"; + + static final public String BLANK_LINE = "blankLine"; + + static final public String COMMENT = "comment"; + + static final public String SUBSTITUTION_DEFINITION = "substitution_definition"; + + static final public String BULLET_LIST = "bullet_list"; + + static final public String FIELD_LIST = "field_list"; + + static final public String DEFINITION_LIST = "definition_list"; + + static final public String ENUMERATED_LIST = "enumerated_list"; + + static final public String OPTION_LIST = "option_list"; + + public static final String LINE_BLOCK = "line_block"; + + public static final String LINE = "line"; + + public static final String ATTRIBUTION = "attribution"; + + public static final String DOCTEST_BLOCK = "doctest_block"; + + public static final String ADMONITION = "admonition"; + + public static final String TARGET = "target"; + + public static final String FOOTNOTE = "footnote"; + + public static final String FOOTNOTES = "footnotes"; + + public static final String LEVEL = "level"; + + public static final String TARGETANONYMOUS = "targetAnonymous"; + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Table Elements + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + static final public String TABLE = "table"; + + static final public String ROW = "row"; + + static final public String CELL = "cell"; + + static final public String TABLE_HEADER = "header"; + + static final public String TABLE_WIDTH = "width"; + + static final public String ROW_END_HEADER = "endHeader"; + + static final public String CELL_INDEX_START = "indexStart"; + + static final public String CELL_INDEX_END = "indexEnd"; + + static final public String CELL_BEGIN = "begin"; + + static final public String CELL_END = "end"; + + static final public String REMOVE = "remove"; + + static final public String INCLUDE = "include"; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Directive Elements + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + static final public String DIRECTIVE = "directive"; + + static final public String DIRECTIVE_TYPE = "type"; + + static final public String DIRECTIVE_VALUE = "value"; + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Attributs + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + static final public String AUTONUM = "autoNum"; + + static final public String AUTONUMLABEL = "autoNumLabel"; + + static final public String AUTOSYMBOL = "autoSymbol"; + + static final public String BULLET = "bullet"; + + static final public String CHAR = "char"; + + static final public String ID = "id"; + + static final public String CLASSIFIERS = "classifiers"; + + static final public String DELIMITER = "delimiter"; + + static final public String DELIMITEREXISTE ="delimiterExiste"; + + static final public String ENUMTYPE = "enumtype"; + + static final public String REFURI = "refuri"; + + static final public String OPTION = "option"; + + static final public String LITERAL = "literal"; + + static final public String NAME = "name"; + + static final public String NUM ="num"; + + static final public String OPTIONARGUMENT = "option_argument"; + + static final public String OPTIONSTRING = "option_string"; + + static final public String PREFIX = "prefix"; + + static final public String START = "start"; + + static final public String SUBEXISTE = "subExiste"; + + static final public String SUFFIX = "suffix"; + + static final public String SUBTITLE = "subtitle"; + + static final public String TERM = "term"; + + static final public String TITLEATTR = "title"; + + static final public String XMLSPACE = "xml:space"; + + static final public String TYPE = "type"; + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + protected static final String TRUE = "true"; + + protected static final String FALSE = "false"; + + + /** + * retient le niveau du titre, pour un titre de type double, on met deux + * fois le caratere dans la chaine, sinon on le met une seul fois. + * + * <pre> + * ===== + * Super + * ===== + * titre + * ----- + * </pre> + * + * donnera dans la liste ["==", "-"] + */ + private List<String> titleLevels; + + private AdvancedReader in; + + /** + * length of the last element returned (number of char need to this element) + */ + private int elementLength; + + + + + public JRSTLexer(Reader reader) { + titleLevels = new ArrayList<String>(); + in = new AdvancedReader(reader); + } + + /** + * true if no more element to read + * + * @return boolean + * @throws IOException + */ + public boolean eof() throws IOException { + in.mark(); + in.skipBlankLines(); + boolean result = in.eof(); + in.reset(); + return result; + } + + /** + * remove one element from list of element already read + * + * @throws IOException + */ + public void remove() throws IOException { + in.skip(elementLength); + } + + /** + * start peek + * + * @throws IOException + */ + private void beginPeek() throws IOException { + elementLength = 0; + in.mark(); + } + + /** + * end peek + * + * @throws IOException + */ + private void endPeek() throws IOException { + elementLength = in.readSinceMark(); + in.reset(); + } + + /** + * Read block text, block text have same indentation and is continu (no + * blank line) + * + * @param minLeftMargin + * min left blank needed to accept to read block + * @return String[] + * @throws IOException + */ + private String[] readBlock(int minLeftMargin) throws IOException { + String[] result = new String[0]; + String firstLine = in.readLine(); + if (firstLine != null) { + in.unread(firstLine, true); + int level = level(firstLine); + if (level >= minLeftMargin) { + result = in.readWhile("^\\s{" + level + "}\\S+.*"); + } + } + + return result; + } + + /** + * All lines are joined and left and right spaces are removed during join + * + * @param String + * [] lines + * @return String + */ + private String joinBlock(String[] lines) { + String result = joinBlock(lines, " ", true); + return result; + } + + /** + * All lines are joined whith the String joinSep and left and right spaces + * are removed if trim + * + * @param String + * [] lines + * @param String + * joinSep + * @param Boolean + * trim + * @return String + */ + private String joinBlock(String[] lines, String joinSep, boolean trim) { + String result = ""; + String sep = ""; + for (String line : lines) { + if (trim) { + line = line.trim(); + } + result += sep + line; + sep = joinSep; + } + return result; + } + + /** + * search if the doc have an header + * + * <pre> + * .. header:: This space for rent. aaaa **aaaa** + * </pre> + * + * @return Element + * @throws IOException + */ + public Element peekHeader() throws IOException { + beginPeek(); + Element result = null; + String[] line = in.readAll(); + if (line != null) { + int i = 0; + for (String l : line) { + i++; + if (l.matches("^\\s*.. " + HEADER + ":: .*")) { + int level = level(l); + l = l.replaceAll("^\\s*.. " + HEADER + ":: ", ""); + result = DocumentHelper.createElement(HEADER).addAttribute( + LEVEL, String.valueOf(level)); + result.addAttribute(LINE, "" + i); + result.setText(l); + } + + } + } + endPeek(); + return result; + + } + + /** + * search if the doc have an header + * + * <pre> + * .. footer:: design by **LETELLIER Sylvain** + * </pre> + * + * @return Element + * @throws IOException + */ + public Element peekFooter() throws IOException { + beginPeek(); + Element result = null; + String[] line = in.readAll(); + if (line != null) { + int i = 0; + for (String l : line) { + i++; + + if (l.matches("^\\s*.. " + FOOTER + ":: .*")) { + int level = level(l); + l = l.replaceAll("^\\s*.. " + FOOTER + ":: ", ""); + result = DocumentHelper.createElement(FOOTER).addAttribute( + LEVEL, String.valueOf(level)); + result.addAttribute(LINE, "" + i); + result.setText(l); + } + } + } + endPeek(); + return result; + + } + + /** + * <pre> + * .. __: http://www.python.org + * </pre> + * + * @return Element + * @throws IOException + */ + public LinkedList<Element> peekTargetAnonymous() throws IOException { + beginPeek(); + LinkedList<Element> result = new LinkedList<Element>(); + String[] line = in.readAll(); + if (line != null) { + int i = 0; + for (String l : line) { + i++; + + if (l.matches("^\\s*__ .+$|^\\s*\\.\\. __\\:.+$")) { + log.debug(l); + Element resultTmp = DocumentHelper + .createElement(TARGETANONYMOUS); + resultTmp.addAttribute(LEVEL, "" + level(l)); + Matcher matcher = Pattern.compile("__ |.. __: ").matcher(l); + + if (matcher.find()) { + resultTmp.addAttribute(REFURI, l.substring(matcher + .end(), l.length())); + } + + result.add(resultTmp); + } + } + + } + endPeek(); + return result; + } + + /** + * Return title or para + * + * @return Element + * @throws IOException + */ + public Element peekTitleOrBodyElement() throws IOException { + Element result = null; + if (result == null) { + result = peekTitle(); + } + if (result == null) { + result = peekBodyElement(); + } + + return result; + } + + /** + * read doc info author, date, version, ... or field list element + * + * <pre> + * :author: Benjamin Poussin + * :address: + * Quelque part + * Dans le monde + * </pre> + * + * @return Element + * @throws IOException + */ + public Element peekDocInfo() throws IOException { + Element result = null; + if (result == null) { + result = peekDocInfoItem(); + + } + if (result == null) { + result = peekFieldList(); + } + + return result; + + } + + /** + * Return para + * + * @return Element + * @throws IOException + */ + public Element peekBodyElement() throws IOException { + Element result = null; + if (result == null) { + result = peekInclude(); + } + if (result == null) { + result = peekDoctestBlock(); + } + if (result == null) { + result = peekAdmonition(); + } + if (result == null) { + result = peekSidebar(); + } + if (result == null) { + result = peekTopic(); + } + if (result == null) { + result = peekRemove(); + } + if (result == null) { + result = peekDirectiveOrReference(); + } + if (result == null) { + result = peekTransition(); + } + if (result == null) { + result = peekTable(); + } + if (result == null) { + result = peekLineBlock(); + } + if (result == null) { + result = peekBulletList(); + } + if (result == null) { + result = peekOption(); + } + if (result == null) { + result = peekEnumeratedList(); + } + if (result == null) { + result = peekTarget(); + } + if (result == null) { + result = peekFootnote(); + } + // comment must be read after peekDirectiveOrReference() + // and peekFootnote() + if (result == null) { + result = peekComment(); + } + if (result == null) { + result = peekDefinitionList(); + } + if (result == null) { + result = peekFieldList(); + } + if (result == null) { + result = peekTargetAnonymousBody(); + } + if (result == null) { + result = peekLiteralBlock(); + } + if (result == null) { + result = peekBlockQuote(); + } + if (result == null) { + result = peekBlankLine(); + } + if (result == null) { + result = peekPara(); + } + + return result; + } + + /** + * Remove already read elements + * + * @return Element + * @throws IOException + */ + public Element peekRemove() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + // Le header est parse des le debut + if (line.matches("^\\s*.. " + HEADER + ":: .*")) { + result = DocumentHelper.createElement(REMOVE).addAttribute( + LEVEL, "" + level(line)); + } + // Le footer + if (line.matches("^\\s*.. " + FOOTER + ":: .*")) { + result = DocumentHelper.createElement(REMOVE).addAttribute( + LEVEL, "" + level(line)); + } + + } + endPeek(); + return result; + } + + /** + * read include + * + * <pre> + * .. include:: text.txt + * or + * .. include:: literal + * text.txt + * + * </pre> + * + * @return Element + * @throws IOException + */ + private Element peekInclude() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("^\\s*\\.\\.\\sinclude\\:\\:.+$")) { + result = DocumentHelper.createElement(INCLUDE); + result.addAttribute(LEVEL, "" + level(line)); + String option = line.substring(line.indexOf("::") + 2).trim(); + result.addAttribute(OPTION, ""); + if (option.trim().equalsIgnoreCase(LITERAL)) { + result.addAttribute(OPTION, LITERAL); + line = in.readLine(); + result.setText(line.trim()); + } else { + result.setText(option); + } + + } + } + endPeek(); + return result; + } + + /** + * read options + * + * <pre> + * Ex : -a command-line option "a" + * -1 file, --one=file, --two file + * Multiple options with arguments. + * Schéma : ________________________________ + * v | | + * -{1,2}\w+ ->|',' | + * |'='-----|-> \w+ --->|',' + * |' '-----| |' '---+ + * |" " -----> \w+ ---> end | + * ˆ | + * |_________________________| + * Légende : + * + * -{1,2} --> 1 or 2 tirets + * \w+ -----> word characters one or more times + * </pre> + * + * @return Element + * @throws IOException + */ + public Element peekOption() throws IOException { + + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("^(\\s*((--?)|(//?))\\w+([ =][<a-zA-Z][\\w-><]*)?)\\s*.*$")) { + result = DocumentHelper.createElement(OPTION_LIST) + .addAttribute(LEVEL, "" + level(line)); + char delimiter; + do { + Matcher matcher = Pattern.compile("[-/][-/]?.+").matcher(line); + matcher.find(); + Element option = result.addElement(OPTION); + String option_stringTmp = matcher.group(); + matcher = Pattern.compile("^[-/][-/]?\\w+").matcher(option_stringTmp); + matcher.find(); + String option_string = matcher.group(); + option.addAttribute(OPTIONSTRING, option_string); + + boolean done = false; + // Delimiteur bidon + delimiter = '.'; + if (option_stringTmp.length() > matcher.end()) { + delimiter = option_stringTmp.charAt(matcher.end()); + option_stringTmp = option_stringTmp.substring( + matcher.end(), option_stringTmp.length()); + } else { + done = true; + } + option.addAttribute(DELIMITEREXISTE, FALSE); + + if (delimiter == ' ') { // S'il y a 2 espaces a suivre, + // l'option est finie + if (option_stringTmp.charAt(1) == ' ') { + done = true; + } + } + String option_argument = null; + if ((delimiter == '=' || delimiter == ' ') && !done) { + option.addAttribute(DELIMITEREXISTE, TRUE); + option.addAttribute(DELIMITER, "" + delimiter); + matcher = Pattern.compile(delimiter + "(([a-zA-Z][\\w-]+)|(<[a-zA-Z][^>]*>))").matcher( + option_stringTmp); + if (matcher.find()) { + option_argument = matcher.group().substring(1, + matcher.group().length()); + option.addAttribute(OPTIONARGUMENT, option_argument); + int size = option_argument.length() + 1; + if (option_stringTmp.length() < size && option_stringTmp.charAt(size) == ',') { + delimiter = ','; + } else { + done = true; + } + } else { // Si la description n'est pas sur la meme + // ligne + option_argument = option_stringTmp; + option.addAttribute(OPTIONARGUMENT, + option_argument); + line = in.readLine(); + if (line != null) { + result.setText(line.trim()); + } + } + } + if (delimiter == ',') { + line = line.substring(option_string.length() + 1 + + (option_argument == null ? 0 : option_argument.length())); + } + if (done) { + result.setText(option_stringTmp.substring(matcher.end(), option_stringTmp.length()).trim() + + " " + joinBlock(readBlock(1))); + } + } while (delimiter == ','); + } + } + endPeek(); + return result; + } + + /** + * read topic + * + * <pre> + * -.. topic:: Title + * Body. + * </pre> + * + * @return Element + * @throws IOException + */ + private Element peekTopic() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("^\\.\\.\\s+(" + TOPIC + ")::\\s+(.*)$")) { + Matcher matcher = Pattern.compile(TOPIC + "::").matcher(line); + matcher.find(); + result = DocumentHelper.createElement(TOPIC).addAttribute( + LEVEL, "" + level(line)); + String title = line.substring(matcher.end(), line.length()); + result.addAttribute(TITLE, title); + line = in.readLine(); + if (line.matches("\\s*")) { + line = in.readLine(); + } + int level = level(line); + String[] lines = null; + if (level != 0) { + lines = in.readWhile("(^ {" + level + "}.*)|(\\s*)"); + String txt = line; + for (String txtTmp : lines) { + txt += "\n" + txtTmp.trim(); + } + result.setText(txt); + } + + } + } + + endPeek(); + return result; + } + + /** + * read sidebar + * + * <pre> + * .. sidebar:: Title + * :subtitle: If Desired + * Body. + * </pre> + * + * @return Element + * @throws IOException + */ + private Element peekSidebar() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("^\\.\\.\\s*(" + SIDEBAR + ")::\\s*(.*)$")) { + Matcher matcher = Pattern.compile(SIDEBAR + "::").matcher(line); + matcher.find(); + result = DocumentHelper.createElement(SIDEBAR).addAttribute( + LEVEL, "" + level(line)); + String title = line.substring(matcher.end(), line.length()); + result.addAttribute(TITLE, title); + line = in.readLine(); + if (line.matches("^\\s+:subtitle:\\s*(.*)$*")) { + matcher = Pattern.compile(":subtitle:\\s*").matcher(line); + matcher.find(); + String subTitle = line.substring(matcher.end(), line + .length()); + result.addAttribute(SUBEXISTE, TRUE); + result.addAttribute(SUBTITLE, subTitle); + line = in.readLine(); + } else { + result.addAttribute(SUBEXISTE, FALSE); + } + String txt = joinBlock(readBlock(level(line))); + result.setText(txt); + + } + } + + endPeek(); + return result; + } + + /** + * read line block + * + * <pre> + * | A one, two, a one two three four + * | + * | Half a bee, philosophically, + * | must, *ipso facto*, half not be. + * | But half the bee has got to be, + * | *vis a vis* its entity. D'you see? + * </pre> + * + * @return Element + * @throws IOException + */ + private Element peekLineBlock() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("\\|\\s.*")) { + String[] linesTmp = readBlock(0); + String[] lines = new String[linesTmp.length + 1]; + lines[0] = line; + for (int i = 0; i < linesTmp.length; i++) { + lines[i + 1] = linesTmp[i]; + } + int[] levelsTmp = new int[lines.length]; + int levelmin = 999; + result = DocumentHelper.createElement(LINE_BLOCK).addAttribute( + LEVEL, 0 + ""); + for (int i = 0; i < levelsTmp.length; i++) { + // on enleve | + lines[i] = lines[i].replaceAll("\\|\\s?", ""); + } + for (int i = 0; i < levelsTmp.length; i++) { + // determination des levels + levelsTmp[i] = level(lines[i]); + } + for (int i : levelsTmp) { + // level minimal + levelmin = Math.min(levelmin, i); + } + int cnt = 0; + String lineAv = ""; + int[] levels = new int[levelsTmp.length]; + for (String l : lines) { + if (!l.matches("\\s*")) { // Si la ligne courante n'est + // pas vide + int level = levelsTmp[cnt] - levelmin; + if (level != 0) { + if (cnt != 0) { + if (!lineAv.matches("\\s*")) { // Si la ligne + // d'avant n'est + // pas vide + int levelAv = levelsTmp[cnt - 1] - levelmin; + if (levelAv < level) { + levels[cnt] = levels[cnt - 1] + 1; + if (cnt != levels.length) { + int levelAp = levelsTmp[cnt + 1] + - levelmin; + if (levelAp < level + && levelAv < levelAp) { + levels[cnt]++; + } + } + } else { + levels[cnt] = levels[cnt - 1] - 1; + } + } else { + levels[cnt] = 1; + } + } else { + levels[cnt] = 1; + } + } else { + levels[cnt] = 0; + } + } else { + if (cnt != 0) { + levels[cnt] = levels[cnt - 1]; + } + else { + levels[cnt] = 0; + } + } + cnt++; + lineAv = l; + } + for (int i = 0; i < levels.length; i++) { + Element eLine = result.addElement(LINE); + eLine.addAttribute(LEVEL, "" + levels[i]); + eLine.setText(lines[i].trim()); + } + } + } + endPeek(); + return result; + } + + /** + * read doctest block + * + * <pre> + * >>> print 'this is a Doctest block' + * this is a Doctest block + * </pre> + * + * @return Element + * @throws IOException + */ + private Element peekDoctestBlock() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("^\\s*>>>\\s.*")) { + int level = level(line); + result = DocumentHelper.createElement(DOCTEST_BLOCK) + .addAttribute(LEVEL, String.valueOf(level)); + result.addAttribute(XMLSPACE, "preserve"); + line += "\n" + joinBlock(readBlock(level)); + result.setText(line); + } + } + endPeek(); + return result; + } + + /** + * read block quote + * + * <pre> + * As a great paleontologist once said, + * + * This theory, that is mine, is mine. + * + * -- Anne Elk (Miss) + * </pre> + * + * @return Element + * @throws IOException + */ + private Element peekBlockQuote() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("\\s.*")) { + int level = level(line); + String savedLine = line + " " + joinBlock(readBlock(level)); + line = in.readLine(); + + if (line != null) { + level = level(line); + String blockQuote = null; + if (level != 0) { + String txt = line; + String[] lines = in.readWhile("(^ {" + level + + "}.*)|(\\s*)"); + for (String l : lines) { + if (l.matches("^ {" + level + "}--\\s*.*")) { + blockQuote = l; + blockQuote = blockQuote.replaceAll("--", "") + .trim(); + } else { + txt += "\n" + l; + } + } + result = DocumentHelper.createElement(BLOCK_QUOTE) + .addAttribute(LEVEL, String.valueOf(level)); + if (blockQuote != null) { + result.addAttribute(ATTRIBUTION, blockQuote); + } + result.setText(savedLine + txt); + } + } + } + } + endPeek(); + return result; + } + + /** + * read admonitions : + * admonition|attention|caution|danger|error|hint|important|note|tip|warning + * + * <pre> + * .. Attention:: All your base are belong to us. + * .. admonition:: And, by the way... + * + * You can make up your own admonition too. + * </pre> + * + * @return Element + * @throws IOException + */ + protected Element peekAdmonition() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + String lineTest = line.toLowerCase(); + Pattern pAdmonition = Pattern.compile("^\\s*\\.\\.\\s(" + + ADMONITION_PATTERN + ")::\\s*(.*)$"); + Matcher matcher = pAdmonition.matcher(lineTest); + + if (matcher.matches()) { + + boolean admonition = false; + matcher = Pattern.compile(ADMONITION_PATTERN).matcher(lineTest); + matcher.find(); + int level = level(line); + result = DocumentHelper.createElement(ADMONITION).addAttribute( + LEVEL, "" + level); + + if (matcher.group().equals(ADMONITION)) { // Il y a un titre + // pour un + // admonition + // general + admonition = true; + result.addAttribute(TYPE, ADMONITION); + String title = line.substring(matcher.end() + 2, line + .length()); + + result.addAttribute(TITLEATTR, title); + } else { + result.addAttribute(TYPE, matcher.group()); + } + + String firstLine = ""; + if (!admonition && matcher.end() + 2 < line.length()) { + firstLine = line + .substring(matcher.end() + 2, line.length()); + } + line = in.readLine(); + if (line != null) { + if (line.matches("\\s*")) { + line = in.readLine(); + } + if (line != null && !line.matches("\\s*")) { + level = level(line); + String txt = firstLine.trim() + "\n" + line + "\n"; + txt += "\n" + readBlockWithBlankLine(level); + result.setText(txt); + } else { + result.setText(firstLine); + } + } else { + result.setText(firstLine); + } + } + } + endPeek(); + return result; + } + + /** + * read blank line + * + * @return Element + * @throws IOException + */ + public Element peekBlankLine() throws IOException { + beginPeek(); + Element result = null; + + // must have one blank line before + String line = in.readLine(); + if (line != null && line.matches("\\s*")) { + int level = level(line); + result = DocumentHelper.createElement(BLANK_LINE).addAttribute( + LEVEL, String.valueOf(level)); + } + + endPeek(); + return result; + } + + /** + * read directive or reference + * + * @return Element + * @throws IOException + */ + public Element peekDirectiveOrReference() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + Pattern pImage = Pattern + .compile("^\\.\\.\\s*(?:\\|([^|]+)\\|)?\\s*(\\w+)::\\s*(.*)$"); + Matcher matcher = pImage.matcher(line); + if (matcher.matches()) { + String ref = matcher.group(1); + String directiveType = matcher.group(2); + String directiveValue = matcher.group(3); + Element directive = null; + if (ref != null && !"".equals(ref)) { + result = DocumentHelper + .createElement(SUBSTITUTION_DEFINITION); + result.addAttribute(NAME, ref); + directive = result.addElement(DIRECTIVE); + } else { + result = DocumentHelper.createElement(DIRECTIVE); + directive = result; + } + result.addAttribute(LEVEL, "0"); + + directive.addAttribute(DIRECTIVE_TYPE, directiveType); + directive.addAttribute(DIRECTIVE_VALUE, directiveValue); + + String[] lines = readBlock(1); + String text = joinBlock(lines, "\n", false); + + directive.setText(text); + } + } + endPeek(); + return result; + } + + /** + * read transition + * + * @return Element + * @throws IOException + */ + public Element peekTransition() throws IOException { + beginPeek(); + + Element result = null; + // no eat blank line, see next comment + + // must have one blank line before + String line = in.readLine(); + if (line != null && line.matches("\\s*")) { + // in.skipBlankLines(); + line = in.readLine(); + if (line != null && line.matches("-{3,}\\s*")) { + line = in.readLine(); + // must have one blank line after + if (line != null && line.matches("\\s*")) { + result = DocumentHelper.createElement(TRANSITION) + .addAttribute(LEVEL, String.valueOf(0)); + } + } + } + endPeek(); + return result; + } + + /** + * read paragraph with attribut level that represente the space numbers at + * left side + * + * @return <paragraph level="[int]">[text]</paragraph> + * @throws IOException + */ + public Element peekPara() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + + String[] lines; + do { + lines = readBlock(0); + if (lines.length > 0) { + int level = level(lines[0]); + String para = joinBlock(lines); + + boolean literal = false; + if (para.endsWith(": ::")) { + para = para.substring(0, para.length() - " ::".length()); + + in.unread("::", true); + for(int i=0;i<level;i++) { + in.add(' '); + } + + literal = true; + } else if (para.endsWith("::")) { + para = para.substring(0, para.length() - ":".length()); // keep + // one + // : + + in.unread("::", true); + + for(int i=0;i<level;i++) { + in.add(' '); + } + + literal = true; + } + + if (para.length() == 0 || ":".equals(para)) { + if (literal) { + in.readLine(); // eat "::" + } + } else { + // if para is empty, there are error and possible + // infiny loop on para, force read next line + result = DocumentHelper.createElement(PARAGRAPH) + .addAttribute(LEVEL, String.valueOf(level)) + .addText(para); + } + } + } while (result == null && lines.length > 0); + + endPeek(); + return result; + } + + /** + * read literal block + * + * <pre> + * :: + * + * LiteralBlock + * </pre> + * + * @return Element + * @throws IOException + */ + public Element peekLiteralBlock() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + + + String[] prefix = in.readLines(2); + if (prefix.length == 2 && prefix[0].matches("\\s*::\\s*") + && prefix[1].matches("\\s*")) { + + int level = level(prefix[0]); + + String para = in.readLine(); + if (para != null) { + level=level+1; + para = para.substring(level) + "\n"; + + // it's literal block until level is down + String[] lines = in.readWhile("(^ {" + level + "}.*|\\s*)"); + while (lines.length > 0) { + for (String line : lines) { + if (!line.matches("\\s*")) { + para += line.substring(level) + "\n"; + } + else { + para += "\n"; + } + } + lines = in.readWhile("(^ {" + level + "}.*|\\s*)"); + } + + result = DocumentHelper.createElement(LITERAL_BLOCK) + .addAttribute(LEVEL, String.valueOf(level)).addText( + para); + } + } + + endPeek(); + return result; + } + + /** + * read doc info author, date, version, ... + * + * <pre> + * :author: Benjamin Poussin + * :address: + * Quelque part + * Dans le monde + * </pre> + * + * @return Element + * @throws IOException + */ + public Element peekDocInfoItem() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + String line = in.readLine(); + // (?i) case inensitive on docinfo item + if (line != null && line.matches("^:((?i)" + DOCINFO_ITEM + "):.*$")) { + + result = DocumentHelper.createElement(DOCINFO); + result.addAttribute(LEVEL, "0"); + String infotype = line.substring(1, line.indexOf(":", 1)); + + /* + * if (!in.eof()) { String [] content = readBlock(1); line += + * joinBlock(content); } + */ + String text = line.substring(line.indexOf(":", 1) + 1).trim(); + String[] textTmp = in.readWhile("^\\s+.*"); + if (textTmp.length != 0) { + in.mark(); + } + + for (String txt : textTmp) { + text += "\n" + txt.trim(); + } + + // CVS, RCS support + text = text.replaceAll("\\$\\w+: (.+?)\\$", "$1"); + + result.addAttribute(TYPE, infotype).addText(text); + } + endPeek(); + return result; + } + + /** + * read table simple and complexe + * + * <pre> + * +------------------------+------------+----------+----------+ + * | Header row, column 1 | Header 2 | Header 3 | Header 4 | + * | (header rows optional) | | | | + * +========================+============+==========+==========+ + * | body row 1, column 1 | column 2 | column 3 | column 4 | + * +------------------------+------------+----------+----------+ + * | body row 2 | Cells may span columns. | + * +------------------------+------------+---------------------+ + * </pre> + * + * @return Element + * @throws IOException + */ + @SuppressWarnings("unchecked") + public Element peekTable() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + String line = in.readLine(); + + if (line != null) { + Pattern pTableBegin = Pattern.compile("^\\s*(\\+-+)+\\+\\s*$"); + Matcher matcher = null; + + matcher = pTableBegin.matcher(line); + if (matcher.matches()) { // complexe table + result = DocumentHelper.createElement(TABLE); + result.addAttribute(TABLE_HEADER, FALSE); + int level = level(line); + result.addAttribute(LEVEL, String.valueOf(level)); + line = line.trim(); + int tableWidth = line.length(); + result.addAttribute(TABLE_WIDTH, String.valueOf(tableWidth)); + + Pattern pCellEnd = Pattern + .compile("^\\s{" + + level + + "}(\\+-+\\+|\\|(?:[^+]+))([^+]+(?:\\+|\\|\\s*$)|-+\\+)*\\s*"); // fin + // de + // ligne + Pattern pCell = Pattern.compile("^\\s{" + level + + "}(\\|[^|]+)+\\|\\s*$"); // une ligne + Pattern pHeader = Pattern.compile("^\\s{" + level + + "}(\\+=+)+\\+\\s*$"); // fin du header + Pattern pEnd = Pattern.compile("^\\s{" + level + + "}(\\+-+)+\\+\\s*$"); // fin de table + + // used to know if | is cell separator or not + String lastSeparationLine = line; + String lastLine = line; + + Element row = DocumentHelper.createElement(ROW); + String[] table = in.readUntilBlank(); + + boolean done = false; + for (String l : table) { + done = false; + l = l.trim(); + if (l.length() != tableWidth) { + // Erreur dans la table, peut-etre lever une exception ? + result = null; + break; + } + matcher = pEnd.matcher(l); + if (!done && matcher.matches()) { + // fin normale de ligne, on peut directement l'assigner + lastSeparationLine = l; + for (Element cell : (List<Element>) row.elements()) { + cell.addAttribute(CELL_END, TRUE); + } + row.addAttribute(ROW_END_HEADER, FALSE); + result.add(row); + row = DocumentHelper.createElement(ROW); + done = true; + } + matcher = pHeader.matcher(l); + if (!done && matcher.matches()) { + // fin de du header, on peut directement l'assigner + lastSeparationLine = l; + for (Element cell : (List<Element>) row.elements()) { + cell.addAttribute(CELL_END, TRUE); + } + row.addAttribute(ROW_END_HEADER, TRUE); + result.add(row); + result.addAttribute(TABLE_HEADER, TRUE); + row = DocumentHelper.createElement(ROW); + done = true; + } + matcher = pCell.matcher(l); + if (!done && matcher.matches()) { + // debug + row.addAttribute("debug", "pCell"); + // recuperation des textes des cellules + int start = -1; + String content = ""; + matcher = Pattern.compile("([^|]+)\\|").matcher(l); + for (int cellNumber = 0; matcher.find(); cellNumber++) { + int tmpstart = matcher.start(1); + int end = matcher.end(1); + String tmpcontent = matcher.group(1); + // on a forcement un | ou un + au dessus du + + // et forcement un + sur lastSeparationLine + // sinon ca veut dire qu'il y avait un | dans la + // cell + if ((lastLine.charAt(end) == '|' || lastLine + .charAt(end) == '+') + && lastSeparationLine.charAt(end) == '+') { + if ("".equals(content)) { + content = tmpcontent; + } + else { + content += tmpcontent; + } + if (start == -1) { + start = tmpstart; + } + Element cell = null; + if (row.nodeCount() <= cellNumber) { + cell = row.addElement(CELL); + cell.addAttribute(CELL_END, FALSE); + } else { + cell = (Element) row.node(cellNumber); + } + cell.addAttribute(CELL_INDEX_START, String + .valueOf(start)); + cell.addAttribute(CELL_INDEX_END, String + .valueOf(end)); + cell.setText(cell.getText() + content + "\n"); + start = end + 1; // +1 to pass + or | at end + // of cell + content = ""; + } else { + // start = tmpstart; + if (start == -1) { + start = tmpstart; + } + content += tmpcontent + "|"; + cellNumber--; + } + } + done = true; + } + matcher = pCellEnd.matcher(l); + if (!done && matcher.matches()) { + // debug + row.addAttribute("debug", "pCellEnd"); + // fin d'une ligne, on ne peut pas l'assigner + // directement + // pour chaque continuation de cellule, il faut copier + // l'ancienne valeur + + // mais on commence tout de meme par fermer tout les + // cells + for (Element cell : (List<Element>) row.elements()) { + cell.addAttribute(CELL_END, TRUE); + } + + StringBuffer tmp = new StringBuffer(l); + int start = -1; + String content = ""; + matcher = Pattern.compile("([^+|]+|-+)([+|])").matcher( + l); + for (int cellNumber = 0; matcher.find(); cellNumber++) { + int tmpstart = matcher.start(1); + int end = matcher.end(1); + String tmpcontent = matcher.group(1); + String ender = matcher.group(2); + if (!tmpcontent.matches("-+")) { + // on a forcement un | au dessus du + ou du | + // sinon ca veut dire qu'il y avait un + dans la + // cell + if (lastLine.charAt(end) == '|') { + if (start == -1) { + start = tmpstart; + } + // -1 and +1 to take the + or | at begin and + // end + String old = lastSeparationLine.substring( + start - 1, end + 1); + tmp.replace(start - 1, end + 1, old); + if ("".equals(content)) { + content = tmpcontent; + } + Element cell = null; + if (row.nodeCount() <= cellNumber) { + cell = row.addElement(CELL); + } else { + cell = (Element) row.node(cellNumber); + + } + cell.setText(cell.getText() + content + + "\n"); + // on a ajouter des choses dans la cell, + // donc + // ce n'est pas la fin + cell.addAttribute(CELL_END, FALSE); + cell.addAttribute(CELL_INDEX_START, String + .valueOf(start)); + cell.addAttribute(CELL_INDEX_END, String + .valueOf(end)); + start = end + 1; // +1 to pass + or | at + // end of cell + content = ""; + } else { + // start = tmpstart; + content += tmpcontent + ender; + } + } + } + lastSeparationLine = tmp.toString(); + row.addAttribute(ROW_END_HEADER, FALSE); + result.add(row); + row = DocumentHelper.createElement(ROW); + done = true; + } + if (!done) { + log.warn("Bad table format line " + in.getLineNumber()); + } + lastLine = l; + } + + // + // line += "\n" + joinBlock(table, "\n", false); + // + // result.addText(line); + } else if (line.matches("^\\s*(=+ +)+=+\\s*$")) { + // Les donnees de la table peuvent depasser de celle-ci + /* + * ===== ===== ====== Inputs Output ------------ ------ A B A or + * B ===== ===== ====== False False Second column of row 1. True + * False Second column of row 2. + * + * True 2 - Second column of row 3. + * + * - Second item in bullet list (row 3, column 2). ============ + * ====== + */ + + result = DocumentHelper.createElement(TABLE); + line = line.trim(); + Pattern pBordersEquals = Pattern.compile("^\\s*(=+ +)+=+\\s*$"); // Separation + // = + Pattern pBordersTiret = Pattern.compile("^\\s*(-+ +)+-+\\s*$"); // Separation + // - + Pattern pBorders = Pattern.compile("^\\s*([=-]+ +)+[=-]+\\s*$"); // = + // ou + // - + String[] table = in.readUntilBlank(); // Recuperation de la + // table + + int tableWidth = line.length(); + int nbSeparations = 0; + for (String l : table) { + if (l.length() > tableWidth) { + tableWidth = l.length(); // Determination de la + } // Determination de la + // longueur max + matcher = pBordersEquals.matcher(l); + if (matcher.matches()) { + nbSeparations++; + } + + } + // Header if the table contains 3 equals separations + result.addAttribute(TABLE_HEADER, "" + (nbSeparations == 2)); + int level = level(line); + result.addAttribute(LEVEL, String.valueOf(level)); + result + .addAttribute(TABLE_WIDTH, String + .valueOf(tableWidth + 1)); + Element row = DocumentHelper.createElement(ROW); + // Determination of the columns positions + List<Integer> columns = new LinkedList<Integer>(); + matcher = Pattern.compile("=+\\s+").matcher(line); + for (int cellNumber = 0; matcher.find(); cellNumber++) { + columns.add(matcher.end()); + } + columns.add(tableWidth); + + // Traitement du tbl + /* + * ===== ===== ====== Inputs Output ------------ ------ A B A or + * B ===== ===== ====== False False Second column of row 1. True + * False Second column of row 2. + * + * True 2 - Second column of row 3. + * + * - Second item in bullet list (row 3, column 2). ============ + * ====== devient l'equivalent : ===== ===== ====== Inputs + * Output ------------ ------ A B A or B ===== ===== ====== + * False False Second column of row 1. ----- ----- ------ True + * False Second column of row 2. ----- ----- ------ True 2 - + * Second column of row 3. - Second item in bullet list (row 3, + * column 2). ============ ====== + */ + String lineRef = line.replace('=', '-'); + Matcher matcher2; + List<String> tableTmp = new LinkedList<String>(); + + for (int i = 0; i < table.length - 1; i++) { + tableTmp.add(table[i]); + if (!table[i].equals("")) { + if (!table[i + 1] + .substring(0, columns.get(0)) + .matches("\\s*")) { + matcher = pBorders.matcher(table[i]); + matcher2 = pBorders.matcher(table[i + 1]); + if (!matcher.matches() && !matcher2.matches() + && !table[i + 1].equals("")) { + tableTmp.add(lineRef); + } + } + } + } + tableTmp.add(table[table.length - 1]); + table = new String[tableTmp.size()]; + for (int i = 0; i < tableTmp.size(); i++) { + table[i] = tableTmp.get(i); + } + + boolean done = false; + LinkedList<String> lastLines = new LinkedList<String>(); + int separation = 1; + for (String l : table) { + if (l != null) { + done = false; + matcher = pBordersTiret.matcher(l); + matcher2 = pBordersEquals.matcher(l); + if (matcher.matches() || matcher2.matches()) { // Intermediate + // separation + while (!lastLines.isEmpty()) { + matcher = Pattern.compile("[-=]+\\s*").matcher( + l); + String tmpLine = lastLines.getLast(); + lastLines.removeLast(); + int cellNumber; + for (cellNumber = 0; matcher.find(); cellNumber++) { + Element cell = null; + if (row.nodeCount() <= cellNumber) { + cell = row.addElement(CELL); + } else { + cell = (Element) row.node(cellNumber); + } + if (matcher.start() < tmpLine.length()) { + if (columns.size() - 1 == cellNumber) { + cell.setText(tmpLine.substring( + matcher.start(), tmpLine + .length()) + + "\n"); + } else { + if (matcher.end() < tmpLine + .length()) { + cell.setText(tmpLine.substring(matcher.start(), matcher.end()) + "\n"); + } + else { + cell.setText(tmpLine.substring(matcher.start(), tmpLine.length()) + "\n"); + } + } + } + + if (lastLines.size() == 0) { + row.addAttribute("debug", "pCell"); + cell.addAttribute(CELL_END, TRUE); + } else { + row.addAttribute("debug", "pCellEnd"); + cell.addAttribute(CELL_END, FALSE); + } + cell.addAttribute(CELL_INDEX_START, String + .valueOf(matcher.start() + 1)); + if (line.length() == matcher.end()) { + cell.addAttribute(CELL_INDEX_END, String.valueOf(columns.get(columns.size() - 1))); + } + else { + cell.addAttribute(CELL_INDEX_END, String.valueOf(matcher.end())); + } + } + + if (matcher2.matches()) { + separation++; + row.addAttribute(ROW_END_HEADER, "" + + (separation == 2)); + } else { + row.addAttribute(ROW_END_HEADER, FALSE); + } + + result.add(row); + row = DocumentHelper.createElement(ROW); + done = true; + } + } + if (!done && l.matches("^\\s*(.+ +)+.+\\s*$")) { + // Data + lastLines.addFirst(l); // Les donnees sont stoquee + // dans une file d'attente + // lastLines (FIFO) + done = true; + } + if (!done) { + log.warn("Bad table format line " + + in.getLineNumber()); + } + } + } + } + } + endPeek(); + + return result; + } + + /** + * read list + * + * <pre> + * - first line + * - next line + * </pre> + * + * @return <bullet_list level="[int]" + * bullet="char"><[text];</bullet_list> + * @throws IOException + */ + public Element peekBulletList() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + String line = in.readLine(); + if (line != null + && line.matches("^\\s*[" + escapeRegex(BULLET_CHAR) + + "] +\\S.*")) { + int level = level(line); + String bullet = line.substring(level, level + 1); + + result = DocumentHelper.createElement(BULLET_LIST).addAttribute( + LEVEL, String.valueOf(level)).addAttribute(BULLET, + bullet); + + if (!in.eof()) { + String[] content = readBlock(level + 1); + line += " " + joinBlock(content); + } + String text = line.substring(level + 1).trim(); + + result.addText(text); + in.skipBlankLines(); + } + + endPeek(); + return result; + } + + /** + * read field list + * + * <pre> + * :first: text + * :second: text + * and other text + * :last empty: + * </pre> + * + * @return <field_list level="[int]" + * name="[text]">[text]</field_list> + * @throws IOException + */ + public Element peekFieldList() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + String line = in.readLine(); + if (line != null) { + Pattern pattern = Pattern.compile("^\\s*:([^:]+): [^\\s].*"); + Matcher matcher = pattern.matcher(line); + if (matcher.matches()) { + int level = level(line); + String name = matcher.group(1); + int begin = matcher.end(1) + 1; + + result = DocumentHelper.createElement(FIELD_LIST).addAttribute( + LEVEL, String.valueOf(level)).addAttribute(NAME, + name); + + if (!in.eof()) { + String[] content = readBlock(level + 1); + line += " " + joinBlock(content); + } + String text = line.substring(begin).trim(); + + result.addText(text); + } + } + + endPeek(); + return result; + } + + /** + * read definition list + * + * <pre> + * un autre mot + * une autre definition + * le mot : la classe + * la definition + * le mot : la classe 1 : la classe 2 + * la definition + * </pre> + * + * @return <definition_list level="[int]" term="[text]" + * classifiers="[text]">[text]</definition_list> + * @throws IOException + */ + public Element peekDefinitionList() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + String[] lines = in.readLines(2); + if (lines.length == 2) { + int level = level(lines[0]); + int levelDef = level(lines[1]); + if ((levelDef != lines[1].length()) && (level < levelDef)) { + in.unread(lines[1], true); + Pattern pattern = Pattern.compile("^\\s*([^:]+)(?: : (.*))?"); + Matcher matcher = pattern.matcher(lines[0]); + if (matcher.matches()) { + String term = matcher.group(1); + String classifiers = matcher.group(2); + + result = DocumentHelper.createElement(DEFINITION_LIST) + .addAttribute(LEVEL, String.valueOf(level)) + .addAttribute(TERM, term).addAttribute( + CLASSIFIERS, classifiers); + + // poussin 20070207 don't read block here because can't + // interpret it correctly in JRSTReader + // if (!in.eof()) { + // String [] content = readBlock(level + 1); + // String text = joinBlock(content); + // result.addText(text); + // } + } + } + } + + endPeek(); + return result; + } + + /** + * read enumarted list + * + * can be: <li>1, 2, 3, ... <li>a, b, c, ... <li>A, B, C, ... <li>i, ii, + * iii, iv, ... <li>I, II, III, IV, ... + * + * or # for auto-numbered + * + * <pre> + * + * 1. next line 1) next line (1) first line + * + * </pre> + * + * @return <enumerated_list level="[int]" start="[number]" + * prefix="[char]" suffix="[char]" + * enumtype="[(arabic|loweralpha|upperalpha|lowerroman|upperroman]"> + * ;[text]</enumerated_list> + * @throws IOException + */ + public Element peekEnumeratedList() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + + String line = in.readLine(); + if (line != null) { + Pattern pattern = Pattern + .compile("^\\s*(\\(?)(#|\\d+|[a-z]|[A-Z]|[ivxlcdm]+|[IVXLCDM]+)([\\.)]) [^\\s].*"); + Matcher matcher = pattern.matcher(line); + if (matcher.matches()) { + int level = level(line); + String prefix = matcher.group(1); + String start = matcher.group(2); + String suffix = matcher.group(3); + int begin = matcher.end(3); + + // arabic|loweralpha|upperalpha|lowerroman|upperroman + String enumtype = "auto"; + if (start.matches("\\d+")) { + enumtype = "arabic"; + } else if (start.matches("(i|[ivxlcdm][ivxlcdm]+)")) { + enumtype = "lowerroman"; + start = "1"; // TODO transform romain to arabic + } else if (start.matches("(I|[IVXLCDM][IVXLCDM]+)")) { + enumtype = "upperroman"; + start = "1"; // TODO transform romain to arabic + } else if (start.matches("[a-z]+")) { + enumtype = "loweralpha"; + start = String.valueOf((int) start.charAt(0) - (int) 'a'); + } else if (start.matches("[A-Z]+")) { + enumtype = "upperalpha"; + start = String.valueOf((int) start.charAt(0) - (int) 'A'); + } + + result = DocumentHelper.createElement(ENUMERATED_LIST) + .addAttribute(LEVEL, String.valueOf(level)) + .addAttribute(START, start).addAttribute(PREFIX, + prefix).addAttribute(SUFFIX, suffix) + .addAttribute(ENUMTYPE, enumtype); + + if (line.endsWith(": ::")) { + line = line.substring(0, line.length() - " ::".length()); + in.unread("::", true); + + } else if (line.endsWith("::")) { + line = line.substring(0, line.length() - ":".length()); // keep + // one + // : + in.unread("::", true); + } + + + if (!in.eof()) { + String[] content = readBlock(level + 1); + String tempLine = " " + joinBlock(content); + + if (tempLine.endsWith(": ::")) { + tempLine = tempLine.substring(0, tempLine.length() - " ::".length()); + in.unread("::", true); + + } else if (tempLine.endsWith("::")) { + tempLine = tempLine.substring(0, tempLine.length() - ":".length()); // keep + // one + // : + in.unread("::", true); + } + + line+=tempLine; + + + } + String text = line.substring(begin).trim(); + + result.addText(text); + in.skipBlankLines(); + } + } + + endPeek(); + return result; + } + + /** + * Parse un titre simple ou double + * + * simple: + * + * <pre> + * + * Le titre ======== + * + * </pre> + * + * double: + * + * <pre> + * + * ============ le titre ============ + * + * </pre> + * + * @return <title level="[int]" type="[simple|double]" char="[underline + * char]"> + * @throws IOException + */ + public Element peekTitle() throws IOException { + beginPeek(); + + Element result = null; + // in.skipBlankLines(); + String line = in.readLine(); + + if (line != null) { + if (startsWithTitleChar(line)) { + String[] titles = in.readLines(2); + if (titles.length == 2 && line.length() >= titles[0].length() + && line.length() == titles[1].length() + && line.equals(titles[1])) { + result = DocumentHelper.createElement(TITLE).addAttribute( + TYPE, "double").addAttribute(CHAR, + titles[1].substring(0, 1)).addText(titles[0]); + } + } else { + String title = in.readLine(); + if (title != null + && startsWithTitleChar(title) + && line.replaceFirst("\\s*$", "").length() == title + .length()) { + + result = DocumentHelper.createElement(TITLE).addAttribute( + TYPE, "simple").addAttribute(CHAR, + title.substring(0, 1)).addText( + line.replaceFirst("\\s*$", "")); + } + } + } + + if (result != null) { + // add level information + String titleLevel = result.attributeValue(CHAR); + + if ("double".equals(result.attributeValue(TYPE))) { + titleLevel += titleLevel; + } + int level = titleLevels.indexOf(titleLevel); + if (level == -1) { + level = titleLevels.size(); + titleLevels.add(titleLevel); + } + result.addAttribute(LEVEL, String + .valueOf(JRSTReader.MAX_SECTION_DEPTH + level)); + } + + endPeek(); + return result; + } + + public Element peekTarget() throws IOException { + beginPeek(); + + String line = in.readLine(); + Element result = null; + if (line != null) { + if (line.matches("^\\s*\\.\\.\\s_[^_:].+:.*")) { + result = DocumentHelper.createElement(TARGET); + Matcher matcher = Pattern.compile("\\.\\.\\s_").matcher(line); + if (matcher.find()) { + int i = line.indexOf(':'); + result.addAttribute(ID, URLEncoder.encode(line.substring(matcher.end(), i) + .toLowerCase().replaceAll(" ", "-"), "UTF-8")); + result.addAttribute(LEVEL, "" + level(line)); + } + } + } + endPeek(); + return result; + } + + /** + * .. _frungible doodads: http://www.example.org/ + * + * @return Element + * @throws IOException + */ + public LinkedList<Element> refTarget() throws IOException { + beginPeek(); + + String[] lines = in.readAll(); + LinkedList<Element> result = new LinkedList<Element>(); + for (String line : lines) { + if (line.matches("^\\s*\\.\\.\\s_[^_:].+:.*")) { + result.add(DocumentHelper.createElement(TARGET)); + Matcher matcher = Pattern.compile("\\.\\.\\s_").matcher(line); + if (matcher.find()) { + boolean done = false; + for (int i = matcher.end(); i < line.length() && !done; i++) { + if (line.charAt(i) == ':') { + result.getLast().addAttribute(LEVEL, + "" + level(line)); + result.getLast().addAttribute( + ID, + URLEncoder.encode(line.substring(matcher.end(), i) + .replaceAll(" ", "-").toLowerCase(), "UTF-8")); + result.getLast().addAttribute( + NAME, + line.substring(matcher.end(), i) + .toLowerCase()); + if (i + 2 > line.length()) { + line = in.readLine(); + // FIXME 20071129 chatellier + // line = null if link is non well formed + // .. _Unifying types and classes in Python: + // miss uri + if (line == null) { + line = ""; + } + result.getLast().addAttribute(REFURI, + line.trim()); + } else { + result.getLast().addAttribute(REFURI, line.substring(i + 2, line.length())); + } + + done = true; + } + } + } + } + } + endPeek(); + return result; + } + + /** + * .. __: http://www.python.org + * + * @return Element + * @throws IOException + */ + private Element peekTargetAnonymousBody() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("^\\s*__ .+$|^\\s*\\.\\. __\\:.+$")) { + result = DocumentHelper.createElement(TARGETANONYMOUS); + result.addAttribute(LEVEL, "" + level(line)); + + } + } + + endPeek(); + return result; + } + + /** + * .. comment + * + * @return Element + * @throws IOException + */ + private Element peekComment() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("^\\.\\.\\s+.*$")) { + result = DocumentHelper.createElement(COMMENT); + result.addAttribute(LEVEL, "0"); + result.addAttribute(XMLSPACE, "preserve"); + + // first line is part of comment + result.setText(line.substring(2).trim()); + line = in.readLine(); + if(line != null) { + int level = level(line); + if (level > 0) { + String[] lines = readBlock(level); + String text = line.substring(level); + for (String l : lines) { + text += "\n" + l.substring(level); + } + result.addText(text); + } + } + } + } + + endPeek(); + return result; + } + + /** + * .. comment + * + * @return Element + * @throws IOException + */ + public List<Element> peekAllComment() throws IOException { + beginPeek(); + List<Element> result = new ArrayList<Element>(); + String[] lines = in.readWhile("^\\.\\.\\s*.*$"); + if (lines != null) { +// int levelRef = level(line); + for (String line : lines) { + Element comment = DocumentHelper.createElement(COMMENT); + comment.addAttribute(LEVEL, "0"); + comment.addAttribute(XMLSPACE, "preserve"); + + // first line is part of comment + comment.setText(line.substring(2).trim()); + result.add(comment); + +// int level = level(line); +// if (level == levelRef) { +// String[] lines = readBlock(level); +// String text = line.substring(level); +// for (String l : lines) { +// text += "\n" + l.substring(level); +// } +// result.addText(text); +// } + } + in.mark(); + } + + endPeek(); + return result; + } + + /** + * .. _frungible doodads: http://www.example.org/ + * + * @return Element + * @throws IOException + */ + public Element peekFootnote() throws IOException { + beginPeek(); + Element result = null; + String line = in.readLine(); + if (line != null) { + if (line.matches("^\\s*\\.\\.\\s\\[(#|[0-9]|\\*).*\\]\\s.+$")) { + result = DocumentHelper.createElement(FOOTNOTES); + boolean bLine = false; + do { + + bLine = false; + Element footnote = result.addElement(FOOTNOTE); + Matcher matcher = Pattern.compile("\\.\\.\\s\\[").matcher( + line); + + if (matcher.find()) { + + boolean done = false; + for (int i = matcher.end(); i < line.length() && !done; i++) { + if (line.charAt(i) == ']') { + + result.addAttribute(LEVEL, "" + level(line)); + String id = line.substring(matcher.end(), i); + if (id.matches("\\*")) { + footnote.addAttribute(TYPE, AUTOSYMBOL); + } else if (id.matches("[0-9]")) { + footnote.addAttribute(TYPE, NUM); + footnote.addAttribute(NAME, id); + } else if (id.equals("#")) { + footnote.addAttribute(TYPE, AUTONUM); + } else { + footnote.addAttribute(TYPE, + AUTONUMLABEL); + footnote.addAttribute(NAME, id + .substring(1)); + } + String text = line.substring(i + 2, line + .length()); + + int levelAv = level(line); + line = in.readLine(); + if (line != null) { + if (line + .matches("^\\s*\\.\\.\\s\\[(#|[0-9]|\\*).*\\]\\s.+$")) { + bLine = true; + } else { + + int level = level(line); + if (levelAv < level) { + String[] lines = in + .readWhile("(^ {" + level + + "}.*)|(\\s*)"); + text += "\n" + line.trim(); + for (String l : lines) { + text += "\n" + l.trim(); + } + + } else if (line.matches("\\s*")) { + level = levelAv + 1; + String[] lines = in + .readWhile("(^ {" + level + + "}.*)|(\\s*)"); + text += "\n" + line.trim(); + for (String l : lines) { + text += "\n" + l.trim(); + } + + } + } + if (!bLine) { + in.skipBlankLines(); + String[] linesTmp = in + .readWhile("^\\s*\\.\\.\\s\\[(#|[0-9]|\\*).*\\]\\s.+$"); + + if (linesTmp.length > 0) { + line = linesTmp[0]; + bLine = true; + } + } + + } + if (line == null) { + line = ""; + } + footnote.setText(text); + done = true; + } + + } + } + + } while (bLine); + } + } + endPeek(); + return result; + } + + /** + * Read block text, block text have same indentation + * + * @param minLeftMargin + * min left blank needed to accept to read block + * @return String + * @throws IOException + */ + private String readBlockWithBlankLine(int level) throws IOException { + String txt = ""; + String[] lines = in.readWhile("(^ {" + level + "}.*)|(\\s*)"); + while (lines.length > 0) { + for (String l : lines) { + l = l.trim(); + txt += l + "\n"; + + } + lines = in.readWhile("(^ {" + level + "}.*)|(\\s*)"); + } + return txt; + } + + /** + * Lit les premieres ligne non vide et les retourne, rien n'est modifier par + * rapport aux positions dans le fichier. Util pour afficher a l'utilisateur + * les lignes qui ont produit une erreur + * + * @return les lignes non vides + * @throws IOException + */ + public String readNotBlanckLine() throws IOException { + beginPeek(); + in.skipBlankLines(); + String line = joinBlock(in.readUntilBlank(), "\n", false); + endPeek(); + return line; + } + + /** + * return the number of line read + * + * @return int + */ + public int getLineNumber() { + return in.getLineNumber(); + } + + /** + * return the number of char read + * + * @return int + */ + public int getCharNumber() { + return in.getCharNumber(); + } + + /** + * return true if line can be underline or overline for title + * + * @param line + * @return boolean + */ + private boolean startsWithTitleChar(String line) { + if (line == null || line.length() < 2) { + return false; + } + // est-ce que la ligne est constituer entierement du meme caractere et + // qu'il y en a au moins 2 + boolean result = line + .matches("([" + escapeRegex(TITLE_CHAR) + "])\\1+"); + return result; + } + + /** + * @param title_charescapeRegex + * @return String + */ + private String escapeRegex(String text) { + String result = text.replaceAll("([()[\\\\]*+?.])", "\\\\$1"); + return result; + } + + /** + * @param String + * line + * @return int + * @throws IOException + */ + private int level(String line) { + int result = 0; + String sTmp = line.replaceAll("\\s", " "); + while (sTmp.length() > result && sTmp.charAt(result) == ' ') { + result++; + } + return result; + } +} Added: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTReader.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTReader.java (rev 0) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/JRSTReader.java 2012-06-01 15:05:38 UTC (rev 693) @@ -0,0 +1,2418 @@ +/* + * #%L + * JRst :: Api + * + * $Id: JRSTReader.java 638 2011-12-23 10:35:21Z jruchaud $ + * $HeadURL: http://svn.nuiton.org/svn/jrst/branches/jrst-docutils-jython/jrst/src/main/j... $ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * JRSTReader.java + * + * Created: 27 oct. 06 00:15:34 + * + * @author poussin + * @version $Revision: 638 $ + * + * Last update: $Date: 2011-12-23 11:35:21 +0100 (ven. 23 déc. 2011) $ + * by : $Author: jruchaud $ + */ + +package org.nuiton.jrst; + +import static org.nuiton.i18n.I18n._; +import static org.nuiton.jrst.ReStructuredText.ADDRESS; +import static org.nuiton.jrst.ReStructuredText.ADMONITION; +import static org.nuiton.jrst.ReStructuredText.ATTRIBUTION; +import static org.nuiton.jrst.ReStructuredText.AUTHOR; +import static org.nuiton.jrst.ReStructuredText.AUTHORS; +import static org.nuiton.jrst.ReStructuredText.BLOCK_QUOTE; +import static org.nuiton.jrst.ReStructuredText.BULLET_LIST; +import static org.nuiton.jrst.ReStructuredText.COLSPEC; +import static org.nuiton.jrst.ReStructuredText.COMMENT; +import static org.nuiton.jrst.ReStructuredText.CONTACT; +import static org.nuiton.jrst.ReStructuredText.COPYRIGHT; +import static org.nuiton.jrst.ReStructuredText.DATE; +import static org.nuiton.jrst.ReStructuredText.DECORATION; +import static org.nuiton.jrst.ReStructuredText.DEFINITION; +import static org.nuiton.jrst.ReStructuredText.DEFINITION_LIST; +import static org.nuiton.jrst.ReStructuredText.DEFINITION_LIST_ITEM; +import static org.nuiton.jrst.ReStructuredText.DESCRIPTION; +import static org.nuiton.jrst.ReStructuredText.DOCINFO; +import static org.nuiton.jrst.ReStructuredText.DOCTEST_BLOCK; +import static org.nuiton.jrst.ReStructuredText.DOCUMENT; +import static org.nuiton.jrst.ReStructuredText.EMPHASIS; +import static org.nuiton.jrst.ReStructuredText.ENTRY; +import static org.nuiton.jrst.ReStructuredText.ENUMERATED_LIST; +import static org.nuiton.jrst.ReStructuredText.FIELD; +import static org.nuiton.jrst.ReStructuredText.FIELD_BODY; +import static org.nuiton.jrst.ReStructuredText.FIELD_LIST; +import static org.nuiton.jrst.ReStructuredText.FIELD_NAME; +import static org.nuiton.jrst.ReStructuredText.FOOTER; +import static org.nuiton.jrst.ReStructuredText.FOOTNOTE; +import static org.nuiton.jrst.ReStructuredText.FOOTNOTE_REFERENCE; +import static org.nuiton.jrst.ReStructuredText.FOOTNOTE_SYMBOL; +import static org.nuiton.jrst.ReStructuredText.GENERATED; +import static org.nuiton.jrst.ReStructuredText.HEADER; +import static org.nuiton.jrst.ReStructuredText.IMAGE; +import static org.nuiton.jrst.ReStructuredText.LABEL; +import static org.nuiton.jrst.ReStructuredText.LINE; +import static org.nuiton.jrst.ReStructuredText.LINE_BLOCK; +import static org.nuiton.jrst.ReStructuredText.LIST_ITEM; +import static org.nuiton.jrst.ReStructuredText.LITERAL; +import static org.nuiton.jrst.ReStructuredText.LITERAL_BLOCK; +import static org.nuiton.jrst.ReStructuredText.OPTION; +import static org.nuiton.jrst.ReStructuredText.OPTION_ARGUMENT; +import static org.nuiton.jrst.ReStructuredText.OPTION_GROUP; +import static org.nuiton.jrst.ReStructuredText.OPTION_LIST; +import static org.nuiton.jrst.ReStructuredText.OPTION_LIST_ITEM; +import static org.nuiton.jrst.ReStructuredText.OPTION_STRING; +import static org.nuiton.jrst.ReStructuredText.ORGANIZATION; +import static org.nuiton.jrst.ReStructuredText.PARAGRAPH; +import static org.nuiton.jrst.ReStructuredText.REFERENCE; +import static org.nuiton.jrst.ReStructuredText.REGEX_ANONYMOUS_HYPERLINK_REFERENCE; +import static org.nuiton.jrst.ReStructuredText.REGEX_EMAIL; +import static org.nuiton.jrst.ReStructuredText.REGEX_EMPHASIS; +import static org.nuiton.jrst.ReStructuredText.REGEX_FOOTNOTE_REFERENCE; +import static org.nuiton.jrst.ReStructuredText.REGEX_HYPERLINK_REFERENCE; +import static org.nuiton.jrst.ReStructuredText.REGEX_INLINE_REFERENCE; +import static org.nuiton.jrst.ReStructuredText.REGEX_LITERAL; +import static org.nuiton.jrst.ReStructuredText.REGEX_REFERENCE; +import static org.nuiton.jrst.ReStructuredText.REGEX_STRONG; +import static org.nuiton.jrst.ReStructuredText.REGEX_SUBSTITUTION_REFERENCE; +import static org.nuiton.jrst.ReStructuredText.REVISION; +import static org.nuiton.jrst.ReStructuredText.ROW; +import static org.nuiton.jrst.ReStructuredText.SECTION; +import static org.nuiton.jrst.ReStructuredText.SIDEBAR; +import static org.nuiton.jrst.ReStructuredText.STATUS; +import static org.nuiton.jrst.ReStructuredText.STRONG; +import static org.nuiton.jrst.ReStructuredText.SUBSTITUTION_DEFINITION; +import static org.nuiton.jrst.ReStructuredText.SUBTITLE; +import static org.nuiton.jrst.ReStructuredText.TABLE; +import static org.nuiton.jrst.ReStructuredText.TARGET; +import static org.nuiton.jrst.ReStructuredText.TBODY; +import static org.nuiton.jrst.ReStructuredText.TERM; +import static org.nuiton.jrst.ReStructuredText.TGROUP; +import static org.nuiton.jrst.ReStructuredText.THEAD; +import static org.nuiton.jrst.ReStructuredText.TITLE; +import static org.nuiton.jrst.ReStructuredText.TOPIC; +import static org.nuiton.jrst.ReStructuredText.TRANSITION; +import static org.nuiton.jrst.ReStructuredText.VERSION; + + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.NoSuchElementException; + +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.jrst.directive.ContentDirective; +import org.nuiton.jrst.directive.DateDirective; +import org.nuiton.jrst.directive.ImageDirective; +import org.nuiton.jrst.directive.SectnumDirective; +import org.nuiton.util.StringUtil; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.IllegalAddException; +import org.dom4j.Node; +import org.dom4j.VisitorSupport; + +/* + * + * <pre> +--------------------------------------------------------------------+ | + * document [may begin with a title, subtitle, decoration, docinfo] | | + * +--------------------------------------+ | | sections [each begins with a + * title] | + * +-----------------------------+-------------------------+------------+ | + * [body elements:] | (sections) | | | - literal | - lists | | - hyperlink + * +------------+ | | blocks | - tables | | targets | | para- | - doctest | - + * block | foot- | - sub. defs | | graphs | blocks | quotes | notes | - comments | + * +---------+-----------+----------+-------+--------------+ | [text]+ | [text] | + * (body elements) | [text] | | (inline + * +-----------+------------------+--------------+ | markup) | +---------+ + * </pre> + * + * + * Inline support: http://docutils.sourceforge.net/docs/user/rst/quickref.html + * + * <li> STRUCTURAL ELEMENTS: document, section, topic, sidebar <li> STRUCTURAL + * SUBELEMENTS: title, subtitle, decoration, docinfo, transition <li> docinfo: + * address, author, authors, contact, copyright, date, field, organization, + * revision, status, version <li> decoration: footer, header <li> BODY ELEMENTS: + * admonition, attention, block_quote, bullet_list, caution, citation, comment, + * compound, container, danger, definition_list, doctest_block, enumerated_list, + * error, field_list, figure, footnote, hint, image, important, line_block, + * literal_block, note, option_list, paragraph, pending, raw, rubric, + * substitution_definition, system_message, table, target, tip, warning <li> + * SIMPLE BODY ELEMENTS: comment, doctest_block, image, literal_block, + * paragraph, pending, raw, rubric, substitution_definition, target <li> + * COMPOUND BODY ELEMENTS: admonition, attention, block_quote, bullet_list, + * caution, citation, compound, container, danger, definition_list, + * enumerated_list, error, field_list, figure, footnote, hint, important, + * line_block, note, option_list, system_message, table, tip, warning <li> BODY + * SUBELEMENTS: attribution, caption, classifier, colspec, field_name, label, + * line, option_argument, option_string, term definition, definition_list_item, + * description, entry, field, field_body, legend, list_item, option, + * option_group, option_list_item, row, tbody, tgroup, thead <li> INLINE + * ELEMENTS: abbreviation, acronym, citation_reference, emphasis, + * footnote_reference, generated, image, inline, literal, problematic, + * reference, strong, subscript, substitution_reference, superscript, target, + * title_reference, raw + * + * <pre> DOCUMENT :: ( (title, subtitle?)?, decoration?, (docinfo, + * transition?)?, STRUCTURE.MODEL; ) decoration :: (header?, footer?) header, + * footer, definition, description, attention, caution, danger, error, hint, + * important, note, tip, warning :: (BODY.ELEMENTS;)+ transition :: EMPTY + * docinfo :: (BIBLIOGRAPHIC.ELEMENTS;)+ BIBLIOGRAPHIC.ELEMENTS :: author | + * authors | organization | contact | address | version | revision | status | + * date | copyright | field authors :: ( (author)+ ) field :: (field_name, + * field_body) field_body, list_item :: (BODY.ELEMENTS;)* STRUCTURE.MODEL :: ( ( + * (BODY.ELEMENTS; | topic | sidebar)+, transition? )*, ( (section), + * (transition?, (section) )* )? ) BODY.ELEMENTS :: paragraph | compound | + * container | literal_block | doctest_block | line_block | block_quote | table | + * figure | image | footnote | citation | rubric | bullet_list | enumerated_list | + * definition_list | field_list | option_list | attention | caution | danger | + * error | hint | important | note | tip | warning | admonition | reference | + * target | substitution_definition | comment | pending | system_message | raw + * topic :: (title?, (BODY.ELEMENTS;)+) sidebar :: (title, subtitle?, + * (BODY.ELEMENTS; | topic)+) section :: (title, STRUCTURE.MODEL;) line_block :: + * (line | line_block)+ block_quote:: ((BODY.ELEMENTS;)+, attribution?) + * bullet_list, enumerated_list :: (list_item +) definition_list :: + * (definition_list_item +) definition_list_item :: (term, classifier?, + * definition) field_list :: (field +) option_list :: (option_list_item +) + * option_list_item :: (option_string, option_argument *, description) + * option_string, option_argument :: (#PCDATA) admonition :: (title, + * (BODY.ELEMENTS;)+) + * + * title, subtitle, author, organization, contact, address, version, revision, + * status, date, copyright, field_name, paragraph, compound, container, + * literal_block, doctest_block, attribution, line, term, classifier :: + * TEXT.MODEL; + * + * TEXT.MODEL :: (#PCDATA | INLINE.ELEMENTS;)* INLINE.ELEMENTS :: emphasis | + * strong | literal | reference | footnote_reference | citation_reference | + * substitution_reference | title_reference | abbreviation | acronym | subscript | + * superscript | inline | problematic | generated | target | image | raw + * emphasis :: '*' #PCDATA '*' strong :: '**' #PCDATA '**' literal :: '``' + * #PCDATA '``' footnote_reference :: '[' ([0-9]+|#) ']' citation_reference :: + * '[' [a-zA-Z]+ ']' + * + * </pre> + */ + +/** + * Le principe est d'utiliser les methodes peek* {@link JRSTLexer} pour + * prendre l'element que l'on attend, si la methode retourne null ou un autre + * element et bien c que ce n'est pas le bon choix, cela veut dire que l'element + * courant est fini d'etre lu (plus de paragraphe dans la section par exemple) + * ou qu'il y a une erreur dans le fichier en entre. + * <p> + * On construit un arbre XML representant le RST au fur et a mesure, on peut + * ensuite appliquer une feuille de style ou autre chose avec + * {@link JRSTGenerator} + * + * <p> + * Tous les elements ont un attribut level qui permet de savoir on il est dans + * la hierarchie. Le Document a le level -1001, et les sections/titres on pour + * level les valeurs 1000, -999, -998, ... + * <p> + * de cette facon les methods isUpperLevel et isSameLevel fonctionne pour tous + * les elements de la meme facon + * + * <pre> + * abbreviation + * acronym + * address (done) + * admonition (done) + * attention (done) + * attribution + * author (done) + * authors (done) + * block_quote (done) + * bullet_list (done) + * caption + * caution (done) + * citation + * citation_reference + * classifier (done) + * colspec (done) + * comment + * compound + * contact (done) + * container + * copyright (done) + * danger (done) + * date (done) + * decoration (done) + * definition (done) + * definition_list (done) + * definition_list_item (done) + * description (done) + * docinfo (done) + * doctest_block (done) + * document (done) + * emphasis (done) + * entry (done) + * enumerated_list (done) + * error (done) + * field (done) + * field_body (done) + * field_list (done) + * field_name (done) + * figure + * footer (done) + * footnote (done) + * footnote_reference (done) + * generated + * header (done) + * hint (done) + * image (done) + * important (done) + * inline + * label + * legend + * line (done) + * line_block (done) + * list_item (done) + * literal (done) + * literal_block (done) + * note (done) + * option (done) + * option_argument (done) + * option_group (done) + * option_list (done) + * option_list_item (done) + * option_string (done) + * organization (done) + * paragraph (done) + * pending + * problematic + * raw + * reference (done) + * revision (done) + * row (done) + * rubric + * section (done) + * sidebar (done) + * status (done) + * strong (done) + * subscript + * substitution_definition + * substitution_reference + * subtitle (done) + * superscript + * system_message + * table (done) + * target (done) + * tbody (done) + * term (done) + * tgroup (done) + * thead (done) + * tip (done) + * title (done) + * title_reference + * topic (done) + * transition (done) + * version (done) + * warning (done) + * </pre> + * + * Created: 27 oct. 06 00:15:34 + * + * @author poussin, letellier + * @version $Revision: 638 $ + * + * Last update: $Date: 2011-12-23 11:35:21 +0100 (ven. 23 déc. 2011) $ + * by : $Author: jruchaud $ + */ +public class JRSTReader { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static Log log = LogFactory.getLog(JRSTReader.class); + + protected static final String ANONYMOUS = "anonymous"; + + protected static final String AUTO = "auto"; + + protected static final String AUTONUM = "autoNum"; + + protected static final String AUTONUMLABEL = "autoNumLabel"; + + protected static final String AUTOSYMBOL = "autoSymbol"; + + protected static final String ATTR_REFID = "refid"; + + protected static final String ATTR_INLINE = "inline"; + + protected static final String ATTR_IDS = "ids"; + + protected static final String BACKREFS = "backrefs"; + + protected static final String BULLET = "bullet"; + + protected static final String CLASS = "class"; + + protected static final String CONTENTS = "contents"; + + protected static final String DELIMITER = "delimiter"; + + protected static final String DELIMITEREXISTE ="delimiterExiste"; + + protected static final String ENUMTYPE = "enumtype"; + + protected static final String FOOTNOTES = "footnotes"; + + protected static final String ID = "id"; + + protected static final String INCLUDE = "include"; + + protected static final String LEVEL = "level"; + + protected static final String NAME = "name"; + + protected static final String NAMES = "names"; + + protected static final String NUM = "num"; + + protected static final String REFURI = "refuri"; + + protected static final String PREFIX = "prefix"; + + protected static final String REMOVE = "remove"; + + protected static final String START = "start"; + + protected static final String SECTNUM = "sectnum"; + + protected static final String SUBEXISTE = "subExiste"; + + protected static final String SUFFIX = "suffix"; + + protected static final String TRUE = "true"; + + protected static final String TYPE = "type"; + + protected static final String TARGETANONYMOUS = "targetAnonymous"; + + protected static final String VALUE = "value"; + + protected boolean ERROR_MISSING_ITEM; + + protected static int MAX_SECTION_DEPTH = -1000; + + protected static Map<String, JRSTDirective> defaultDirectives; + + protected Map<String, JRSTDirective> directives = new HashMap<String, JRSTDirective>(); + + private boolean sectnum; + + private Element footer; + + private int idMax; + + private int symbolMax; + + private int symbolMaxRef; + + private LinkedList<Integer> lblFootnotes = new LinkedList<Integer>(); + + private LinkedList<Integer> lblFootnotesRef = new LinkedList<Integer>(); + + private LinkedList<Element> eFootnotes = new LinkedList<Element>(); + + private LinkedList<Element> eTarget = new LinkedList<Element>(); + + private LinkedList<Element> eTargetAnonymous = new LinkedList<Element>(); + + private LinkedList<Element> eTargetAnonymousCopy = new LinkedList<Element>(); + + private LinkedList<Element> eTitle = new LinkedList<Element>(); + + static { + defaultDirectives = new HashMap<String, JRSTDirective>(); + defaultDirectives.put(IMAGE, new ImageDirective()); + defaultDirectives.put(DATE, new DateDirective()); + defaultDirectives.put("time", new DateDirective()); + defaultDirectives.put(CONTENTS, new ContentDirective()); + // defaultDirectives.put("calc", new CalcDirective()); + defaultDirectives.put(SECTNUM, new SectnumDirective()); + // TODO put here all other directive + } + + /** + * + */ + public JRSTReader() { + } + + /** + * @param name + * @return the defaultDirectives + */ + public static JRSTDirective getDefaultDirective(String name) { + return defaultDirectives.get(name); + } + + /** + * @param name + * @param directive the defaultDirectives to set + */ + public static void addDefaultDirectives(String name, JRSTDirective directive) { + JRSTReader.defaultDirectives.put(name, directive); + } + + /** + * @param name + * @return the defaultDirectives + */ + public JRSTDirective getDirective(String name) { + return directives.get(name); + } + + /** + * @param name + * @param directive the defaultDirectives to set + */ + public void addDirectives(String name, JRSTDirective directive) { + directives.put(name, directive); + } + + /** + * On commence par decouper tout le document en Element, puis on construit + * l'article a partir de ces elements. + * + * @param reader + * @return le document cree + * @throws Exception + */ + public Document read(Reader reader) throws Exception { + JRSTLexer lexer = new JRSTLexer(reader); + try { + Element root = composeDocument(lexer); + + Document result = DocumentHelper.createDocument(); + result.setRootElement(root); + + root.accept(new VisitorSupport() { + @Override + public void visit(Element e) { + // remove all level attribute + e.addAttribute(LEVEL, null); + // Constrution du sommaire + String type = e.attributeValue(TYPE); + if (type != null) { + if (type.equals(CONTENTS)) { + composeContents(e); + e.addAttribute(TYPE, null); + } + } + + if (TRUE.equalsIgnoreCase(e.attributeValue(ATTR_INLINE))) { + e.addAttribute(ATTR_INLINE, null); + try { + inline(e); + } catch (DocumentException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't inline text for " + e, eee); + } + } catch (UnsupportedEncodingException ee) { + if (log.isWarnEnabled()) { + log.warn("Unsupported encoding " + e, ee); + } + } + } + } + }); + + return result; + } catch (Exception eee) { + log.error(_("JRST parsing error line %d char %s:\n%s", lexer + .getLineNumber(), lexer.getCharNumber(), lexer + .readNotBlanckLine())); + throw eee; + } + } + + /** + * <p> + * exemple : + * </p> + * + * <pre> + * ..contents : Sommaire + * depth: 3 + * </pre> + * + * <p> + * depth sert a limiter la profondeur du sommaire + * </p> + * + * @param Element + * + */ + private void composeContents(Element e) { + Element result = DocumentHelper.createElement(TOPIC); + String option = e.getText(); + int depth = -1; + // depth: 3 + Pattern pattern = Pattern.compile("\\s*\\:depth\\:\\s*\\p{Digit}+"); + Matcher matcher = pattern.matcher(option); + if (matcher.matches()) { + pattern = Pattern.compile("\\p{Digit}+"); + matcher = pattern.matcher(matcher.group()); + if (matcher.find()) { + depth = Integer.parseInt(matcher.group()); + } + } + int levelInit = 0; + boolean noTitle = false; + + try { + levelInit = Integer.parseInt(eTitle.getFirst().attributeValue( + LEVEL)); + } catch (NumberFormatException eee) { + log.error("Can't parse level in: " + + eTitle.getFirst().asXML(), eee); + return; + } catch (NoSuchElementException eee) { + noTitle = true; + } + + LinkedList<Element> title = new LinkedList<Element>(); + // on rajoute les refid + for (int i = 0; i < eTitle.size(); i++) { + idMax++; + eTitle.get(i).addAttribute(ATTR_REFID, ID + idMax); + } + // on enleve les titres limites par depth + for (Element el : eTitle) { + int level = Integer.parseInt(el.attributeValue(LEVEL)); + level = level - levelInit; + el.addAttribute(LEVEL, "" + level); + if (depth == -1) { + title.add(el); + } + else { + if (depth > level) { + title.add(el); + } + } + } + e.addAttribute(CLASS, CONTENTS); + String titleValue = e.attributeValue(VALUE); + e.addAttribute(VALUE, null); + String value = titleValue.trim().toLowerCase(); + // sans titre c "contents" par default + if (value.matches("\\s*")) { + value = CONTENTS; + titleValue = "Contents"; + } + e.addAttribute(ATTR_IDS, value); + e.addAttribute(NAMES, value); + result.addElement(TITLE).setText(titleValue); + // on compose les lignes + if (!noTitle) { //Si il y a des titres à lier à la table des matières + result.add(composeLineContent(title, "")); + } + e.setText(""); + e.appendContent(result); + } + + /** + * @param title + * <Element> title, String num + * @return Element + */ + private Element composeLineContent(LinkedList<Element> title, String num) { + Element result = DocumentHelper.createElement(BULLET_LIST); + if (sectnum) { + result.addAttribute(CLASS, "auto-toc"); + } + Element item = null; + int cnt = 0; + while (!title.isEmpty()) { + + Element e = title.getFirst(); + int level = Integer.parseInt(e.attributeValue(LEVEL)); + LinkedList<Element> child = new LinkedList<Element>(); + + if (level <= 0) { + cnt++; + title.removeFirst(); + item = result.addElement(LIST_ITEM); + Element para = item.addElement(PARAGRAPH); + Element reference = para.addElement(REFERENCE); + String text = e.getText(); + String id = e.attributeValue(ATTR_REFID); + reference.addAttribute(ATTR_IDS, id); + reference.addAttribute(ATTR_REFID, text.replaceAll("\\W+", " ") + .trim().toLowerCase().replaceAll("\\W+", "-")); + // si l'on doit les numeroter + if (sectnum) { + Element generated = reference.addElement(GENERATED) + .addAttribute(CLASS, SECTNUM); + generated.setText(num + cnt + " "); + for (int i = 0; i < eTitle.size(); i++) { + if (eTitle.get(i).attributeValue(ATTR_REFID).equals(id)) { + Element generatedTitle = eTitle.get(i).addElement( + GENERATED); + generatedTitle.addAttribute(CLASS, SECTNUM); + generatedTitle.setText(num + cnt + " "); + } + + } + } + + text = text.trim(); + text = text.replaceAll("_", ""); + + text = REGEX_STRONG.matcher(text).replaceAll( + "<" + STRONG + ">$1</" + STRONG + ">"); + text = REGEX_EMPHASIS.matcher(text).replaceAll( + "<" + EMPHASIS + ">$1</" + EMPHASIS + ">"); + + try { + Element textElement = DocumentHelper.parseText("<TMP>" + text + "</TMP>").getRootElement(); + reference.appendContent(textElement); + + } catch (DocumentException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't inline text for " + e, eee); + } + } + + } else { + do { + e.addAttribute(LEVEL, "" + (level - 1)); + child.add(e); + title.removeFirst(); + if (!title.isEmpty()) { + e = title.getFirst(); + level = Integer.parseInt(e.attributeValue(LEVEL)); + } + } while (!title.isEmpty() && level > 0); + String numTmp = ""; + // numerotation + if (sectnum) { + numTmp = num + cnt + "."; + } + if (item != null) { + item.add(composeLineContent(child, numTmp)); // Appel + // recursif + } else { + result.add(composeLineContent(child, numTmp)); // Appel + // recursif + } + } + } + return result; + } + + /** + * @param lexer + * @return Element + * @throws Exception + */ + private Element composeDocument(JRSTLexer lexer) throws Exception { + Element result = DocumentHelper.createElement(DOCUMENT); + result.addAttribute(LEVEL, String.valueOf(MAX_SECTION_DEPTH - 1)); + + Element item = null; + + // skip blank line + skipBlankLine(lexer); + + // les liens anonymes + LinkedList<Element> items = lexer.refTarget(); + for (Element e : items) { + eTarget.add(e); + + } + + // le header + item = lexer.peekHeader(); + if (itemEquals(HEADER, item)) { + Element decoration = result.addElement(DECORATION); + Element header = decoration.addElement(HEADER); + header.addAttribute(ATTR_INLINE, TRUE).setText(item.getText()); + } + + // le footer + item = lexer.peekFooter(); + if (itemEquals(FOOTER, item)) { + footer = DocumentHelper.createElement(DECORATION); + Element header = footer.addElement(FOOTER); + header.addAttribute(ATTR_INLINE, TRUE).setText(item.getText()); + } + + // les hyperlinks + LinkedList<Element> listItem = lexer.peekTargetAnonymous(); + if (listItem != null) { + for (Element e : listItem) { + Element anonym = DocumentHelper.createElement(TARGET); + anonym.addAttribute(ANONYMOUS, "1"); + idMax++; + anonym.addAttribute(ATTR_IDS, ID + idMax); + + anonym.addAttribute(REFURI, e.attributeValue(REFURI).trim()); + + eTargetAnonymous.add(anonym); + eTargetAnonymousCopy.add(anonym); + } + } + + // les eléments a enlever (deja parser : header, footer...) + item = lexer.peekRemove(); + if (itemEquals(REMOVE, item)) { + lexer.remove(); + } + + // skip blank line + skipBlankLine(lexer); + + // les commentaires + List<Element> comments = lexer.peekAllComment(); + + // le titre du doc + item = lexer.peekTitle(); + if (itemEquals(TITLE, item)) { + lexer.remove(); + Element title = result.addElement(TITLE); + String txt = item.getText(); + result.addAttribute(ATTR_IDS, txt.replaceAll("[(\\W+)_]", " ") + .toLowerCase().trim().replaceAll("\\s+", "-")); + result.addAttribute(NAMES, txt.toLowerCase().replaceAll( + "[(\\W+)_&&[^\\:]]+", " ").trim()); + copyLevel(item, title); + title.addAttribute(ATTR_INLINE, TRUE).setText(txt.trim()); + } + + // skip blank line + skipBlankLine(lexer); + + // le sous titre du doc + item = lexer.peekTitle(); + if (itemEquals(TITLE, item)) { + lexer.remove(); + Element subtitle = result.addElement(SUBTITLE); + String txt = item.getText(); + subtitle.addAttribute(ATTR_IDS, txt.replaceAll("[(\\W+)_]", " ") + .toLowerCase().trim().replaceAll("\\s+", "-")); + subtitle.addAttribute(NAMES, txt.toLowerCase().replaceAll( + "[(\\W+)_]", " ").trim()); + copyLevel(item, subtitle); + DocumentHelper.createElement(FOOTNOTES); + subtitle.addAttribute(ATTR_INLINE, TRUE).setText(txt.trim()); + } + + // skip blank line + skipBlankLine(lexer); + + // les infos du doc + item = lexer.peekDocInfo(); + Element documentinfo = null; + while (itemEquals(DOCINFO, item) || itemEquals(FIELD_LIST, item)) { + + if (documentinfo == null) { + documentinfo = result.addElement(DOCINFO); + } + skipBlankLine(lexer); + if (itemEquals(FIELD_LIST, item)) { + Element field = composeFieldItemList(lexer); + documentinfo.add(field); + } else { + if ("author".equalsIgnoreCase(item.attributeValue(TYPE))) { + documentinfo.addElement(AUTHOR).addAttribute(ATTR_INLINE, + TRUE).setText(item.getText()); + } else if ("date".equalsIgnoreCase(item.attributeValue(TYPE))) { + documentinfo.addElement(DATE) + .addAttribute(ATTR_INLINE, TRUE).setText( + item.getText().trim()); + } else if ("organization".equalsIgnoreCase(item + .attributeValue(TYPE))) { + documentinfo.addElement(ORGANIZATION).addAttribute( + ATTR_INLINE, TRUE).setText(item.getText().trim()); + } else if ("contact".equalsIgnoreCase(item + .attributeValue(TYPE))) { + documentinfo.addElement(CONTACT).addAttribute(ATTR_INLINE, + TRUE).setText(item.getText().trim()); + } else if ("address".equalsIgnoreCase(item + .attributeValue(TYPE))) { + documentinfo.addElement(ADDRESS).addAttribute(ATTR_INLINE, + TRUE).setText(item.getText().trim()); + } else if ("version".equalsIgnoreCase(item + .attributeValue(TYPE))) { + documentinfo.addElement(VERSION).addAttribute(ATTR_INLINE, + TRUE).setText(item.getText().trim()); + } else if ("revision".equalsIgnoreCase(item + .attributeValue(TYPE))) { + documentinfo.addElement(REVISION).addAttribute(ATTR_INLINE, + TRUE).setText(item.getText().trim()); + } else if ("status".equalsIgnoreCase(item + .attributeValue(TYPE))) { + documentinfo.addElement(STATUS).addAttribute(ATTR_INLINE, + TRUE).setText(item.getText().trim()); + } else if ("copyright".equalsIgnoreCase(item + .attributeValue(TYPE))) { + documentinfo.addElement(COPYRIGHT).addAttribute(ATTR_INLINE, + TRUE).setText(item.getText().trim()); + } else if ("authors".equalsIgnoreCase(item + .attributeValue(TYPE))) { + Element authors = documentinfo.addElement(AUTHORS); + int t = 0; + String line = item.getText(); + for (int i = 0; i < line.length(); i++) { + if (line.charAt(i) == ';' || line.charAt(i) == ',') { + authors.addElement(AUTHOR).addAttribute(ATTR_INLINE, + TRUE) + .setText(line.substring(t, i).trim()); + t = i + 1; + } + + } + authors.addElement(AUTHOR).addAttribute(ATTR_INLINE, TRUE) + .setText(line.substring(t, line.length()).trim()); + } + lexer.remove(); + } + // skip blank line + // skipBlankLine(lexer); + item = lexer.peekDocInfo(); + + } + + // Ajout des commentaires + // System.out.println(comment.asXML()); + for (Element comment : comments){ + result.add(composeComment(comment)); + } + + // l'abstract du doc + item = lexer.peekTitle(); + while (itemNotEquals(TITLE, item) && !lexer.eof()) { + composeBody(lexer, result); + item = lexer.peekTitle(); + } + + // les sections + item = lexer.peekTitle(); + while (itemEquals(TITLE, item, true, lexer.eof())) { + Element section = composeSection(lexer); + result.add(section); + item = lexer.peekTitle(); + } + + // on ajoute le footer a la fin + if (footer != null) { + result.add(footer); + } + + return result; + } + + /** + * <p> + * skip blank line + * </p> + * + * @param lexer + * @throws DocumentException + * @throws IOException + */ + private void skipBlankLine(JRSTLexer lexer) throws IOException, + DocumentException { + Element item = lexer.peekBlankLine(); + // skip blank line + while (itemEquals(JRSTLexer.BLANK_LINE, item)) { + // go to the next element + lexer.remove(); + item = lexer.peekBlankLine(); + } + } + + /** + * * + * <p> + * Corps du document + * </p> + * + * @param lexer + * @return Element + * @throws DocumentException + * @throws IOException + */ + private Element composeBody(JRSTLexer lexer, Element parent) + throws Exception { + + Element item = lexer.peekTitleOrBodyElement(); + if (item == null && !lexer.eof()) { + item = lexer.peekTitleOrBodyElement(); + } + + while (!lexer.eof() && itemNotEquals(TITLE, item) + && isUpperLevel(item, parent)) { + if (itemEquals(JRSTLexer.BLANK_LINE, item)) { + // go to the next element + lexer.remove(); + } else if (itemEquals(REMOVE, item)) { + lexer.remove(); + } else if (itemEquals(INCLUDE, item)) { + lexer.remove(); + Element list = composeInclude(item); + parent.add(list); + } else if (itemEquals(DOCTEST_BLOCK, item)) { + lexer.remove(); + Element list = composeDoctestBlock(item); + parent.add(list); + } else if (itemEquals(ADMONITION, item)) { + lexer.remove(); + Element list = composeAdmonition(item); + parent.add(list); + } else if (itemEquals(SIDEBAR, item)) { + lexer.remove(); + Element list = composeSidebar(item); + parent.add(list); + } else if (itemEquals(TOPIC, item)) { + lexer.remove(); + Element list = composeTopic(item); + parent.add(list); + } else if (itemEquals(TRANSITION, item)) { + lexer.remove(); + Element para = parent.addElement(TRANSITION); + copyLevel(item, para); + } else if (itemEquals(PARAGRAPH, item)) { + lexer.remove(); + Element para = parent.addElement(PARAGRAPH); + copyLevel(item, para); + para.addAttribute(ATTR_INLINE, TRUE).setText(item.getText()); + } else if (itemEquals(JRSTLexer.DIRECTIVE, item)) { + lexer.remove(); + Node directive = composeDirective(item); + parent.add(directive); + } else if (itemEquals(SUBSTITUTION_DEFINITION, item)) { + lexer.remove(); + Element subst = composeSubstitutionDefinition(item); + parent.add(subst); + } else if (itemEquals(LITERAL_BLOCK, item)) { + lexer.remove(); + Element para = parent.addElement(LITERAL_BLOCK); + copyLevel(item, para); + para.setText(item.getText()); + } else if (itemEquals(JRSTLexer.TABLE, item)) { + lexer.remove(); + Element table = composeTable(item); + parent.add(table); + // Element para = parent.addElement(TABLE); + // copyLevel(item, para); + // para.setText(item.getText()); + } else if (itemEquals(LINE_BLOCK, item)) { + lexer.remove(); + Element list = composeLineBlock(lexer, item); + parent.add(list); + } else if (itemEquals(BULLET_LIST, item)) { + Element list = composeBulletList(lexer); + parent.add(list); + } else if (itemEquals(ENUMERATED_LIST, item)) { + Element list = composeEnumeratedList(lexer); + parent.add(list); + } else if (itemEquals(DEFINITION_LIST, item)) { + Element list = composeDefinitionList(lexer); + parent.add(list); + } else if (itemEquals(FIELD_LIST, item)) { + Element list = composeFieldList(lexer); + parent.add(list); + } else if (itemEquals(BLOCK_QUOTE, item)) { + lexer.remove(); + Element list = composeBlockQuote(item); + parent.add(list); + } else if (itemEquals(OPTION_LIST, item)) { + Element list = composeOptionList(lexer); + parent.add(list); + } else if (itemEquals(TARGET, item)) { + lexer.remove(); + Element list = composeTarget(item); + if (list != null) { + try { + parent.add(list); + } catch (IllegalAddException e) {} + } else + System.err.println("Unknown target name : \"" + item.attributeValue(ATTR_IDS) + "\""); + } else if (itemEquals(TARGETANONYMOUS, item)) { + lexer.remove(); + Element list = composeTargetAnonymous(item); + parent.add(list); + } else if (itemEquals(FOOTNOTES, item)) { + lexer.remove(); + Element[] list = composeFootnote(item); + for (Element l : list) { + parent.add(l); + } + } else if (itemEquals(COMMENT, item)) { + lexer.remove(); + Element list = composeComment(item); + parent.add(list); + } + + else { + if (ERROR_MISSING_ITEM) { + throw new DocumentException("Unknow item type: " + + item.getName()); + } else { + lexer.remove(); + } + } + + // Pour afficher le "PseudoXML" + // if (item!=null) System.out.println(item.asXML()); + + item = lexer.peekTitleOrBodyElement(); + } + + return parent; + } + + /** + * <p> + * include un document rst + * </p> + * + * <pre> + * .. include:: doc.rst + * </pre> + * + * <p> + * include un document literal (code...) + * </p> + * + * <pre> + * .. include:: literal + * doc.rst + * </pre> + * + * @param item + * @return Element + * @throws Exception + */ + private Element composeInclude(Element item) throws Exception { + String option = item.attributeValue(OPTION); + String path = item.getText(); + Element result = null; + if (option.equals(LITERAL)) { + result = DocumentHelper.createElement(LITERAL_BLOCK); + FileReader reader = new FileReader(path); + BufferedReader bf = new BufferedReader(reader); + String line = ""; + String lineTmp = bf.readLine(); + while (lineTmp != null) { + line += '\n' + lineTmp; + lineTmp = bf.readLine(); + } + result.setText(line); + } else { + File fileIn = new File(path); + URL url = fileIn.toURI().toURL(); + Reader in = new InputStreamReader(url.openStream()); + + Document doc = newJRSTReader(in); + + result = doc.getRootElement(); + } + return result; + } + + /** + * <pre> + * .. + * So this block is not "lost", + * despite its indentation. + * </pre> + * + * @param item + * @return Element + */ + private Element composeComment(Element item) { + + return item; + } + + /** + * <pre> + * __ http://truc.html + * </pre> + * + * @param item + * @return Element + */ + private Element composeTargetAnonymous(Element item) { + Element result = null; + result = eTargetAnonymousCopy.getFirst(); + eTargetAnonymousCopy.removeFirst(); + return result; + } + + /** + * <pre _ target: target.html </pre> + * + * @param item + * @return Element + */ + private Element composeTarget(Element item) { + Element result = null; + for (Element e : eTarget) { + if (e.attributeValue(ID).equals(item.attributeValue(ID))) { + result = e; + } + } + return result; + } + + /** + * <pre> + * .. [#] This is a footnote + * </pre> + * + * @param item + * @return Element + * @throws Exception + */ + private Element[] composeFootnote(Element item) throws Exception { + Element[] result = null; + if (itemEquals(FOOTNOTES, item)) { + List<Element> footnotes = (List<Element>) item + .selectNodes(FOOTNOTE); + result = new Element[footnotes.size()]; + int cnt = 0; + for (Element footnote : footnotes) { + result[cnt] = DocumentHelper.createElement(FOOTNOTE); + Element efootnote = DocumentHelper.createElement(FOOTNOTE); + int labelMax = 0; + + for (int i = 0; i < lblFootnotes.size(); i++) { + int lbl = lblFootnotes.get(i); + labelMax = Math.max(lbl, labelMax); + } + + boolean[] labels = new boolean[labelMax]; + for (int i = 0; i < labels.length; i++) { + labels[i] = false; + } + for (int i = 0; i < lblFootnotes.size(); i++) { + labels[lblFootnotes.get(i) - 1] = true; + } + idMax++; + String name = null; + String id = ""; + String label = null; + String type = footnote.attributeValue(TYPE); + if (type.equals(AUTONUM) || type.equals(AUTONUMLABEL)) { + result[cnt].addAttribute(AUTO, "1"); + } + if (type.equals(AUTOSYMBOL)) { + result[cnt].addAttribute(AUTO, "*"); + } + result[cnt].addAttribute(BACKREFS, ID + idMax); + efootnote.addAttribute(BACKREFS, ID + idMax); + if (type.equals(NUM) || type.equals(AUTONUMLABEL)) { + name = footnote.attributeValue(NAME); + if (type.equals(AUTONUMLABEL)) { + id = name; + } + else { + label = name; + } + } + if (type.equals(AUTONUM) || type.equals(AUTONUMLABEL)) { + boolean done = false; + + for (int i = 0; i < labels.length && !done; i++) { + if (!labels[i]) { + done = true; + label = "" + (i + 1); + } + } + if (!done) { + label = "" + (labels.length + 1); + } + if (type.equals(AUTONUM)) { + name = label; + } + } + if (type.equals(AUTOSYMBOL)) { + + int nb = Math.abs(symbolMax / 10) + 1; + char symbol = FOOTNOTE_SYMBOL.charAt(symbolMax % 10); + label = ""; + for (int j = 0; j < nb; j++) { + label += symbol; + } + symbolMax++; + + } + result[cnt].addAttribute(ATTR_IDS, "" + id); + efootnote.addAttribute(ATTR_IDS, "" + id); + if (!type.equals(AUTOSYMBOL)) { + result[cnt].addAttribute(NAME, "" + name); + efootnote.addAttribute(NAME, "" + name); + } + result[cnt].addElement(LABEL).setText("" + label); + efootnote.addAttribute(LABEL, "" + label); + if (!type.equals(AUTOSYMBOL)) { + lblFootnotes.add(Integer.parseInt(label)); + } + efootnote.addAttribute(TYPE, type); + eFootnotes.add(efootnote); + String text = footnote.getText(); + Document doc = newJRSTReader(new StringReader(text)); + result[cnt].appendContent(doc.getRootElement()); + + cnt++; + } + } + for (int i = 0; i < result.length; i++) { + if (result[i].attributeValue(ATTR_IDS).equals("")) { + idMax++; + result[i].addAttribute(ATTR_IDS, ID + idMax); + (eFootnotes.get(i)).addAttribute(ATTR_IDS, ID + idMax); + } + + } + + return result; + } + + /** + * <pre> + * -a command-line option "a" -1 file, --one=file, --two file Multiple + * options with arguments. + * </pre> + * + * @param lexer + * @return Element + * @throws Exception + * @throws DocumentException + */ + private Element composeOptionList(JRSTLexer lexer) + throws DocumentException, Exception { + Element item = lexer.peekOption(); + Element result = DocumentHelper.createElement(OPTION_LIST); + while (itemEquals(OPTION_LIST, item)) { + lexer.remove(); + Element optionListItem = result.addElement(OPTION_LIST_ITEM); + Element optionGroup = optionListItem.addElement(OPTION_GROUP); + List<Element> option = (List<Element>) item.selectNodes(OPTION); + for (Element e : option) { + Element eOption = optionGroup.addElement(OPTION); + eOption.addElement(OPTION_STRING).setText( + e.attributeValue(OPTION_STRING)); + if (e.attributeValue(DELIMITEREXISTE).equals(TRUE)) { + eOption.addElement(OPTION_ARGUMENT).addAttribute( + DELIMITER, e.attributeValue(DELIMITER)) + .setText(e.attributeValue(OPTION_ARGUMENT)); + } + } + Element description = optionListItem.addElement(DESCRIPTION); + + String text = item.getText(); + Document doc = newJRSTReader(new StringReader(text)); + description.appendContent(doc.getRootElement()); + + item = lexer.peekOption(); + } + return result; + } + + /** + * <pre> + * .. topic:: Title + * + * Body. + * </pre> + * + * @param Element + * item + * @return Element + * @throws Exception + */ + + private Element composeTopic(Element item) throws Exception { + Element result = null; + result = DocumentHelper.createElement(TOPIC); + result.addElement(TITLE).addAttribute(ATTR_INLINE, TRUE).setText( + item.attributeValue(TITLE)); + String text = item.getText(); + Document doc = newJRSTReader(new StringReader(text)); + result.appendContent(doc.getRootElement()); + + return result; + } + + /** + * <pre> + * .. sidebar:: Title + * :subtitle: If Desired + * + * Body. + * </pre> + * + * @param Element + * @return Element + * @throws Exception + */ + + private Element composeSidebar(Element item) throws Exception { + Element result = null; + result = DocumentHelper.createElement(SIDEBAR); + result.addElement(TITLE).addAttribute(ATTR_INLINE, TRUE).setText( + item.attributeValue(TITLE)); + if (item.attributeValue(SUBEXISTE).equals(TRUE)) { + result.addElement(SUBTITLE).addAttribute(ATTR_INLINE, TRUE).setText( + item.attributeValue(SUBTITLE)); + } + + String text = item.getText(); + Document doc = newJRSTReader(new StringReader(text)); + result.appendContent(doc.getRootElement()); + + return result; + } + + /** + * <pre> + * | line block + * | + * | indent + * </pre> + * + * @param lexer + * @param item + * @return Element + * @throws Exception + */ + private Element composeLineBlock(JRSTLexer lexer, Element item) + throws Exception { + Element result = null; + result = DocumentHelper.createElement(LINE_BLOCK); + List<Element> lines = (List<Element>) item.selectNodes(LINE); + int[] levels = new int[lines.size()]; + int cnt = 0; + for (Element l : lines) { + levels[cnt] = Integer.parseInt(l.attributeValue(LEVEL)); + cnt++; + } + cnt = 0; + boolean[] lineDone = new boolean[lines.size()]; + for (int i = 0; i < lineDone.length; i++) { + lineDone[i] = false; + } + for (Element l : lines) { + if (levels[cnt] == 0) { + result.addElement(LINE).addAttribute(ATTR_INLINE, TRUE).setText( + l.getText()); + } + else { + if (!lineDone[cnt]) { + Element newItem = DocumentHelper.createElement(LINE_BLOCK); + Boolean done = false; + for (int i = cnt; i < lines.size() && !done; i++) { + if (levels[i] > 0) { + Element eLine = newItem.addElement(LINE); + eLine.addAttribute(LEVEL, "" + (levels[i] - 1)); + eLine.setText(lines.get(i).getText()); + lineDone[i] = true; + } else { + done = true; + } + + } + Element eLineBlock = result.addElement(LINE_BLOCK); + // Appel recursif + eLineBlock.appendContent(composeLineBlock(lexer, newItem)); + } + } + cnt++; + + } + return result; + } + + /** + * <pre> + * >>> print 'this is a Doctest block' + * this is a Doctest block + * </pre> + * + * @param Element + * @return Element + */ + private Element composeDoctestBlock(Element item) { + return item; + } + + /** + * <pre> + * As a great paleontologist once said, + * + * This theory, that is mine, is mine. + * + * -- Anne Elk (Miss) + * </pre> + * + * @param Element + * @return Element + * @throws Exception + * + */ + private Element composeBlockQuote(Element item) throws Exception { + Element result = null; + result = DocumentHelper.createElement(BLOCK_QUOTE); + + String text = item.getText(); + Document doc = newJRSTReader(new StringReader(text)); + result.appendContent(doc.getRootElement()); + String sAttribution = item.attributeValue(ATTRIBUTION); + if (sAttribution != null) { + Element attribution = result.addElement(ATTRIBUTION); + attribution.setText(sAttribution); + attribution.addAttribute(ATTR_INLINE, TRUE); + } + return result; + } + + /** + * <pre> + * .. admonition:: And, by the way... + * + * You can make up your own admonition too. + * </pre> + * + * @param Element + * @return Element + * @throws Exception + * + */ + private Element composeAdmonition(Element item) throws Exception { + Element result = null; + if (item.attributeValue(TYPE).equalsIgnoreCase(ADMONITION)) { + result = DocumentHelper.createElement(ADMONITION); + String title = item.attributeValue(TITLE); + String admonitionClass = "admonition_" + title; + admonitionClass = admonitionClass.toLowerCase().replaceAll( + "\\p{Punct}", ""); + admonitionClass = admonitionClass.replace(' ', '-'); + admonitionClass = admonitionClass.replace('\n', '-'); + result.addAttribute(CLASS, admonitionClass); + result.addElement(TITLE).addAttribute(ATTR_INLINE, TRUE).setText( + title.trim()); + } else { + result = DocumentHelper.createElement(item.attributeValue(TYPE) + .toLowerCase()); + } + + String text = item.getText(); + Document doc = newJRSTReader(new StringReader(text)); + result.appendContent(doc.getRootElement()); + return result; + } + + /** + * parse all directives + * + * @param Element + * @return Node + */ + private Node composeDirective(Element item) { + Node result = item; + String type = item.attributeValue(JRSTLexer.DIRECTIVE_TYPE); + if (type.equals(SECTNUM)) { + sectnum = true; + } + JRSTDirective directive = getDirective(type); + if (directive == null) { + directive = getDefaultDirective(type); + } + if (directive != null) { + result = directive.parse(item); + } else { + log.warn("Unknow directive type '" + type + "' in: " + item); + } + return result; + } + + /** + * <pre> + * .. |biohazard| image:: biohazard.png + * </pre> + * + * @param Element + * @return Element + */ + private Element composeSubstitutionDefinition(Element item) { + Element result = item; + Element child = (Element) item.selectSingleNode("*"); + Node newChild = composeDirective(child); + result.remove(child); // remove old after composeDirective, because + // directive can be used this parent + result.add(newChild); + return result; + } + + /** + * <p> + * Complexe Table + * </p> + * + * <pre> + * +------------------------+------------+---------------------+ + * | body row 3 | Cells may | - Table cells | + * +------------------------+ span rows. | - contain | + * | body row 4 | | - body elements. | + * +------------------------+------------+---------------------+ + * </pre> + * + * <p> + * And simple table + * </p> + * + * <pre> + * ===== ===== ====== + * Inputs Output + * ============ ====== + * A B A or B + * ------------ ------ + * A B A or B + * ===== ===== ====== + * </pre> + * + * @param Element + * @return Element + * + */ + private Element composeTable(Element item) throws Exception { + + Element result = DocumentHelper.createElement(TABLE); + + int tableWidth = Integer.parseInt(item + .attributeValue(JRSTLexer.TABLE_WIDTH)); + + TreeSet<Integer> beginCellList = new TreeSet<Integer>(); + + for (Element cell : (List<Element>) item.selectNodes(JRSTLexer.ROW + + "/" + JRSTLexer.CELL)) { + Integer begin = Integer.valueOf(cell + .attributeValue(JRSTLexer.CELL_INDEX_START)); + beginCellList.add(begin); + } + + int[] beginCell = new int[beginCellList.size() + 1]; // + 1 to put + // table width + // to simulate + // new cell + int[] lengthCell = new int[beginCellList.size()]; + + int cellNumber = 0; + for (int b : beginCellList) { + beginCell[cellNumber] = b; + if (cellNumber > 0) { + lengthCell[cellNumber - 1] = beginCell[cellNumber] + - beginCell[cellNumber - 1]; + } + cellNumber++; + } + beginCell[cellNumber] = tableWidth; + lengthCell[cellNumber - 1] = beginCell[cellNumber] + - beginCell[cellNumber - 1]; + + Element tgroup = result.addElement(TGROUP).addAttribute("cols", + String.valueOf(cellNumber)); + for (int width : lengthCell) { + tgroup.addElement(COLSPEC).addAttribute("colwidth", + String.valueOf(width)); + } + + Element rowList = null; + if (TRUE.equals(item.attributeValue(JRSTLexer.TABLE_HEADER))) { + rowList = tgroup.addElement(THEAD); + } else { + rowList = tgroup.addElement(TBODY); + } + List<Element> rows = (List<Element>) item.selectNodes(JRSTLexer.ROW); + for (int r = 0; r < rows.size(); r++) { + Element row = rowList.addElement(ROW); + List<Element> cells = (List<Element>) rows.get(r).selectNodes( + JRSTLexer.CELL); + for (int c = 0; c < cells.size(); c++) { + Element cell = cells.get(c); + // si la cellule a ete utilise pour un regroupement vertical on + // la passe + if (!TRUE.equals(cell.attributeValue("used"))) { + Element entry = row.addElement(ENTRY); + String text = ""; + + // on regroupe les cellules verticalement + int morerows = -1; + Element tmpCell = null; + String cellStart = cell + .attributeValue(JRSTLexer.CELL_INDEX_START); + do { + morerows++; + tmpCell = (Element) rows.get(r + morerows) + .selectSingleNode( + JRSTLexer.CELL + "[@" + + JRSTLexer.CELL_INDEX_START + + "=" + cellStart + "]"); + text += tmpCell.getText(); + // on marque la cellule comme utilise + tmpCell.addAttribute("used", TRUE); + } while (!TRUE.equals(tmpCell + .attributeValue(JRSTLexer.CELL_END))); + + if (morerows > 0) { + entry + .addAttribute("morerows", String + .valueOf(morerows)); + } + + // on compte le nombre de cellules regroupees + // horizontalement + int morecols = 0; + tmpCell = cells.get(c + morecols); + int cellEnd = Integer.parseInt(tmpCell + .attributeValue(JRSTLexer.CELL_INDEX_END)); + while (cellEnd + 1 != beginCell[c + morecols + 1]) { + morecols++; + // tmpCell = cells.get(c + morecols); + // cellEnd = + // Integer.parseInt(tmpCell.attributeValue(JRSTLexer. + // CELL_INDEX_END)); + } + if (morecols > 0) { + entry + .addAttribute("morecols", String + .valueOf(morecols)); + } + // parse entry text in table + Document doc = newJRSTReader(new StringReader(text)); + entry.appendContent(doc.getRootElement()); + } + } + if (TRUE.equals(rows.get(r).attributeValue( + JRSTLexer.ROW_END_HEADER))) { + rowList = tgroup.addElement(TBODY); + } + } + + return result; + } + + /** + * <p> + * items begin with "-", "+", or "*" + * </p> + * + * <pre> + * * aaa + * - bbb + * * ccc + * - ddd + * + eee + * </pre> + * + * @param lexer + * @return Element + * @throws Exception + */ + private Element composeBulletList(JRSTLexer lexer) throws Exception { + Element item = lexer.peekBulletList(); + Element result = DocumentHelper.createElement(BULLET_LIST); + copyLevel(item, result); + result.addAttribute(BULLET, item.attributeValue(BULLET)); + while (itemEquals(BULLET_LIST, item) && isSameLevel(item, result) + && hasSameAttribute(item, result, BULLET)) { + lexer.remove(); + Element bullet = result.addElement(LIST_ITEM); + copyLevel(item, bullet); + bullet.addElement(PARAGRAPH).addAttribute(ATTR_INLINE, TRUE) + .setText(item.getText()); + composeBody(lexer, bullet); + + item = lexer.peekBulletList(); + } + return result; + } + + /** + * <pre> + * 3. et meme + * * #. pour voir + * * I) de tout + * (a) pour tout + * (#) vraiment tout + * </pre> + * + * @param lexer + * @return Element + * @throws Exception + */ + private Element composeEnumeratedList(JRSTLexer lexer) throws Exception { + Element item = lexer.peekEnumeratedList(); + Element result = DocumentHelper.createElement(ENUMERATED_LIST); + copyLevel(item, result); + String enumType = item.attributeValue(ENUMTYPE); + if (!enumType.equals("arabic")) { + result.addAttribute(START, item.attributeValue(START)); + } + result.addAttribute(PREFIX, item.attributeValue(PREFIX)); + result.addAttribute(SUFFIX, item.attributeValue(SUFFIX)); + result.addAttribute(ENUMTYPE, enumType); + while (itemEquals(ENUMERATED_LIST, item) + && isSameLevel(item, result) + && hasSameAttribute(item, result, PREFIX, SUFFIX) + && (AUTO.equals(item.attributeValue(ENUMTYPE)) || hasSameAttribute( + item, result, ENUMTYPE))) { + lexer.remove(); + Element e = result.addElement(LIST_ITEM); + copyLevel(item, e); + e.addElement(PARAGRAPH).addAttribute(ATTR_INLINE, TRUE).setText( + item.getText()); + composeBody(lexer, e); + + item = lexer.peekEnumeratedList(); + } + return result; + } + + /** + * <pre> + * le mot : la classe + * la definition + * </pre> + * + * @param lexer + * @return Element + * @throws Exception + */ + private Element composeDefinitionList(JRSTLexer lexer) throws Exception { + Element item = lexer.peekBodyElement(); + Element result = DocumentHelper.createElement(DEFINITION_LIST); + copyLevel(item, result); + while (itemEquals(DEFINITION_LIST, item) && isSameLevel(item, result)) { + lexer.remove(); + Element def = result.addElement(DEFINITION_LIST_ITEM); + copyLevel(item, def); + + Element term = def.addElement(TERM); + copyLevel(item, term); + term.addAttribute(ATTR_INLINE, TRUE).setText( + item.attributeValue("term")); + + String[] classifiers = StringUtil.split(item + .attributeValue("classifiers"), " : "); + for (String classifierText : classifiers) { + Element classifier = def.addElement("classifier"); + copyLevel(item, classifier); + classifier.addAttribute(ATTR_INLINE, TRUE).setText( + classifierText); + } + + Element definition = def.addElement(DEFINITION); + definition.addElement(PARAGRAPH).addAttribute(ATTR_INLINE, TRUE) + .setText(item.getText()); + copyLevel(item, definition); + + composeBody(lexer, definition); + + item = lexer.peekBodyElement(); + } + return result; + } + + /** + * <pre> + * :un peu: de field + * ca ne fait pas + * de mal + * </pre> + * + * @param lexer + * @return Element + * @throws Exception + */ + private Element composeFieldList(JRSTLexer lexer) throws Exception { + Element item = lexer.peekBodyElement(); + Element result = DocumentHelper.createElement(FIELD_LIST); + copyLevel(item, result); + while (itemEquals(FIELD_LIST, item) && isSameLevel(item, result)) { + Element field = composeFieldItemList(lexer); + result.add(field); + item = lexer.peekBodyElement(); + } + return result; + } + + /** + * <pre> + * :field1: avec un + * petit texte + * - et meme un + * - debut + * - de list + * </pre> + * + * @param lexer + * @return Element + * @throws Exception + */ + private Element composeFieldItemList(JRSTLexer lexer) throws Exception { + Element item = lexer.peekFieldList(); + if (itemEquals(FIELD_LIST, item)) { + lexer.remove(); + Element field = DocumentHelper.createElement(FIELD); + copyLevel(item, field); + Element fieldName = field.addElement(FIELD_NAME); + copyLevel(item, fieldName); + fieldName.addAttribute(ATTR_INLINE, TRUE).setText( + item.attributeValue(NAME)); + Element fieldBody = field.addElement(FIELD_BODY); + fieldBody.addElement(PARAGRAPH).addAttribute(ATTR_INLINE, TRUE) + .setText(item.getText()); + copyLevel(item, fieldBody); + composeBody(lexer, fieldBody); + + return field; + } else { + throw new DocumentException("Waiting for " + FIELD_LIST + + " and found " + item.getName()); + } + } + + /** + * <pre> + * DEFINITIONS + * ----------- + * </pre> + * + * @param lexer + * @return Element + * @throws Exception + */ + private Element composeSection(JRSTLexer lexer) throws Exception { + Element result = DocumentHelper.createElement(SECTION); + Element firstTitle = null; + + Element item = null; + + // le titre de la section + item = lexer.peekTitle(); + if (itemEquals(TITLE, item, true, lexer.eof())) { + lexer.remove(); + firstTitle = item; + Element title = result.addElement(TITLE); + copyLevel(item, result); + copyLevel(item, title); + title.addAttribute(ATTR_INLINE, TRUE).setText(item.getText().trim()); + result.addAttribute(ID, item.getText().replaceAll("\\W+", " ") + .trim().toLowerCase().replaceAll("\\W+", "-")); + result.addAttribute(NAME, item.getText().toLowerCase().trim()); + eTitle.add(title); + } + + // le contenu de la section + item = lexer.peekTitle(); + while (itemNotEquals(TITLE, item) && !lexer.eof()) { + composeBody(lexer, result); + item = lexer.peekTitle(); + } + + // les sous sections + item = lexer.peekTitle(); + while (itemEquals(TITLE, item) && isUpperLevel(item, firstTitle)) { + Element section = composeSection(lexer); + result.add(section); + item = lexer.peekTitle(); + } + + return result; + } + + /** + * Indique si la sous section est bien une sous section, c-a-d dire que son + * level est superieur a celui de la section + * + * @param item + * @param firstTitle + * @return boolean + * @throws DocumentException + */ + private boolean isUpperLevel(Element subSection, Element section) + throws DocumentException { + // if (!(itemEquals(SECTION, subSection) && itemEquals(SECTION, + // section)) + // || itemEquals(DOCUMENT, section) || itemEquals(SECTION, section)) { + // // all element is upper than Document or section + // return true; + // } + int subSectionLevel = Integer.parseInt(subSection + .attributeValue(LEVEL)); + int sectionLevel = Integer.parseInt(section.attributeValue(LEVEL)); + boolean result = subSectionLevel > sectionLevel; + return result; + } + + /** + * Indique si les deux elements sont au meme niveau + * + * @param item + * @param firstTitle + * @return boolean + * @throws DocumentException + */ + private boolean isSameLevel(Element subSection, Element section) + throws DocumentException { + // if (itemEquals(DOCUMENT, section) || itemEquals(SECTION, section)) { + // // all element is upper than Document or section + // return false; + // } + int subSectionLevel = Integer.parseInt(subSection + .attributeValue(LEVEL)); + int sectionLevel = Integer.parseInt(section.attributeValue(LEVEL)); + boolean result = subSectionLevel == sectionLevel; + return result; + } + + /** + * @param Element + * e1 + * @param Element + * e2 + * @param String + * ... attnames + * @return boolean + */ + private boolean hasSameAttribute(Element e1, Element e2, String... attnames) { + boolean result = true; + for (String attname : attnames) { + String a1 = e1.attributeValue(attname); + String a2 = e2.attributeValue(attname); + if (!ObjectUtils.equals(a1, a2)) { + result = false; + break; + } + } + return result; + } + + /** + * @param Element + * from + * @param Element + * to + * @throws DocumentException + */ + private void copyLevel(Element from, Element to) throws DocumentException { + String level = from.attributeValue(LEVEL); + if (level == null) { + throw new DocumentException("Element without level: " + from); + } + to.addAttribute(LEVEL, level); + } + + /** + * @param String + * name + * @param Element + * e + * @return boolean + * @throws DocumentException + */ + private boolean itemEquals(String name, Element e) throws DocumentException { + boolean result = itemEquals(name, e, false, false); + return result; + } + + /** + * @param String + * name + * @param Element + * e + * @param throwError + * @param eof + * @return boolean + * @throws DocumentException + */ + private boolean itemEquals(String name, Element e, boolean throwError, + boolean eof) throws DocumentException { + boolean result = e != null && name.equals(e.getName()); + if (ERROR_MISSING_ITEM && !result && throwError && !eof) { + throw new DocumentException("Malformed document waiting " + name + + " and found " + (e != null ? e.getName() : "null")); + } + return result; + } + + /** + * @param String + * name + * @param Element + * e + * @return boolean + */ + private boolean itemNotEquals(String name, Element e) { + boolean result = e == null || !name.equals(e.getName()); + return result; + } + + private Document newJRSTReader(Reader r) throws Exception { + JRSTReader reader = new JRSTReader(); + reader.setVariable(idMax, symbolMax, symbolMaxRef, lblFootnotes, + lblFootnotesRef, eFootnotes, eTarget, eTargetAnonymous, + eTargetAnonymousCopy); + + return reader.read(r); + + } + + /** + * <p> + * Initialises les variables d'environements par ex, les hyperlinks peuvent + * etre referencer dans tous le document + * </p> + * + * @param idMax + * @param symbolMax + * @param symbolMaxRef + * @param lblFootnotes + * @param lblFootnotesRef + * @param eFootnotes + * @param eTarget + * @param eTargetAnonymous + * @param eTargetAnonymousCopy + */ + public void setVariable(int idMax, int symbolMax, int symbolMaxRef, + LinkedList<Integer> lblFootnotes, + LinkedList<Integer> lblFootnotesRef, + LinkedList<Element> eFootnotes, LinkedList<Element> eTarget, + LinkedList<Element> eTargetAnonymous, + LinkedList<Element> eTargetAnonymousCopy) { + this.idMax = idMax; + this.symbolMax = symbolMax; + this.symbolMaxRef = symbolMaxRef; + this.lblFootnotes = lblFootnotes; + this.lblFootnotesRef = lblFootnotesRef; + this.eFootnotes = eFootnotes; + this.eTarget = eTarget; + this.eTargetAnonymous = eTargetAnonymous; + this.eTargetAnonymousCopy = eTargetAnonymousCopy; + } + + /** + * Parse text in element and replace text with parse result + * + * @param Element + * e + * @throws DocumentException + * @throws UnsupportedEncodingException + */ + + private void inline(Element e) throws DocumentException, UnsupportedEncodingException { + String text = e.getText(); + + text = StringEscapeUtils.escapeXml(text); + // search all LITERAL and replace it with special mark + // this prevent substitution in literal, example **something** must not + // change in literal + Map<String, String> temporaries = new HashMap<String, String>(); + Matcher matcher = REGEX_LITERAL.matcher(text); + int index = 0; + while (matcher.find()) { + int start = matcher.start(); + int end = matcher.end(); + String literal = "<" + LITERAL + ">" + matcher.group(1) + "</" + + LITERAL + ">"; + String key = LITERAL + index++; + temporaries.put(key, literal); + text = text.substring(0, start) + "<tmp>" + key + "</tmp>" + + text.substring(end); + matcher = REGEX_LITERAL.matcher(text); + } + // search all REGEX_INLINE_REFERENCE and replace it with special mark + // this prevent substitution of URL with REGEX_REFERENCE. Use same + // mechanisme as literal for that + matcher = REGEX_INLINE_REFERENCE.matcher(text); + index = 0; + while (matcher.find()) { + int start = matcher.start(); + int end = matcher.end(); + Element ref = DocumentHelper.createElement(REFERENCE); + ref.addAttribute(REFURI, StringEscapeUtils.unescapeXml(matcher.group(2))); + ref.setText(StringEscapeUtils.unescapeXml(matcher.group(1))); + String key = "inlineReference" + index++; + temporaries.put(key, ref.asXML()); + text = text.substring(0, start) + "<tmp>" + key + "</tmp>" + + text.substring(end); + matcher = REGEX_INLINE_REFERENCE.matcher(text); + + } + // do all substitution inline + text = REGEX_EMAIL.matcher(text).replaceAll( + "$1<" + REFERENCE + " refuri='mailto:$2'>$2</" + REFERENCE + + ">$3"); + text = REGEX_STRONG.matcher(text).replaceAll( + "<" + STRONG + ">$1</" + STRONG + ">"); + text = REGEX_EMPHASIS.matcher(text).replaceAll( + "<" + EMPHASIS + ">$1</" + EMPHASIS + ">"); + text = REGEX_REFERENCE.matcher(text).replaceAll( + "<" + REFERENCE + " refuri='$1'>$1</" + REFERENCE + ">$2"); + // _[#]truc + matcher = REGEX_FOOTNOTE_REFERENCE.matcher(text); + while (matcher.find()) { + String txtDebut = text.substring(0, matcher.start()); + String txtFin = text.substring(matcher.end()-1, text.length()-1); + Element footnote = DocumentHelper.createElement(FOOTNOTE_REFERENCE); + String sFootnote = matcher.group(); + boolean done = false; + for (int i = 0; i < sFootnote.length() && !done; i++) { + if (sFootnote.charAt(i) == ']') { + String id = sFootnote.substring(1, i); + if (id.equals("*")) { + int nb = Math.abs(symbolMaxRef / 10) + 1; + char symbol = FOOTNOTE_SYMBOL.charAt(symbolMaxRef % 10); + String label = ""; + for (int j = 0; j < nb; j++) { + label += symbol; + } + symbolMaxRef++; + footnote.addAttribute(AUTO, "*"); + for (int j = 0; j < eFootnotes.size(); j++) { + Element eFootnote = eFootnotes.get(j); + if (eFootnote.attributeValue(LABEL).equals(label)) { + + footnote.addAttribute(ATTR_IDS, eFootnote + .attributeValue(BACKREFS)); + footnote.addAttribute(ATTR_REFID, eFootnote + .attributeValue(ATTR_IDS)); + + } + } + footnote.setText(label); + + } else if (id.matches("[1-9]+")) { + + for (int j = 0; j < eFootnotes.size(); j++) { + Element eFootnote = eFootnotes.get(j); + if (eFootnote.attributeValue(LABEL).equals(id)) { + footnote.addAttribute(ATTR_IDS, eFootnote + .attributeValue(BACKREFS)); + footnote.addAttribute(ATTR_REFID, eFootnote + .attributeValue(ATTR_IDS)); + } + } + footnote.setText(id); + lblFootnotesRef.add(Integer.parseInt(id)); + + } else if (id.equals("#")) { + int lblMax = 0; + for (int j = 0; j < lblFootnotesRef.size(); j++) { + lblMax = Math.max(lblMax, lblFootnotesRef.get(j)); + } + + boolean[] lbls = new boolean[lblMax]; + for (int j = 0; j < lbls.length; j++) { + lbls[j] = false; + } + for (int j = 0; j < lblFootnotesRef.size(); j++) { + lbls[lblFootnotesRef.get(j) - 1] = true; + } + boolean valide = false; + do { + boolean trouve = false; + String label = null; + for (int j = 0; j < lbls.length && !trouve; j++) { + + if (!lbls[j]) { + trouve = true; + label = "" + (j + 1); + } + } + if (!trouve) { + label = "" + (lbls.length + 1); + } + footnote.addAttribute(AUTO, "1"); + for (int j = 0; j < eFootnotes.size(); j++) { + Element eFootnote = eFootnotes.get(j); + if (eFootnote.attributeValue(LABEL).equals( + label)) { + if (!(eFootnote.attributeValue(TYPE) + .equals(AUTONUMLABEL))) { + footnote.addAttribute(ATTR_IDS, eFootnote + .attributeValue(BACKREFS)); + footnote.addAttribute(ATTR_REFID, + eFootnote.attributeValue(ATTR_IDS)); + footnote.setText(label); + lblFootnotesRef.add(Integer + .parseInt(label)); + valide = true; + } else { + valide = false; + lbls[Integer.parseInt(label) - 1] = true; + } + } + } + } while (!valide); + + } + + else { + footnote.addAttribute(AUTO, "1"); + + String name = id.substring(1); + boolean trouve = false; + for (int j = 0; j < eFootnotes.size() && !trouve; j++) { + Element eFootnote = eFootnotes.get(j); + if (eFootnote.attributeValue(NAMES).equals(name)) { + footnote.addAttribute(ATTR_IDS, eFootnote + .attributeValue(BACKREFS)); + footnote.addAttribute(ATTR_REFID, eFootnote + .attributeValue(ATTR_IDS)); + String label = eFootnote + .attributeValue(LABEL); + footnote.setText(label); + lblFootnotesRef.add(Integer.parseInt(label)); + trouve = true; + } + } + + footnote.addAttribute(NAMES, name); + } + done = true; + } + } + text = txtDebut + footnote.asXML() + txtFin; + matcher = REGEX_FOOTNOTE_REFERENCE.matcher(text); + } + // .. __http://truc.html + matcher = REGEX_ANONYMOUS_HYPERLINK_REFERENCE.matcher(text); + while (matcher.find()) { + String txtDebut = text.substring(0, matcher.start()); + String txtFin = text.substring(matcher.end(), text.length()); + String ref = text.substring(matcher.start(), matcher.end() - 2); + ref = ref.replaceAll("`", ""); + Element anonym = DocumentHelper.createElement(REFERENCE); + anonym.addAttribute(ANONYMOUS, "1"); + anonym.addAttribute(NAME, ref.trim()); + if (!eTargetAnonymous.isEmpty()) { + Element target = eTargetAnonymous.getFirst(); + eTargetAnonymous.removeFirst(); + anonym.addAttribute(REFURI, target.attributeValue(REFURI)); + } + anonym.setText(ref); + text = txtDebut + anonym.asXML() + txtFin; + matcher = REGEX_ANONYMOUS_HYPERLINK_REFERENCE.matcher(text); + } + // .. _truc: http://truc.html + matcher = REGEX_HYPERLINK_REFERENCE.matcher(text); + while (matcher.find()) { + String txtDebut = text.substring(0, matcher.start()); + String txtFin = text.substring(matcher.end(), text.length()); + String ref = text.substring(matcher.start(), matcher.end() - 1); + ref = StringEscapeUtils.unescapeXml(ref); + ref = ref.replaceAll("('|_)", ""); + ref = ref.replaceAll("`", ""); + Element hyper = DocumentHelper.createElement(REFERENCE); + hyper.addAttribute(NAME, ref); + boolean trouve = false; + for (int i = 0; i < eTarget.size() && !trouve; i++) { + Element el = eTarget.get(i); + String refTmp = URLEncoder.encode(ref.replaceAll("\\s", "-").toLowerCase(), "UTF-8"); + if (el.attributeValue(ID).equalsIgnoreCase((refTmp))) { + hyper.addAttribute(REFURI, el.attributeValue(REFURI)); + trouve = true; + } + } + if (!trouve) { + hyper.addAttribute(ATTR_REFID, ref); + } + hyper.setText(ref); + text = txtDebut + hyper.asXML() + " " + txtFin; + matcher = REGEX_HYPERLINK_REFERENCE.matcher(text); + + } + + // substitution reference + matcher = REGEX_SUBSTITUTION_REFERENCE.matcher(text); + int begin = 0; + while (matcher.find(begin)) { + String start = text.substring(0, matcher.start()); + String end = text.substring(matcher.end()); + String ref = matcher.group(1); + + Node subst = e.selectSingleNode("//" + SUBSTITUTION_DEFINITION + + "[@name='" + ref + "']/child::node()"); + + if (subst == null) { + text = start + "|" + ref + "|"; + } else { + text = start + subst.asXML(); + } + + begin = text.length(); + text += end; + matcher = REGEX_SUBSTITUTION_REFERENCE.matcher(text); + + } + // undo substitution in LITERAL + Pattern p = Pattern.compile("<tmp>([^<>]+)</tmp>"); + + matcher = p.matcher(text); + while (matcher.find()) { + String start = text.substring(0, matcher.start()); + String end = text.substring(matcher.end()); + + String tempKey = matcher.group(1); + text = start + temporaries.get(tempKey) + end; + matcher = p.matcher(text); + } + + String resultElementText = text.trim(); + Element result = DocumentHelper.parseText( + "<TMP>" + resultElementText + "</TMP>").getRootElement(); + + e.setText(""); + e.appendContent(result); + } +} Added: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ContentDirective.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ContentDirective.java (rev 0) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ContentDirective.java 2012-06-01 15:05:38 UTC (rev 693) @@ -0,0 +1,56 @@ +/* + * #%L + * JRst :: Api + * + * $Id: ContentDirective.java 512 2010-11-22 14:47:17Z tchemit $ + * $HeadURL: http://svn.nuiton.org/svn/jrst/branches/jrst-docutils-jython/jrst/src/main/j... $ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.jrst.directive; + +import org.nuiton.jrst.JRSTDirective; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.Node; + +/** + * ContentDirective + * + * @author poussin + * @version $Revision: 512 $ + * + * Last update : $Date: 2010-11-22 15:47:17 +0100 (lun. 22 nov. 2010) $ + * By : $Author: tchemit $ + */ +public class ContentDirective implements JRSTDirective { + + /* + * @see org.nuiton.jrst.JRSTDirective#parse(org.dom4j.Element) + */ + @Override + public Node parse(Element e) { + Element result = DocumentHelper.createElement("topic").addAttribute( + "value", e.attributeValue("value")); + result.addAttribute("type", "contents"); + result.setText(e.getText()); + return result; + } + +} \ No newline at end of file Added: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/DateDirective.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/DateDirective.java (rev 0) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/DateDirective.java 2012-06-01 15:05:38 UTC (rev 693) @@ -0,0 +1,66 @@ +/* + * #%L + * JRst :: Api + * + * $Id: DateDirective.java 512 2010-11-22 14:47:17Z tchemit $ + * $HeadURL: http://svn.nuiton.org/svn/jrst/branches/jrst-docutils-jython/jrst/src/main/j... $ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.jrst.directive; + +import org.nuiton.jrst.JRSTDirective; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.Node; +import org.dom4j.Text; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * .. date:: .. time:: + * + * Created: 4 nov. 06 13:29:25 + * + * @author poussin + * @version $Revision: 512 $ + * + * Last update: $Date: 2010-11-22 15:47:17 +0100 (lun. 22 nov. 2010) $ + * by : $Author: tchemit $ + */ +public class DateDirective implements JRSTDirective { + + /* + * @see org.nuiton.jrst.JRSTDirective#parse(org.dom4j.Element) + */ + @Override + public Node parse(Element e) { + + // String format = e.attributeValue(JRSTLexer.DIRECTIVE_VALUE); + + // TODO used format to format date, this format is not Java standard + // is python standard "%Y-%m-%d" + + Text result = DocumentHelper.createText(SimpleDateFormat.getInstance() + .format(new Date())); + return result; + } + +} Added: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ImageDirective.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ImageDirective.java (rev 0) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/ImageDirective.java 2012-06-01 15:05:38 UTC (rev 693) @@ -0,0 +1,94 @@ +/* + * #%L + * JRst :: Api + * + * $Id: ImageDirective.java 549 2011-03-07 16:23:50Z echatellier $ + * $HeadURL: http://svn.nuiton.org/svn/jrst/branches/jrst-docutils-jython/jrst/src/main/j... $ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.jrst.directive; + +import org.nuiton.jrst.JRSTDirective; +import org.nuiton.jrst.JRSTLexer; +import static org.nuiton.jrst.ReStructuredText.IMAGE; +import static org.nuiton.jrst.ReStructuredText.SUBSTITUTION_DEFINITION; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.Node; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * .. image:: picture.jpeg :height: 100 :width: 200 :scale: 50 :alt: alternate + * text :align: right + * + * Created: 4 nov. 06 12:52:02 + * + * @author poussin + * @version $Revision: 549 $ + * + * Last update: $Date: 2011-03-07 17:23:50 +0100 (lun. 07 mars 2011) $ + * by : $Author: echatellier $ + */ +public class ImageDirective implements JRSTDirective { + + protected static final String SCALE = "scale"; + protected static final String WIDTH = "width"; + protected static final String HEIGHT = "height"; + + /* + * (non-Javadoc) + * + * @see org.nuiton.jrst.JRSTDirective#parse(org.dom4j.Element) + */ + @Override + public Node parse(Element e) { + Element result = DocumentHelper.createElement(IMAGE); + + if (e.getParent() != null + && SUBSTITUTION_DEFINITION.equals(e.getParent().getName())) { + String ref = e.getParent().attributeValue("name"); + result.addAttribute("alt", ref); + } + result.addAttribute("uri", e.attributeValue(JRSTLexer.DIRECTIVE_VALUE)); + + Pattern arg = Pattern.compile(":([^:]+):\\s*(.*)"); + String[] lines = e.getText().split("\n"); + for (String l : lines) { + Matcher matcher = arg.matcher(l.trim()); + if (matcher.matches()) { + String name = matcher.group(1); + String value = matcher.group(2); + if (SCALE.equalsIgnoreCase(name)) { + if (!result.asXML().matches(".*" + WIDTH + "=\".*\".*")) { + result.addAttribute(WIDTH, value + (value.matches(".*%") ? "" : "%")); + } + if (!result.asXML().matches(".*" + HEIGHT + "=\".*\".*")) { + result.addAttribute(HEIGHT, value + (value.matches(".*%") ? "" : "%")); + } + } + result.addAttribute(name, value); + } + } + return result; + } + +} Added: branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/SectnumDirective.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/SectnumDirective.java (rev 0) +++ branches/jrst-docutils-jython/jrst/src/main/java/org/nuiton/jrst/directive/SectnumDirective.java 2012-06-01 15:05:38 UTC (rev 693) @@ -0,0 +1,52 @@ +/* + * #%L + * JRst :: Api + * + * $Id: SectnumDirective.java 512 2010-11-22 14:47:17Z tchemit $ + * $HeadURL: http://svn.nuiton.org/svn/jrst/branches/jrst-docutils-jython/jrst/src/main/j... $ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.jrst.directive; + +import org.nuiton.jrst.JRSTDirective; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.Node; + +/** + * SectnumDirective. + * + * @author poussin + * @version $Revision: 512 $ + * + * Last update : $Date: 2010-11-22 15:47:17 +0100 (lun. 22 nov. 2010) $ + * By : $Author: tchemit $ + */ +public class SectnumDirective implements JRSTDirective { + + /* + * @see org.nuiton.jrst.JRSTDirective#parse(org.dom4j.Element) + */ + @Override + public Node parse(Element e) { + Element result = DocumentHelper.createElement("sectnum"); + return result; + } +} Modified: branches/jrst-docutils-jython/jrst/src/main/resources/i18n/jrst_en_GB.properties =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/resources/i18n/jrst_en_GB.properties 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/main/resources/i18n/jrst_en_GB.properties 2012-06-01 15:05:38 UTC (rev 693) @@ -7,6 +7,7 @@ jrst.option.intermediatefile= jrst.option.outfile=Output file jrst.option.outtype=Output type (xml|xhtml|docbook|html|xdoc|rst|fo|pdf) +jrst.option.simple= jrst.option.xslfile=XSL file list to apply, comma separated open=Open file \: openEmpty?=This field must be filled in. Modified: branches/jrst-docutils-jython/jrst/src/main/resources/i18n/jrst_fr_FR.properties =================================================================== --- branches/jrst-docutils-jython/jrst/src/main/resources/i18n/jrst_fr_FR.properties 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/main/resources/i18n/jrst_fr_FR.properties 2012-06-01 15:05:38 UTC (rev 693) @@ -7,6 +7,7 @@ jrst.option.intermediatefile= jrst.option.outfile=Fichier de sortie jrst.option.outtype=Type de sortie (xml|xhtml|docbook|html|xdoc|rst|fo|pdf) +jrst.option.simple= jrst.option.xslfile=Fichier XSL a appliquer (séparé par une ,) open=Ouvrir le fichier \: openEmpty?=Ce champ doit etre rempli. Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTAbstractTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTAbstractTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTAbstractTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -24,6 +24,9 @@ */ package org.nuiton.jrst; +import net.sf.saxon.functions.StringFn; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Assert; import org.junit.BeforeClass; @@ -36,6 +39,9 @@ */ public class JRSTAbstractTest extends Assert { + /** to use log facility, just put in your code: log.info("..."); */ + protected static Log log = LogFactory.getLog(JRSTAbstractTest.class); + protected static File testWorkDir; protected static File testResourcesDir; @@ -88,4 +94,51 @@ public static InputStream getTestStream(String testName) { return JRSTAbstractTest.class.getResourceAsStream("/" + testName); } + + public static class JRSTTestGenerator { + protected String outputType; + protected File in; + protected File out; + protected JRST.Overwrite overwrite; + + public JRSTTestGenerator(String outputType, File in, File out, JRST.Overwrite overwrite) throws Exception { + this.outputType = outputType; + this.in = in; + this.out = out; + this.overwrite = overwrite; + generate(); + } + + public void generate() throws Exception { + + //time + JRST.generate(outputType, in, out, overwrite, false); + assertJRST(in, out); + + //time + JRST.generate(outputType, in, out, overwrite, true); + assertJRST(in, out); + + //log time + } + + /** + * Overide this method to add assert + * @param in + * @param out + * @throws Exception + */ + public void assertJRST(File in, File out) throws Exception { + } + } + + public void generate(String outputType, File in, File out, JRST.Overwrite overwrite) throws Exception { + new JRSTTestGenerator(outputType, in, out, overwrite); + } + + public void generateCommandLine(String[] commands) throws Exception { + JRST.main(commands); + commands[commands.length-1] = ""; + JRST.main(commands); + } } Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTGeneratorTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTGeneratorTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTGeneratorTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -63,7 +63,7 @@ } String test1 = getOutputTestPath("jrst-RstToRst.rst"); - JRST.main(new String[]{"-t", "rst", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "rst", "--force", "-o", test1, getResourcesTestPath()}); } @Test @@ -74,7 +74,7 @@ } String test1 = getOutputTestPath("jrst-RstToHtml.html"); - JRST.main(new String[]{"-t", "html", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "html", "--force", "-o", test1, getResourcesTestPath(), "--simple"}); } @Test @@ -85,7 +85,7 @@ } String test1 = getOutputTestPath("jrst-RstToHtml2.html"); - JRST.main(new String[]{"-t", "html", "--force", "-o", test1, getTestFile("docDeveloppeur.rst").getPath()}); + generateCommandLine(new String[]{"-t", "html", "--force", "-o", test1, getTestFile("docDeveloppeur.rst").getPath(), "--simple"}); } @@ -97,7 +97,7 @@ } String test1 = getOutputTestPath("jrst-RstToDocbook.dbk"); - JRST.main(new String[]{"-t", "docbook", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "docbook", "--force", "-o", test1, getResourcesTestPath(), "--simple"}); } @Test @@ -108,7 +108,7 @@ } String test1 = getOutputTestPath("jrst-RstToXdoc.xdoc"); - JRST.main(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath(), "--simple"}); } /** @@ -124,7 +124,7 @@ } String test1 = getOutputTestPath("jrst-RstToXdoc2.xdoc"); - JRST.main(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath(), "--simple"}); } @Test @@ -135,7 +135,7 @@ } String test1 = getOutputTestPath("jrst-RstToXdoc3.xdoc"); - JRST.main(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath(), "--simple"}); } @Test @@ -146,7 +146,7 @@ } String test1 = getOutputTestPath("jrst-RstToXdoc4.xdoc"); - JRST.main(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath(), "--simple"}); } @Test @@ -157,7 +157,7 @@ } String test1 = getOutputTestPath("jrst-RstToXdocJrstSite.xdoc"); - JRST.main(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "xdoc", "--force", "-o", test1, getResourcesTestPath(), "--simple"}); } @@ -169,7 +169,7 @@ } String test1 = getOutputTestPath("jrst-RstToPDF.pdf"); - JRST.main(new String[]{"-t", "pdf", "--force", "-o", test1, getResourcesTestPath()}); + generateCommandLine(new String[]{"-t", "pdf", "--force", "-o", test1, getResourcesTestPath(), "--simple"}); } @Test @@ -180,6 +180,6 @@ } String test1 = getOutputTestPath("jrst-RstToPDF2.pdf"); - JRST.main(new String[]{"-t", "pdf", "--force", "-o", test1, getTestFile("docDeveloppeur.rst").getPath()}); + generateCommandLine(new String[]{"-t", "pdf", "--force", "-o", test1, getTestFile("docDeveloppeur.rst").getPath(), "--simple"}); } } Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/JRSTTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -64,9 +64,18 @@ @Test public void generateXml() throws Exception { - File in = getTestFile("text.rst"); + File in = getTestFile("test.rst"); File out = getOutputTestFile("jrst-RstToXml.xml"); - JRST.generate(JRST.TYPE_XML, in, out, JRST.Overwrite.ALLTIME); + generate(JRST.TYPE_XML, in, out, JRST.Overwrite.ALLTIME); } + + @Test + public void generateSimpleXml() throws Exception { + + File in = getTestFile("test.rst"); + File out = getOutputTestFile("jrst-RstToSimpleXml.xml"); + + generate(JRST.TYPE_XML, in, out, JRST.Overwrite.ALLTIME); + } } \ No newline at end of file Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/AdmonitionTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/AdmonitionTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/AdmonitionTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -56,11 +56,16 @@ File in = getBugTestFile("testAdminitionInList1787.rst"); File out = getOutputTestFile("jrst-testAdminitionInList1787.html"); // out.deleteOnExit(); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - String content = FileUtils.readFileToString(out, JRST.UTF_8); -// Must contains <div class="note"> - assertTrue(content.contains("<div class=\"note\">")); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + // Must contains <div class="note"> + assertTrue(content.contains("<div class=\"note\">")); + } + }; } } \ No newline at end of file Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/DirectiveTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/DirectiveTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/DirectiveTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -54,16 +54,22 @@ File in = getBugTestFile("testImages21.rst"); File out = getOutputTestFile("jrst-testImages.html"); // out.deleteOnExit(); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("alt=\"alternate text\"") > 0); - assertTrue(content.indexOf("width=\"200px\"") > 0); - assertTrue(content.indexOf("align=\"center\"") > 0); - assertTrue(content.indexOf("alt=\"tab alternate text\"") > 0); - // scale - assertTrue(content.indexOf("alt=\"tab alternate text\"") > 0); - //assertTrue(content.indexOf("width=\"49%\" height=\"49%\"") > 0); + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + log.info(content); + assertTrue(content.indexOf("alt=\"alternate text\"") > 0); + assertTrue(content.indexOf("width=\"200px\"") > 0 + || content.indexOf("width=\"200 px\"") > 0); + assertTrue(content.indexOf("align=\"center\"") > 0); + assertTrue(content.indexOf("alt=\"tab alternate text\"") > 0); + // scale + assertTrue(content.indexOf("alt=\"tab alternate text\"") > 0); + //assertTrue(content.indexOf("width=\"49%\" height=\"49%\"") > 0); + } + }; } /** @@ -76,10 +82,15 @@ File in = getBugTestFile("testContent877.rst"); File out = getOutputTestFile("jrst-testContent.html"); // out.deleteOnExit(); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("Table des matières") > 0); + + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.indexOf("Table des matières") > 0); + } + }; } /** @@ -92,10 +103,15 @@ File in = getBugTestFile("testContents.rst"); File out = getOutputTestFile("jrst-testContents.html"); // out.deleteOnExit(); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("<b>") > 0); - assertTrue(content.indexOf("<em>") > 0); + + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.indexOf("<b>") > 0); + assertTrue(content.indexOf("<em>") > 0); + } + }; } } Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/ErrorsTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/ErrorsTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/ErrorsTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -26,7 +26,7 @@ File in = getBugTestFile("testDisplayErrors.rst"); File out = getOutputTestFile("jrst-testDisplayErrors.html"); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); + JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME, false); String content = FileUtils.readFileToString(out, JRST.UTF_8); assertTrue(content.contains("Explicit markup ends without a blank line; unexpected unindent.")); Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/OptionTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/OptionTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/OptionTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -52,7 +52,7 @@ public void testOptionArgumentSizeInList() throws Exception { File in = getBugTestFile("testOptionArgumentSize1788.rst"); File out = getOutputTestFile("jrst-testOptionArgumentSize1788.html"); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); + generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); } } Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TableTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TableTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TableTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -54,11 +54,16 @@ File in = getBugTestFile("testTable1290.rst"); File out = getOutputTestFile("jrst-tables.html"); // out.deleteOnExit(); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("Annee|Trait|Espece") > 0); - assertTrue(content.indexOf("<p>Fatal</p>") > 0); + + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.indexOf("Annee|Trait|Espece") > 0); + assertTrue(content.indexOf("<p>Fatal</p>") > 0); + } + }; } /** @@ -71,9 +76,14 @@ File in = getBugTestFile("testTable1375.rst"); File out = getOutputTestFile("jrst-tables.html"); // out.deleteOnExit(); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("<em>légérs</em>") > 0); + + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.indexOf("<em>légérs</em>") > 0); + } + }; } } Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TextTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TextTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TextTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -56,12 +56,16 @@ File in = getBugTestFile("testLinks.rst"); File out = getOutputTestFile("jrst-testLinks.html"); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("href=\"http://labs.libre-entreprise.org/tracker/?atid=113&group_id=8&func=browse\"") > 0); - assertTrue(content.indexOf("href=\"http://www.docbook.org/\"") > 0); - assertTrue(content.indexOf("href=\"http://www.docbook2.org/\"") > 0); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.indexOf("href=\"http://labs.libre-entreprise.org/tracker/?atid=113&group_id=8&func=browse\"") > 0); + assertTrue(content.indexOf("href=\"http://www.docbook.org/\"") > 0); + assertTrue(content.indexOf("href=\"http://www.docbook2.org/\"") > 0); + } + }; } /** @@ -75,11 +79,15 @@ File in = getBugTestFile("testLinks1380.rst"); File out = getOutputTestFile("jrst-testLinks.html"); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("nuiton's forge") > 0); - assertTrue(content.indexOf("build.xml") > 0); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.indexOf("nuiton's forge") > 0); + assertTrue(content.indexOf("build.xml") > 0); + } + }; } /** @@ -93,12 +101,16 @@ File in = getBugTestFile("testTab1378.rst"); File out = getOutputTestFile("jrst-testTab1378.html"); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - Pattern pattern = Pattern.compile(".*<blockquote>.*du bla bla \u00E9l\u00E9mentaire.*</blockquote>.*", Pattern.DOTALL); - Matcher matcher = pattern.matcher(content); - assertTrue(matcher.find()); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + Pattern pattern = Pattern.compile(".*<blockquote>.*du bla bla \u00E9l\u00E9mentaire.*</blockquote>.*", Pattern.DOTALL); + Matcher matcher = pattern.matcher(content); + assertTrue(matcher.find()); + } + }; } /** @@ -110,14 +122,19 @@ File in = getBugTestFile("testOptionsList644.rst"); File out = getOutputTestFile("jrst-testOptionList644.html"); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("<span class=\"option\">-a</span>") > 0); - assertTrue(content.indexOf("<p>options can have arguments\nand long descriptions</p>") > 0); - assertTrue(content.indexOf("<v3region.zip|v2region.xml|v2region.xml.gz>") > 0); - assertTrue(content.indexOf("<span class=\"option\">/V</span>") > 0); - assertTrue(content.indexOf("<p>DOS/VMS-style options too</p>") > 0); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.indexOf("<span class=\"option\">-a</span>") > 0); + assertTrue(content.indexOf("<p>options can have arguments\nand long descriptions</p>") > 0 + ||content.indexOf("<p>options can have arguments and long descriptions</p>") > 0); + assertTrue(content.indexOf("<v3region.zip|v2region.xml|v2region.xml.gz>") > 0); + assertTrue(content.indexOf("<span class=\"option\">/V</span>") > 0); + assertTrue(content.indexOf("<p>DOS/VMS-style options too</p>") > 0); + } + }; } /** @@ -133,11 +150,15 @@ File in = getBugTestFile("testLinks.rst"); File out = getOutputTestFile("jrst-testLinks.html"); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.indexOf("echapement de lien1_") > 0); - assertTrue(content.indexOf("echapement de *.txt") > 0); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.indexOf("echapement de lien1_") > 0); + assertTrue(content.indexOf("echapement de *.txt") > 0); + } + }; } /** @@ -148,13 +169,17 @@ File in = getBugTestFile("testEmbeddedURIs.rst"); File out = getOutputTestFile("jrst-testEmbeddedURIs.html"); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); - - String content = FileUtils.readFileToString(out, JRST.UTF_8); - assertTrue(content.contains("href=\"http://www.python.org\"")); - assertTrue(content.contains("href=\"./python\"")); - assertTrue(content.contains("href=\"http://www.rfc-editor.org/rfc/rfc2396.txt\"")); - assertTrue(content.contains("href=\"http://www.rfc-editor.org/rfc/rfc2732.txt\"")); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { + + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + assertTrue(content.contains("href=\"http://www.python.org\"")); + assertTrue(content.contains("href=\"./python\"")); + assertTrue(content.contains("href=\"http://www.rfc-editor.org/rfc/rfc2396.txt\"")); + assertTrue(content.contains("href=\"http://www.rfc-editor.org/rfc/rfc2732.txt\"")); + } + }; } } Modified: branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TitlesTest.java =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TitlesTest.java 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/java/org/nuiton/jrst/bugs/TitlesTest.java 2012-06-01 15:05:38 UTC (rev 693) @@ -30,6 +30,7 @@ import org.nuiton.jrst.JRSTAbstractTest; import java.io.File; +import java.io.IOException; /** * Test concernant les titres en général. @@ -52,9 +53,10 @@ File in = new File("src/test/resources/bugs/testNoSubtitle.rst"); File out = File.createTempFile("jrst-RstToHtml2", ".html"); // out.deleteOnExit(); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); + generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); } - + + /** * Test que s'il n'y a pas de content avant la premiere section, la section * est bien parsée comme tel. @@ -66,9 +68,14 @@ File in = new File("src/test/resources/bugs/testNoContentSubtitles.rst"); File out = File.createTempFile("jrst-testNoContentSubtitles", ".html"); // out.deleteOnExit(); - JRST.generate(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME); + new JRSTTestGenerator(JRST.TYPE_HTML, in, out, JRST.Overwrite.ALLTIME) { - String content = FileUtils.readFileToString(out, JRST.UTF_8); - Assert.assertTrue(content.indexOf("<h2>Prérequis</h2>") > 0); + @Override + public void assertJRST(File in, File out) throws Exception { + String content = FileUtils.readFileToString(out, JRST.UTF_8); + Assert.assertTrue(content.indexOf("<h2>Prérequis</h2>") > 0 + || content.indexOf("<h2 class=\"title\">Prérequis</h2>") > 0); + } + }; } } Modified: branches/jrst-docutils-jython/jrst/src/test/resources/test.rst =================================================================== --- branches/jrst-docutils-jython/jrst/src/test/resources/test.rst 2012-05-31 09:08:57 UTC (rev 692) +++ branches/jrst-docutils-jython/jrst/src/test/resources/test.rst 2012-06-01 15:05:38 UTC (rev 693) @@ -22,6 +22,7 @@ .. * <http://www.gnu.org/licenses/lgpl-3.0.html>. .. * #L% .. - + ===================================== An Introduction to reStructuredText ===================================== @@ -209,7 +210,7 @@ StructuredText. I decided that a complete rewrite was in order, and even started a -`reStructuredText SourceForge project`_ (now inactive). My +`reStructuredText SourceForge project` (now inactive). My motivations (the "itches" I aim to "scratch") are as follows: - I need a standard format for inline documentation of the programs I @@ -283,15 +284,13 @@ immediately merged, renamed to "Docutils_", and a 0.1 release soon followed. -.. __: `reStructuredText SourceForge project`_ .. [#spec-2] The second draft of the spec: - `An Introduction to reStructuredText`__ - `Problems With StructuredText`__ - `reStructuredText Markup Specification`__ - - `Python Extensions to the reStructuredText Markup - Specification`__ + - `Python Extensions to the reStructuredText Markup Specification`__ __ http://mail.python.org/pipermail/doc-sig/2001-June/001858.html __ http://mail.python.org/pipermail/doc-sig/2001-June/001859.html @@ -311,12 +310,11 @@ __ http://mail.python.org/pipermail/doc-sig/2001-June/001855.html __ http://mail.python.org/pipermail/doc-sig/2001-June/001856.html __ http://mail.python.org/pipermail/doc-sig/2001-June/001857.html + __ http://mail.python.org/pipermail/doc-sig/2001-June/001857.html .. _Zope Corporation: http://www.zope.com .. _ZOPE: http://www.zope.org -.. _reStructuredText SourceForge project: - http://structuredtext.sourceforge.net/ .. _pythondoc: http://starship.python.net/crew/danilo/pythondoc/ .. _StructuredTextNG: http://www.zope.org/DevHome/Members/jim/StructuredTextWiki/StructuredTextNG @@ -335,4 +333,4 @@ indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 - End: \ No newline at end of file + End:
participants (1)
-
jpages@users.nuiton.org