001: /*
002: * FindInFiles.java
003: *
004: * Copyright (C) 1998-2004 Peter Graves
005: * $Id: FindInFiles.java,v 1.16 2004/04/26 19:49:28 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import gnu.regexp.RE;
025: import gnu.regexp.REMatch;
026: import java.io.BufferedReader;
027: import java.io.IOException;
028: import java.io.InputStream;
029: import java.io.InputStreamReader;
030: import java.util.ArrayList;
031: import java.util.Iterator;
032: import java.util.List;
033: import java.util.StringTokenizer;
034: import java.util.Vector;
035: import java.util.regex.Matcher;
036: import java.util.regex.Pattern;
037: import java.util.regex.PatternSyntaxException;
038: import javax.swing.SwingUtilities;
039: import javax.swing.undo.CompoundEdit;
040:
041: public final class FindInFiles extends Replacement implements
042: Constants, BackgroundProcess {
043: private static FindInFiles findInFiles;
044:
045: public static final FindInFiles getFindInFiles() {
046: return findInFiles;
047: }
048:
049: private final Frame frame;
050: private String files;
051:
052: private boolean includeSubdirs;
053: private boolean searchFilesInMemory = true;
054: private ListOccurrencesInFiles outputBuffer;
055: private boolean listEachOccurrence;
056:
057: private Mode mode;
058:
059: private Vector results = new Vector();
060:
061: private boolean cancelled;
062:
063: private int numFilesExamined;
064: private int numFilesModified;
065:
066: private List filters;
067:
068: private SaveException saveException;
069: private ConfirmReplacementDialog confirmDialog;
070:
071: private final String encoding;
072:
073: public FindInFiles(Editor editor) {
074: super (editor);
075: this .frame = editor.getFrame();
076: encoding = Editor.preferences().getStringProperty(
077: Property.DEFAULT_ENCODING);
078: }
079:
080: public final boolean getIncludeSubdirs() {
081: return includeSubdirs;
082: }
083:
084: public final void setIncludeSubdirs(boolean b) {
085: includeSubdirs = b;
086: }
087:
088: public final boolean getSearchFilesInMemory() {
089: return searchFilesInMemory;
090: }
091:
092: public final void setSearchFilesInMemory(boolean b) {
093: searchFilesInMemory = b;
094: }
095:
096: public final Mode getMode() {
097: return mode;
098: }
099:
100: public final void setMode(Mode mode) {
101: this .mode = mode;
102: }
103:
104: public final ListOccurrencesInFiles getOutputBuffer() {
105: return outputBuffer;
106: }
107:
108: public final void setOutputBuffer(ListOccurrencesInFiles buf) {
109: outputBuffer = buf;
110: }
111:
112: public final boolean getListEachOccurrence() {
113: return listEachOccurrence;
114: }
115:
116: public final void setListEachOccurrence(boolean b) {
117: listEachOccurrence = b;
118: }
119:
120: public void listFiles(Editor editor) {
121: if (outputBuffer != null && editor.getBuffer() != outputBuffer) {
122: Buffer buf = null;
123: for (BufferIterator it = new BufferIterator(); it.hasNext();) {
124: Buffer b = it.nextBuffer();
125: if (b == outputBuffer) {
126: buf = b;
127: break;
128: }
129: }
130: if (buf == null) {
131: // Output buffer was closed.
132: outputBuffer.relink();
133: }
134: editor.makeNext(outputBuffer);
135: Editor ed = editor.activateInOtherWindow(outputBuffer);
136: if (buf == null) {
137: // Need to restore dot pos.
138: ed.setDot(outputBuffer.getLastDotPos());
139: ed.moveCaretToDotCol();
140: ed.setUpdateFlag(REFRAME);
141: ed.updateDisplay();
142: }
143: }
144: }
145:
146: public final String getFiles() {
147: return files;
148: }
149:
150: public void setFiles(String files) throws Exception {
151: ArrayList list = new ArrayList();
152: StringTokenizer st = new StringTokenizer(files, ";");
153: // We start in the editor's current directory.
154: File currentDir = getEditor().getCurrentDirectory();
155: if (currentDir == null || currentDir.isRemote())
156: throw new Exception(
157: "Operation not supported for remote files");
158: while (st.hasMoreTokens()) {
159: String token = st.nextToken().trim();
160: File file = File.getInstance(currentDir, token);
161: if (file == null) {
162: String message = "Invalid path \"" + token + '"';
163: throw new Exception(message);
164: }
165: File parent = file.getParentFile();
166: // Verify that the parent directory actually exists.
167: if (parent == null || !parent.isDirectory()) {
168: String message = "Invalid path \"" + token + '"';
169: throw new Exception(message);
170: }
171: // Parent is our new current directory.
172: currentDir = parent;
173: String canonicalPath = file.canonicalPath();
174: // This will throw an exception if canonicalPath isn't an
175: // acceptable wildcard pattern.
176: try {
177: list.add(new Filter(canonicalPath));
178: } catch (Exception e) {
179: String message = "Unsupported wild card pattern";
180: throw new Exception(message);
181: }
182: }
183: // Success.
184: this .files = files;
185: filters = list;
186: }
187:
188: public final void run() {
189: Debug.assertTrue(outputBuffer != null);
190: outputBuffer.setBusy(true);
191: outputBuffer.setBackgroundProcess(this );
192: runInternal();
193: outputBuffer.setBackgroundProcess(null);
194: outputBuffer.setBusy(false);
195: if (!cancelled && getReplaceWith() != null) {
196: Runnable r = new Runnable() {
197: public void run() {
198: Editor.getTagFileManager().setEnabled(false);
199: replaceInAllFiles();
200: Editor.getTagFileManager().setEnabled(true);
201: }
202: };
203: SwingUtilities.invokeLater(r);
204: }
205: }
206:
207: private void runInternal() {
208: frame.setWaitCursor();
209: for (Iterator it = filters.iterator(); it.hasNext();) {
210: Filter filter = (Filter) it.next();
211: File dir = null;
212: File spec = File.getInstance(filter.getOriginalPattern());
213: if (spec != null) {
214: File parent = spec.getParentFile();
215: if (parent != null)
216: dir = parent;
217: }
218: if (dir == null)
219: dir = getEditor().getCurrentDirectory();
220: searchDirectory(dir, filter);
221: // Did the user cancel?
222: if (cancelled)
223: break;
224: }
225: if (getReplaceWith() == null) {
226: // Find in files, not replace in files.
227: Runnable runnable = new Runnable() {
228: public void run() {
229: frame.setDefaultCursor();
230: if (outputBuffer != null) {
231: if (cancelled)
232: getEditor().status("Search cancelled");
233: else
234: getEditor().status("Search completed");
235: FastStringBuffer sb = new FastStringBuffer(
236: "Pattern found in ");
237: sb.append(results.size());
238: sb.append(" of ");
239: sb.append(numFilesExamined);
240: sb.append(" files examined");
241: if (cancelled)
242: sb.append(" (search cancelled by user)");
243: outputBuffer.appendStatusLine(sb.toString());
244: outputBuffer.invalidate();
245: outputBuffer.renumber();
246: outputBuffer.setBusy(false);
247: EditorIterator iter = new EditorIterator();
248: while (iter.hasNext()) {
249: Editor ed = iter.nextEditor();
250: if (ed.getBuffer() == outputBuffer) {
251: ed.setTopLine(outputBuffer
252: .getFirstLine());
253: ed.setDot(outputBuffer
254: .getInitialDotPos());
255: ed.moveCaretToDotCol();
256: ed.setUpdateFlag(REPAINT);
257: ed.updateDisplay();
258: }
259: }
260: }
261: }
262: };
263: SwingUtilities.invokeLater(runnable);
264: // Nothing more to do.
265: return;
266: }
267: SwingUtilities.invokeLater(updateDisplayRunnable);
268: }
269:
270: public final void cancel() {
271: cancelled = true;
272: }
273:
274: private void searchDirectory(File dir, Filter filter) {
275: String[] files = dir.list();
276: if (files == null)
277: return;
278: for (int i = 0; i < files.length; i++) {
279: if (cancelled)
280: return;
281: File file = File.getInstance(dir, files[i]);
282: if (file.isDirectory()) {
283: if (includeSubdirs)
284: searchDirectory(file, filter); // Recurse!
285: continue;
286: }
287: if (!filter.accepts(files[i]))
288: continue;
289: if (isBinaryFile(file))
290: continue;
291: if (searchFilesInMemory) {
292: Buffer buf = Editor.getBufferList().findBuffer(file);
293: if (buf != null && buf.isLoaded()) {
294: Position pos = findInBuffer(buf);
295: if (pos != null) {
296: results.add(file);
297: processFile(file, buf.getMode(), pos);
298: }
299: ++numFilesExamined;
300: continue;
301: }
302: // No buffer found, fall through...
303: }
304: Debug.assertTrue(outputBuffer != null);
305: processFile(file);
306: ++numFilesExamined;
307: }
308: }
309:
310: private void processFile(File file) {
311: try {
312: boolean update = false;
313: BufferedReader reader = new BufferedReader(
314: new InputStreamReader(file.getInputStream(),
315: encoding));
316: int lineNumber = 0;
317: int matches = 0;
318: final boolean delimited = wholeWordsOnly();
319: String s;
320: while ((s = reader.readLine()) != null) {
321: ++lineNumber;
322: boolean found = delimited ? findDelimited(s, mode)
323: : find(s);
324: if (found) {
325: try {
326: outputBuffer.lockWrite();
327: } catch (InterruptedException e) {
328: Log.error(e);
329: return;
330: }
331: try {
332: if (matches == 0) {
333: // First match in this file.
334: if (!listEachOccurrence
335: && results.size() == 0)
336: outputBuffer.appendLine("Found in:");
337: outputBuffer.appendFileLine(file,
338: listEachOccurrence);
339: results.add(file);
340: update = true;
341: }
342: ++matches;
343: if (listEachOccurrence)
344: outputBuffer.appendOccurrenceLine(s,
345: lineNumber);
346: else
347: break;
348: } finally {
349: outputBuffer.renumber();
350: outputBuffer.unlockWrite();
351: }
352: }
353: }
354: // Update display once per file.
355: if (update)
356: SwingUtilities.invokeLater(updateDisplayRunnable);
357: } catch (IOException e) {
358: Log.error(e);
359: }
360: }
361:
362: // BUG!! Unicode files are treated as binary.
363: private static boolean isBinaryFile(File file) {
364: try {
365: InputStream in = file.getInputStream();
366: byte[] bytes = new byte[4096];
367: int bytesRead = in.read(bytes);
368: in.close();
369: for (int i = 0; i < bytesRead; i++) {
370: if (bytes[i] == 0)
371: return true;
372: }
373: return false;
374: } catch (IOException e) {
375: Log.error(e);
376: return true;
377: }
378: }
379:
380: private void processFile(File file, Mode mode, Position pos) {
381: Debug.assertTrue(outputBuffer != null);
382: try {
383: outputBuffer.lockWrite();
384: } catch (InterruptedException e) {
385: Log.error(e);
386: return;
387: }
388: try {
389: processFileInternal(file, mode, pos);
390: outputBuffer.renumber();
391: } finally {
392: outputBuffer.unlockWrite();
393: }
394: // Update display once per file.
395: SwingUtilities.invokeLater(updateDisplayRunnable);
396: }
397:
398: private final Runnable updateDisplayRunnable = new Runnable() {
399: public void run() {
400: Position end = null;
401: for (EditorIterator iter = new EditorIterator(); iter
402: .hasNext();) {
403: Editor ed = iter.nextEditor();
404: if (ed.getBuffer() == outputBuffer) {
405: if (end == null) {
406: end = outputBuffer.getEnd();
407: end.setOffset(0);
408: }
409: ed.moveDotTo(end);
410: ed.setUpdateFlag(REPAINT);
411: ed.updateDisplay();
412: }
413: }
414: }
415: };
416:
417: private void processFileInternal(File file, Mode mode, Position pos) {
418: if (!listEachOccurrence && results.size() == 1)
419: outputBuffer.appendLine("Found in:");
420: outputBuffer.appendFileLine(file, listEachOccurrence);
421: if (listEachOccurrence) {
422: outputBuffer.appendOccurrenceLine(pos.getLine());
423: while (pos.getLine().next() != null) {
424: pos.moveTo(pos.getLine().next(), 0);
425: if ((pos = find(mode, pos)) != null)
426: outputBuffer.appendOccurrenceLine(pos.getLine());
427: else
428: break;
429: }
430: }
431: }
432:
433: private void replaceInAllFiles() {
434: final Editor editor = getEditor();
435: final Buffer oldBuffer = editor.getBuffer();
436: for (int i = 0; i < results.size(); i++) {
437: File file = (File) results.get(i);
438: try {
439: replaceInFile(file);
440: } catch (CheckFileException e) {
441: handleCheckFileException(e);
442: } catch (SaveException e) {
443: handleSaveException(e);
444: }
445: if (cancelled)
446: break;
447: }
448:
449: // Restore state and display completion message.
450: Runnable runnable = new Runnable() {
451: public void run() {
452: editor.activate(oldBuffer);
453: editor.setUpdateFlag(REPAINT);
454: frame.setDefaultCursor();
455: editor.updateDisplay();
456: completed();
457: }
458: };
459: if (SwingUtilities.isEventDispatchThread())
460: runnable.run();
461: else
462: SwingUtilities.invokeLater(runnable);
463: }
464:
465: private void handleCheckFileException(final CheckFileException e) {
466: Runnable runnable = new Runnable() {
467: public void run() {
468: String title = "Replace In Files";
469: String message = e.getMessage();
470: if (message == null)
471: message = "Error.";
472: message += " Continue?";
473: cancelled = !getEditor().confirm(title, message);
474: }
475: };
476:
477: if (SwingUtilities.isEventDispatchThread()) {
478: runnable.run();
479: } else {
480: try {
481: SwingUtilities.invokeAndWait(runnable);
482: } catch (Exception ex) {
483: Log.error(ex);
484: }
485: }
486: }
487:
488: private void handleSaveException(final SaveException e) {
489: Runnable runnable = new Runnable() {
490: public void run() {
491: String title = "Replace In Files";
492: String message = e.getMessage();
493:
494: // Tell user exactly what error occurred.
495: if (message != null)
496: MessageDialog.showMessageDialog(message, title);
497:
498: // Display summary message.
499: message = "Unable to save "
500: + e.getFile().canonicalPath();
501: MessageDialog.showMessageDialog(message, title);
502:
503: message = "Continue anyway?";
504: cancelled = !getEditor().confirm(title, message);
505: }
506: };
507:
508: if (SwingUtilities.isEventDispatchThread()) {
509: runnable.run();
510: } else {
511: try {
512: SwingUtilities.invokeAndWait(runnable);
513: } catch (Exception ex) {
514: Log.error(ex);
515: }
516: }
517: }
518:
519: private void replaceInFile(final File file)
520: throws CheckFileException, SaveException {
521: checkFile(file);
522:
523: if (confirmChanges()) {
524: if (SwingUtilities.isEventDispatchThread()) {
525: frame.setDefaultCursor();
526: replaceInFileConfirm(file);
527: frame.setWaitCursor();
528: } else {
529: Runnable runnable = new Runnable() {
530: public void run() {
531: frame.setDefaultCursor();
532: try {
533: replaceInFileConfirm(file);
534: } catch (SaveException e) {
535: FindInFiles.this .saveException = e;
536: }
537: frame.setWaitCursor();
538: }
539: };
540: try {
541: SwingUtilities.invokeAndWait(runnable);
542: } catch (Exception e) {
543: Log.error(e);
544: }
545: if (FindInFiles.this .saveException != null)
546: throw FindInFiles.this .saveException;
547: }
548: } else
549: replaceInFileNoConfirm(file);
550: }
551:
552: private void replaceInFileNoConfirm(File file) throws SaveException {
553: Buffer buffer = Editor.getBufferList().findBuffer(file);
554: if (buffer != null) {
555: // Found existing buffer. It may or may not be loaded at this
556: // point.
557: if (!buffer.isLoaded()) {
558: if (!buffer.initialized())
559: buffer.initialize();
560: buffer.load();
561: if (!buffer.isLoaded())
562: return; // Error handling?
563: }
564: final boolean wasModified = buffer.isModified();
565: int oldReplacementCount = getReplacementCount();
566: try {
567: buffer.lockWrite();
568: } catch (InterruptedException e) {
569: Log.error(e);
570: return;
571: }
572: try {
573: CompoundEdit compoundEdit = new CompoundEdit();
574: Position pos = new Position(buffer.getFirstLine(), 0);
575: while ((pos = find(mode, pos)) != null) {
576: compoundEdit.addEdit(new UndoLineEdit(buffer, pos
577: .getLine()));
578: replaceOccurrence(pos);
579: buffer.incrementModCount();
580: }
581: compoundEdit.end();
582: buffer.addEdit(compoundEdit);
583: } finally {
584: buffer.unlockWrite();
585: }
586: if (buffer.isModified() != wasModified)
587: Sidebar
588: .setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
589: if (getReplacementCount() > oldReplacementCount)
590: ++numFilesModified;
591: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
592: Editor ed = it.nextEditor();
593: if (ed.getBuffer() == buffer) {
594: if (ed.getDotOffset() > ed.getDotLine().length()) {
595: ed.getDot().setOffset(ed.getDotLine().length());
596: ed.moveCaretToDotCol();
597: ed.updateDotLine();
598: ed.updateDisplay();
599: }
600: }
601: }
602: } else {
603: // There was no buffer for the file in question prior to this operation.
604: SystemBuffer buf = new SystemBuffer(file);
605: buf.load();
606: if (!buf.isLoaded())
607: return; // Error handling?
608: boolean modified = false;
609: Position pos = new Position(buf.getFirstLine(), 0);
610: while ((pos = find((Mode) null, pos)) != null) {
611: if (cancelled)
612: break;
613: replaceOccurrence(pos);
614: modified = true;
615: }
616: if (modified)
617: buf.writeBuffer(); // Throws SaveException if there's an error.
618: if (modified)
619: ++numFilesModified;
620: }
621: replacedInFile(file);
622: }
623:
624: private void replaceInFileConfirm(File file) throws SaveException {
625: final Editor editor = getEditor();
626: boolean close = false;
627: Buffer buffer = Editor.getBufferList().findBuffer(file);
628: if (buffer == null) {
629: buffer = Buffer.createBuffer(file);
630: if (buffer == null)
631: return; // Error handling?
632:
633: // We created the buffer for this operation, so we should close it
634: // when we're done.
635: close = true;
636: }
637: editor.activate(buffer);
638: int oldReplacementCount = getReplacementCount();
639: Position saved = new Position(editor.getDot());
640: CompoundEdit compoundEdit = buffer.beginCompoundEdit();
641: editor.moveDotTo(buffer.getFirstLine(), 0);
642: Position pos = find(mode, editor.getDot());
643: if (pos == null) {
644: // Not found.
645: buffer.endCompoundEdit(compoundEdit);
646: editor.undo();
647: return;
648: }
649: final boolean wasModified = buffer.isModified();
650: editor.moveDotTo(pos);
651: editor.markFoundPattern(this );
652: editor.updateDisplay();
653: confirmDialog = new ConfirmReplacementDialog(this , true);
654: confirmDialog.setTitle(file.netPath());
655:
656: // This is modal: carry out all replacements in this file.
657: confirmDialog.show();
658:
659: if (confirmDialog.cancelled())
660: cancelled = true;
661: editor.moveDotTo(saved);
662: buffer.endCompoundEdit(compoundEdit);
663: if (close) {
664: if (buffer.isModified()) {
665: buffer.writeBuffer(); // Throws SaveException if there's an error.
666: buffer.saved();
667: buffer.setLastModified(buffer.getFile().lastModified());
668: }
669: if (!buffer.isModified())
670: buffer.kill();
671: } else {
672: // We're keeping the buffer open. Make sure the sidebar shows the
673: // modified status for the file correctly.
674: if (buffer.isModified() != wasModified) {
675: // The file was just modified for the fist time. The buffer
676: // lists need to be updated.
677: Sidebar
678: .setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
679: }
680: }
681:
682: if (getReplacementCount() > oldReplacementCount) {
683: ++numFilesModified;
684: replacedInFile(file);
685: }
686: }
687:
688: private void replacedInFile(File file) {
689: if (outputBuffer != null) {
690: try {
691: outputBuffer.lockWrite();
692: } catch (InterruptedException e) {
693: Log.error(e);
694: return;
695: }
696: try {
697: if (numFilesModified == 1)
698: outputBuffer.appendLine("Replaced in:");
699: outputBuffer.appendFileLine(file, false);
700: } finally {
701: outputBuffer.renumber();
702: outputBuffer.unlockWrite();
703: }
704: // Update display once per file.
705: if (SwingUtilities.isEventDispatchThread()) {
706: Position end = null;
707: for (EditorIterator iter = new EditorIterator(); iter
708: .hasNext();) {
709: Editor ed = iter.nextEditor();
710: if (ed.getBuffer() == outputBuffer) {
711: if (end == null)
712: end = outputBuffer.getEnd();
713: ed.moveDotTo(end);
714: ed.setUpdateFlag(REPAINT);
715: ed.updateDisplay();
716: ed.repaintNow();
717: }
718: }
719: } else {
720: Debug.bug();
721: SwingUtilities.invokeLater(updateDisplayRunnable);
722: }
723: }
724: }
725:
726: private void checkFile(File file) throws CheckFileException {
727: if (file.isRemote())
728: checkFileError(file, "file is not local");
729: if (file.isDirectory())
730: checkFileError(file, "file is a directory");
731: if (!file.isFile())
732: checkFileError(file, "file not found");
733: if (!file.canRead())
734: checkFileError(file, "file is not readable");
735: boolean writable = file.canWrite();
736: if (!writable) {
737: if (Editor.preferences().getBooleanProperty(
738: Property.P4_AUTO_EDIT)) {
739: if (P4.autoEdit(file))
740: writable = file.canWrite();
741: }
742: if (!writable)
743: checkFileError(file, "file is read only");
744: }
745: }
746:
747: private void checkFileError(File file, String reason)
748: throws CheckFileException {
749: FastStringBuffer sb = new FastStringBuffer("Can't process ");
750: sb.append(file.netPath());
751: sb.append(" (");
752: sb.append(reason);
753: sb.append(')');
754: throw new CheckFileException(sb.toString());
755:
756: }
757:
758: // Completion message for replace in files only.
759: private void completed() {
760: FastStringBuffer sb = new FastStringBuffer();
761: int replacementCount = getReplacementCount();
762: if (replacementCount == 0) {
763: sb.append("No occurrences replaced");
764: } else {
765: sb.append("Replaced ");
766: sb.append(replacementCount);
767: sb.append(" occurrence");
768: if (replacementCount > 1)
769: sb.append('s');
770: sb.append(" in ");
771: sb.append(numFilesModified);
772: sb.append(" file");
773: if (numFilesModified > 1)
774: sb.append('s');
775: }
776: if (cancelled)
777: sb.append(" (operation cancelled)");
778: else
779: sb.append(" (" + numFilesExamined + " files examined)");
780: if (outputBuffer != null) {
781: outputBuffer.appendStatusLine(sb.toString());
782: outputBuffer.renumber();
783: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
784: Editor ed = it.nextEditor();
785: if (ed.getBuffer() == outputBuffer) {
786: ed.setTopLine(outputBuffer.getFirstLine());
787: ed.setDot(outputBuffer.getEnd());
788: ed.moveCaretToDotCol();
789: ed.setUpdateFlag(REPAINT);
790: ed.updateDisplay();
791: }
792: }
793: } else
794: MessageDialog.showMessageDialog(getEditor(), sb.toString(),
795: "ReplaceInFiles");
796: }
797:
798: public static void findInFiles() {
799: final Editor editor = Editor.currentEditor();
800: final File dir = editor.getCurrentDirectory();
801: if (dir != null && !dir.isRemote())
802: findOrReplaceInFiles(editor, false);
803: }
804:
805: public static void replaceInFiles() {
806: final Editor editor = Editor.currentEditor();
807: final File dir = editor.getCurrentDirectory();
808: if (dir != null && !dir.isRemote())
809: findOrReplaceInFiles(editor, true);
810: }
811:
812: private static void findOrReplaceInFiles(Editor editor,
813: boolean replace) {
814: FindInFilesDialog d = new FindInFilesDialog(editor, replace);
815: editor.centerDialog(d);
816: d.show();
817: editor.repaintNow();
818: if (d.getFindInFiles() == null)
819: return;
820: if (findInFiles != null) {
821: // Kill old output buffer.
822: Buffer buf = findInFiles.getOutputBuffer();
823: if (Editor.getBufferList().contains(buf))
824: buf.kill();
825: }
826: findInFiles = d.getFindInFiles();
827: findInFiles.setOutputBuffer(new ListOccurrencesInFiles(
828: findInFiles));
829: new Thread(findInFiles).start();
830: Buffer outputBuffer = findInFiles.getOutputBuffer();
831: if (outputBuffer != null) {
832: Editor otherEditor = editor.getOtherEditor();
833: if (otherEditor != null) {
834: outputBuffer.setUnsplitOnClose(otherEditor.getBuffer()
835: .unsplitOnClose());
836: otherEditor.makeNext(outputBuffer);
837: } else
838: outputBuffer.setUnsplitOnClose(true);
839: editor.activateInOtherWindow(outputBuffer);
840: }
841: editor.status("Press Escape to cancel search");
842: }
843:
844: public static void listFiles() {
845: if (findInFiles != null)
846: findInFiles.listFiles(Editor.currentEditor());
847: }
848:
849: private static final class Filter {
850: private final String originalPattern;
851: private final boolean ignoreCase;
852: private Pattern pattern;
853:
854: public Filter(String s) throws Exception {
855: this .originalPattern = s;
856: ignoreCase = Platform.isPlatformWindows();
857: File file = File.getInstance(ignoreCase ? s.toLowerCase()
858: : s);
859: if (!processFilter(file.getName()))
860: throw new Exception("process pattern failed");
861: }
862:
863: public String getOriginalPattern() {
864: return originalPattern;
865: }
866:
867: private boolean processFilter(String s) {
868: FastStringBuffer sb = new FastStringBuffer();
869: for (int i = 0; i < s.length(); i++) {
870: char c = s.charAt(i);
871: switch (c) {
872: case '.':
873: sb.append("\\.");
874: break;
875: case '*':
876: sb.append(".*");
877: break;
878: case '?':
879: sb.append(".?");
880: break;
881: default:
882: sb.append(c);
883: break;
884: }
885: }
886: try {
887: pattern = Pattern.compile(sb.toString());
888: return true;
889: } catch (PatternSyntaxException e) {
890: Log.error(e);
891: return false;
892: }
893: }
894:
895: public boolean accepts(String name) {
896: if (ignoreCase)
897: name = name.toLowerCase();
898: Matcher matcher = pattern.matcher(name);
899: return matcher.matches();
900: }
901: }
902:
903: private static final class CheckFileException extends Exception {
904: CheckFileException(String message) {
905: super(message);
906: }
907: }
908: }
|