OSDN Git Service

f2b961fbcb2cc028d382f999291ab7f3f96cecaf
[neighbornote/NeighborNote.git] / src / com / swabunga / spell / event / DocumentWordTokenizer.java
1 /*\r
2 Jazzy - a Java library for Spell Checking\r
3 Copyright (C) 2001 Mindaugas Idzelis\r
4 Full text of license can be found in LICENSE.txt\r
5 \r
6 This library is free software; you can redistribute it and/or\r
7 modify it under the terms of the GNU Lesser General Public\r
8 License as published by the Free Software Foundation; either\r
9 version 2.1 of the License, or (at your option) any later version.\r
10 \r
11 This library is distributed in the hope that it will be useful,\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
14 Lesser General Public License for more details.\r
15 \r
16 You should have received a copy of the GNU Lesser General Public\r
17 License along with this library; if not, write to the Free Software\r
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
19 */\r
20 package com.swabunga.spell.event;\r
21 \r
22 \r
23 import java.text.BreakIterator;\r
24 \r
25 import javax.swing.text.AttributeSet;\r
26 import javax.swing.text.BadLocationException;\r
27 import javax.swing.text.Document;\r
28 import javax.swing.text.Segment;\r
29 import javax.swing.text.StyledDocument;\r
30 \r
31 \r
32 /** This class tokenizes a swing document model. It also allows for the\r
33  *  document model to be changed when corrections occur.\r
34  *\r
35  * @author Jason Height (jheight@chariot.net.au)\r
36  */\r
37 public class DocumentWordTokenizer implements WordTokenizer {\r
38   /** Holds the start character position of the current word*/\r
39   private int currentWordPos = 0;\r
40   /** Holds the end character position of the current word*/\r
41   private int currentWordEnd = 0;\r
42   /** Holds the start character position of the next word*/\r
43   private int nextWordPos = -1;\r
44   /** The actual text that is being tokenized*/\r
45   private final Document document;\r
46   /** The character iterator over the document*/\r
47   private final Segment text;\r
48   /** The cumulative word count that have been processed*/\r
49   private int wordCount = 0;\r
50   /** Flag indicating if there are any more tokens (words) left*/\r
51   private boolean moreTokens = true;\r
52   /** Is this a special case where the currentWordStart, currntWordEnd and\r
53    *  nextWordPos have already been calculated. (see nextWord)\r
54    */\r
55   private boolean first = true;\r
56   private final BreakIterator sentenceIterator;\r
57   private boolean startsSentence = true;\r
58 \r
59   /**\r
60    * Creates a new DocumentWordTokenizer to work on a document\r
61    * @param document The document to spell check\r
62    */\r
63   public DocumentWordTokenizer(Document document) {\r
64     this.document = document;\r
65     //Create a text segment over the entire document\r
66     text = new Segment();\r
67     sentenceIterator = BreakIterator.getSentenceInstance();\r
68     try {\r
69       document.getText(0, document.getLength(), text);\r
70       sentenceIterator.setText(text);\r
71       currentWordPos = getNextWordStart(text, 0);\r
72       //If the current word pos is -1 then the string was all white space\r
73       if (currentWordPos != -1) {\r
74         currentWordEnd = getNextWordEnd(text, currentWordPos);\r
75         nextWordPos = getNextWordStart(text, currentWordEnd);\r
76       } else {\r
77         moreTokens = false;\r
78       }\r
79     } catch (BadLocationException ex) {\r
80       moreTokens = false;\r
81     }\r
82   }\r
83 \r
84   /** This helper method will return the start character of the next\r
85    * word in the buffer from the start position\r
86    */\r
87   private static int getNextWordStart(Segment text, int startPos) {\r
88     if (startPos <= text.getEndIndex())\r
89       for (char ch = text.setIndex(startPos); ch != Segment.DONE; ch = text.next()) {\r
90         if (Character.isLetterOrDigit(ch)) {\r
91           return text.getIndex();\r
92         }\r
93       }\r
94     return -1;\r
95   }\r
96 \r
97   /** This helper method will return the end of the next word in the buffer.\r
98    *\r
99    */\r
100   private static int getNextWordEnd(Segment text, int startPos) {\r
101     for (char ch = text.setIndex(startPos); ch != Segment.DONE; ch = text.next()) {\r
102       if (!Character.isLetterOrDigit(ch)) {\r
103         if (ch == '-' || ch == '\'') { // handle ' and - inside words\r
104           char ch2 = text.next();\r
105           text.previous();\r
106           if (ch2 != Segment.DONE && Character.isLetterOrDigit(ch2))\r
107             continue;\r
108         }\r
109         return text.getIndex();\r
110       }\r
111     }\r
112     return text.getEndIndex();\r
113   }\r
114 \r
115   /**\r
116    * Indicates if there are more words left\r
117    * @return true if more words can be found in the text.\r
118    */\r
119   public boolean hasMoreWords() {\r
120     return moreTokens;\r
121   }\r
122   \r
123   /**\r
124    * Sets the current word position at the start of the word containing\r
125    * the char at position pos. This way a call to nextWord() will return\r
126    * this word.\r
127    * \r
128    * @param pos position in the word we want to set as current.\r
129    */\r
130   public void posStartFullWordFrom(int pos){\r
131         currentWordPos=text.getBeginIndex();\r
132         if(pos>text.getEndIndex())\r
133                 pos=text.getEndIndex();\r
134         for (char ch = text.setIndex(pos); ch != Segment.DONE; ch = text.previous()) {\r
135                 if (!Character.isLetterOrDigit(ch)) {\r
136                         if (ch == '-' || ch == '\'') { // handle ' and - inside words\r
137                                 char ch2 = text.previous();\r
138                                 text.next();\r
139                                 if (ch2 != Segment.DONE && Character.isLetterOrDigit(ch2))\r
140                                         continue;\r
141                         }\r
142                         currentWordPos=text.getIndex()+1;\r
143                         break;\r
144                 }\r
145         }\r
146         //System.out.println("CurPos:"+currentWordPos);\r
147         if(currentWordPos==0)\r
148                 first=true;\r
149         moreTokens=true;\r
150         currentWordEnd = getNextWordEnd(text, currentWordPos);\r
151         nextWordPos = getNextWordStart(text, currentWordEnd + 1);\r
152   }\r
153 \r
154   /**\r
155    * Returns the number of word tokens that have been processed thus far\r
156    * @return the number of words found so far.\r
157    */\r
158   public int getCurrentWordPosition() {\r
159     return currentWordPos;\r
160   }\r
161 \r
162   /**\r
163    * Returns an index representing the end location of the current word in the text.\r
164    * @return index of the end of the current word in the text.\r
165    */\r
166   public int getCurrentWordEnd() {\r
167     return currentWordEnd;\r
168   }\r
169 \r
170   /**\r
171    * This returns the next word in the iteration. Note that any implementation should return\r
172    * the current word, and then replace the current word with the next word found in the\r
173    * input text (if one exists).\r
174    * @return the next word in the iteration.\r
175    */\r
176   public String nextWord() {\r
177     if (!first) {\r
178       currentWordPos = nextWordPos;\r
179       currentWordEnd = getNextWordEnd(text, currentWordPos);\r
180       nextWordPos = getNextWordStart(text, currentWordEnd + 1);\r
181     }\r
182     int current = sentenceIterator.current();\r
183     if (current == currentWordPos)\r
184       startsSentence = true;\r
185     else {\r
186       startsSentence = false;\r
187       if (currentWordEnd > current)\r
188         sentenceIterator.next();\r
189     }\r
190     //The nextWordPos has already been populated\r
191     String word = null;\r
192     try {\r
193       word = document.getText(currentWordPos, currentWordEnd - currentWordPos);\r
194     } catch (BadLocationException ex) {\r
195       moreTokens = false;\r
196     }\r
197     wordCount++;\r
198     first = false;\r
199     if (nextWordPos == -1)\r
200       moreTokens = false;\r
201     return word;\r
202   }\r
203 \r
204   /**\r
205    * Returns the number of word tokens that have been processed thus far\r
206    * @return the number of words found so far.\r
207    */\r
208   public int getCurrentWordCount() {\r
209     return wordCount;\r
210   }\r
211 \r
212   /** Replaces the current word token\r
213    * @param newWord The new word to replace the misspelt one\r
214    */\r
215   public void replaceWord(String newWord) {\r
216     @SuppressWarnings("unused")\r
217         AttributeSet attr=null;\r
218     if (currentWordPos != -1) {\r
219       try {\r
220         if(document instanceof StyledDocument)\r
221             attr=((StyledDocument)document).getCharacterElement(currentWordPos).getAttributes();\r
222         document.remove(currentWordPos, currentWordEnd - currentWordPos);\r
223         document.insertString(currentWordPos, newWord, null);\r
224         //Need to reset the segment\r
225         document.getText(0, document.getLength(), text);\r
226       } catch (BadLocationException ex) {\r
227         throw new RuntimeException(ex.getMessage());\r
228       }\r
229       //Position after the newly replaced word(s)\r
230       first = true;\r
231       currentWordPos = getNextWordStart(text, currentWordPos + newWord.length());\r
232       if (currentWordPos != -1) {\r
233         currentWordEnd = getNextWordEnd(text, currentWordPos);\r
234         nextWordPos = getNextWordStart(text, currentWordEnd);\r
235         sentenceIterator.setText(text);\r
236         sentenceIterator.following(currentWordPos);\r
237       } else\r
238         moreTokens = false;\r
239     }\r
240   }\r
241 \r
242   /** Returns the current text that is being tokenized (includes any changes\r
243    *  that have been made)\r
244    * @return The text, including changes.\r
245    */\r
246   public String getContext() {\r
247     return text.toString();\r
248   }\r
249 \r
250   /** Indicates if the current word is at the start of a sentence\r
251    * @return true if the current word is at the start of a sentence\r
252    */\r
253   public boolean isNewSentence() {\r
254     // BreakIterator doesn't work when the first word in a sentence is not capitalised,\r
255     // but we need to check for capitalisation\r
256     if (startsSentence || currentWordPos < 2)\r
257       return(true);\r
258     \r
259     String textBefore = null;\r
260     try {\r
261       textBefore = document.getText(currentWordPos-2, 2);\r
262     } catch (BadLocationException ex) {\r
263       return(false);\r
264     }\r
265     return(textBefore != null && ".".equals(textBefore.trim()));\r
266   }\r
267 }