r9969 - helma/helma/trunk/src/helma/util

[email protected] Mon, 28 Sep 2009 14:21:17 +0200 (CEST)
Newsgroups gmane.comp.java.helma.cvs
Message-ID <20090928122117.170313D0E2@mia>
Author: hannes
Date: 2009-09-28 14:21:16 +0200 (Mon, 28 Sep 2009)
New Revision: 9969

Modified:
   helma/helma/trunk/src/helma/util/MarkdownProcessor.java
Log:
Fix some markdown bugs.

Details at http://dev.helma.org/trac/helma/changeset/9969

Modified: helma/helma/trunk/src/helma/util/MarkdownProcessor.java
===================================================================
--- helma/helma/trunk/src/helma/util/MarkdownProcessor.java	2009-09-27 18:59:38 UTC (rev 9968)
+++ helma/helma/trunk/src/helma/util/MarkdownProcessor.java	2009-09-28 12:21:16 UTC (rev 9969)
@@ -60,27 +60,30 @@
     public MarkdownProcessor() {}
 
     public MarkdownProcessor(String text) {
-        length = text.length();
-        chars = new char[length + 2];
-        text.getChars(0, length, chars, 0);
-        chars[length] = chars[length + 1] = '\n';
+        init(text);
     }
 
     public MarkdownProcessor(File file) throws IOException {
         length = (int) file.length();
         chars = new char[length + 2];
         FileReader reader = new FileReader(file);
-        if (reader.read(chars) != length) {
-            throw new IOException("Couldn't read file");
+        int read = 0;
+        try {
+            while (read < length) {
+                int r = reader.read(chars, read, length - read);
+                if (r == -1)
+                    break;
+                read += r;
+            }
+        } finally {
+            reader.close();
         }
+        length = read;
         chars[length] = chars[length + 1] = '\n';
     }
 
     public synchronized String process(String text) {
-        length = text.length();
-        chars = new char[length + 2];
-        text.getChars(0, length, chars, 0);
-        chars[length] = chars[length + 1] = '\n';
+        init(text);
         return process();
     }
 
@@ -95,6 +98,18 @@
         return result;
     }
 
