improved PHP parser
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / text / spelling / SpellReconcileStrategy.java
1 /*****************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *****************************************************************************/
11
12 package net.sourceforge.phpdt.internal.ui.text.spelling;
13
14 import java.text.MessageFormat;
15 import java.util.Locale;
16
17 import net.sourceforge.phpdt.core.IProblemRequestor;
18 import net.sourceforge.phpdt.core.compiler.IProblem;
19 import net.sourceforge.phpdt.internal.ui.PHPUIMessages;
20 import net.sourceforge.phpdt.internal.ui.text.IPHPPartitions;
21 import net.sourceforge.phpdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
22 import net.sourceforge.phpdt.internal.ui.text.spelling.engine.ISpellCheckPreferenceKeys;
23 import net.sourceforge.phpdt.internal.ui.text.spelling.engine.ISpellChecker;
24 import net.sourceforge.phpdt.internal.ui.text.spelling.engine.ISpellEvent;
25 import net.sourceforge.phpdt.internal.ui.text.spelling.engine.ISpellEventListener;
26 import net.sourceforge.phpeclipse.phpeditor.php.PHPDocumentPartitioner;
27
28 import org.eclipse.core.runtime.IProgressMonitor;
29 import org.eclipse.jface.preference.IPreferenceStore;
30 import org.eclipse.jface.text.BadLocationException;
31 import org.eclipse.jface.text.IDocument;
32 import org.eclipse.jface.text.IRegion;
33 import org.eclipse.jface.text.ITypedRegion;
34 import org.eclipse.jface.text.TextUtilities;
35 import org.eclipse.jface.text.reconciler.DirtyRegion;
36 import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
37 import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;
38 import org.eclipse.jface.text.source.IAnnotationModel;
39 import org.eclipse.ui.texteditor.ITextEditor;
40
41 /**
42  * Reconcile strategy to spell-check comments.
43  * 
44  * @since 3.0
45  */
46 public class SpellReconcileStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension, ISpellEventListener {
47
48         /**
49          * Spelling problem to be accepted by problem requestors.
50          */
51         public class SpellProblem implements IProblem {
52
53                 /** The id of the problem */
54                 public static final int Spelling= 0x80000000;
55
56                 /** The end offset of the problem */
57                 private int fEnd= 0;
58
59                 /** The line number of the problem */
60                 private int fLine= 1;
61
62                 /** Was the word found in the dictionary? */
63                 private boolean fMatch;
64
65                 /** Does the word start a new sentence? */
66                 private boolean fSentence= false;
67
68                 /** The start offset of the problem */
69                 private int fStart= 0;
70
71                 /** The word which caused the problem */
72                 private final String fWord;
73
74                 /**
75                  * Creates a new spelling problem
76                  * 
77                  * @param word
78                  *                   The word which caused the problem
79                  */
80                 protected SpellProblem(final String word) {
81                         fWord= word;
82                 }
83
84                 /*
85                  * @see net.sourceforge.phpdt.core.compiler.IProblem#getArguments()
86                  */
87                 public String[] getArguments() {
88
89                         String prefix= ""; //$NON-NLS-1$
90                         String postfix= ""; //$NON-NLS-1$
91
92                         try {
93
94                                 final IRegion line= fDocument.getLineInformationOfOffset(fStart);
95
96                                 prefix= fDocument.get(line.getOffset(), fStart - line.getOffset());
97                                 postfix= fDocument.get(fEnd + 1, line.getOffset() + line.getLength() - fEnd);
98
99                         } catch (BadLocationException exception) {
100                                 // Do nothing
101                         }
102                         return new String[] { fWord, prefix, postfix, fSentence ? Boolean.toString(true) : Boolean.toString(false), fMatch ? Boolean.toString(true) : Boolean.toString(false)};
103                 }
104
105                 /*
106                  * @see net.sourceforge.phpdt.core.compiler.IProblem#getID()
107                  */
108                 public int getID() {
109                         return Spelling;
110                 }
111
112                 /*
113                  * @see net.sourceforge.phpdt.core.compiler.IProblem#getMessage()
114                  */
115                 public String getMessage() {
116
117                         if (fSentence && fMatch)
118                                 return MessageFormat.format(PHPUIMessages.getString("Spelling.error.case.label"), new String[] { fWord }); //$NON-NLS-1$
119
120                         return MessageFormat.format(PHPUIMessages.getString("Spelling.error.label"), new String[] { fWord }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
121                 }
122
123                 /*
124                  * @see net.sourceforge.phpdt.core.compiler.IProblem#getOriginatingFileName()
125                  */
126                 public char[] getOriginatingFileName() {
127                         return fEditor.getEditorInput().getName().toCharArray();
128                 }
129
130                 /*
131                  * @see net.sourceforge.phpdt.core.compiler.IProblem#getSourceEnd()
132                  */
133                 public final int getSourceEnd() {
134                         return fEnd;
135                 }
136
137                 /*
138                  * @see net.sourceforge.phpdt.core.compiler.IProblem#getSourceLineNumber()
139                  */
140                 public final int getSourceLineNumber() {
141                         return fLine;
142                 }
143
144                 /*
145                  * @see net.sourceforge.phpdt.core.compiler.IProblem#getSourceStart()
146                  */
147                 public final int getSourceStart() {
148                         return fStart;
149                 }
150
151                 /**
152                  * Was the problem word found in the dictionary?
153                  * 
154                  * @return <code>true</code> iff the word was found, <code>false</code>
155                  *               otherwise
156                  */
157                 public final boolean isDictionaryMatch() {
158                         return fMatch;
159                 }
160
161                 /*
162                  * @see net.sourceforge.phpdt.core.compiler.IProblem#isError()
163                  */
164                 public final boolean isError() {
165                         return false;
166                 }
167
168                 /**
169                  * Does the problem word start a new sentence?
170                  * 
171                  * @return <code>true</code> iff it starts a new sentence, <code>false</code>
172                  *               otherwise
173                  */
174                 public final boolean isSentenceStart() {
175                         return fSentence;
176                 }
177
178                 /*
179                  * @see net.sourceforge.phpdt.core.compiler.IProblem#isWarning()
180                  */
181                 public final boolean isWarning() {
182                         return true;
183                 }
184
185                 /**
186                  * Sets whether the problem word was found in the dictionary.
187                  * 
188                  * @param match
189                  *                   <code>true</code> iff the word was found, <code>false</code>
190                  *                   otherwise
191                  */
192                 public final void setDictionaryMatch(final boolean match) {
193                         fMatch= match;
194                 }
195
196                 /**
197                  * Sets whether the problem word starts a new sentence.
198                  * 
199                  * @param sentence
200                  *                   <code>true</code> iff the word starts a new sentence,
201                  *                   <code>false</code> otherwise.
202                  */
203                 public final void setSentenceStart(final boolean sentence) {
204                         fSentence= sentence;
205                 }
206
207                 /*
208                  * @see net.sourceforge.phpdt.core.compiler.IProblem#setSourceEnd(int)
209                  */
210                 public final void setSourceEnd(final int end) {
211                         fEnd= end;
212                 }
213
214                 /*
215                  * @see net.sourceforge.phpdt.core.compiler.IProblem#setSourceLineNumber(int)
216                  */
217                 public final void setSourceLineNumber(final int line) {
218                         fLine= line;
219                 }
220
221                 /*
222                  * @see net.sourceforge.phpdt.core.compiler.IProblem#setSourceStart(int)
223                  */
224                 public final void setSourceStart(final int start) {
225                         fStart= start;
226                 }
227         }
228
229         /** The document to operate on */ 
230         private IDocument fDocument= null;
231
232         /** The text editor to operate on */
233         private final ITextEditor fEditor;
234
235         /** The current locale */
236         private Locale fLocale= SpellCheckEngine.getDefaultLocale();
237
238         /** The partitioning of the document */
239         private final String fPartitioning;
240
241         /** The preference store to use */
242         private final IPreferenceStore fPreferences;
243
244         /** The problem requestor */
245         private IProblemRequestor fRequestor;
246
247         /**
248          * Creates a new comment reconcile strategy.
249          * 
250          * @param editor
251          *                   The text editor to operate on
252          * @param partitioning
253          *                   The partitioning of the document
254          * @param store
255          *                   The preference store to get the preferences from
256          */
257         public SpellReconcileStrategy(final ITextEditor editor, final String partitioning, final IPreferenceStore store) {
258                 fEditor= editor;
259                 fPartitioning= partitioning;
260                 fPreferences= store;
261
262                 updateProblemRequestor(); 
263         }
264
265         /**
266          * Returns the current locale of the spell checking preferences.
267          * 
268          * @return The current locale of the spell checking preferences
269          */
270         public Locale getLocale() {
271
272                 final String locale= fPreferences.getString(ISpellCheckPreferenceKeys.SPELLING_LOCALE);
273                 if (locale.equals(fLocale.toString()))
274                         return fLocale;
275
276                 if (locale.length() >= 5)
277                         return new Locale(locale.substring(0, 2), locale.substring(3, 5));
278
279                 return SpellCheckEngine.getDefaultLocale();
280         }
281
282         /*
283          * @see net.sourceforge.phpdt.internal.ui.text.spelling.engine.ISpellEventListener#handle(net.sourceforge.phpdt.internal.ui.text.spelling.engine.ISpellEvent)
284          */
285         public void handle(final ISpellEvent event) {
286                 
287                 if (fRequestor != null) {
288                         
289                         final SpellProblem problem= new SpellProblem(event.getWord());
290                         
291                         problem.setSourceStart(event.getBegin());
292                         problem.setSourceEnd(event.getEnd());
293                         problem.setSentenceStart(event.isStart());
294                         problem.setDictionaryMatch(event.isMatch());
295                         
296                         try {
297                                 problem.setSourceLineNumber(fDocument.getLineOfOffset(event.getBegin()) + 1);
298                         } catch (BadLocationException x) {
299                                 // Do nothing
300                         }
301                         
302                         fRequestor.acceptProblem(problem);
303                 }
304         }
305
306         /*
307          * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#initialReconcile()
308          */
309         public void initialReconcile() {
310         }
311
312         /*
313          * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion,org.eclipse.jface.text.IRegion)
314          */
315         public void reconcile(final DirtyRegion dirtyRegion, final IRegion subRegion) {
316                 reconcile(subRegion);
317         }
318
319         /*
320          * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion)
321          */
322         public void reconcile(final IRegion region) {
323
324                 if (fPreferences.getBoolean(ISpellCheckPreferenceKeys.SPELLING_CHECK_SPELLING) && fRequestor != null) {
325
326                         try {
327
328                                 fRequestor.beginReporting();
329
330                                 ITypedRegion partition= null;
331                                 final ITypedRegion[] partitions= TextUtilities.computePartitioning(fDocument, fPartitioning, 0, fDocument.getLength(), false);
332
333                                 final Locale locale= getLocale();
334                                 final ISpellCheckEngine engine= SpellCheckEngine.getInstance();
335
336                                 final ISpellChecker checker= engine.createSpellChecker(locale, fPreferences);
337                                 if (checker != null) {
338                                         try {
339                                                 checker.addListener(this);
340                                                 
341                                                 for (int index= 0; index < partitions.length; index++) {
342                                                         partition= partitions[index];
343                                                         if (!partition.getType().equals(IDocument.DEFAULT_CONTENT_TYPE) &&
344                                                             !partition.getType().equals(PHPDocumentPartitioner.PHP_SCRIPT_CODE))
345                                                                 checker.execute(new SpellCheckIterator(fDocument, partition, locale));
346                                                 }
347                                                 
348                                         } finally {
349                                                 checker.removeListener(this);
350                                         }
351                                 }
352                         } catch (BadLocationException exception) {
353                                 // Do nothing
354                         } finally {
355                                 fRequestor.endReporting();
356                         }
357                 }
358         }
359
360         /*
361          * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)
362          */
363         public final void setDocument(final IDocument document) {
364                 fDocument= document;
365                 
366                 updateProblemRequestor();
367         }
368
369         /*
370          * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor)
371          */
372         public final void setProgressMonitor(final IProgressMonitor monitor) {
373                 // Do nothing
374         }
375
376         /**
377          * Update the problem requestor based on the current editor
378          * 
379          * @since 3.0
380          */
381         private void updateProblemRequestor() {
382                 final IAnnotationModel model= fEditor.getDocumentProvider().getAnnotationModel(fEditor.getEditorInput());
383                 fRequestor= (model instanceof IProblemRequestor) ? (IProblemRequestor) model : null;
384         }
385 }