Add a reference syntax to other pages
[from now] 2013/10/18 16:53:26

diff --git a/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy b/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
index 2d7dd2d..5d832cd 100644
--- a/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
+++ b/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
@@ -68,7 +68,7 @@ class GaidenLinkRenderer extends LinkRenderer {

         def targetPageSource = context.documentSource.findPageByPath(linkRef.path)
         if (!targetPageSource) {
-            System.err.println("WARNING: " + Holders.getMessage("output.page.reference.not.exists.message", [url, pageSource.outputPath]))
+            System.err.println("WARNING: " + Holders.getMessage("output.page.reference.not.exists.message", [url, pageSource.path]))
             return createRendering(url, title, text)
         }

diff --git a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
index fbe3984..031174e 100644
--- a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
+++ b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
@@ -81,7 +81,7 @@ class GaidenLinkRendererSpec extends Specification {
         linkRenderer.render("/not-exist.md", "Test Title", "Test Text")

         then:
-        1 * printStream.println("WARNING: '/not-exist.md' in test.html refers to non-existent page")
+        1 * printStream.println("WARNING: '/not-exist.md' in test.md refers to non-existent page")
     }

 }
[from now] 2013/10/18 17:07:42

diff --git a/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy b/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
index 5d832cd..90e0664 100644
--- a/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
+++ b/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
@@ -52,6 +52,10 @@ class GaidenLinkRenderer extends LinkRenderer {
     }

     protected LinkRenderer.Rendering render(String url, String title, String text) {
+        if (url =~ /^\w+:\/\//) {  // URL
+            return createRendering(url, title, text)
+        }
+
         def linkRef = new LinkRef(url)
         def extension = linkRef.extension
         if (extension != null && extension != "md") {
diff --git a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
index 031174e..2808b13 100644
--- a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
+++ b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
@@ -84,4 +84,38 @@ class GaidenLinkRendererSpec extends Specification {
         1 * printStream.println("WARNING: '/not-exist.md' in test.md refers to non-existent page")
     }

+    def "'render' should not output a warning message when a path is URL"() {
+        setup:
+        def context = new PageBuildContext(documentSource: new DocumentSource())
+        def pageSource = new PageSource(path: "test.md")
+        def linkRenderer = new GaidenLinkRenderer(context, pageSource)
+
+        and:
+        def printStream = Mock(PrintStream)
+        System.err = printStream
+
+        when:
+        def rendering = linkRenderer.render(path, "Test Title", "Test Text")
+
+        then:
+        rendering.href == path
+        rendering.text == "Test Text"
+
+        and:
+        0 * printStream.println(_)
+
+        where:
+        path << [
+            "http://www.example.com",
+            "http://www.example.com/",
+            "http://www.example.com/test",
+            "http://www.example.com/test/",
+            "http://www.example.com/test.html",
+            "http://www.example.com/test.md",
+            "https://www.example.com/test.md",
+            "ftp://www.example.com/test.md",
+            "file://www.example.com/test.md",
+        ]
+    }
+
 }
[from now] 2013/10/18 17:09:11

diff --git a/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy b/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
index 90e0664..92a3a44 100644
--- a/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
+++ b/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
@@ -51,15 +51,15 @@ class GaidenLinkRenderer extends LinkRenderer {
         this.render(url, title, text)
     }

-    protected LinkRenderer.Rendering render(String url, String title, String text) {
-        if (url =~ /^\w+:\/\//) {  // URL
-            return createRendering(url, title, text)
+    protected LinkRenderer.Rendering render(String path, String title, String text) {
+        if (path =~ /^\w+:\/\//) {  // URL
+            return createRendering(path, title, text)
         }

-        def linkRef = new LinkRef(url)
+        def linkRef = new LinkRef(path)
         def extension = linkRef.extension
         if (extension != null && extension != "md") {
-            return createRendering(url, title, text)
+            return createRendering(path, title, text)
         }

         if (!linkRef.path.startsWith("/")) {
@@ -72,8 +72,8 @@ class GaidenLinkRenderer extends LinkRenderer {

         def targetPageSource = context.documentSource.findPageByPath(linkRef.path)
         if (!targetPageSource) {
-            System.err.println("WARNING: " + Holders.getMessage("output.page.reference.not.exists.message", [url, pageSource.path]))
-            return createRendering(url, title, text)
+            System.err.println("WARNING: " + Holders.getMessage("output.page.reference.not.exists.message", [path, pageSource.path]))
+            return createRendering(path, title, text)
         }

         def from = new File("/", pageSource.outputPath)
diff --git a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
index 2808b13..92b9aea 100644
--- a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
+++ b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
@@ -48,14 +48,14 @@ class GaidenLinkRendererSpec extends Specification {
         def linkRenderer = new GaidenLinkRenderer(context, pageSource)

         when:
-        def rendering = linkRenderer.render(url, "Test Title", "Test Text")
+        def rendering = linkRenderer.render(path, "Test Title", "Test Text")

         then:
-        rendering.href == expectedHref
+        rendering.href == renderingHref
         rendering.text == "Test Text"

         where:
-        pageSourcePath | url                     | expectedHref
+        pageSourcePath | path                    | renderingHref
         "test.md"      | "../markdown.md"        | "../markdown.html"
         "test.md"      | "../markdown"           | "../markdown.html"
         "test.md"      | "../markdown.html"      | "../markdown.html"
[from now] 2013/10/22 17:10:25

diff --git a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
index 8a9ff58..a8c72a2 100644
--- a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
+++ b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
@@ -22,7 +22,6 @@ import gaiden.PageSource
 import gaiden.context.PageBuildContext
 import gaiden.message.MessageSource
 import spock.lang.Specification
-import spock.lang.Unroll

 class GaidenLinkRendererSpec extends Specification {

@@ -38,12 +37,11 @@ class GaidenLinkRendererSpec extends Specification {
         Holders.messageSource = new MessageSource()
     }

-    @Unroll("pageSourcePath=#pageSourcePath, path=#path, renderingHref=#renderingHref")
     def "'render' should return a rendering object which has replaced link url"() {
         setup:
         def documentSource = new DocumentSource(pageSources: [
-            new PageSource(path: "markdown.md"),
-            new PageSource(path: "path/markdown.md"),
+            new PageSource(path: "first.md"),
+            new PageSource(path: "path/second.md"),
         ])
         def context = new PageBuildContext(documentSource: documentSource)
         def pageSource = new PageSource(path: pageSourcePath)
@@ -64,26 +62,63 @@ class GaidenLinkRendererSpec extends Specification {
         0 * printStream.println(_)

         where:
-        pageSourcePath | path                    | renderingHref
-        "test.md"      | "markdown.md"           | "markdown.html"
-        "test.md"      | "markdown"              | "markdown.html"
-        "test.md"      | "./markdown.md"         | "./markdown.html"
-        "test.md"      | "./markdown"            | "./markdown.html"
-
-        "test.md"      | "../markdown.md"        | "../markdown.html"
-        "test.md"      | "../markdown"           | "../markdown.html"
-        "test.md"      | "../markdown.html"      | "../markdown.html"
-        "test.md"      | "/markdown"             | "markdown.html"
-        "test.md"      | "/markdown.md"          | "markdown.html"
-        "path/test.md" | "/markdown.md"          | "../markdown.html"
-        "test.md"      | "/path/markdown.md"     | "path/markdown.html"
-        "test.md"      | "markdown.md#fragment"  | "markdown.html#fragment"
-        "test.md"      | "/markdown.md#fragment" | "markdown.html#fragment"
+        pageSourcePath | path                 | renderingHref
+        "test.md"      | "first.md"           | "first.html"
+        "test.md"      | "first"              | "first.html"
+        "test.md"      | "./first.md"         | "./first.html"
+        "test.md"      | "./first"            | "./first.html"
+        "test.md"      | "/first.md"          | "first.html"
+        "test.md"      | "/first"             | "first.html"
+        "path/test.md" | "../first.md"        | "../first.html"
+        "path/test.md" | "/first.md"          | "../first.html"
+        "path/test.md" | "second.md"          | "second.html"
+        "path/test.md" | "/path/second.md"    | "second.html"
+        "test.md"      | "first.md#fragment"  | "first.html#fragment"
+        "test.md"      | "first#fragment"     | "first.html#fragment"
+        "test.md"      | "/first.md#fragment" | "first.html#fragment"
     }

     def "'render' should not replace path if not found a page source"() {
         setup:
         def context = new PageBuildContext(documentSource: new DocumentSource())
+        def pageSource = new PageSource(path: pageSourcePath)
+        def linkRenderer = new GaidenLinkRenderer(context, pageSource)
+
+        and:
+        def printStream = Mock(PrintStream)
+        System.err = printStream
+
+        when:
+        def rendering = linkRenderer.render(path, "Test Title", "Test Text")
+
+        then:
+        rendering.href == path
+        rendering.text == "Test Text"
+
+        and:
+        1 * printStream.println("WARNING: '$path' in $pageSourcePath refers to non-existent page")
+
+        where:
+        pageSourcePath | path
+        "test.md"      | "not-exist.md"
+        "test.md"      | "not-exist"
+        "test.md"      | "./not-exist.md"
+        "test.md"      | "./not-exist"
+        "test.md"      | "/not-exist.md"
+        "test.md"      | "/not-exist"
+        "test.md"      | "../not-exist.md"
+        "test.md"      | "../not-exist"
+        "path/test.md" | "not-exist.md"
+        "path/test.md" | "./not-exist.md"
+        "path/test.md" | "/not-exist.md"
+        "path/test.md" | "../not-exist.md"
+        "test.md"      | "not-exist.md#fragment"
+        "test.md"      | "/not-exist.md#fragment"
+    }
+
+    def "'render' should not output a warning message when a path is not Markdown"() {
+        setup:
+        def context = new PageBuildContext(documentSource: new DocumentSource())
         def pageSource = new PageSource(path: "test.md")
         def linkRenderer = new GaidenLinkRenderer(context, pageSource)

@@ -92,10 +127,30 @@ class GaidenLinkRendererSpec extends Specification {
         System.err = printStream

         when:
-        linkRenderer.render("/not-exist.md", "Test Title", "Test Text")
+        def rendering = linkRenderer.render(path, "Test Title", "Test Text")

         then:
-        1 * printStream.println("WARNING: '/not-exist.md' in test.md refers to non-existent page")
+        rendering.href == path
+        rendering.text == "Test Text"
+
+        and:
+        0 * printStream.println(_)
+
+        where:
+        pageSourcePath | path
+        "test.md"      | "first.html"
+        "test.md"      | "first.test"
+        "test.md"      | "./first.html"
+        "test.md"      | "./first.test"
+        "test.md"      | "/first.html"
+        "test.md"      | "/first.test"
+        "path/test.md" | "../first.test"
+        "path/test.md" | "/first.test"
+        "path/test.md" | "second.test"
+        "path/test.md" | "/path/second.test"
+        "test.md"      | "first.html#fragment"
+        "test.md"      | "first.test#fragment"
+        "test.md"      | "/first.test#fragment"
     }

     def "'render' should not output a warning message when a path is URL"() {
[from now] 2013/11/27 00:59:38

diff --git a/src/main/groovy/gaiden/PageReference.groovy b/src/main/groovy/gaiden/PageReference.groovy
index b2a739e..6954d62 100644
--- a/src/main/groovy/gaiden/PageReference.groovy
+++ b/src/main/groovy/gaiden/PageReference.groovy
@@ -66,4 +66,17 @@ class PageReference {
         FileUtils.getExtension(new File(path).name)
     }

+    /**
+     * Checks the path is URL.
+     *
+     * @return {@code true} if the path is URL
+     */
+    boolean isUrl() {
+        try {
+            new URL(path)
+        } catch (MalformedURLException e) {
+            return false
+        }
+        true
+    }
 }
diff --git a/src/main/groovy/gaiden/SourceCollector.groovy b/src/main/groovy/gaiden/SourceCollector.groovy
index d5dbfe0..7e6582d 100644
--- a/src/main/groovy/gaiden/SourceCollector.groovy
+++ b/src/main/groovy/gaiden/SourceCollector.groovy
@@ -26,7 +26,7 @@ import gaiden.util.FileUtils
  */
 class SourceCollector {

-    private static final PAGE_SOURCE_EXTENSIONS = ['md', 'markdown']
+    static final PAGE_SOURCE_EXTENSIONS = ['md', 'markdown']

     private File pagesDirectory
     private String inputEncoding
diff --git a/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy b/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
index bbc2970..208323a 100644
--- a/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
+++ b/src/main/groovy/gaiden/markdown/GaidenLinkRenderer.groovy
@@ -16,17 +16,18 @@

 package gaiden.markdown

-import gaiden.PageReference
-
-import static org.pegdown.FastEncoder.*
 import gaiden.Holders
+import gaiden.PageReference
 import gaiden.PageSource
+import gaiden.SourceCollector
 import gaiden.context.PageBuildContext
 import gaiden.util.FileUtils
 import org.pegdown.LinkRenderer
 import org.pegdown.ast.ExpLinkNode
 import org.pegdown.ast.RefLinkNode

+import static org.pegdown.FastEncoder.*
+
 /**
  * A Renderer for link node.
  *
@@ -37,10 +38,12 @@ class GaidenLinkRenderer extends LinkRenderer {

     private PageBuildContext context
     private PageSource pageSource
+    private File pagesDirectory

-    GaidenLinkRenderer(PageBuildContext context, PageSource pageSource) {
+    GaidenLinkRenderer(PageBuildContext context, PageSource pageSource, File pagesDirectory = Holders.config.pagesDirectory) {
         this.context = context
         this.pageSource = pageSource
+        this.pagesDirectory = pagesDirectory
     }

     @Override
@@ -56,22 +59,32 @@ class GaidenLinkRenderer extends LinkRenderer {
     protected LinkRenderer.Rendering render(String url, String title, String text) {
         def pageReference = new PageReference(url)
         def extension = pageReference.extension
-        if (extension != null && extension != "md") {
-            return createRendering(path, title, text)
+        if (pageReference.url || (extension != null && !(extension in SourceCollector.PAGE_SOURCE_EXTENSIONS))) {
+            return createRendering(url, title, text)
         }

+        def pageSourceFile = new File(pagesDirectory, pageSource.path)
         if (!pageReference.path.startsWith("/")) {
-            if (extension == "md") {
+            if (extension) {
+                if (!checkFileExists(new File(pageSourceFile.parentFile, pageReference.path), url)) {
+                    return createRendering(url, title, text)
+                }
                 return createRendering(FileUtils.replaceExtension(pageReference.path, "html") + pageReference.fragment, title, text)
             } else {
+                def existing = SourceCollector.PAGE_SOURCE_EXTENSIONS.any {
+                    checkFileExists(new File(pageSourceFile.parentFile, pageReference.path + ".$it"), url)
+                }
+                if (!existing) {
+                    return createRendering(url, title, text)
+                }
                 return createRendering(pageReference.path + ".html" + pageReference.fragment, title, text)
             }
         }

         def targetPageSource = context.documentSource.findPageSource(pageReference)
         if (!targetPageSource) {
-            System.err.println("WARNING: " + Holders.getMessage("output.page.reference.not.exists.message", [path, pageSource.path]))
-            return createRendering(path, title, text)
+            System.err.println("WARNING: " + Holders.getMessage("output.page.reference.not.exists.message", [url, pageSource.path]))
+            return createRendering(url, title, text)
         }

         def from = new File("/", pageSource.outputPath)
@@ -86,4 +99,11 @@ class GaidenLinkRenderer extends LinkRenderer {
         return title ? rendering : rendering.withAttribute("title", encode(title))
     }

+    private boolean checkFileExists(File file, String path) {
+        if (!file.canonicalFile.exists()) {
+            System.err.println("WARNING: " + Holders.getMessage("output.page.reference.not.exists.message", [path, pageSource.path]))
+            return false
+        }
+        true
+    }
 }
diff --git a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
index 075f890..e14de8a 100644
--- a/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
+++ b/src/test/groovy/gaiden/markdown/GaidenLinkRendererSpec.groovy
@@ -19,8 +19,10 @@ package gaiden.markdown
 import gaiden.DocumentSource
 import gaiden.Holders
 import gaiden.PageSource
+import gaiden.SourceCollector
 import gaiden.context.PageBuildContext
 import gaiden.message.MessageSource
+import spock.lang.AutoCleanup
 import spock.lang.Specification

 class GaidenLinkRendererSpec extends Specification {
@@ -29,6 +31,9 @@ class GaidenLinkRendererSpec extends Specification {
     def savedSystemErr
     def savedSystemSecurityManager

+    @AutoCleanup("deleteDir")
+    def pagesDirectory = File.createTempDir()
+
     def setup() {
         savedSystemOut = System.out
         savedSystemErr = System.err
@@ -43,9 +48,10 @@ class GaidenLinkRendererSpec extends Specification {
             new PageSource(path: "first.md"),
             new PageSource(path: "path/second.md"),
         ])
+        createFiles("first.md", "path/second.md", pageSourcePath)
         def context = new PageBuildContext(documentSource: documentSource)
         def pageSource = new PageSource(path: pageSourcePath)
-        def linkRenderer = new GaidenLinkRenderer(context, pageSource)
+        def linkRenderer = new GaidenLinkRenderer(context, pageSource, pagesDirectory)

         when:
         def rendering = linkRenderer.render(path, "Test Title", "Test Text")
@@ -75,7 +81,8 @@ class GaidenLinkRendererSpec extends Specification {
         setup:
         def context = new PageBuildContext(documentSource: new DocumentSource())
         def pageSource = new PageSource(path: pageSourcePath)
-        def linkRenderer = new GaidenLinkRenderer(context, pageSource)
+        def linkRenderer = new GaidenLinkRenderer(context, pageSource, pagesDirectory)
+        createFile(pageSourcePath)

         and:
         def printStream = Mock(PrintStream)
@@ -89,7 +96,7 @@ class GaidenLinkRendererSpec extends Specification {
         rendering.text == "Test Text"

         and:
-        1 * printStream.println("WARNING: '$path' in $pageSourcePath refers to non-existent page")
+        (1..SourceCollector.PAGE_SOURCE_EXTENSIONS.size()) * printStream.println("WARNING: '$path' in $pageSourcePath refers to non-existent page")

         where:
         pageSourcePath | path
@@ -113,7 +120,7 @@ class GaidenLinkRendererSpec extends Specification {
         setup:
         def context = new PageBuildContext(documentSource: new DocumentSource())
         def pageSource = new PageSource(path: "test.md")
-        def linkRenderer = new GaidenLinkRenderer(context, pageSource)
+        def linkRenderer = new GaidenLinkRenderer(context, pageSource, pagesDirectory)

         and:
         def printStream = Mock(PrintStream)
@@ -150,7 +157,7 @@ class GaidenLinkRendererSpec extends Specification {
         setup:
         def context = new PageBuildContext(documentSource: new DocumentSource())
         def pageSource = new PageSource(path: "test.md")
-        def linkRenderer = new GaidenLinkRenderer(context, pageSource)
+        def linkRenderer = new GaidenLinkRenderer(context, pageSource, pagesDirectory)

         and:
         def printStream = Mock(PrintStream)
@@ -180,4 +187,15 @@ class GaidenLinkRendererSpec extends Specification {
         ]
     }

+    private void createFiles(String[] paths) {
+        paths.each { createFile(it) }
+    }
+
+    private void createFile(String path) {
+        def file = new File(pagesDirectory, path)
+        if (!file.parentFile.exists()) {
+            assert file.parentFile.mkdirs()
+        }
+        assert file.createNewFile()
+    }
 }