]> gitweb.fperrin.net Git - DictionaryPC.git/blob - src/com/hughes/android/dictionary/parser/WikiTokenizer.java
go
[DictionaryPC.git] / src / com / hughes / android / dictionary / parser / WikiTokenizer.java
1 package com.hughes.android.dictionary.parser;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.regex.Matcher;
6 import java.util.regex.Pattern;
7
8 public final class WikiTokenizer {
9
10   //private static final Pattern wikiTokenEvent = Pattern.compile("($)", Pattern.MULTILINE);
11   private static final Pattern wikiTokenEvent = Pattern.compile("(\\{\\{|\\}\\}|\\[\\[|\\]\\]|<!--|''|$)", Pattern.MULTILINE);
12   private static final String listChars = "*#:;";
13   
14     
15     final String wikiText;
16     final Matcher matcher;
17
18     boolean justReturnedNewline = true;
19     int end = 0;
20     int start = -1;
21
22     public String header;
23     public int headerDepth;
24     
25     final List<String> tokenStack = new ArrayList<String>();
26     
27   public WikiTokenizer(final String wikiText) {
28     this.wikiText = wikiText;
29     this.matcher = wikiTokenEvent.matcher(wikiText);
30   }
31     
32   private void clear() {
33     header = null;
34     headerDepth = 0;
35     tokenStack.clear();
36   }
37
38
39   public WikiTokenizer nextToken() {
40     this.clear();
41     
42     start = end;
43     
44     final int len = wikiText.length();
45     if (start >= len) {
46       return null;
47     }
48     
49     // Eat a newline if we're looking at one:
50     final boolean atNewline = wikiText.charAt(end) == '\n';
51     if (atNewline) {
52       justReturnedNewline = true;
53       ++end;
54       return this;
55     }
56     
57     if (justReturnedNewline) {
58       final char firstChar = wikiText.charAt(end);
59       if (firstChar == '=') {
60         final int headerStart = end;
61         while (++end < len && wikiText.charAt(end) == '=') {}
62         final int headerTitleStart = end;
63         while (++end < len && wikiText.charAt(end) != '=' && wikiText.charAt(end) != '\n') {}
64         final int headerTitleEnd = end;
65         while (++end < len && wikiText.charAt(end) == '=') {}
66         final int headerEnd = end;
67         
68         return this;
69       }
70       if (listChars.indexOf(firstChar) != -1) {
71         while (++end < len && listChars.indexOf(wikiText.charAt(end)) != -1) {}
72         end = escapedFind(start, "\n");
73         return this;
74       }
75     }
76     justReturnedNewline = false;
77
78     if (wikiText.startsWith("'''", start)) {
79       end = start + 3;
80       return this;
81     }
82     
83     if (wikiText.startsWith("''", start)) {
84       end = start + 2;
85       return this;
86     }
87
88     if (wikiText.startsWith("[[", start)) {
89       end = escapedFind(start + 2, "]]");
90       return this;
91     }
92
93     if (wikiText.startsWith("{{", start)) {
94       end = escapedFind(start + 2, "}}");
95       return this;
96     }
97
98     if (wikiText.startsWith("<pre>", start)) {
99       end = safeIndexOf(wikiText, start, "</pre>", "\n");
100       return this;
101     }
102
103     if (wikiText.startsWith("<math>", start)) {
104       end = safeIndexOf(wikiText, start, "</math>", "\n");
105       return this;
106     }
107
108     if (wikiText.startsWith("<!--", start)) {
109       end = safeIndexOf(wikiText, start, "-->", "\n");
110       return this;
111     }
112
113     if (wikiText.startsWith("}}", start) || wikiText.startsWith("]]", start)) {
114       System.err.println("Close without open!");
115       end += 2;
116       return this;
117     }
118
119     
120     if (this.matcher.find(start)) {
121       end = this.matcher.start(1);
122       if (end == start) {
123         System.err.println(this.matcher.group());
124         assert false;
125       }
126       return this;
127     }
128     
129     end = wikiText.length();
130     return this;
131     
132   }
133   
134   public String token() {
135     return wikiText.substring(start, end);
136   }
137   
138   private int escapedFind(final int start, final String toFind) {
139     assert tokenStack.isEmpty();
140     
141     int end = start;
142     while (end < wikiText.length()) {
143       if (matcher.find(end)) {
144         final String matchText = matcher.group();
145         final int matchStart = matcher.start();
146         
147         if (matchText.length() == 0) {
148           assert matchStart == wikiText.length() || wikiText.charAt(matchStart) == '\n';
149           if (tokenStack.isEmpty() && toFind.equals("\n")) {
150             return matchStart;
151           }
152           ++end;
153         } else if (tokenStack.isEmpty() && matchText.equals(toFind)) {
154           // The normal return....
155           return matcher.end();
156         } else if (matchText.equals("[[") || matchText.equals("{{")) {
157           tokenStack.add(matchText);
158         } else if (matchText.equals("]]") || matchText.equals("}}")) {
159           if (tokenStack.size() > 0) {
160             final String removed = tokenStack.remove(tokenStack.size() - 1);
161             if (removed.equals("{{") && !matcher.group().equals("}}")) {
162               System.err.println("Unmatched {{ error: " + wikiText.substring(start));
163               return safeIndexOf(wikiText, start, "\n", "\n");
164             } else if (removed.equals("[[") && !matcher.group().equals("]]")) {
165               System.err.println("Unmatched [[ error: " + wikiText.substring(start));
166               return safeIndexOf(wikiText, start, "\n", "\n");
167             }
168           } else {
169             System.err.println("Pop too many error: " + wikiText.substring(start).replaceAll("\n", "\\n"));
170             // If we were looking for a newline
171             return safeIndexOf(wikiText, start, "\n", "\n");
172           }
173         } else if (matchText.equals("<!--")) {
174           end = wikiText.indexOf("-->");
175           if (end == -1) {
176             System.err.println("Unmatched <!-- error: " + wikiText.substring(start));
177           }
178         } else {
179           assert false : "Match text='" + matchText + "'";
180           throw new IllegalStateException();
181         }
182       } else {
183         // Hmmm, we didn't find the closing symbol we were looking for...
184         System.err.println("Couldn't find: " + toFind + ", "+ wikiText.substring(start));
185         return safeIndexOf(wikiText, start, "\n", "\n");
186       }
187       
188       // Inside the while loop.
189       end = Math.max(end, matcher.end());
190     }
191     return end;
192   }
193
194   static int safeIndexOf(final String s, final int start, final String target, final String backup) {
195     int close = s.indexOf(target, start);
196     if (close != -1) {
197       return close + target.length();
198     }
199     close = s.indexOf(backup, start);
200     if (close != -1) {
201       return close + backup.length();
202     }
203     return s.length();
204   }
205
206 }