0001: package tide.sources;
0002:
0003: import snow.lookandfeel.ThemesManager;
0004: import java.io.File;
0005: import snow.utils.SysUtils;
0006: import tide.outputtabs.search.SearchTab;
0007: import tide.outputtabs.search.SearchHit;
0008: import tide.outputtabs.search.SearchResultsManager;
0009: import tide.editor.*;
0010: import tide.utils.SyntaxUtils;
0011: import snow.utils.StringUtils;
0012: import snow.utils.gui.*;
0013: import java.util.*;
0014: import java.util.regex.*;
0015: import javax.swing.*;
0016: import javax.swing.event.*;
0017: import javax.swing.border.*;
0018: import java.awt.Component;
0019: import java.awt.EventQueue;
0020: import java.awt.FlowLayout;
0021: import java.awt.BorderLayout;
0022: import java.awt.Insets;
0023: import java.awt.event.*;
0024:
0025: /** User search UI + control, search/replace tool writes results in the search tab (filterable table).
0026: Replace feature is available from the hits table.
0027:
0028: Future: (using the syntax tree...)
0029: */
0030: public class SearchTool extends JDialog {
0031: // {short desc, regex, long descr tooltip (maybe <html>) }
0032: public final static String[][] someRegexes = new String[][] {
0033: {
0034: "toArray calls",
0035: "(?m)^\\s*\\w+\\.toArray\\(",
0036: "calls to collections toArray(..) ignoring return. Reference may differ if the passed array has the wrong length. " },
0037: {
0038: "indexOf(1 str)",
0039: "indexof\\(\".\"\\s*[,\\)]",
0040: "String.indexOf and lastIndexOf with a single char argument is 20% quicker as with a string of length 1." },
0041: {
0042: "old for loop",
0043: "for\\(int .+=0; .+<.+\\.((length)|(size\\(\\)));.+\\+\\+\\)",
0044: "some loops may be refactored into the new foreach construct if no concurrent modifications." },
0045: {
0046: "unuseful Number creations",
0047: "new\\s+((Short)|(Boolean)|(Float)|(Integer)|(Double))\\([^,]*?\\)",
0048: "may be unuseful object creation (since Java5)." },
0049: { "unuseful Number conversions",
0050: "\\.((double)|(int)|(float)|(boolean))Value\\(",
0051: "may be unuseful calls (since Java5)." },
0052: { "octal constants", "[^0-9a-z.,]0\\\\d+[^.,a-z]",
0053: "octal constants are dangerous, 012 is in fact the decimal value 10." },
0054: null,
0055: { "empty javadoc", "(?m)/\\*\\*\\s*\\*/", "empty javadoc" },
0056: { "default end comment", "//\\s*#jsn\\s*$",
0057: "autogenerated class end comments, unuseful if no inner class." },
0058: { "default constructor javadoc",
0059: "/\\*\\*?[\\s\\*]*constructor\\.?[\\s\\*]*/",
0060: "default constructor javadoc are unuseful." },
0061: { "default constructor end comment", "// constructor",
0062: "default constructor end comment are unuseful." },
0063: { "default class javadoc",
0064: "/\\*\\* ((class)|(interface)) #jsn\\.\\s*\\*/",
0065: "autogenerated default comments may be removed." },
0066: { "empty comment", "(?s)/\\*[\\W]*?\\*/",
0067: "empty comments may be removed." },
0068: { "empty comment", "(?m)^\\s*//\\s*$",
0069: "empty comments may be removed." }, // (?m) is required because of ^ and $
0070: { "javadocs lowercase", "(?s)/\\*\\*\\s*([a-z]).*?\\*/",
0071: "javadocs comments should start with an uppercase letter." },
0072: { "all javadocs", "(?s)/\\*\\*.*?\\*/",
0073: "just all the javadocs" },
0074: null,
0075: { "mail", "([\\w-$.]+)@([\\w-$.]+)", "mail address." },
0076: { "http", "http://[\\w-$_\\%/\\.]+", "http address." },
0077: { "urls", "\\w+://[\\w-\\\\$_\\%/\\.]+",
0078: "urls (http, file)." },
0079: { "paths", "(\\w+:)?[\\/][\\w-\\\\$_\\%/\\.@]+",
0080: "some windows paths." },
0081: {
0082: "ip's",
0083: "\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?",
0084: "ip addresses" },
0085: null,
0086: {
0087: "class usages",
0088: "#njc(class or package name)#njc",
0089: "all usages (declaration, calls) of a class or package. Useful for refactorings. Should be case sensitive!" },
0090: { "name.this", "#jsn\\.this\\W",
0091: "references on enclosing class." },
0092: { "name.class", "#jsn\\.class\\W",
0093: "maybe a reference on the class file." },
0094: { "name.java", "#jsn\\.java\\W",
0095: "maybe a reference on the source file." },
0096: { "new name(", "new\\s+#jsn\\(",
0097: "classes that instanciate themselves." },
0098: null,
0099: { "casts", "(\\([\\w0-9_\\.]+?\\))\\s*[\\w_$0-9]",
0100: "casts (and other constructs)" },
0101: {
0102: "Class.forName",
0103: "\\.forName\\((.*?)\\)",
0104: "Class.forName() calls must be considered during refactorings or when using Proguard" },
0105: {
0106: "divisions",
0107: "(?m)^.*[^/*](/)[^/*].*$",
0108: "Floating point divisions are roughly 10 times slower than multiplications. Use x*0.25 instead of x/4.0 !" },
0109: { "unicode chars", "\\\\u\\d{4}",
0110: "unicode chars. Make sure you use a font capable of rendering them." } };
0111:
0112: private final JTextField searchTF = new JTextField(20);
0113: private final JTextField replaceTF = new JTextField(20); // not implemented now...
0114: private final JCheckBox regex = new JCheckBox("Regex", false);
0115: private final JCheckBox replace = new JCheckBox("Replace", false);
0116: private final JCheckBox ignoreCase = new JCheckBox("Ignore Case",
0117: true);
0118: // allow all combinations {code, strings, comments}
0119: private final JCheckBox includeCode = new JCheckBox(
0120: "Include code ", true);
0121: private final JCheckBox includeStrings = new JCheckBox(
0122: "Include strings ", true);
0123: private final JCheckBox includeComments = new JCheckBox(
0124: "Include comments ", true);
0125:
0126: private final JLabel capturingGroupLabel = new JLabel(
0127: "Capturing group target ");
0128: private final JPanel regexPanel = new JPanel(new FlowLayout(
0129: FlowLayout.LEFT, 0, 0));
0130: private final JComboBox capturingGroupTarget = new JComboBox(
0131: new String[] { "all", "1", "2", "3", "4", "5", "6", "7",
0132: "8", "9", "10", "11", "12" });
0133:
0134: private final Component regextitle;
0135:
0136: private boolean replaceAllInAllFilesWithoutConfirm = false;
0137: private boolean replaceAllInThisFileWithoutConfirm = false;
0138: private ConfirmDialog confirmDialog;
0139:
0140: public SearchTool(final List<? extends FileItem> sf,
0141: final boolean fileNamesOnly, String customTitle) {
0142: super (MainEditorFrame.instance,
0143: customTitle != null ? customTitle : "Search in "
0144: + sf.size() + " "
0145: + (fileNamesOnly ? "filenames" : "files"),
0146:
0147: true);
0148: // this.setIconImage( Icons.createImage( Icons.SearchIcon.shared18 ) );
0149:
0150: // set from props
0151: if (MainEditorFrame.instance != null) {
0152: searchTF.setText(MainEditorFrame.instance.globalProperties
0153: .getProperty("SearchTool_searchTF", ""));
0154: replaceTF.setText(MainEditorFrame.instance.globalProperties
0155: .getProperty("SearchTool_replaceTF", ""));
0156: regex.setSelected(MainEditorFrame.instance.globalProperties
0157: .getBoolean("SearchTool_regex", false));
0158: // too dangerous
0159: // replace.setSelected(MainEditorFrame.instance.globalProperties.getBoolean("SearchTool_replace", false));
0160: ignoreCase
0161: .setSelected(MainEditorFrame.instance.globalProperties
0162: .getBoolean("SearchTool_ignoreCase", true));
0163: includeCode
0164: .setSelected(MainEditorFrame.instance.globalProperties
0165: .getBoolean("SearchTool_includeCode", true));
0166: includeStrings
0167: .setSelected(MainEditorFrame.instance.globalProperties
0168: .getBoolean("SearchTool_includeStrings",
0169: true));
0170: includeComments
0171: .setSelected(MainEditorFrame.instance.globalProperties
0172: .getBoolean("SearchTool_includeComments",
0173: true));
0174: capturingGroupTarget
0175: .setSelectedItem(MainEditorFrame.instance.globalProperties
0176: .getProperty(
0177: "SearchTool_capturingGroupTarget",
0178: "all"));
0179: }
0180:
0181: JPanel inputPanel = new JPanel();
0182: inputPanel.setBorder(new EmptyBorder(3, 3, 3, 3));
0183: GridLayout3 gl = new GridLayout3(2, inputPanel);
0184: add(inputPanel, BorderLayout.CENTER);
0185: gl.add("Search: ");
0186: JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT, 1, 0));
0187: gl.add(p);
0188: p.add(searchTF);
0189: final JButton histo = new JButton("...");
0190: histo.setMargin(new Insets(0, 2, 0, 2));
0191: p.add(histo);
0192: histo.addActionListener(new ActionListener() {
0193: public void actionPerformed(ActionEvent ae) {
0194: JPopupMenu pop = new JPopupMenu();
0195:
0196: //if(regex.isSelected())
0197: {
0198: JMenu reme = new JMenu("Some useful regexes");
0199: pop.add(reme);
0200: pop.addSeparator();
0201: for (final String[] sr : someRegexes) {
0202: if (sr == null) {
0203: reme.addSeparator();
0204: } else {
0205: JMenuItem mi = new JMenuItem(sr[0]
0206: + " " + sr[1]);
0207: mi.setToolTipText(sr[2]);
0208: reme.add(mi);
0209: mi.addActionListener(new ActionListener() {
0210: public void actionPerformed(
0211: ActionEvent ae) {
0212:
0213: if (!regex.isSelected()) {
0214: regex.doClick();
0215: }
0216: capturingGroupTarget
0217: .setSelectedIndex(0); // all
0218: searchTF.setText(sr[1]);
0219: searchTF.requestFocus(); // so we can press enter to start the search...
0220: }
0221: });
0222: }
0223: }
0224: }
0225:
0226: if (MainEditorFrame.instance != null) {
0227: for (final List<Object> si : MainEditorFrame.instance
0228: .getActualProject().getSearchHistory()) {
0229: JMenuItem mi = new JMenuItem("" + si.get(0));
0230: pop.add(mi);
0231: mi.addActionListener(new ActionListener() {
0232: public void actionPerformed(ActionEvent ae) {
0233: searchTF.setText("" + si.get(0));
0234: //System.out.println("Set ignore case to "+(Boolean) si.get(1));
0235: ignoreCase.setSelected((Boolean) si
0236: .get(1));
0237: includeStrings.setSelected((Boolean) si
0238: .get(2));
0239: includeComments
0240: .setSelected((Boolean) si
0241: .get(3));
0242:
0243: regex.setSelected((Boolean) si.get(4));
0244: if (si.size() > 5) {
0245: includeCode
0246: .setSelected((Boolean) si
0247: .get(5));
0248: }
0249:
0250: searchTF.requestFocus();
0251: searchTF.selectAll();
0252: }
0253: });
0254: }
0255: }
0256:
0257: if (pop.getComponentCount() > 0) {
0258: pop.show(searchTF, 0, searchTF.getHeight());
0259: }
0260: }
0261: });
0262:
0263: if (!fileNamesOnly) {
0264: JPanel includePan = new JPanel(new FlowLayout(
0265: FlowLayout.LEFT, 0, 0));
0266: gl.insertLineBreakAfterNextComponent();
0267: gl.add(includePan);
0268: includePan.add(includeCode);
0269: includePan.add(includeStrings);
0270: includePan.add(includeComments);
0271: }
0272:
0273: gl.add(ignoreCase);
0274: gl.add(regex);
0275:
0276: regex.addActionListener(new ActionListener() {
0277: public void actionPerformed(ActionEvent ae) {
0278: updateVisibilities();
0279: checkRegex();
0280: pack();
0281: }
0282: });
0283:
0284: final CloseControlPanel ccp = new CloseControlPanel(this , true,
0285: true, "Search");
0286: ccp.getOkButton().setIcon(Icons.sharedSearch);
0287: searchTF.addActionListener(new ActionListener() {
0288: public void actionPerformed(ActionEvent ae) {
0289: ccp.acceptAndClose();
0290: }
0291: });
0292:
0293: searchTF.addKeyListener(new KeyAdapter() {
0294: @Override
0295: public void keyPressed(KeyEvent ke) {
0296: // todo: wait some time...
0297: checkRegex();
0298: }
0299:
0300: @Override
0301: public void keyReleased(KeyEvent ke) {
0302: checkRegex();
0303: }
0304: });
0305:
0306: if (!fileNamesOnly) {
0307: // gl.add(replace);
0308: // gl.add(replaceTF);
0309: replace.addActionListener(new ActionListener() {
0310: public void actionPerformed(ActionEvent ae) {
0311: updateVisibilities();
0312:
0313: ccp
0314: .getOkButton()
0315: .setText(
0316: (replace.isSelected() ? "Search/Replace next"
0317: : "Search"));
0318: pack();
0319: }
0320: });
0321: }
0322:
0323: regextitle = gl.addTitleSeparator("Regex options");
0324: gl.insertLineBreakAfterNextComponent();
0325: gl.add(regexPanel);
0326: regexPanel.add(capturingGroupLabel);
0327: regexPanel.add(capturingGroupTarget);
0328: capturingGroupTarget.setMaximumRowCount(20);
0329: regexPanel.add(Box.createHorizontalStrut(10));
0330: JButton helpRegex = new JButton("Regex syntax help", Icons
0331: .createHelpIcon(16, true));
0332: helpRegex
0333: .setToolTipText("<html><body>displays the api javadoc of java.util.regex.Pattern."
0334: + "<br>Special cases:<ul><li>#jn will be replaced with the java name (based on file name)."
0335: + "<li>#jsn will be replaced by the java simple name (without package)."
0336: + "<li>#jc (java character) will be replaced with [\\w$]."
0337: + "<li>#njc (non java character) will be replaced with [^\\w\\$]."
0338: + "</ul>");
0339: helpRegex.setMargin(new Insets(0, 2, 0, 2));
0340: regexPanel.add(helpRegex);
0341: helpRegex.addActionListener(new ActionListener() {
0342: public void actionPerformed(ActionEvent ae) {
0343: File jdh = null;
0344: if (MainEditorFrame.instance != null) {
0345: jdh = MainEditorFrame.instance.getActualProject()
0346: .getJDKDoc_Home();
0347: }
0348:
0349: if (jdh == null) {
0350: jdh = new File("C:/java/docs/docs1.6/");
0351: }
0352:
0353: if (jdh == null || !jdh.exists()) {
0354: JOptionPane
0355: .showMessageDialog(
0356: SearchTool.this ,
0357: "Cannot display the regex help, please set the JDK docs path\nin the project settings (CTRL+SHIFT+P)",
0358: "Error", JOptionPane.ERROR_MESSAGE);
0359: return;
0360: }
0361: File paf = new File(jdh,
0362: "api/java/util/regex/Pattern.html");
0363: if (!paf.exists()) {
0364: JOptionPane
0365: .showMessageDialog(
0366: SearchTool.this ,
0367: "Cannot display the regex help, "
0368: + "\nthe path is not valid: "
0369: + paf
0370: + "\nplease set the JDK docs path in the project settings (CTRL+SHIFT+P)",
0371: "Error", JOptionPane.ERROR_MESSAGE);
0372: return;
0373: }
0374:
0375: try {
0376: SysUtils.openBrowser(paf.getAbsolutePath());
0377: } catch (Exception e) {
0378: JOptionPane
0379: .showMessageDialog(
0380: SearchTool.this ,
0381: "Cannot display the regex help, "
0382: + "\nthe path is not valid: "
0383: + paf
0384: + "\nplease set the JDK docs path in the project settings (CTRL+SHIFT+P)",
0385: "Error", JOptionPane.ERROR_MESSAGE);
0386: }
0387: }
0388: });
0389:
0390: add(ccp, BorderLayout.SOUTH);
0391: searchTF.selectAll();
0392:
0393: updateVisibilities();
0394: pack();
0395: this .setLocationRelativeTo(MainEditorFrame.instance);
0396:
0397: this .setVisible(true);
0398: // modal => waits
0399:
0400: if (ccp.getWasCancelled() || !ccp.getWasAccepted()) {
0401: return;
0402: }
0403:
0404: // save to props
0405: MainEditorFrame.instance.globalProperties.setProperty(
0406: "SearchTool_searchTF", searchTF.getText());
0407: MainEditorFrame.instance.globalProperties.setProperty(
0408: "SearchTool_replaceTF", replaceTF.getText());
0409: MainEditorFrame.instance.globalProperties.setBoolean(
0410: "SearchTool_regex", regex.isSelected());
0411: MainEditorFrame.instance.globalProperties.setBoolean(
0412: "SearchTool_replace", replace.isSelected());
0413: MainEditorFrame.instance.globalProperties.setBoolean(
0414: "SearchTool_ignoreCase", ignoreCase.isSelected());
0415:
0416: MainEditorFrame.instance.globalProperties.setBoolean(
0417: "SearchTool_includeCode", includeCode.isSelected());
0418: MainEditorFrame.instance.globalProperties.setBoolean(
0419: "SearchTool_includeStrings", includeStrings
0420: .isSelected());
0421: MainEditorFrame.instance.globalProperties.setBoolean(
0422: "SearchTool_includeComments", includeComments
0423: .isSelected());
0424: MainEditorFrame.instance.globalProperties.setProperty(
0425: "SearchTool_capturingGroupTarget", ""
0426: + capturingGroupTarget.getSelectedItem());
0427:
0428: // TODO: only if >1 hits present...
0429: MainEditorFrame.instance.outputPanels.selectSearchTab();
0430: SearchResultsManager.getInstance().clear();
0431:
0432: final String searchText = searchTF.getText();
0433: final ProgressModalDialog pmd = new ProgressModalDialog(
0434: MainEditorFrame.instance, "Searching \"" + searchText
0435: + "\"", false);
0436: confirmDialog = new ConfirmDialog(pmd);
0437:
0438: Thread t = new Thread() {
0439: public void run() {
0440: try {
0441: if (replace.isSelected()) {
0442: String repl = replaceTF.getText();
0443:
0444: // todo: analyse the search and maybe warn !!
0445: searchT(sf, searchText, fileNamesOnly, repl,
0446: pmd);
0447: } else {
0448: // simple search
0449: searchT(sf, searchText, fileNamesOnly, null,
0450: pmd);
0451: }
0452:
0453: } catch (Exception e) {
0454: JOptionPane.showMessageDialog(
0455: MainEditorFrame.instance, ""
0456: + e.getMessage(),
0457: "Search/Replace error",
0458: JOptionPane.ERROR_MESSAGE);
0459: }
0460: }
0461: };
0462: t.setPriority(Thread.NORM_PRIORITY - 1);
0463: t.setName("searchThread");
0464: t.start();
0465:
0466: } // Constructor
0467:
0468: public void checkRegex() {
0469: if (!regex.isSelected()) {
0470: searchTF.setBackground(UIManager
0471: .getColor("TextArea.background"));
0472: return;
0473: }
0474:
0475: try {
0476: Pattern p = Pattern.compile(searchTF.getText());
0477: //p.capturingGroupCount is not public !!, but can be accessed through a dummy pattern
0478: int ng = p.matcher("").groupCount();
0479:
0480: if (this .capturingGroupTarget.getSelectedIndex() > 0
0481: && ng < this .capturingGroupTarget
0482: .getSelectedIndex()) {
0483: //throw new Exception("not enough groups");
0484: }
0485:
0486: searchTF.setBackground(UIManager
0487: .getColor("TextArea.background"));
0488: } catch (Exception e) {
0489: searchTF
0490: .setBackground(ThemesManager.getInstance().getRed());
0491: }
0492: }
0493:
0494: private void updateVisibilities() {
0495: regexPanel.setVisible(regex.isSelected());
0496: //capturingGroupLabel.setVisible(regex.isSelected());
0497: replaceTF.setEnabled(replace.isSelected());
0498: regextitle.setVisible(regex.isSelected());
0499: //replaceTF.setVisible(replace.isSelected() );
0500:
0501: }
0502:
0503: // quicker than instanciating each time a new pattern
0504: private Matcher sharedMatcher = null;
0505: private int hitsCountLimitWarn = 100;
0506:
0507: private Matcher getMatcherFor(FileItem fi, String toSearch,
0508: String content) {
0509: if (toSearch.indexOf("#j") >= 0 || sharedMatcher == null) {
0510: try {
0511: // first init OR custom pattern for file
0512: toSearch = toSearch.replace("#jn", "\\Q"
0513: + fi.getJavaName() + "\\E");
0514: toSearch = toSearch.replace("#jsn", "\\Q"
0515: + fi.getJavaPartName() + "\\E");
0516: toSearch = toSearch.replace("#jc", "[\\w\\$]");
0517: toSearch = toSearch.replace("#njc", "[^\\w\\$]");
0518:
0519: final Pattern pattern;
0520: if (this .ignoreCase.isSelected()) {
0521: pattern = Pattern.compile(toSearch,
0522: Pattern.CASE_INSENSITIVE);
0523: } else {
0524: pattern = Pattern.compile(toSearch);
0525: }
0526: sharedMatcher = pattern.matcher(content);
0527: } catch (PatternSyntaxException e) {
0528: MainEditorFrame.instance.outputPanels
0529: .selectToolsTab(true);
0530: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
0531: .appendError("Bad search pattern: "
0532: + e.getMessage());
0533: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
0534: .appendError("Search aborted");
0535: throw new RuntimeException("Invalid search pattern");
0536: }
0537:
0538: } else {
0539: sharedMatcher.reset(content);
0540: }
0541:
0542: return sharedMatcher;
0543: }
0544:
0545: int hits = 0;
0546:
0547: /**
0548: * Must be called from a user thread works in another thread than the EDT...
0549: */
0550: private void searchT(final List<? extends FileItem> sf,
0551: final String searchText, boolean fileNamesOnly,
0552: String replacement, final ProgressModalDialog pmd) {
0553: MainEditorFrame.instance.editorPanel
0554: .storeTemporary_ActualEditedSource();
0555:
0556: MainEditorFrame.instance.getActualProject()
0557: .addSearchToHistory(
0558: new ArrayList<Object>(Arrays
0559: .asList(new Object[] { searchText,
0560: ignoreCase.isSelected(),
0561: includeStrings.isSelected(),
0562: includeComments.isSelected(),
0563: regex.isSelected(),
0564: includeCode.isSelected() })));
0565:
0566: final String toSearch = (ignoreCase.isSelected()
0567: && !this .regex.isSelected() ? searchText.toUpperCase()
0568: : searchText);
0569:
0570: pmd.setProgressBounds(sf.size());
0571: pmd.start();
0572: try {
0573: String descr = " "
0574: + (this .regex.isSelected() ? "regex " : "")
0575: + "\""
0576: + searchText
0577: + "\""
0578: + (ignoreCase.isSelected() ? ", ignoring case, "
0579: : " ") + "in " + sf.size() + " files";
0580:
0581: SearchTab.getInstance().setDescription(descr);
0582: int maxNrOfGroups = 0;
0583:
0584: replaceAllInAllFilesWithoutConfirm = false;
0585:
0586: ml: for (final FileItem sfi : sf) //main loop (over files)
0587: {
0588: pmd.incrementProgress(1);
0589: if (pmd.getWasCancelled())
0590: break;
0591:
0592: // for replacements in this file.
0593: replaceAllInThisFileWithoutConfirm = replaceAllInAllFilesWithoutConfirm; // when true => no ask !
0594:
0595: String cont;
0596:
0597: if (fileNamesOnly) {
0598: cont = sfi.getJavaName();
0599: } else {
0600: cont = sfi.getContent();
0601: }
0602:
0603: if (ignoreCase.isSelected() && !regex.isSelected()) {
0604: cont = cont.toUpperCase();
0605: }
0606:
0607: int foundStart;
0608: int foundEnd;
0609: String matchFound;
0610: // robust
0611: int startLine;
0612: int startCol;
0613:
0614: if (this .regex.isSelected()) {
0615: int lastMatchLength = 1;
0616: int posInFile = -1;
0617: sharedMatcher = getMatcherFor(sfi, toSearch, cont);
0618: wm: while (sharedMatcher.find(posInFile
0619: + lastMatchLength)) {
0620: int group = capturingGroupTarget
0621: .getSelectedIndex(); // 0: all, n: n
0622: if (group > sharedMatcher.groupCount()) {
0623: // error:
0624: MainEditorFrame.instance.outputPanels
0625: .selectToolsTab(true);
0626: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
0627: .appendErrorLine("\tat "
0628: + sfi.getJavaName()
0629: + ":"
0630: + SyntaxUtils
0631: .countLinesUpToPosition(
0632: cont,
0633: sharedMatcher
0634: .start())
0635: + ": group "
0636: + group
0637: + " of regex not existing, group 0 is "
0638: + sharedMatcher.group());
0639: posInFile = sharedMatcher.start(); // important: continue to the next...
0640: continue wm;
0641: }
0642:
0643: posInFile = sharedMatcher.start(group);
0644: foundStart = sharedMatcher.start(group);
0645: foundEnd = sharedMatcher.end(group);
0646:
0647: matchFound = cont.substring(foundStart,
0648: foundEnd);
0649: lastMatchLength = matchFound.length();
0650:
0651: // [May2007]: replaced || with &&
0652: if (!fileNamesOnly
0653: && (!includeStrings.isSelected()
0654: || !includeComments
0655: .isSelected() || !includeCode
0656: .isSelected())) // boost: analyse only if excluding smtng
0657: {
0658: boolean inString = SyntaxUtils.isInString(
0659: cont, foundStart);
0660: if (!includeStrings.isSelected()
0661: && inString)
0662: continue wm;
0663:
0664: boolean inComment = SyntaxUtils
0665: .isInComment(cont, foundStart); // not 100% nice when searching for comments ignoring code
0666: // also not 100% nice when capturing all and having groups...
0667: //System.out.println("in com: "+inComment+" for "+foundStart);
0668: if (!includeComments.isSelected()
0669: && inComment)
0670: continue wm;
0671:
0672: if (!includeCode.isSelected()
0673: && (!inString && !inComment))
0674: continue wm;
0675: }
0676:
0677: startLine = SyntaxUtils.countLinesUpToPosition(
0678: cont, foundStart) + 1;
0679: startCol = fileNamesOnly ? -1 : StringUtils
0680: .getColumnNumberForPosition(cont,
0681: foundStart);
0682:
0683: int endLine = SyntaxUtils
0684: .countLinesUpToPosition(cont, foundEnd) + 1;
0685: int endCol = fileNamesOnly ? -1 : StringUtils
0686: .getColumnNumberForPosition(cont,
0687: foundEnd);
0688:
0689: hits++;
0690: pmd.setCommentLabel("" + hits
0691: + " hits so far...");
0692:
0693: SearchHit sh = new SearchHit(sfi, startLine,
0694: startCol, endLine, endCol, matchFound);
0695: SearchResultsManager.getInstance().add(sh);
0696:
0697: if (sharedMatcher.groupCount() > 0) // don't includes the group 0
0698: {
0699: maxNrOfGroups = Math.max(maxNrOfGroups,
0700: sharedMatcher.groupCount());
0701:
0702: // add the subgroups only
0703: for (int gi = 0; gi < sharedMatcher
0704: .groupCount(); gi++) {
0705: sh.supplementaryRegexCaptureGroups
0706: .add(sharedMatcher
0707: .group(gi + 1));
0708: }
0709: }
0710:
0711: EventQueue.invokeLater(new Runnable() {
0712: public void run() {
0713: SearchTab.getInstance().refresh();
0714: }
0715: });
0716:
0717: /*if(replace.isSelected())
0718: {
0719: replace(sfi, replacement, startLine, startCol, foundStart, foundEnd);
0720: }*/
0721:
0722: if (hits == hitsCountLimitWarn) // equals
0723: {
0724: // being swing safe is not always straightforward :-(
0725: final int[] reply = new int[1];
0726: new SwingSafeRunnable(new Runnable() {
0727: public void run() {
0728: reply[0] = JOptionPane
0729: .showConfirmDialog(
0730: SearchTool.this ,
0731: "Already "
0732: + hits
0733: + " hits were found, continue?",
0734: "Continue",
0735: JOptionPane.YES_NO_CANCEL_OPTION);
0736: }
0737: }, true).run(); // waits
0738: if (reply[0] == JOptionPane.NO_OPTION) {
0739: break ml;
0740: }
0741: }
0742: }
0743:
0744: } else {
0745: // NOT regex
0746: int posInFile = -toSearch.length();
0747: wt: while (true) {
0748: posInFile = cont.indexOf(toSearch, posInFile
0749: + toSearch.length());
0750: if (posInFile == -1)
0751: break wt;
0752:
0753: foundStart = posInFile;
0754: foundEnd = posInFile + toSearch.length();
0755: matchFound = cont.substring(foundStart,
0756: foundEnd);
0757:
0758: if (!fileNamesOnly
0759: && (!includeStrings.isSelected()
0760: || !includeComments
0761: .isSelected() || !includeCode
0762: .isSelected())) // boost
0763: {
0764: boolean inString = SyntaxUtils.isInString(
0765: cont, posInFile);
0766: if (!includeStrings.isSelected()
0767: && inString)
0768: continue wt;
0769:
0770: boolean inComment = SyntaxUtils
0771: .isInComment(cont, posInFile);
0772: if (!includeComments.isSelected()
0773: && inComment)
0774: continue wt;
0775:
0776: if (!includeCode.isSelected()
0777: && (!inString && !inComment))
0778: continue wt;
0779: }
0780:
0781: hits++;
0782: pmd.setCommentLabel("" + hits
0783: + " hits so far...");
0784:
0785: // found !
0786: startCol = fileNamesOnly ? -1 : StringUtils
0787: .getColumnNumberForPosition(cont,
0788: foundStart);
0789: startLine = SyntaxUtils.countLinesUpToPosition(
0790: cont, foundStart) + 1;
0791:
0792: int endLine = SyntaxUtils
0793: .countLinesUpToPosition(cont, foundEnd) + 1;
0794: int endCol = fileNamesOnly ? -1 : StringUtils
0795: .getColumnNumberForPosition(cont,
0796: foundEnd);
0797:
0798: SearchResultsManager.getInstance().add(
0799: new SearchHit(sfi, startLine, startCol,
0800: endLine, endCol, matchFound));
0801: EventQueue.invokeLater(new Runnable() {
0802: public void run() {
0803: SearchTab.getInstance().refresh();
0804: }
0805: });
0806:
0807: /*if(replace.isSelected())
0808: {
0809: replace(sfi, replacement, startLine, startCol, foundStart, foundEnd);
0810: }*/
0811:
0812: if (hits == hitsCountLimitWarn) // equals
0813: {
0814: // being swing safe is not always straightforward :-(
0815: final int[] reply = new int[1];
0816: new SwingSafeRunnable(new Runnable() {
0817: public void run() {
0818: reply[0] = JOptionPane
0819: .showConfirmDialog(
0820: SearchTool.this ,
0821: "Already "
0822: + hits
0823: + " hits were found, continue?",
0824: "Continue",
0825: JOptionPane.YES_NO_CANCEL_OPTION);
0826: }
0827: }, true).run(); // waits
0828: if (reply[0] == JOptionPane.NO_OPTION) {
0829: break ml;
0830: }
0831: }
0832: }
0833: } // end of else (!regex)
0834: }
0835: SearchTab.getInstance().setHits(hits, maxNrOfGroups, true); // autoselects if one single hit...
0836:
0837: if (hits != 1) {
0838: MainEditorFrame.instance.outputPanels.selectSearchTab();
0839: }
0840:
0841: } catch (Exception e) {
0842: e.printStackTrace();
0843: } finally {
0844: pmd.closeDialog();
0845: }
0846: }
0847:
0848: // TODO
0849: /*
0850: private void replace(final FileItem sfi, String replacement, int startLine, int startCol, int foundStart, int foundEnd) throws Exception
0851: {
0852: if(replacement==null) return;
0853: if( startLine<=0 || startCol<=0) return;
0854:
0855: // 1) if not already displayed, display the source
0856: if(MainEditorFrame.instance.editorPanel.getActualDisplayedFile() != sfi)
0857: {
0858: // EDTIZE !
0859: EventQueue.invokeAndWait(new Runnable() { public void run() {
0860: MainEditorFrame.instance.setSourceOrItemToEditOrView(sfi, true);
0861: }});
0862: Thread.sleep(100);
0863: }
0864:
0865: // be sure !!
0866: if(MainEditorFrame.instance.editorPanel.getActualDisplayedFile() != sfi)
0867: {
0868: throw new RuntimeException("Bad file in editor: cannot replace text.");
0869: }
0870:
0871: // 2) mark the text, jump to position
0872: MainEditorFrame.instance.editorPanel.selectLinePart(startLine-1, startCol-1, foundEnd-foundStart);
0873:
0874: // 3) wait for response
0875: boolean replaceThis = false;
0876: if(replaceAllInThisFileWithoutConfirm)
0877: {
0878: replaceThis = true;
0879: }
0880: else
0881: {
0882: confirmDialog.choice = ConfirmDialog.Choice.Undecided;
0883: confirmDialog.setVisible(true); // MODAL
0884: // 4) analyse response
0885:
0886: if(confirmDialog.choice == ConfirmDialog.Choice.Cancel)
0887: {
0888: //MainEditorFrame.instance.outputPanels.searchOutputPanel.doc.appendLine("Replacement cancelled");
0889: return;
0890: }
0891: else if(confirmDialog.choice == ConfirmDialog.Choice.Yes)
0892: {
0893: replaceThis = true;
0894: }
0895: else if(confirmDialog.choice == ConfirmDialog.Choice.YesAll)
0896: {
0897: replaceThis = true;
0898: replaceAllInThisFileWithoutConfirm = true;
0899: }
0900: else if(confirmDialog.choice == ConfirmDialog.Choice.YesAllAll)
0901: {
0902: replaceThis = true;
0903: replaceAllInThisFileWithoutConfirm = true;
0904: replaceAllInAllFilesWithoutConfirm = true;
0905: }
0906: }
0907:
0908: // 5) replace
0909: if(replaceThis)
0910: {
0911: //System.out.println("REPLACE !");
0912: int docpos = DocumentUtils.getDocPositionFor(MainEditorFrame.instance.editorPanel.getDocument(), startLine-1, startCol-1);
0913: MainEditorFrame.instance.editorPanel.getDocument().replace( replacement, docpos, foundEnd-foundStart);
0914:
0915: // TODO:
0916: // reload the text, it has changed !!
0917: }
0918:
0919: }*/
0920:
0921: static final class ConfirmDialog extends JDialog {
0922: JButton yes = new JButton("yes");
0923: JButton yesAll = new JButton("yes for all in this file");
0924: JButton yesAllAll = new JButton("yes for all in ALL files");
0925: JButton cancel = new JButton("cancel");
0926: JButton no = new JButton("no");
0927:
0928: public enum Choice {
0929: Yes, No, YesAll, Cancel, Undecided, YesAllAll
0930: }
0931:
0932: public Choice choice = Choice.Undecided;
0933:
0934: public final JTextArea textArea = new JTextArea(
0935: "Do you want to replace that occurence ?");
0936:
0937: public ConfirmDialog(JDialog parent) {
0938: super (parent, "Text Replacement", true);
0939: setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
0940:
0941: JPanel btPanel = new JPanel(new FlowLayout());
0942: btPanel.add(yes);
0943: btPanel.add(yesAll);
0944: btPanel.add(yesAllAll);
0945: btPanel.add(no);
0946: btPanel.add(cancel);
0947:
0948: yes.setMargin(new Insets(0, 1, 0, 1));
0949: yesAll.setMargin(new Insets(0, 1, 0, 1));
0950: yesAllAll.setMargin(new Insets(0, 1, 0, 1));
0951: no.setMargin(new Insets(0, 1, 0, 1));
0952: cancel.setMargin(new Insets(0, 1, 0, 1));
0953:
0954: add(btPanel, BorderLayout.SOUTH);
0955:
0956: add(textArea, BorderLayout.CENTER);
0957: textArea.setEditable(false);
0958: textArea.setBorder(new EmptyBorder(5, 5, 5, 5));
0959:
0960: ActionListener acli = new ActionListener() {
0961: public void actionPerformed(ActionEvent ae) {
0962: if (ae.getSource() == yes) {
0963: choice = Choice.Yes;
0964: }
0965: if (ae.getSource() == no) {
0966: choice = Choice.No;
0967: }
0968: if (ae.getSource() == cancel) {
0969: choice = Choice.Cancel;
0970: }
0971: if (ae.getSource() == yesAll) {
0972: choice = Choice.YesAll;
0973: }
0974: if (ae.getSource() == yesAllAll) {
0975: choice = Choice.YesAllAll;
0976: }
0977: setVisible(false);
0978: }
0979: };
0980:
0981: yes.addActionListener(acli);
0982: no.addActionListener(acli);
0983: yesAll.addActionListener(acli);
0984: yesAllAll.addActionListener(acli);
0985: cancel.addActionListener(acli);
0986:
0987: pack();
0988: setLocationRelativeTo(MainEditorFrame.instance);
0989: }
0990: }
0991:
0992: /*test
0993:
0994: public static void main(String[] a)
0995: {
0996: for(String[] sr : someRegexes)
0997: {
0998: if(sr==null) continue;
0999: System.out.println(" \t"+sr[1]+" \t"+sr[2]);
1000: }
1001: try{
1002: SearchTool sp = new SearchTool(new ArrayList<SourceFile>(), false, null);
1003: } finally {
1004: System.exit(0);
1005: }
1006:
1007: }*/
1008: }
|