Adding a Confluence to DocBook converter to the plugin

git-svn-id: https://svn.apache.org/repos/asf/servicemix/sandbox/gertv/documentation/trunk@938975 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/docs/jbi/src/confluence/servicemix-camel.wiki b/docs/jbi/src/confluence/servicemix-camel.wiki
new file mode 100644
index 0000000..5c43c5b
--- /dev/null
+++ b/docs/jbi/src/confluence/servicemix-camel.wiki
@@ -0,0 +1,45 @@
+h1. servicemix-camel
+
+h2. Overview
+The servicemix-camel component provides support for using Apache Camel to provide a full set of Enterprise Integration Patterns and flexible routing and transformation in both Java code or Spring XML to route services on the Normalized Message Router.
+
+h3. Namespace and camel-context.xml
+When creating a servicemix-camel service unit, we reuse the default Camel namespace {{http://camel.apache.org/schema/spring}}.
+
+This is an example {{camel-context.xml}} which uses the Spring DSL to define the Camel routes
+{code}
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+         http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+         http://camel.apache.org/schema/spring
+           http://camel.apache.org/schema/spring/camel-spring.xsd">
+
+          <camelContext xmlns="http://camel.apache.org/schema/spring">
+            <route>
+              <!-- route defined in the Spring DSL -->
+            </route>
+          </camelContext>
+
+</beans>
+{code}
+
+It is also possible to use the Java DSL inside a servicemix-camel service unit by referring to the package that contains the {{RouteBuilder}} classes.  An example: this {{camel-context.xml}} file will activate all routes defined by {{RouteBuilder}}s in the {{org.apache.servicemix.example.camel}} package.
+{code}
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+         http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+         http://camel.apache.org/schema/spring
+           http://camel.apache.org/schema/spring/camel-spring.xsd">
+
+          <camelContext xmlns="http://camel.apache.org/schema/spring">
+            <packages>org.apache.servicemix.examples.camel</packages>
+          </camelContext>
+
+</beans>
+{code}
\ No newline at end of file
diff --git a/docs/jbi/src/docbkx/index.xml b/docs/jbi/src/docbkx/index.xml
index 5f1ae2c..6a566bd 100644
--- a/docs/jbi/src/docbkx/index.xml
+++ b/docs/jbi/src/docbkx/index.xml
@@ -50,6 +50,7 @@
   <part>
     <title>ServiceMix JBI Components</title>
     <xi:include href="servicemix-bean.xml" parse="xml"/>
+    <xi:include href="servicemix-camel.wiki.xml" parse="xml"/>
     <xi:include href="servicemix-saxon.xml" parse="xml"/>
 
 
diff --git a/docs/pom.xml b/docs/pom.xml
index 317b918..3d309d0 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -158,14 +158,21 @@
         <groupId>org.apache.servicemix.docs</groupId>
         <artifactId>tools</artifactId>
         <version>${pom.version}</version>
-        <inherited>false</inherited>  <!-- URL handlers only have to be registered once -->
         <executions>
           <execution>
-            <phase>initialize</phase>
+            <id>configure-snippet-handler</id>
+            <inherited>false</inherited>    <!-- URL handlers only have to be registered once -->
             <goals>
               <goal>snippet</goal>
             </goals>
           </execution>
+          <execution>
+            <id>confluence-to-docbook</id>
+            <inherited>true</inherited>
+            <goals>
+              <goal>confluence-to-docbook</goal>
+            </goals>
+          </execution>
         </executions>
         <configuration>
           <snippetBase>http://svn.apache.org/repos/asf/servicemix/</snippetBase>
diff --git a/tools/pom.xml b/tools/pom.xml
index f94f4db..8d34688 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -24,6 +24,11 @@
       <version>2.0.9</version>
     </dependency>
     <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>1.4</version>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.7</version>
diff --git a/tools/src/main/java/org/apache/servicemix/docs/ConfluenceToDocbookMojo.java b/tools/src/main/java/org/apache/servicemix/docs/ConfluenceToDocbookMojo.java
new file mode 100644
index 0000000..1209556
--- /dev/null
+++ b/tools/src/main/java/org/apache/servicemix/docs/ConfluenceToDocbookMojo.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.docs;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.servicemix.docs.confluence.ConfluenceConverter;
+import org.apache.servicemix.docs.snippet.SnippetHandler;
+
+/**
+ * Convert pages in Confluence wiki markup to Docbook syntax
+ *
+ * @goal confluence-to-docbook
+ * @phase generate-resources
+ */
+public class ConfluenceToDocbookMojo extends AbstractMojo {
+
+    /**
+     * Location of the snippet cache
+     * @parameter default-value="${basedir}/src/confluence"
+     */
+    protected File input;
+
+    /**
+     * Output directory for the Docbook sources
+     * @parameter default-value="${project.build.directory}/docbkx/sources"
+     */
+    protected File output;
+
+    public void execute() throws MojoExecutionException {
+        if (!output.exists()) {
+            output.mkdirs();
+        }
+        if (input.exists() && input.isDirectory()) {
+            for (File file : input.listFiles()) {
+                getLog().info("Creating DocBook from " + file.getAbsolutePath());
+                FileWriter writer = null;
+                try {
+                    writer = new FileWriter(new File(output, file.getName() + ".xml"));
+
+                    ConfluenceConverter converter =
+                        new ConfluenceConverter(new FileReader(file), writer);
+                    
+                    converter.convert();
+                } catch (IOException e) {
+                    throw new MojoExecutionException("Unable to convert " + file, e);
+                } finally {
+                    if (writer != null) {
+                        try {
+                            writer.close();
+                        } catch (IOException e) {
+                            // ignore this
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/src/main/java/org/apache/servicemix/docs/SnippetMojo.java b/tools/src/main/java/org/apache/servicemix/docs/SnippetMojo.java
index ff82f8e..841ae7c 100644
--- a/tools/src/main/java/org/apache/servicemix/docs/SnippetMojo.java
+++ b/tools/src/main/java/org/apache/servicemix/docs/SnippetMojo.java
@@ -30,6 +30,7 @@
  *
  * @goal snippet
  * @phase initialize
+ * @inheritByDefault false
  */
 public class SnippetMojo extends AbstractMojo {
 
diff --git a/tools/src/main/java/org/apache/servicemix/docs/confluence/ConfluenceConverter.java b/tools/src/main/java/org/apache/servicemix/docs/confluence/ConfluenceConverter.java
new file mode 100644
index 0000000..b5f0548
--- /dev/null
+++ b/tools/src/main/java/org/apache/servicemix/docs/confluence/ConfluenceConverter.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.docs.confluence;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Represents a document in Confluence wiki markup
+ */
+public class ConfluenceConverter {
+
+    private static Pattern CHAPTER = Pattern.compile("^h1\\. .*");
+    private static Pattern CHAPTER_OR_SECTION = Pattern.compile("^h[1-6]\\. .*"); 
+
+    private BufferedReader reader;
+    private PrintWriter writer;
+
+    private int currentLevel = 0;
+
+    private Stack<String> elements = new Stack<String>();
+
+    public ConfluenceConverter(Reader reader, Writer writer) {
+        if (reader instanceof BufferedReader) {
+            this.reader = (BufferedReader) reader;
+        } else {
+            this.reader = new BufferedReader(reader);
+        }
+        if (writer instanceof PrintWriter) {
+            this.writer = (PrintWriter) writer;
+        } else {
+            this.writer = new PrintWriter(writer);
+        }
+    }
+
+    public void convert() throws IOException {
+        String line = null;
+        
+        while ((line = reader.readLine()) != null) {
+            if (CHAPTER_OR_SECTION.matcher(line).matches()) {
+                writeChapterOrSection(line);
+            } else if ("{code}".equals(line.trim())) {
+                writeProgramListing();
+            } else {
+                writePara(line);
+            }
+        }
+        
+        // let's close everying that was opened
+        closeElements();
+
+    }
+
+    private void writeProgramListing() throws IOException {
+        writeText("<programlisting><![CDATA[");
+
+        String line = null;
+        while ((line = reader.readLine()) != null && !line.equals("{code}")) {
+            writeText(line, "");
+        }
+
+        writeText("]]></programlisting>");
+    }
+
+    private void writePara(String line) {
+        if (line.trim().length() == 0) {
+            if (elements.contains("para")) {
+                closeElement("para");
+            }
+        } else {
+            if (!elements.peek().equals("para")) {
+                openElement("para");
+            }
+            writeText(filter(line));
+        }
+    }
+
+    private String filter(String line) {
+        return line.replaceAll("[\\{]{2}", "<code>").replaceAll("[\\}]{2}", "</code>");
+    }
+
+    private void writeText(String text) {
+        writeText(text, indent());
+    }
+
+    private void writeText(String text, String indent) {
+        writer.printf("%s%s%n", indent, text);
+    }
+
+    private void writeChapterOrSection(String line) {
+        int level = Integer.parseInt(line.substring(1, 2));
+        while (currentLevel >= level) {
+            closeElement("section");
+            currentLevel--;
+        }
+        if (CHAPTER.matcher(line).matches()) {
+            openElement("chapter");
+        } else {
+            openElement("section");
+        }
+        writeElement("title", line.substring(3).trim());
+        currentLevel = level;
+    }
+
+    private void writeElement(String element, String value) {
+        writer.printf("%s<%s>%s</%s>%n", indent(), element, value, element);
+    }
+
+    private void openElement(String element) {
+        writer.printf("%s<%s>%n", indent(), element);
+        elements.push(element);
+    }
+
+    private void closeElements() {
+        while (!elements.isEmpty()) {
+            closeElement(elements.peek());
+        }
+    }
+
+    private void closeElement(String element) {
+        String next = elements.pop();
+        writer.printf("%s</%s>%n", indent(), next);
+        if (!element.equals(next)) {
+            // recursive call to close everything down to the required element
+            closeElement(element);
+        }
+    }
+
+    private String indent() {
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0 ; i < elements.size(); i++) {
+            buffer.append("  ");
+        }
+        return buffer.toString();
+    }
+}
diff --git a/tools/src/main/java/org/apache/servicemix/docs/snippet/SnippetHandler.java b/tools/src/main/java/org/apache/servicemix/docs/snippet/SnippetHandler.java
index 627d1e4..f589034 100644
--- a/tools/src/main/java/org/apache/servicemix/docs/snippet/SnippetHandler.java
+++ b/tools/src/main/java/org/apache/servicemix/docs/snippet/SnippetHandler.java
@@ -83,7 +83,11 @@
                         copy(in, out, id);
                     }
                 } catch (IOException e) {
-                    mojo.getLog().warn("Unable to include snippet " + url.toExternalForm(), e);
+                    out.println("missing snippet: " + url);
+
+                    // summary warning message and full detail debug logging
+                    mojo.getLog().warn("Unable to include snippet " + url.toExternalForm());
+                    mojo.getLog().debug("Unable to include snippet " + url.toExternalForm(), e);
                 }
 
                 out.println("]]></programlisting>");
@@ -141,8 +145,9 @@
             copy(in, out);
             out.flush();
         } catch (IOException e) {
-            mojo.getLog().warn("Unable to cache data for " + name, e);
-            throw e;
+            // summary warning message and full detail debug message
+            mojo.getLog().warn("Unable to cache data for " + name + " : " + e.getMessage());
+            mojo.getLog().debug("Unable to cache data for " + name + " : " + e.getMessage(), e);
         } finally {
             if (in != null) {
                 in.close();
diff --git a/tools/src/test/java/org/apache/servicemix/docs/SnippetMojoTest.java b/tools/src/test/java/org/apache/servicemix/docs/SnippetMojoTest.java
index 9248913..dd00f95 100644
--- a/tools/src/test/java/org/apache/servicemix/docs/SnippetMojoTest.java
+++ b/tools/src/test/java/org/apache/servicemix/docs/SnippetMojoTest.java
@@ -24,6 +24,7 @@
 
 import org.apache.maven.plugin.MojoExecutionException;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import static org.junit.Assert.assertFalse;
@@ -34,10 +35,10 @@
  */
 public class SnippetMojoTest {
 
-    private SnippetMojo mojo;
+    private static SnippetMojo mojo;
 
-    @Before
-    public void setUp() throws MojoExecutionException, MalformedURLException {
+    @BeforeClass
+    public static void setUp() throws MojoExecutionException, MalformedURLException {
         mojo = new SnippetMojo();
         mojo.snippetCache = new File("target/tests/snippets-" + System.currentTimeMillis()); 
         mojo.snippetBase = new File("src/test/resources").toURI().toURL();
diff --git a/tools/src/test/java/org/apache/servicemix/docs/confluence/ConfluenceDocumentTest.java b/tools/src/test/java/org/apache/servicemix/docs/confluence/ConfluenceDocumentTest.java
new file mode 100644
index 0000000..6b39d55
--- /dev/null
+++ b/tools/src/test/java/org/apache/servicemix/docs/confluence/ConfluenceDocumentTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.docs.confluence;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+
+/**
+ * Test cases for {@link ConfluenceConverter}
+ */
+public class ConfluenceDocumentTest {
+
+    @org.junit.Test
+    public void generateChapterAndSections() throws Exception {
+        assertDocBook("document1");
+    }
+
+    @org.junit.Test
+    public void generateChapterSectionsAndParagraphs() throws Exception {
+        assertDocBook("document2");
+    }
+
+    @org.junit.Test
+    public void generateProgramListingBlock() throws Exception {
+        assertDocBook("code-blocks");
+    }
+
+    private void assertDocBook(String name) throws IOException {
+        StringWriter writer = new StringWriter();
+
+        List<String> expected = IOUtils.readLines(getClass().getResourceAsStream(name + ".xml"));
+
+        ConfluenceConverter document =
+                new ConfluenceConverter(new InputStreamReader(getClass().getResourceAsStream(name + ".wiki")),
+                                        writer);
+
+        document.convert();
+        writer.flush();
+        writer.close();
+
+        String[] results = writer.toString().split("\\r?\\n\\r?");
+        int i = 0;
+
+        for (String result : results) {
+            System.out.println(result);
+            if (result.trim().length() > 0) {
+
+                assertEquals("Line " + (i +1) + " should match the expected DocBook XML output",
+                             expected.get(i).trim(), result.trim());
+                i++;
+            }
+        }
+    }
+}
diff --git a/tools/src/test/resources/org/apache/servicemix/docs/confluence/code-blocks.wiki b/tools/src/test/resources/org/apache/servicemix/docs/confluence/code-blocks.wiki
new file mode 100644
index 0000000..37830ed
--- /dev/null
+++ b/tools/src/test/resources/org/apache/servicemix/docs/confluence/code-blocks.wiki
@@ -0,0 +1,11 @@
+h1. Chapter
+
+h2. Section 1
+This section contains a piece of {{code text}}.
+
+{code}
+<?xml version="1.0"?>
+<hello>
+  <world/>
+</hello>
+{code}
\ No newline at end of file
diff --git a/tools/src/test/resources/org/apache/servicemix/docs/confluence/code-blocks.xml b/tools/src/test/resources/org/apache/servicemix/docs/confluence/code-blocks.xml
new file mode 100644
index 0000000..8684e42
--- /dev/null
+++ b/tools/src/test/resources/org/apache/servicemix/docs/confluence/code-blocks.xml
@@ -0,0 +1,15 @@
+<chapter>
+  <title>Chapter</title>
+  <section>
+    <title>Section 1</title>
+    <para>
+      This section contains a piece of <code>code text</code>.
+    </para>
+    <programlisting><![CDATA[
+    <?xml version="1.0"?>
+    <hello>
+      <world/>
+    </hello>
+    ]]></programlisting>
+  </section>
+</chapter>
\ No newline at end of file
diff --git a/tools/src/test/resources/org/apache/servicemix/docs/confluence/document1.wiki b/tools/src/test/resources/org/apache/servicemix/docs/confluence/document1.wiki
new file mode 100644
index 0000000..c7245a9
--- /dev/null
+++ b/tools/src/test/resources/org/apache/servicemix/docs/confluence/document1.wiki
@@ -0,0 +1,7 @@
+h1. Chapter
+
+h2. Section 1
+
+h3. Section 1.1
+
+h2. Section 2
\ No newline at end of file
diff --git a/tools/src/test/resources/org/apache/servicemix/docs/confluence/document1.xml b/tools/src/test/resources/org/apache/servicemix/docs/confluence/document1.xml
new file mode 100644
index 0000000..670d844
--- /dev/null
+++ b/tools/src/test/resources/org/apache/servicemix/docs/confluence/document1.xml
@@ -0,0 +1,12 @@
+<chapter>
+  <title>Chapter</title>
+  <section>
+    <title>Section 1</title>
+    <section>
+      <title>Section 1.1</title>
+    </section>
+  </section>
+  <section>
+    <title>Section 2</title>
+  </section>
+</chapter>
\ No newline at end of file
diff --git a/tools/src/test/resources/org/apache/servicemix/docs/confluence/document2.wiki b/tools/src/test/resources/org/apache/servicemix/docs/confluence/document2.wiki
new file mode 100644
index 0000000..976cf38
--- /dev/null
+++ b/tools/src/test/resources/org/apache/servicemix/docs/confluence/document2.wiki
@@ -0,0 +1,9 @@
+h1. Chapter
+
+h2. Section 1
+Paragraph 1 starts here...
+
+Paragraph 2 starts here...
+
+h3. Section 1.1
+And one more paragraph
\ No newline at end of file
diff --git a/tools/src/test/resources/org/apache/servicemix/docs/confluence/document2.xml b/tools/src/test/resources/org/apache/servicemix/docs/confluence/document2.xml
new file mode 100644
index 0000000..8de779c
--- /dev/null
+++ b/tools/src/test/resources/org/apache/servicemix/docs/confluence/document2.xml
@@ -0,0 +1,18 @@
+<chapter>
+  <title>Chapter</title>
+  <section>
+    <title>Section 1</title>
+    <para>
+      Paragraph 1 starts here...
+    </para>
+    <para>
+      Paragraph 2 starts here...
+    </para>
+    <section>
+      <title>Section 1.1</title>
+      <para>
+        And one more paragraph
+      </para>
+    </section>
+  </section>
+</chapter>
\ No newline at end of file