blob: b709f58b74f0ab292678eadf8ad38f19069f2e5e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 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(this);
}
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.addChildren(this, 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.addChildren(this, 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$
// Fix for bug 204174 - Begin
if(target.charAt(fileProtocol.length()) == '/') //$NON-NLS-1$
{
target = fileProtocol + target.substring(fileProtocol.length() + 1);
}
// Fix for bug 204174 - End
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;
}
}