+    public synchronized String processLinkText(String text) {
+        init(text);
+        return processLinkText();
+    }
+
+    private void init(String text) {
+        length = text.length();
+        chars = new char[length + 2];
+        text.getChars(0, length, chars, 0);
+        chars[length] = chars[length + 1] = '\n';
+    }
+
    /**
     * Retrieve a link defined in the source text. If the link is not found, we call
     * lookupLink(String) to retrieve it from an external source.
@@ -232,7 +247,7 @@
                             // no valid link title - escape
                             state = NONE;
                         }
-                    } else {
+                    } else if (!isSpace(c) || buffer.length() > 0) {
                         buffer.append(c);
                     }
             }
@@ -436,7 +451,7 @@
                 char lastChar = 0;
                 int count = 0;
                 boolean escape = false;
-                for (int k = j; k < length; k++) {
+                for (int k = j; k <= length; k++) {
                     if (chars[k] == '\n' && lastChar == '\n') {
                         break;
                     }
@@ -449,6 +464,8 @@
                             escape = true;
                         } else if (chars[k] == '`') {
                             k = skipCodeSpan(k);
+                        } else if (chars[k] == '[') {
+                            k = skipLink(k);
                         } else if (chars[k] == c) {
                             count += 1;
                         } else {
@@ -575,6 +592,83 @@
         return j;
     }
 
+    private int skipLink(int start) {
+        boolean escape = false;
+        int nesting = 0;
+        int j = start + 1;
+        char c;
+        while (j < length && (escape || chars[j] != ']' || nesting != 0)) {
+            c = chars[j];
+            if (c == '\n' && chars[j - 1] == '\n') {
+                return start;
+            }
+
+            if (escape) {
+                escape = false;
+            } else {
+                escape = c == '\\';
+                if (!escape) {
+                    if (c == '[') {
+                        nesting += 1;
+                    } else if (c == ']') {
+                        nesting -= 1;
+                    }
+                }
+            }
+            j += 1;
+        }
+        int k = j;
+        j += 1;
+        boolean extraSpace = false;
+        if (j < length && Character.isWhitespace(chars[j])) {
+            j += 1;
+            extraSpace = true;
+        }
+        c = chars[j++];
+        if (c == '[') {
+            while (j < length && chars[j] != ']') {
+                if (chars[j] == '\n') {
+                    return start;
+                }
+                j += 1;
+            }
+        } else if (c == '(' && !extraSpace) {
+            while (j < length && chars[j] != ')' && !isSpace(chars[j])) {
+                if (chars[j] == '\n') {
+                    return start;
+                }
+                j += 1;
+            }
+            if (j < length && chars[j] != ')') {
+                while (j < length && chars[j] != ')' && Character.isWhitespace(chars[j])) {
+                    j += 1;
+                }
+                if (chars[j] == '"') {
+                    int quoteStart = j = j + 1;
+                    int len = -1;
+                    while (j < length && chars[j] != '\n') {
+                        if (chars[j] == '"') {
+                            len = j - quoteStart;
+                        } else if (len > -1) {
+                            if (chars[j] == ')') {
+                                break;
+                            } else if (!isSpace(chars[j])) {
+                                len = -1;
+                            }
+                        }
+                        j += 1;
+                    }
+                }
+                if (chars[j] != ')') {
+                    return start;
+                }
+            }
+        } else {
+            j = k;
+        }
+        return j;
+    }
+
     private boolean checkLink(char c) {
         return checkLinkInternal(c, i + 1, false);
     }
@@ -609,7 +703,7 @@
                     } else if (c == ']') {
                         nesting -= 1;
                     }
-                    if (c == '*' || c == '_' || c == '`') {
+                    if (c == '*' || c == '_' || c == '`' || c == '[') {
                         needsEncoding = true;
                     }
                     boolean s = Character.isWhitespace(chars[j]);
@@ -627,10 +721,10 @@
         String linkId;
         int k = j;
         j += 1;
-        // this is weird, bug we follow the official markup implementation here:
+        // this is weird, but we follow the official markup implementation here:
         // only accept space between link text and link target for [][], not for []()
         boolean extraSpace = false;
-        if (j < length && isSpace(chars[j])) {
+        if (j < length && Character.isWhitespace(chars[j])) {
             j += 1;
             extraSpace = true;
         }
@@ -715,19 +809,13 @@
             }
             buffer.append(">");
             if (needsEncoding) {
-                b.append(escapeHtml(text)).append("</a>");
+                MarkdownProcessor wrapped = new MarkdownProcessor();
+                buffer.append(wrapped.processLinkText(text)).append("</a>");
             } else {
                 buffer.append(escapeHtml(text)).append("</a>");
             }
         }
-        if (b.length() > 0) {
-            System.arraycopy(chars, j + 1, chars, i + b.length(), length - j - 1);
-            b.getChars(0, b.length(), chars, i);
-            length = i + (length - j - 1) + b.length();
-        } else {
-            System.arraycopy(chars, j + 1, chars, i, length - j - 1);
-            length -= 1 + j - i;
-        }
+        i = j + 1;
         return true;
     }
 
@@ -1021,6 +1109,51 @@
         return b.toString();
     }
 
+    private synchronized String processLinkText() {
+        buffer = new StringBuilder((int) (length * 1.2));
+        line = 1;
+        boolean escape = false;
+
+        for (i = 0; i < length; ) {
+            char c = chars[i];
+
+            if (escape) {
+                buffer.append(c);
+                escape = false;
+                i += 1;
+                continue;
+            } else if (c == '\\') {
+                escape = true;
+                i += 1;
+                continue;
+            }
+            switch (c) {
+                case '*':
+                case '_':
+                    if (checkEmphasis(c)) {
+                        continue;
+                    }
+                    break;
+
+                case '`':
+                    if (checkCodeSpan(c)) {
+                        continue;
+                    }
+                    break;
+
+                case '!':
+                    if (checkImage()) {
+                        continue;
+                    }
+                    break;
+            }
+
+            buffer.append(c);
+            i += 1;
+        }
+        return buffer.toString().trim();
+    }
+
     boolean isLinkQuote(char c) {
         return c == '"' || c == '\'' || c == '(';
     }