| /******************************************************************************* |
| * Copyright (c) 2004, 2006 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.wst.xml.core.internal.search; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.wst.common.core.search.SearchMatch; |
| import org.eclipse.wst.common.core.search.SearchParticipant; |
| import org.eclipse.wst.common.core.search.SearchRequestor; |
| import org.eclipse.wst.common.core.search.document.ComponentDeclarationEntry; |
| import org.eclipse.wst.common.core.search.document.Entry; |
| import org.eclipse.wst.common.core.search.document.FileReferenceEntry; |
| import org.eclipse.wst.common.core.search.document.SearchDocument; |
| import org.eclipse.wst.common.core.search.document.SearchDocumentSet; |
| import org.eclipse.wst.common.core.search.pattern.ComponentSearchPattern; |
| import org.eclipse.wst.common.core.search.pattern.FileReferencePattern; |
| import org.eclipse.wst.common.core.search.pattern.SearchPattern; |
| import org.eclipse.wst.common.core.search.scope.ContentTypeSearchScope; |
| import org.eclipse.wst.common.core.search.scope.SearchScope; |
| import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolver; |
| import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.core.internal.search.impl.IXMLSearchConstants; |
| import org.eclipse.wst.xml.core.internal.search.impl.XMLSearchDocument; |
| import org.eclipse.wst.xml.core.internal.search.matching.PatternMatcher; |
| import org.eclipse.wst.xml.core.internal.search.matching.XMLSearchPatternMatcher; |
| import org.eclipse.wst.xml.core.internal.search.quickscan.XMLQuickScan; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * |
| */ |
| public abstract class XMLSearchParticipant extends SearchParticipant { |
| |
| protected static final boolean debugPerf = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.xml.core.internal.search/perf")); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| public XMLSearchParticipant() { |
| super(); |
| } |
| |
| |
| /* |
| public boolean initialize(SearchPattern pattern, String[] contentTypes){ |
| |
| super.initialize(pattern, contentTypes); |
| this.supportedContentTypes = contentTypes; |
| |
| if(pattern instanceof XMLComponentSearchPattern){ |
| return true; |
| } |
| return false; |
| }*/ |
| |
| |
| |
| public SearchDocument createSearchDocument(String documentPath) { |
| |
| return new XMLSearchDocument(documentPath, this); |
| |
| } |
| |
| public String getDescription() { |
| return "XML search participant"; //$NON-NLS-1$ |
| } |
| |
| |
| |
| private void locateMatches(SearchPattern pattern, SearchDocument document, |
| SearchRequestor requestor, Map searchOptions, IProgressMonitor monitor) { |
| |
| // TODO... utilize search options (that should get passed down via the SearchEngine) |
| // to specify if accurate source coordinates are req'd if not, simply use the SAX results |
| // |
| if (pattern.getMatchRule() == SearchPattern.R_PATTERN_MATCH) |
| { |
| IFile file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(document.getPath())); |
| // TODO.. don't assume the category is COMPONENT_DECL... handle any arbitarty category |
| Entry[] entries = document.getEntries(IXMLSearchConstants.COMPONENT_DECL, null, 0); |
| for (int i = 0; i < entries.length; i++) |
| { |
| // TODO.. don't assume this is just a component declaration entry |
| ComponentDeclarationEntry entry = (ComponentDeclarationEntry)entries[i]; |
| SearchMatch searchMatch = new SearchMatch(null, 0, 0, file); |
| searchMatch.map.put("name", entry.getName()); //$NON-NLS-1$ |
| searchMatch.map.put("metaName", entry.getMetaName()); //$NON-NLS-1$ |
| try |
| { |
| requestor.acceptSearchMatch(searchMatch); |
| } |
| catch (Exception e) |
| { |
| } |
| } |
| } |
| else |
| { if (document.getModel() instanceof IDOMModel) { |
| IDOMModel domModel = (IDOMModel) document.getModel(); |
| IDOMElement contextNode = (IDOMElement) domModel.getDocument() |
| .getDocumentElement(); |
| DOMVisitor visitor = new DOMVisitor(document.getPath(), pattern, |
| requestor); |
| visitor.visit(contextNode); |
| } |
| } |
| } |
| |
| private PatternMatcher getAdapter(Object adaptableObject, Class adapterType) { |
| if (PatternMatcher.class.equals(adapterType) && |
| (adaptableObject instanceof XMLSearchPattern || |
| adaptableObject instanceof XMLComponentSearchPattern) ) { |
| return new XMLSearchPatternMatcher(); |
| } |
| return null; |
| } |
| |
| private class DOMVisitor { |
| |
| String path; |
| SearchPattern pattern; |
| SearchRequestor requestor; |
| PatternMatcher matcher; |
| |
| |
| protected DOMVisitor(String path, SearchPattern pattern, |
| SearchRequestor requestor) { |
| super(); |
| this.path = path; |
| this.pattern = pattern; |
| |
| matcher = (PatternMatcher)pattern.getAdapter(PatternMatcher.class); |
| if(matcher == null){ |
| matcher = getAdapter(pattern, PatternMatcher.class); |
| } |
| this.requestor = requestor; |
| } |
| |
| private void visit(Node node) { |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| match((Element)node); |
| NodeList nodeList = node.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node aNode = nodeList.item(i); |
| visit(aNode); |
| } |
| } |
| } |
| |
| private void match(Element node) { |
| IFile file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation( |
| new Path(path)); |
| if(matcher != null){ |
| matcher.locateMatches(pattern, file, node, requestor); |
| } |
| |
| |
| } |
| |
| } |
| |
| public SearchScope selectDocumentLocations(SearchPattern pattern, SearchScope scope, Map searchOptions, IProgressMonitor monitor) { |
| /* |
| * gate #1: reduce the scope to the files with the content type that |
| * could be searched using this participant |
| */ |
| String[] contentTypes = getSupportedContentTypes(); |
| if(contentTypes != null && contentTypes.length > 0){ |
| scope = new ContentTypeSearchScope(scope, contentTypes); |
| } |
| return super.selectDocumentLocations(pattern, scope, searchOptions, monitor); |
| } |
| |
| public abstract ComponentSearchContributor getSearchContributor(); |
| |
| |
| public void beginSearching(SearchPattern pattern, Map searchOptions) { |
| |
| super.beginSearching(pattern, searchOptions); |
| if(pattern instanceof XMLComponentDeclarationPattern){ |
| XMLComponentDeclarationPattern componentPattern = (XMLComponentDeclarationPattern)pattern; |
| XMLSearchPattern childPattern = getSearchContributor().getDeclarationPattern(componentPattern.getMetaName()); |
| if(childPattern != null){ |
| childPattern.setSearchName(componentPattern.getName().getLocalName()); |
| childPattern.setSearchNamespace(componentPattern.getName().getNamespace()); |
| componentPattern.setChildren(new XMLSearchPattern[]{childPattern}); |
| } |
| |
| } |
| else if(pattern instanceof XMLComponentReferencePattern){ |
| XMLComponentReferencePattern componentPattern = (XMLComponentReferencePattern)pattern; |
| XMLSearchPattern[] childPatterns = getSearchContributor().getReferencesPatterns(componentPattern.getMetaName()); |
| for (int i = 0; i < childPatterns.length; i++) { |
| XMLSearchPattern childPattern = childPatterns[i]; |
| childPattern.setSearchName(componentPattern.getName().getLocalName()); |
| childPattern.setSearchNamespace(componentPattern.getName().getNamespace()); |
| } |
| componentPattern.setChildren(childPatterns); |
| |
| } |
| } |
| |
| |
| /** |
| * The intend of this method is to limit the search to the files that have content |
| * which can be searched for the given pattern. It is called from |
| * {@link #selectDocumentLocations(SearchPattern, SearchScope, IProgressMonitor)} |
| * |
| * @param pattern the search pattern that is searched for |
| * @return content type's unique identifiers that could be searched for the given pattern. |
| */ |
| public abstract String[] getSupportedContentTypes(); |
| |
| public void populateSearchDocument(SearchDocument document, SearchPattern pattern) |
| { |
| PatternMatcher matcher = (PatternMatcher)pattern.getAdapter(PatternMatcher.class); |
| if(matcher == null){ |
| matcher = getAdapter(pattern, PatternMatcher.class); |
| } |
| XMLQuickScan.populateSearchDocument(document, matcher, pattern); |
| } |
| |
| public void locateMatches(SearchDocumentSet documentSet, SearchPattern pattern, SearchScope scope, SearchRequestor requestor, Map searchOptions, IProgressMonitor monitor) throws CoreException |
| { |
| long time = System.currentTimeMillis(); |
| |
| // TODO: use the file reference entries in the documents to reduce the scope to the referenced files only |
| // SearchDocument[] documents = documentSet.getSearchDocuments(id); |
| |
| // check to see if the search pattern is qualified by a file location |
| // if this is the case then we can use file scoping rules to prune the matches |
| IFile targetFile = null; |
| if (pattern instanceof ComponentSearchPattern) |
| { |
| ComponentSearchPattern componentSearchPattern = (ComponentSearchPattern)pattern; |
| targetFile = componentSearchPattern.getFile(); |
| } |
| |
| // here we should have in scope only referenced files |
| IFile[] files = scope.enclosingFiles(); |
| for (int i = 0; i < files.length; i++) |
| { |
| IFile file = files[i]; |
| String path = file.getLocation().toString(); |
| SearchDocument document = documentSet.getSearchDocument(path, id); |
| if (document != null) |
| { |
| Entry[] entries = document.getEntries(getSearchEntryCategory(pattern), null, 0); |
| if ((entries != null && entries.length > 0) || (searchOptions != null && searchOptions.get("searchDirtyContent") != null)) |
| { |
| //for (int j = 0; j < entries.length; j++) |
| //{ |
| // Entry entry = entries[j]; |
| //System.out.println("entry " + entry.getCategory() + " " + entry.getKey() + " " + entry.getClass().getName()); |
| //} |
| |
| boolean isInScope = true; |
| if (targetFile != null) |
| { |
| try |
| { |
| isInScope = isLinked(documentSet, "file:///" + path, "file:///" + targetFile.getLocation().toString()); //$NON-NLS-1$ //$NON-NLS-2$ |
| //if (path.endsWith("CancelSelection.wsdl") && path.indexOf("clone1") != -1) |
| //{ |
| // fileReferenceTable.debug(qualifiedPath, 0, 5); |
| //} |
| } |
| catch (Exception e) |
| { |
| e.printStackTrace(); |
| } |
| } |
| if (isInScope) |
| { |
| this.locateMatches(pattern, document, requestor, searchOptions, monitor); |
| } |
| } |
| } |
| } |
| |
| |
| if (debugPerf) |
| { |
| System.out |
| .println("" //$NON-NLS-1$ |
| + getDescription() |
| + ": " + (System.currentTimeMillis() - time) + "ms for locateMatches"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| |
| } |
| |
| private boolean isLinked(SearchDocumentSet set, String source, String target) |
| { |
| return isLinked(set, source, target, new HashMap()); |
| } |
| |
| private boolean isLinked(SearchDocumentSet set, String source, String target, HashMap visited) |
| { |
| if (source.equals(target)) |
| return true; |
| |
| String fileProtocol = "file:///"; //$NON-NLS-1$ |
| if (source.startsWith(fileProtocol)) |
| { |
| |
| SearchDocument document = set._tempGetSearchDocumetn(source.substring(fileProtocol.length())); |
| if (document != null) |
| { |
| URIResolver uriResolver = URIResolverPlugin.createResolver(); |
| Entry[] entries = document.getEntries(IXMLSearchConstants.REF, null, 0); |
| String[] resolveEntry = new String[entries.length]; |
| for (int j = 0; j < entries.length; j++) |
| { |
| Entry entry = entries[j]; |
| if (entry instanceof FileReferenceEntry) |
| { |
| FileReferenceEntry fileReferenceEntry = (FileReferenceEntry)entry; |
| // TODO.. record an utilize the public id from the fileReferenceEntry |
| // |
| if (fileReferenceEntry.getRelativeFilePath() != null) |
| { |
| String resolvedURI = uriResolver.resolve(source, null, fileReferenceEntry.getRelativeFilePath()); |
| resolveEntry[j] = resolvedURI; |
| if (resolvedURI.equals(target)) |
| { |
| return true; |
| } |
| } |
| } |
| } |
| // now see if there's an indirect link from the source to the target |
| // we keep track of the nodes we've already visited to avoid cycles |
| if (visited.get(source) == null) |
| { |
| visited.put(source, Boolean.TRUE); |
| for (int j = 0; j < entries.length; j++) |
| { |
| String resolvedURI = resolveEntry[j]; |
| if (resolvedURI != null && isLinked(set, resolveEntry[j], target, visited)) |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| |
| public static String getSearchEntryCategory(SearchPattern pattern){ |
| if(pattern instanceof XMLComponentDeclarationPattern){ |
| return IXMLSearchConstants.COMPONENT_DECL; |
| } |
| else if(pattern instanceof XMLComponentReferencePattern){ |
| return IXMLSearchConstants.COMPONENT_REF; |
| } |
| else if(pattern instanceof FileReferencePattern){ |
| return IXMLSearchConstants.COMPONENT_REF; |
| } |
| return null; |
| } |
| } |