KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > refactoring > nls > search > NLSSearchResultRequestor


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.ui.refactoring.nls.search;
12
13 import java.io.BufferedInputStream JavaDoc;
14 import java.io.ByteArrayInputStream JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.io.InputStream JavaDoc;
17 import java.util.Enumeration JavaDoc;
18 import java.util.HashSet JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IProgressMonitor;
24
25 import org.eclipse.core.filebuffers.FileBuffers;
26 import org.eclipse.core.filebuffers.ITextFileBuffer;
27 import org.eclipse.core.filebuffers.ITextFileBufferManager;
28 import org.eclipse.core.filebuffers.LocationKind;
29
30 import org.eclipse.core.resources.IFile;
31
32 import org.eclipse.jface.text.Position;
33
34 import org.eclipse.search.ui.text.Match;
35
36 import org.eclipse.jdt.core.ICompilationUnit;
37 import org.eclipse.jdt.core.IField;
38 import org.eclipse.jdt.core.IJavaElement;
39 import org.eclipse.jdt.core.ISourceReference;
40 import org.eclipse.jdt.core.ToolFactory;
41 import org.eclipse.jdt.core.compiler.IScanner;
42 import org.eclipse.jdt.core.compiler.ITerminalSymbols;
43 import org.eclipse.jdt.core.compiler.InvalidInputException;
44 import org.eclipse.jdt.core.search.SearchMatch;
45 import org.eclipse.jdt.core.search.SearchRequestor;
46
47 import org.eclipse.jdt.internal.corext.refactoring.nls.PropertyFileDocumentModel;
48 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
49 import org.eclipse.jdt.internal.corext.util.Messages;
50
51 import org.eclipse.jdt.internal.ui.JavaPlugin;
52 import org.eclipse.jdt.internal.ui.util.StringMatcher;
53
54 class NLSSearchResultRequestor extends SearchRequestor {
55     /*
56      * Matches are added to fResult. Element (group key) is IJavaElement or FileEntry.
57      */

58
59     private static final StringMatcher fgGetClassNameMatcher= new StringMatcher("*.class.getName()*", false, false); //$NON-NLS-1$
60

61     private NLSSearchResult fResult;
62     private IFile fPropertiesFile;
63     private Properties fProperties;
64     private HashSet JavaDoc fUsedPropertyNames;
65
66     public NLSSearchResultRequestor(IFile propertiesFile, NLSSearchResult result) {
67         fPropertiesFile= propertiesFile;
68         fResult= result;
69     }
70
71     /*
72      * @see org.eclipse.jdt.core.search.SearchRequestor#beginReporting()
73      */

74     public void beginReporting() {
75         loadProperties();
76         fUsedPropertyNames= new HashSet JavaDoc(fProperties.size());
77     }
78     
79     /*
80      * @see org.eclipse.jdt.core.search.SearchRequestor#acceptSearchMatch(org.eclipse.jdt.core.search.SearchMatch)
81      */

82     public void acceptSearchMatch(SearchMatch match) throws CoreException {
83         if (match.getAccuracy() == SearchMatch.A_INACCURATE)
84             return;
85         
86         int offset= match.getOffset();
87         int length= match.getLength();
88         if (offset == -1 || length == -1)
89             return;
90         
91         if (! (match.getElement() instanceof IJavaElement))
92             return;
93         IJavaElement javaElement= (IJavaElement) match.getElement();
94         
95         // ignore matches in import declarations:
96
if (javaElement.getElementType() == IJavaElement.IMPORT_DECLARATION)
97             return;
98         if (javaElement.getElementType() == IJavaElement.CLASS_FILE)
99             return; //matches in import statements of class files
100
if (javaElement.getElementType() == IJavaElement.TYPE)
101             return; //classes extending the accessor class and workaround for bug 61286
102

103         // heuristic: ignore matches in resource bundle name field:
104
if (javaElement.getElementType() == IJavaElement.FIELD) {
105             IField field= (IField) javaElement;
106             String JavaDoc source= field.getSource();
107             if (source != null && fgGetClassNameMatcher.match(source))
108                 return;
109         }
110         
111         if (javaElement instanceof ISourceReference) {
112             String JavaDoc source= ((ISourceReference) javaElement).getSource();
113             if (source != null) {
114                 if (source.indexOf("NLS.initializeMessages") != -1) //$NON-NLS-1$
115
return;
116             }
117         }
118         
119         // found reference to NLS Wrapper - now check if the key is there:
120
Position mutableKeyPosition= new Position(offset, length);
121         //TODO: What to do if argument string not found? Currently adds a match with type name.
122
String JavaDoc key= findKey(mutableKeyPosition, javaElement);
123         if (key != null && isKeyDefined(key))
124             return;
125
126         ICompilationUnit[] allCompilationUnits= JavaModelUtil.getAllCompilationUnits(new IJavaElement[] {javaElement});
127         Object JavaDoc element= javaElement;
128         if (allCompilationUnits != null && allCompilationUnits.length == 1)
129             element= allCompilationUnits[0];
130
131         fResult.addMatch(new Match(element, mutableKeyPosition.getOffset(), mutableKeyPosition.getLength()));
132     }
133
134     public void reportUnusedPropertyNames(IProgressMonitor pm) {
135         //Don't use endReporting() for long running operation.
136
pm.beginTask("", fProperties.size()); //$NON-NLS-1$
137
boolean hasUnused= false;
138         pm.setTaskName(NLSSearchMessages.NLSSearchResultRequestor_searching);
139         String JavaDoc message= Messages.format(NLSSearchMessages.NLSSearchResultCollector_unusedKeys, getPropertiesName(fPropertiesFile));
140         FileEntry groupElement= new FileEntry(fPropertiesFile, message);
141         
142         for (Enumeration JavaDoc enumeration= fProperties.propertyNames(); enumeration.hasMoreElements();) {
143             String JavaDoc propertyName= (String JavaDoc) enumeration.nextElement();
144             if (!fUsedPropertyNames.contains(propertyName)) {
145                 addMatch(groupElement, propertyName);
146                 hasUnused= true;
147             }
148             pm.worked(1);
149         }
150         if (hasUnused)
151             fResult.addFileEntryGroup(groupElement);
152         pm.done();
153     }
154
155     private String JavaDoc getPropertiesName(IFile propertiesFile) {
156         String JavaDoc path= propertiesFile.getFullPath().removeLastSegments(1).toOSString();
157         return propertiesFile.getName() + " - " + path; //$NON-NLS-1$
158
}
159
160     private void addMatch(FileEntry groupElement, String JavaDoc propertyName) {
161         /*
162          * TODO (bug 63794): Should read in .properties file with our own reader and not
163          * with Properties.load(InputStream) . Then, we can remember start position and
164          * original version (not interpreting escape characters) for each property.
165          *
166          * The current workaround is to escape the key again before searching in the
167          * .properties file. However, this can fail if the key is escaped in a different
168          * manner than what PropertyFileDocumentModel.unwindEscapeChars(.) produces.
169          */

170         String JavaDoc escapedPropertyName= PropertyFileDocumentModel.unwindEscapeChars(propertyName);
171         int start= findPropertyNameStartPosition(escapedPropertyName);
172         int length;
173         if (start == -1) { // not found -> report at beginning
174
start= 0;
175             length= 0;
176         } else {
177             length= escapedPropertyName.length();
178         }
179         fResult.addMatch(new Match(groupElement, start, length));
180     }
181
182     /**
183      * Checks if the key is defined in the property file
184      * and adds it to the list of used properties.
185      *
186      * @param key the key
187      * @return <code>true</code> if the key is defined
188      */

189     private boolean isKeyDefined(String JavaDoc key) {
190         if (key == null)
191             return true; // Parse error - don't check key
192

193         fUsedPropertyNames.add(key);
194         if (fProperties.getProperty(key) != null) {
195             return true;
196         }
197         return false;
198     }
199     
200     public boolean hasPropertyKey(String JavaDoc key) {
201         return fProperties.containsKey(key);
202     }
203     
204     public boolean isUsedPropertyKey(String JavaDoc key) {
205         return fUsedPropertyNames.contains(key);
206     }
207     
208     /**
209      * Finds the key defined by the given match. The assumption is that
210      * the key is the first argument and it is a string i.e. quoted ("...").
211      *
212      * @param keyPositionResult reference parameter: will be filled with the position of the found key
213      * @param enclosingElement enclosing java element
214      * @return a string denoting the key, null if no key can be found
215      * @throws CoreException if a problem occurs while accessing the <code>enclosingElement</code>
216      */

217     private String JavaDoc findKey(Position keyPositionResult, IJavaElement enclosingElement) throws CoreException {
218         ICompilationUnit unit= (ICompilationUnit)enclosingElement.getAncestor(IJavaElement.COMPILATION_UNIT);
219         if (unit == null)
220             return null;
221
222         String JavaDoc source= unit.getSource();
223         if (source == null)
224             return null;
225         
226         IScanner scanner= ToolFactory.createScanner(false, false, false, false);
227         scanner.setSource(source.toCharArray());
228         scanner.resetTo(keyPositionResult.getOffset() + keyPositionResult.getLength(), source.length());
229
230         try {
231             if (scanner.getNextToken() != ITerminalSymbols.TokenNameDOT)
232                 return null;
233             
234             if (scanner.getNextToken() != ITerminalSymbols.TokenNameIdentifier)
235                 return null;
236             
237             String JavaDoc SRC= new String JavaDoc(scanner.getCurrentTokenSource());
238             int keyStart= scanner.getCurrentTokenStartPosition();
239             int keyEnd= scanner.getCurrentTokenEndPosition();
240             
241             if (scanner.getNextToken() == ITerminalSymbols.TokenNameLPAREN) {
242                 // Old school
243
// next must be key string:
244
if (scanner.getNextToken() != ITerminalSymbols.TokenNameStringLiteral)
245                     return null;
246                 // found it:
247
keyStart= scanner.getCurrentTokenStartPosition() + 1;
248                 keyEnd= scanner.getCurrentTokenEndPosition();
249                 keyPositionResult.setOffset(keyStart);
250                 keyPositionResult.setLength(keyEnd - keyStart);
251                 return source.substring(keyStart, keyEnd);
252             } else {
253                 keyPositionResult.setOffset(keyStart);
254                 keyPositionResult.setLength(keyEnd - keyStart + 1);
255                 return src;
256             }
257         } catch (InvalidInputException e) {
258             return null;
259         }
260     }
261     
262     /**
263      * Finds the start position in the property file. We assume that
264      * the key is the first match on a line.
265      *
266      * @param propertyName the property name
267      * @return the start position of the property name in the file, -1 if not found
268      */

269     private int findPropertyNameStartPosition(String JavaDoc propertyName) {
270         // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=19319
271
InputStream JavaDoc stream= null;
272         LineReader lineReader= null;
273         String JavaDoc encoding;
274         try {
275             encoding= fPropertiesFile.getCharset();
276         } catch (CoreException e1) {
277             encoding= "ISO-8859-1"; //$NON-NLS-1$
278
}
279         try {
280             stream= createInputStream(fPropertiesFile);
281             lineReader= new LineReader(stream, encoding);
282         } catch (CoreException cex) {
283             // failed to get input stream
284
JavaPlugin.log(cex);
285             return -1;
286         } catch (IOException JavaDoc e) {
287             if (stream != null) {
288                 try {
289                     stream.close();
290                 } catch (IOException JavaDoc ce) {
291                     JavaPlugin.log(ce);
292                 }
293             }
294             return -1;
295         }
296         int start= 0;
297         try {
298             StringBuffer JavaDoc buf= new StringBuffer JavaDoc(80);
299             int eols= lineReader.readLine(buf);
300             int keyLength= propertyName.length();
301             while (eols > 0) {
302                 String JavaDoc line= buf.toString();
303                 int i= line.indexOf(propertyName);
304                 int charPos= i + keyLength;
305                 char terminatorChar= 0;
306                 boolean hasNoValue= (charPos >= line.length());
307                 if (i > -1 && !hasNoValue)
308                     terminatorChar= line.charAt(charPos);
309                 if (line.trim().startsWith(propertyName) &&
310                         (hasNoValue || Character.isWhitespace(terminatorChar) || terminatorChar == '=')) {
311                     start += line.indexOf(propertyName);
312                     eols= -17; // found key
313
} else {
314                     start += line.length() + eols;
315                     buf.setLength(0);
316                     eols= lineReader.readLine(buf);
317                 }
318             }
319             if (eols != -17)
320                 start= -1; //key not found in file. See bug 63794. This can happen if the key contains escaped characters.
321
} catch (IOException JavaDoc ex) {
322             JavaPlugin.log(ex);
323             return -1;
324         } finally {
325             try {
326                 lineReader.close();
327             } catch (IOException JavaDoc ex) {
328                 JavaPlugin.log(ex);
329             }
330         }
331         return start;
332     }
333
334     private void loadProperties() {
335         Set JavaDoc duplicateKeys= new HashSet JavaDoc();
336         fProperties= new Properties(duplicateKeys);
337         InputStream JavaDoc stream;
338         try {
339             stream= new BufferedInputStream JavaDoc(createInputStream(fPropertiesFile));
340         } catch (CoreException ex) {
341             fProperties= new Properties();
342             return;
343         }
344         try {
345             fProperties.load(stream);
346         } catch (IOException JavaDoc ex) {
347             fProperties= new Properties();
348             return;
349         } finally {
350             try {
351                 stream.close();
352             } catch (IOException JavaDoc ex) {
353             }
354             reportDuplicateKeys(duplicateKeys);
355         }
356     }
357     
358     private InputStream JavaDoc createInputStream(IFile propertiesFile) throws CoreException {
359         ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
360         if (manager != null) {
361             ITextFileBuffer buffer= manager.getTextFileBuffer(propertiesFile.getFullPath(), LocationKind.IFILE);
362             if (buffer != null) {
363                 return new ByteArrayInputStream JavaDoc(buffer.getDocument().get().getBytes());
364             }
365         }
366         
367         return propertiesFile.getContents();
368     }
369
370     private void reportDuplicateKeys(Set JavaDoc duplicateKeys) {
371         if (duplicateKeys.size() == 0)
372             return;
373         
374         String JavaDoc message= Messages.format(NLSSearchMessages.NLSSearchResultCollector_duplicateKeys, getPropertiesName(fPropertiesFile));
375         FileEntry groupElement= new FileEntry(fPropertiesFile, message);
376         Iterator JavaDoc iter= duplicateKeys.iterator();
377         while (iter.hasNext()) {
378             String JavaDoc propertyName= (String JavaDoc) iter.next();
379             addMatch(groupElement, propertyName);
380         }
381         fResult.addFileEntryGroup(groupElement);
382     }
383
384 }
385
Popular Tags