/******************************************************************************* * Copyright (c) 2005, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.ui.tests.DOMAST; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jface.text.IFindReplaceTarget; import org.eclipse.jface.text.IFindReplaceTargetExtension3; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.c.CASTTranslationUnit; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTranslationUnit; /** * @author dsteffle */ public class FindIASTNameTarget implements IFindReplaceTarget, IFindReplaceTargetExtension3 { IASTTranslationUnit tu = null; DOMASTNodeParent tuTreeParent = null; TreeViewer viewer = null; IASTName[] matchingNames = null; boolean wasForward = true; int index = 0; static protected class CNameCollector extends ASTVisitor { private static final int REGULAR_NAME_ADD = -1; private static final String BLANK_STRING = ""; //$NON-NLS-1$ { shouldVisitNames = true; } public List<IASTName> nameList = new ArrayList<IASTName>(); String findString = null; boolean caseSensitive = true; boolean wholeWord = true; boolean regExSearch = false; public CNameCollector(String findString, boolean caseSensitive, boolean wholeWord, boolean regExSearch) { this.findString = findString; this.caseSensitive = caseSensitive; this.wholeWord = wholeWord; this.regExSearch = regExSearch; } public int processName( IASTName name, int offset) { if (name.toString() == null || name.toString() == BLANK_STRING) return PROCESS_CONTINUE; String searchString = null; String match = null; boolean addName = false; if (caseSensitive) { searchString = findString; match = name.toString(); } else { searchString = findString.toUpperCase(); match = name.toString().toUpperCase(); } if (regExSearch) { if (match.matches(searchString)) addName = true; } else if (!wholeWord) { if (match.indexOf(searchString) >= 0) addName = true; } else { if (match.equals(searchString)) addName = true; } if (addName) { if (offset >= 0) nameList.add(offset, name); else nameList.add( name ); } return PROCESS_CONTINUE; } @Override public int visit( IASTName name ){ return processName(name, REGULAR_NAME_ADD); } public IASTName getName( int idx ){ if( idx < 0 || idx >= nameList.size() ) return null; return nameList.get( idx ); } public int size() { return nameList.size(); } private void mergeName(IASTName name) { if (name instanceof ASTNode) { int offset = ((ASTNode)name).getOffset(); for( int i=0; i<nameList.size(); i++) { if (nameList.get(i) instanceof ASTNode && ((ASTNode)nameList.get(i)).getOffset() > offset) { processName(name, i); return; } } // if couldn't find the proper place to put the name, then add default visit(name); } } public IASTName[] getNameArray(IASTPreprocessorStatement[] statements) { // first merge all of the preprocessor names into the array list for (IASTPreprocessorStatement statement : statements) { if (statement instanceof IASTPreprocessorMacroDefinition) { IASTName name = ((IASTPreprocessorMacroDefinition)statement).getName(); if (name != null) { mergeName(name); } } } // convert the array list into an array of IASTNames IASTName[] namedArray = new IASTName[nameList.size()]; for(int i=0; i<nameList.size(); i++) { if (nameList.get(i) instanceof IASTName) namedArray[i] = nameList.get(i); } return namedArray; } } static protected class CPPNameCollector extends ASTVisitor { private static final int REGULAR_NAME_ADD = -1; private static final String BLANK_STRING = ""; //$NON-NLS-1$ { shouldVisitNames = true; } public List<IASTName> nameList = new ArrayList<IASTName>(); String findString = null; boolean caseSensitive = true; boolean wholeWord = true; boolean regExSearch = false; Pattern p = null; Matcher m = null; public CPPNameCollector(String findString, boolean caseSensitive, boolean wholeWord, boolean regExSearch) { this.findString = findString; this.caseSensitive = caseSensitive; this.wholeWord = wholeWord; this.regExSearch = regExSearch; } public int processName( IASTName name, int index) { if (name.toString() == null || name.toString() == BLANK_STRING) return PROCESS_CONTINUE; String searchString = null; String match = null; boolean addName = false; if (caseSensitive) { searchString = findString; match = name.toString(); } else { searchString = findString.toUpperCase(); match = name.toString().toUpperCase(); } if (regExSearch) { if (match.matches(searchString)) addName = true; } else if (!wholeWord) { if (match.indexOf(searchString) >= 0) addName = true; } else { if (match.equals(searchString)) addName = true; } if (addName) { if (index >= 0) nameList.add(index, name); else nameList.add( name ); } return PROCESS_CONTINUE; } @Override public int visit( IASTName name ){ return processName(name, REGULAR_NAME_ADD); } public IASTName getName( int idx ){ if( idx < 0 || idx >= nameList.size() ) return null; return nameList.get( idx ); } public int size() { return nameList.size(); } private void mergeName(IASTName name) { if (name instanceof ASTNode) { int offset = ((ASTNode)name).getOffset(); for( int i=0; i<nameList.size(); i++) { if (nameList.get(i) instanceof ASTNode && ((ASTNode)nameList.get(i)).getOffset() > offset) { processName(name, i); return; } } // if couldn't find the proper place to put the name, then add default visit(name); } } public IASTName[] getNameArray(IASTPreprocessorStatement[] statements) { // first merge all of the preprocessor names into the array list for (IASTPreprocessorStatement statement : statements) { if (statement instanceof IASTPreprocessorMacroDefinition) { IASTName name = ((IASTPreprocessorMacroDefinition)statement).getName(); if (name != null) { mergeName(name); } } } // convert the array list into an array of IASTNames IASTName[] namedArray = new IASTName[nameList.size()]; for(int i=0; i<nameList.size(); i++) { if (nameList.get(i) instanceof IASTName) namedArray[i] = nameList.get(i); } return namedArray; } } public FindIASTNameTarget(TreeViewer viewer) { if (viewer.getContentProvider() instanceof DOMAST.ViewContentProvider) { tu = ((DOMAST.ViewContentProvider)viewer.getContentProvider()).getTU(); tuTreeParent = ((DOMAST.ViewContentProvider)viewer.getContentProvider()).getTUTreeParent(); } this.viewer = viewer; } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#canPerformFind() */ public boolean canPerformFind() { return true; } // recursively search for the next node public IASTName findNextMatchingName(String findString, boolean searchForward, boolean caseSensitive, boolean wholeWord, boolean regExSearch) { if (matchingNames == null && tu != null) { if (tu instanceof CPPASTTranslationUnit) { CPPNameCollector col = new CPPNameCollector(findString, caseSensitive, wholeWord, regExSearch); tu.accept(col); matchingNames = col.getNameArray(tu.getAllPreprocessorStatements()); } else if(tu instanceof CASTTranslationUnit) { CNameCollector col = new CNameCollector(findString, caseSensitive, wholeWord, regExSearch); tu.accept(col); matchingNames = col.getNameArray(tu.getAllPreprocessorStatements()); } } if (searchForward) { if (!wasForward) { wasForward = true; index+=2; } if (index >=0 && index < matchingNames.length && matchingNames[index] != null) return matchingNames[index++]; } else { if (wasForward) { wasForward = false; index-=2; } if (index >= 0 && index < matchingNames.length && matchingNames[index] != null) return matchingNames[index--]; } return null; } private TreeItem expandTreeToTreeObject(TreeItem[] treeItems, DOMASTNodeLeaf treeObj) { for (TreeItem treeItem : treeItems) { if (treeItem.getData() == treeObj) { return treeItem; } DOMASTNodeParent parent = treeObj.getParent(); if (parent == null) return null; while (parent != treeItem.getData()) { parent = parent.getParent(); if (parent == null) break; } if (parent == treeItem.getData()) { treeItem.setExpanded(true); viewer.refresh(); return expandTreeToTreeObject(treeItem.getItems(), treeObj); } } return null; // nothing found } private TreeItem expandTreeToTreeObject(DOMASTNodeLeaf treeObj) { return expandTreeToTreeObject(viewer.getTree().getItems(), treeObj); } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#findAndSelect(int, java.lang.String, boolean, boolean, boolean) */ public int findAndSelect(int widgetOffset, String findString, boolean searchForward, boolean caseSensitive, boolean wholeWord) { return findAndSelect(widgetOffset, findString, searchForward, caseSensitive, wholeWord, false); } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#getSelection() */ public Point getSelection() { IStructuredSelection selection = (IStructuredSelection)viewer.getSelection(); if (selection.isEmpty()) { return new Point(0, 0); } return new Point(((ASTNode)((DOMASTNodeLeaf)selection.getFirstElement()).getNode()).getOffset(), 0); } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#getSelectionText() */ public String getSelectionText() { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#isEditable() */ public boolean isEditable() { // TODO Auto-generated method stub return false; } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTarget#replaceSelection(java.lang.String) */ public void replaceSelection(String text) { // TODO Auto-generated method stub } public void clearMatchingNames() { matchingNames = null; index = 0; } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTargetExtension3#findAndSelect(int, java.lang.String, boolean, boolean, boolean, boolean) */ public int findAndSelect(int offset, String findString, boolean searchForward, boolean caseSensitive, boolean wholeWord, boolean regExSearch) { // find the next name in the list of names IASTName foundName = null; foundName = findNextMatchingName( findString, searchForward, caseSensitive, wholeWord, regExSearch ); // get the DOMASTNodeLeaf from the AST View's model corresponding to that name DOMASTNodeLeaf treeNode = null; TreeItem treeItem = null; treeNode = tuTreeParent.findTreeObject(foundName, true); if (treeNode != null && treeNode.getParent() != null) { // found a matching DOMASTNodeLeaf, so expand the tree to that object treeItem = expandTreeToTreeObject(treeNode); } // loop until the next name within the matchingNames list is found in the tree while ((treeNode == null || treeItem == null) && matchingNames.length > 0 && ((searchForward && index < matchingNames.length) || (!searchForward && index >= 0))) { foundName = findNextMatchingName( findString, searchForward, caseSensitive, wholeWord, regExSearch ); treeNode = tuTreeParent.findTreeObject(foundName, true); if (treeNode != null && treeNode.getParent() != null) { // found a matching DOMASTNodeLeaf, so expand the tree to that object treeItem = expandTreeToTreeObject(treeNode); } } // select the node that was found (and is now displayed) if (treeItem != null) { TreeItem[] items = new TreeItem[1]; items[0] = treeItem; treeItem.getParent().setSelection(items); return 0; } return -1; } /* (non-Javadoc) * @see org.eclipse.jface.text.IFindReplaceTargetExtension3#replaceSelection(java.lang.String, boolean) */ public void replaceSelection(String text, boolean regExReplace) { // TODO Auto-generated method stub } }