KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > search > types > FullTextType


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20
21 package org.netbeans.modules.search.types;
22
23
24 import java.io.*;
25 import java.nio.charset.Charset JavaDoc;
26 import java.nio.charset.IllegalCharsetNameException JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.regex.Matcher JavaDoc;
35 import javax.swing.Action JavaDoc;
36
37 import org.netbeans.modules.search.SearchDisplayer;
38 import org.openide.ErrorManager;
39
40 import org.openide.filesystems.FileObject;
41 import org.openide.loaders.DataObject;
42 import org.openide.nodes.AbstractNode;
43 import org.openide.nodes.Children;
44 import org.openide.nodes.Node;
45 import org.openide.util.actions.NodeAction;
46 import org.openide.util.actions.SystemAction;
47 import org.openide.util.HelpCtx;
48 import org.openide.util.NbBundle;
49 import org.openide.windows.OutputEvent;
50 import org.openide.windows.OutputListener;
51 import org.openide.xml.XMLUtil;
52 import org.openidex.search.SearchPattern;
53
54
55 /**
56  * Test DataObject primaryFile for line full-text match.
57  *
58  * @author Petr Kuzel
59  * @author Marian Petras
60  */

61 public class FullTextType extends TextType {
62
63     /** name of attribute "file encoding" */
64     private static final String JavaDoc ATTR_FILE_ENCODING = "Content-Encoding";//NOI18N
65

66     private static final long serialVersionUID = 1L;
67     //private static final long serialVersionUID = -8671182156199506714L;
68

69     /** array of searchable application/x-<em>suffix</em> MIME-type suffixes */
70     private static final Collection JavaDoc searchableXMimeTypes;
71     
72     static {
73         searchableXMimeTypes = new HashSet JavaDoc(17);
74         searchableXMimeTypes.add("csh"); //NOI18N
75
searchableXMimeTypes.add("httpd-php"); //NOI18N
76
searchableXMimeTypes.add("httpd-php-source"); //NOI18N
77
searchableXMimeTypes.add("javascript"); //NOI18N
78
searchableXMimeTypes.add("latex"); //NOI18N
79
searchableXMimeTypes.add("php"); //NOI18N
80
searchableXMimeTypes.add("sh"); //NOI18N
81
searchableXMimeTypes.add("tcl"); //NOI18N
82
searchableXMimeTypes.add("tex"); //NOI18N
83
searchableXMimeTypes.add("texinfo"); //NOI18N
84
searchableXMimeTypes.add("troff"); //NOI18N
85
}
86
87     /** replacement string which will be put in place of found matches */
88     private String JavaDoc replaceString;
89     /**
90      * Holds information about occurences of matching strings within individual
91      * <code>DataObject</code>s.
92      * <p>
93      * Contains mappings
94      * <blockquote>
95      * (<code>DataObject</code>) -&gt; (list of occurences of matchings strings
96      * withing the <code>DataObject</code>)
97      * </blockquote>
98      * </p>
99      */

100     private transient Map JavaDoc<DataObject, List JavaDoc<TextDetail>> detailsMap;
101
102     /**
103      * Maximum number of matches reported for one line.
104      * It eliminates huge memory consumption for single letter searches on long lines.
105      */

106     private static final int MAX_REPORTED_OCCURENCES_ON_LINE = 5;
107
108     /**
109      * Maximum number of matches reported for one file.
110      * It eliminates huge memory consumption for single letter searches in long files.
111      */

112     private static final int MAX_REPORTED_OCCURENCES_IN_FILE = 200;
113
114     /**
115      * Returns the replacement string.
116      *
117      * @return replace string, or {@code null} if no replace string has been
118      * specified
119      */

120     public String JavaDoc getReplaceString() {
121         return replaceString;
122     }
123
124     /**
125      * Sets a replacement string.
126      *
127      * @param replaceString string to replace matches with
128      * @exception java.lang.IllegalArgumentException
129      * if the specified string is <code>null</code>
130      */

131     public void setReplaceString(String JavaDoc replaceString) {
132         if (replaceString == null) {
133             setValid(false);
134             throw new IllegalArgumentException JavaDoc();
135         }
136
137         if (replaceString.length() == 0) {
138             replaceString = null;
139         }
140         this.replaceString = replaceString;
141     }
142
143     /** */
144     public Object JavaDoc clone() {
145         FullTextType clone = (FullTextType) super.clone();
146         clone.detailsMap = new HashMap JavaDoc<DataObject, List JavaDoc<TextDetail>>(20);
147         clone.replaceString = this.replaceString;
148         return clone;
149     }
150     
151     /** */
152     public void destroy() {
153         if (detailsMap != null) {
154             detailsMap.clear();
155         }
156     }
157     
158     /**
159      */

160     protected String JavaDoc displayName() {
161         /*
162          * For all non-default search types, display name is taken from
163          * the search type's bean descriptor. But this search type has
164          * no bean descriptor so we must override this method.
165          */

166         
167         return NbBundle.getMessage(FullTextType.class,
168                                    "TEXT_FULLTEXT_CRITERION"); //NOI18N
169
}
170
171     /** Gets details map. */
172     private Map JavaDoc<DataObject, List JavaDoc<TextDetail>> getDetailsMap() {
173         if (detailsMap != null) {
174             return detailsMap;
175         }
176         
177         synchronized(this) {
178             if (detailsMap == null) {
179                 detailsMap = new HashMap JavaDoc<DataObject, List JavaDoc<TextDetail>>(20);
180             }
181         }
182         
183         return detailsMap;
184     }
185     
186     /**
187      */

188     public static Charset JavaDoc getCharset(FileObject fileObj) {
189         Object JavaDoc encoding = fileObj.getAttribute(ATTR_FILE_ENCODING);
190         
191         Charset JavaDoc charset = null;
192         if (encoding instanceof String JavaDoc) {
193             String JavaDoc charsetName = (String JavaDoc) encoding;
194             try {
195                 if (Charset.isSupported(charsetName)) {
196                     charset = Charset.forName(charsetName);
197                 }
198             } catch (IllegalCharsetNameException JavaDoc ex) {
199                 /* charset remains <null> */
200                 ErrorManager.getDefault().notify( ErrorManager.WARNING, ex);
201             }
202         }
203         
204         if (charset == null) {
205             charset = Charset.defaultCharset();
206         }
207         
208         return charset;
209     }
210     
211     /**
212      *
213      * @exception java.io.FileNotFoundException
214      * if file determined by the {@code FileObject} does not exist
215      */

216     public LineNumberReader getFileObjectReader(FileObject fileObj)
217                                                 throws FileNotFoundException {
218         InputStream is = fileObj.getInputStream();//throws FileNotFoundException
219
Charset JavaDoc charset = getCharset(fileObj);
220         return new LineNumberReader(new InputStreamReader(is, charset));
221     }
222     
223     /**
224      */

225     public boolean testDataObject(DataObject dobj) {
226         LineNumberReader reader = null;
227         SearchPattern searchPattern = createSearchPattern();
228         try {
229             String JavaDoc line = ""; //NOI18N
230

231             // primary file of the DataObject
232
FileObject fo = dobj.getPrimaryFile();
233             if (fo == null) {
234                 return false;
235             }
236             // primary file content
237
reader = getFileObjectReader(fo);
238
239             ArrayList JavaDoc txtDetails = new ArrayList JavaDoc(5);
240
241             int fileCount = 0;
242             while (fileCount < MAX_REPORTED_OCCURENCES_IN_FILE) {
243                 line = reader.readLine();
244                 if (line == null) {
245                     break;
246                 }
247                 if (matchString != null) {
248                     
249                     if (line.length() < matchString.length()) {
250                         continue;
251                     }
252                     
253                     int lineNum = reader.getLineNumber(); // current line + 1
254
int markLen = matchString.length();
255
256                     String JavaDoc stringToSearch = caseSensitive ? line
257                                                           : line.toUpperCase();
258                     int fromIndex = 1;
259                     int fromIndexMax = stringToSearch.length()
260                                        - matchString.length() + 1;
261                     int matchIndex;
262                     int lineCount = 0;
263                     
264                     do {
265                         matchIndex = matchString(stringToSearch, fromIndex);
266                         if (matchIndex > 0) {
267                             TextDetail det = new TextDetail(dobj, searchPattern);
268                             det.setLine(lineNum);
269                             det.setColumn(matchIndex);
270                             det.setLineText(line);
271                             det.setMarkLength(markLen);
272
273                             txtDetails.add(det);
274
275                             lineCount++;
276                             fileCount++;
277                         }
278                         fromIndex = Math.abs(matchIndex) + 1;
279                     } while ((matchIndex != 0) && (fromIndex <= fromIndexMax)
280                              && (lineCount < MAX_REPORTED_OCCURENCES_ON_LINE));
281                 } else if (matchRE(line)) {
282
283                     // TODO detect all occurences as in above code
284

285                     TextDetail det = new TextDetail(dobj, searchPattern);
286                     det.setLine(reader.getLineNumber());
287                     det.setLineText(line);
288                     Matcher JavaDoc matcher = getMatcher();
289                     int start = matcher.start();
290                     int len = matcher.end() - start;
291                     det.setColumn(start +1);
292                     det.setMarkLength(len);
293
294                     txtDetails.add(det);
295                     fileCount++;
296                 }
297             }
298
299             // TODO somehow notify user if MAX_REPORTED_OCCURENCES_IN_FILE or MAX_REPORTED_OCCURENCES_ON_LINE
300

301             if (txtDetails.isEmpty()) {
302                 return false;
303             }
304
305             txtDetails.trimToSize();
306             getDetailsMap().put(dobj, txtDetails);
307
308             return true;
309         } catch (FileNotFoundException fnfe) {
310             return false;
311         } catch (IOException ioe) {
312             org.openide.ErrorManager.getDefault().notify(org.openide.ErrorManager.INFORMATIONAL, ioe);
313             return false;
314         } finally {
315             if (reader != null) {
316                 try {
317                     reader.close();
318                     reader = null;
319                 } catch (IOException ex) {
320                     org.openide.ErrorManager.getDefault().notify(
321                             org.openide.ErrorManager.INFORMATIONAL, ex);
322                     
323                 }
324             }
325         }
326     }
327     
328     /**
329      * @return <code>true</code> if the file's MIME-type denotes a searchable
330      * file;
331      * <code>false</code> otherwise
332      */

333     protected boolean acceptSearchObject(Object JavaDoc searchObject) {
334         DataObject dataObj = (DataObject) searchObject;
335         FileObject fileObj = dataObj.getPrimaryFile();
336         String JavaDoc mimeType = fileObj.getMIMEType();
337         
338         if (mimeType.equals("content/unknown") //NOI18N
339
|| mimeType.startsWith("text/")) { //NOI18N
340
return true;
341         }
342         if (mimeType.startsWith("application/")) { //NOI18N
343
final String JavaDoc subtype = mimeType.substring(12);
344             return subtype.equals("rtf") //NOI18N
345
|| subtype.equals("sgml") //NOI18N
346
|| subtype.startsWith("xml-") //NOI18N
347
|| subtype.endsWith("+xml") //NOI18N
348
|| subtype.startsWith("x-") //NOI18N
349
&& searchableXMimeTypes.contains(subtype.substring(2));
350         }
351         return false;
352     }
353
354
355     /**
356      * @param resultObject <code>DataObject</code> to create the nodes for
357      * @return <code>DetailNode</code>s representing the matches,
358      * or <code>null</code> if no matching string is known for the
359      * specified object
360      * @see DetailNode
361      */

362     public Node[] getDetails(Object JavaDoc resultObject) {
363         List JavaDoc<TextDetail> details = getDetailsMap().get(resultObject);
364         
365         if(details == null)
366             return null;
367
368         List JavaDoc detailNodes = new ArrayList JavaDoc(details.size());
369         
370         for(Iterator JavaDoc it = details.iterator(); it.hasNext();) {
371             
372             TextDetail txtDetail = (TextDetail)it.next();
373         
374             Node detailNode = new DetailNode(txtDetail);
375             detailNodes.add(detailNode);
376         }
377
378         return (Node[])detailNodes.toArray(new Node[detailNodes.size()]);
379     }
380
381     /**
382      * @param node representing a <code>DataObject</code> with matches
383      * @return <code>DetailNode</code>s representing the matches,
384      * or <code>null</code> if the specified node does not represent
385      * a <code>DataObject</code> or if no matching string is known for
386      * the specified object
387      */

388     public Node[] getDetails(Node node) {
389         DataObject dataObject = (DataObject)node.getCookie(DataObject.class);
390         
391         if(dataObject == null)
392             return null;
393         
394         return getDetails(dataObject);
395     }
396     
397     /**
398      */

399     public int getDetailsCount(Object JavaDoc resultObject) {
400         List JavaDoc<TextDetail> details = getDetailsMap().get(resultObject);
401         return (details != null) ? details.size() : 0;
402     }
403     
404     /**
405      */

406     public List JavaDoc<TextDetail> getTextDetails(Object JavaDoc resultObject) {
407         List JavaDoc<TextDetail> obtained = getDetailsMap().get(resultObject);
408         return (obtained != null) ? new ArrayList JavaDoc<TextDetail>(obtained) : null;
409     }
410
411     public SearchPattern createSearchPattern() {
412         return SearchPattern.create(
413                 matchString!=null?matchString:reString,
414                 wholeWords,
415                 caseSensitive,
416                 matchString==null);
417     }
418     
419     /**
420      * Node that represents information about one occurence of a matching
421      * string.
422      *
423      * @see TextDetail
424      */

425     private static class DetailNode extends AbstractNode
426                                     implements OutputListener {
427         
428         /** Detail to represent. */
429         private TextDetail txtDetail;
430         
431
432         
433         /**
434          * Constructs a node representing the specified information about
435          * a matching string.
436          *
437          * @param txtDetail information to be represented by this node
438          */

439         public DetailNode(TextDetail txtDetail) {
440             super(Children.LEAF);
441             
442             this.txtDetail = txtDetail;
443             
444             setShortDescription(DetailNode.getShortDesc(txtDetail));
445             setValue(SearchDisplayer.ATTR_OUTPUT_LINE,
446                      DetailNode.getFullDesc(txtDetail));
447         }
448         
449         @Override JavaDoc
450         public Action JavaDoc[] getActions(boolean context) {
451             if (!context) {
452                 return new Action JavaDoc[] { getPreferredAction() };
453             } else {
454                 return new Action JavaDoc[0];
455             }
456         }
457         
458         @Override JavaDoc
459         public Action JavaDoc getPreferredAction() {
460             return SystemAction.get(GotoDetailAction.class);
461         }
462
463         @Override JavaDoc
464         public boolean equals(Object JavaDoc anotherObj) {
465             return (anotherObj != null)
466                 && (anotherObj.getClass() == DetailNode.class)
467                 && (((DetailNode) anotherObj).txtDetail.equals(this.txtDetail));
468         }
469         
470         @Override JavaDoc
471         public int hashCode() {
472             return txtDetail.hashCode() + 1;
473         }
474         
475         /** */
476         public String JavaDoc getName() {
477             return txtDetail.getLineText() + " [" + DetailNode.getName(txtDetail) + "]"; // NOI18N
478
}
479
480         public String JavaDoc getHtmlDisplayName() {
481             String JavaDoc colored;
482             if (txtDetail.getMarkLength() > 0 && txtDetail.getColumn() > 0) {
483                 try {
484                     StringBuffer JavaDoc bold = new StringBuffer JavaDoc();
485                     String JavaDoc plain = txtDetail.getLineText();
486                     int col0 = txtDetail.getColumn() -1; // base 0
487

488                     bold.append(XMLUtil.toElementContent(plain.substring(0, col0))); // NOI18N
489
bold.append("<b>"); // NOi18N
490
int end = col0 + txtDetail.getMarkLength();
491                     bold.append(XMLUtil.toElementContent(plain.substring(col0, end)));
492                     bold.append("</b>"); // NOi18N
493
if (txtDetail.getLineText().length() > end) {
494                         bold.append(XMLUtil.toElementContent(plain.substring(end)));
495                     }
496                     colored = bold.toString();
497                 } catch (CharConversionException ex) {
498                     return null;
499                 }
500             } else {
501                 try {
502                     colored = XMLUtil.toElementContent( txtDetail.getLineText());
503                 } catch (CharConversionException e) {
504                     return null;
505                 }
506             }
507
508             try {
509                 return colored + " <font color='!controlShadow'>[" + XMLUtil.toElementContent(DetailNode.getName(txtDetail)) + "]"; // NOI18N
510
} catch (CharConversionException e) {
511                 return null;
512             }
513         }
514       
515         /** Displays the matching string in a text editor. */
516         private void gotoDetail() {
517             txtDetail.showDetail(TextDetail.DH_GOTO);
518         }
519
520         /** Show the text occurence. */
521         private void showDetail() {
522             txtDetail.showDetail(TextDetail.DH_SHOW);
523         }
524
525         /** Implements <code>OutputListener</code> interface method. */
526         public void outputLineSelected (OutputEvent evt) {
527             txtDetail.showDetail(TextDetail.DH_SHOW);
528         }
529
530         /** Implements <code>OutputListener</code> interface method. */
531         public void outputLineAction (OutputEvent evt) {
532             txtDetail.showDetail(TextDetail.DH_GOTO);
533         }
534
535         /** Implements <code>OutputListener</code> interface method. */
536         public void outputLineCleared (OutputEvent evt) {
537             txtDetail.showDetail(TextDetail.DH_HIDE);
538         }
539
540         /**
541          * Returns name of a node representing a <code>TextDetail</code>.
542          *
543          * @param det detailed information about location of a matching string
544          * @return name for the node
545          */

546         private static String JavaDoc getName(TextDetail det) {
547             int line = det.getLine();
548             int col = det.getColumn();
549             
550             if (col > 0) {
551                 
552                 /* position <line>:<col> */
553                 return NbBundle.getMessage(FullTextType.class, "TEXT_DETAIL_FMT_NAME1", //NOI18N
554
Integer.toString(line),
555                         Integer.toString(col));
556             } else {
557                 
558                 /* position <line> */
559                 return NbBundle.getMessage(FullTextType.class, "TEXT_DETAIL_FMT_NAME2", //NOI18N
560
Integer.toString(line));
561             }
562         }
563
564         /**
565          * Returns short description of a visual representation of
566          * a <code>TextDetail</code>. The description may be used e.g.
567          * for a tooltip text of a node.
568          *
569          * @param det detailed information about location of a matching string
570          * @return short description of a visual representation
571          */

572         private static String JavaDoc getShortDesc(TextDetail det) {
573             int line = det.getLine();
574             int col = det.getColumn();
575             
576             if (col > 0) {
577                 
578                 /* line <line>, column <col> */
579                 return NbBundle.getMessage(FullTextType.class, "TEXT_DETAIL_FMT_SHORT1", //NOI18N
580
new Object JavaDoc[] {Integer.toString(line),
581                                       Integer.toString(col)});
582             } else {
583                 
584                 /* line <line> */
585                 return NbBundle.getMessage(FullTextType.class, "TEXT_DETAIL_FMT_SHORT2", //NOI18N
586
Integer.toString(line));
587             }
588         }
589
590         /**
591          * Returns full description of a visual representation of
592          * a <code>TextDetail</code>. The description may be printed e.g. to
593          * an OutputWindow.
594          *
595          * @param det detailed information about location of a matching string
596          * @return full description of a visual representation
597          */

598         private static String JavaDoc getFullDesc(TextDetail det) {
599             String JavaDoc filename = det.getDataObject().getPrimaryFile().getNameExt();
600             String JavaDoc lineText = det.getLineText();
601             int line = det.getLine();
602             int col = det.getColumn();
603
604             if (col > 0) {
605
606                 /* [<filename> at line <line>, column <col>] <text> */
607                 return NbBundle.getMessage(FullTextType.class, "TEXT_DETAIL_FMT_FULL1", //NOI18N
608
new Object JavaDoc[] {lineText,
609                                       filename,
610                                       Integer.toString(line),
611                                       Integer.toString(col)});
612             } else {
613
614                 /* [<filename> line <line>] <text> */
615                 return NbBundle.getMessage(FullTextType.class, "TEXT_DETAIL_FMT_FULL2", //NOI18N
616
new Object JavaDoc[] {lineText,
617                                       filename,
618                                       Integer.toString(line)});
619             }
620         }
621         
622     } // End of DetailNode class.
623

624     
625     /**
626      * This action displays the matching string in a text editor.
627      * This action is to be used in the window/dialog displaying a list of
628      * found occurences of strings matching a search pattern.
629      */

630     private static class GotoDetailAction extends NodeAction {
631         
632         /** */
633         public String JavaDoc getName() {
634             return NbBundle.getBundle(FullTextType.class).getString("LBL_GotoDetailAction");
635         }
636         
637         /** */
638         public HelpCtx getHelpCtx() {
639             return new HelpCtx(GotoDetailAction.class);
640         }
641
642         /**
643          * @return <code>true</code> if at least one node is activated and
644          * the first node is an instance of <code>DetailNode</code>
645          * (or its subclass), <code>false</code> otherwise
646          */

647         protected boolean enable(Node[] activatedNodes) {
648             return activatedNodes != null && activatedNodes.length != 0
649                    && activatedNodes[0] instanceof DetailNode;
650         }
651
652         /**
653          * Displays the matching string in a text editor.
654          * Works only if condition specified in method {@link #enable} is met,
655          * otherwise does nothing.
656          */

657         protected void performAction(Node[] activatedNodes) {
658             if (enable(activatedNodes)) {
659                 ((DetailNode)activatedNodes[0]).gotoDetail();
660             }
661         }
662         
663         /**
664          */

665         protected boolean asynchronous() {
666             return false;
667         }
668         
669         
670     } // End of GotoDetailAction class.
671

672     /** Gets help context for this search type.
673      * Implements superclass abstract method. */

674     public HelpCtx getHelpCtx() {
675         return new HelpCtx(FullTextType.class);
676     }
677         
678 }
679
Popular Tags