001: /*******************************************************************************
002: * Copyright (c) 2005, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.ui.search;
011:
012: import java.util.regex.Matcher;
013: import java.util.regex.Pattern;
014:
015: import org.eclipse.core.resources.IFile;
016: import org.eclipse.core.resources.IProject;
017: import org.eclipse.core.runtime.CoreException;
018: import org.eclipse.core.runtime.IPath;
019: import org.eclipse.core.runtime.IProgressMonitor;
020: import org.eclipse.jdt.core.IJavaElement;
021: import org.eclipse.jdt.core.IType;
022: import org.eclipse.jdt.ui.search.ElementQuerySpecification;
023: import org.eclipse.jdt.ui.search.IMatchPresentation;
024: import org.eclipse.jdt.ui.search.IQueryParticipant;
025: import org.eclipse.jdt.ui.search.ISearchRequestor;
026: import org.eclipse.jdt.ui.search.PatternQuerySpecification;
027: import org.eclipse.jdt.ui.search.QuerySpecification;
028: import org.eclipse.jface.text.BadLocationException;
029: import org.eclipse.jface.text.IDocument;
030: import org.eclipse.osgi.util.ManifestElement;
031: import org.eclipse.pde.core.IBaseModel;
032: import org.eclipse.pde.core.plugin.IPluginAttribute;
033: import org.eclipse.pde.core.plugin.IPluginBase;
034: import org.eclipse.pde.core.plugin.IPluginElement;
035: import org.eclipse.pde.core.plugin.IPluginExtension;
036: import org.eclipse.pde.core.plugin.IPluginModelBase;
037: import org.eclipse.pde.core.plugin.IPluginObject;
038: import org.eclipse.pde.core.plugin.IPluginParent;
039: import org.eclipse.pde.core.plugin.PluginRegistry;
040: import org.eclipse.pde.internal.core.ICoreConstants;
041: import org.eclipse.pde.internal.core.PDECore;
042: import org.eclipse.pde.internal.core.ibundle.IBundle;
043: import org.eclipse.pde.internal.core.ibundle.IBundleModel;
044: import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase;
045: import org.eclipse.pde.internal.core.ibundle.IManifestHeader;
046: import org.eclipse.pde.internal.core.ischema.IMetaAttribute;
047: import org.eclipse.pde.internal.core.ischema.ISchema;
048: import org.eclipse.pde.internal.core.ischema.ISchemaAttribute;
049: import org.eclipse.pde.internal.core.ischema.ISchemaElement;
050: import org.eclipse.pde.internal.core.schema.SchemaRegistry;
051: import org.eclipse.pde.internal.core.text.IDocumentAttributeNode;
052: import org.eclipse.pde.internal.core.text.IEditingModel;
053: import org.eclipse.pde.internal.core.util.PatternConstructor;
054: import org.eclipse.pde.internal.ui.PDEUIMessages;
055: import org.eclipse.pde.internal.ui.util.ModelModification;
056: import org.eclipse.pde.internal.ui.util.PDEModelUtility;
057: import org.eclipse.search.ui.text.Match;
058: import org.osgi.framework.BundleException;
059: import org.osgi.framework.Constants;
060:
061: public class ClassSearchParticipant implements IQueryParticipant {
062:
063: private static final int H_IMP = 0;
064: private static final int H_EXP = 1;
065: private static final int H_BUNACT = 2;
066: private static final int H_PLUGCLASS = 3;
067: private static final int H_TOTAL = 4;
068: private static final String[] SEARCH_HEADERS = new String[H_TOTAL];
069: static {
070: SEARCH_HEADERS[H_IMP] = Constants.IMPORT_PACKAGE;
071: SEARCH_HEADERS[H_EXP] = Constants.EXPORT_PACKAGE;
072: SEARCH_HEADERS[H_BUNACT] = Constants.BUNDLE_ACTIVATOR;
073: SEARCH_HEADERS[H_PLUGCLASS] = ICoreConstants.PLUGIN_CLASS;
074: }
075: // the following are from JavaSearchPage (radio button indexes)
076: private static final int S_LIMIT_REF = 2;
077: private static final int S_LIMIT_ALL = 3;
078: private static final int S_FOR_TYPES = 0;
079: private static final int S_FOR_PACKAGES = 2;
080:
081: private ISearchRequestor fSearchRequestor;
082: private Pattern fSearchPattern;
083: private int fSearchFor = -1; // set since S_FOR_TYPES = 0;
084:
085: public ClassSearchParticipant() {
086: }
087:
088: public void search(ISearchRequestor requestor,
089: QuerySpecification querySpecification,
090: IProgressMonitor monitor) throws CoreException {
091:
092: if (querySpecification.getLimitTo() != S_LIMIT_REF
093: && querySpecification.getLimitTo() != S_LIMIT_ALL)
094: return;
095:
096: String search;
097: if (querySpecification instanceof ElementQuerySpecification) {
098: IJavaElement element = ((ElementQuerySpecification) querySpecification)
099: .getElement();
100: if (element instanceof IType)
101: search = ((IType) element).getFullyQualifiedName('.');
102: else
103: search = element.getElementName();
104: int type = element.getElementType();
105: if (type == IJavaElement.TYPE)
106: fSearchFor = S_FOR_TYPES;
107: else if (type == IJavaElement.PACKAGE_FRAGMENT
108: || type == IJavaElement.PACKAGE_FRAGMENT_ROOT)
109: fSearchFor = S_FOR_PACKAGES;
110: } else {
111: fSearchFor = ((PatternQuerySpecification) querySpecification)
112: .getSearchFor();
113: search = ((PatternQuerySpecification) querySpecification)
114: .getPattern();
115: }
116: if (fSearchFor != S_FOR_TYPES && fSearchFor != S_FOR_PACKAGES)
117: return;
118: fSearchPattern = PatternConstructor.createPattern(search, true);
119: fSearchRequestor = requestor;
120:
121: IPath[] enclosingPaths = querySpecification.getScope()
122: .enclosingProjectsAndJars();
123: IPluginModelBase[] pluginModels = PluginRegistry
124: .getWorkspaceModels();
125: monitor.beginTask(
126: PDEUIMessages.ClassSearchParticipant_taskMessage,
127: pluginModels.length);
128: for (int i = 0; i < pluginModels.length; i++) {
129: IProject project = pluginModels[i].getUnderlyingResource()
130: .getProject();
131: if (!monitor.isCanceled()
132: && encloses(enclosingPaths, project.getFullPath()))
133: searchProject(project, monitor);
134: }
135: }
136:
137: private boolean encloses(IPath[] paths, IPath path) {
138: for (int i = 0; i < paths.length; i++)
139: if (paths[i].equals(path))
140: return true;
141: return false;
142: }
143:
144: private void searchProject(IProject project,
145: IProgressMonitor monitor) throws CoreException {
146: ModelModification mod = new ModelModification(project) {
147: protected void modifyModel(IBaseModel model,
148: IProgressMonitor monitor) throws CoreException {
149: if (model instanceof IBundlePluginModelBase) {
150: IBundleModel bmodel = ((IBundlePluginModelBase) model)
151: .getBundleModel();
152: inspectBundle(bmodel.getBundle());
153: model = ((IBundlePluginModelBase) model)
154: .getExtensionsModel();
155: }
156: if (model instanceof IPluginModelBase) {
157: IFile file = (IFile) ((IPluginModelBase) model)
158: .getUnderlyingResource();
159: SchemaRegistry registry = PDECore.getDefault()
160: .getSchemaRegistry();
161: IPluginBase pbase = ((IPluginModelBase) model)
162: .getPluginBase();
163: IPluginExtension[] extensions = pbase
164: .getExtensions();
165: for (int j = 0; j < extensions.length; j++) {
166: ISchema schema = registry
167: .getSchema(extensions[j].getPoint());
168: if (schema != null && !monitor.isCanceled())
169: inspectExtension(schema, extensions[j],
170: file);
171: }
172: }
173: }
174: };
175: PDEModelUtility.modifyModel(mod, monitor);
176: }
177:
178: private void inspectExtension(ISchema schema, IPluginParent parent,
179: IFile file) {
180: IPluginObject[] children = parent.getChildren();
181: for (int i = 0; i < children.length; i++) {
182: IPluginElement child = (IPluginElement) children[i];
183: ISchemaElement schemaElement = schema.findElement(child
184: .getName());
185: if (schemaElement != null) {
186: IPluginAttribute[] attributes = child.getAttributes();
187: for (int j = 0; j < attributes.length; j++) {
188: IPluginAttribute attr = attributes[j];
189: ISchemaAttribute attInfo = schemaElement
190: .getAttribute(attr.getName());
191: if (attInfo != null
192: && attInfo.getKind() == IMetaAttribute.JAVA
193: && attr instanceof IDocumentAttributeNode)
194: checkMatch(attr, file);
195: }
196: }
197: inspectExtension(schema, child, file);
198: }
199: }
200:
201: private void checkMatch(IPluginAttribute attr, IFile file) {
202: String value = null;
203: Matcher matcher = null;
204: if (fSearchFor == S_FOR_TYPES) {
205: value = attr.getValue().replaceAll("\\$", "."); //$NON-NLS-1$ //$NON-NLS-2$
206: matcher = getMatcher(value);
207: }
208: if (value == null || (matcher != null && !matcher.matches())) {
209: value = getProperValue(attr.getValue()).replaceAll(
210: "\\$", "."); //$NON-NLS-1$ //$NON-NLS-2$
211: matcher = getMatcher(value);
212: }
213: if (matcher.matches()) {
214: String group = matcher.group(0);
215: int offset = ((IDocumentAttributeNode) attr)
216: .getValueOffset()
217: + value.indexOf(group);
218: int attOffset = attr.getValue().indexOf(value);
219: if (attOffset != -1)
220: offset += attOffset;
221: int length = group.length();
222: fSearchRequestor.reportMatch(new Match(file,
223: Match.UNIT_CHARACTER, offset, length));
224: }
225: }
226:
227: private void inspectBundle(IBundle bundle) {
228: for (int i = 0; i < H_TOTAL; i++) {
229: if (fSearchFor == S_FOR_TYPES && (i == H_IMP || i == H_EXP))
230: continue;
231: IManifestHeader header = bundle
232: .getManifestHeader(SEARCH_HEADERS[i]);
233: if (header != null) {
234: try {
235: ManifestElement[] elements = ManifestElement
236: .parseHeader(header.getName(), header
237: .getValue());
238: if (elements == null)
239: continue;
240: for (int j = 0; j < elements.length; j++) {
241: String value = null;
242: Matcher matcher = null;
243: if (fSearchFor == S_FOR_TYPES) {
244: value = elements[j].getValue();
245: matcher = getMatcher(value);
246: }
247: if (value == null
248: || (matcher != null && !matcher
249: .matches())) {
250: value = getProperValue(elements[j]
251: .getValue());
252: matcher = getMatcher(value);
253: }
254: if (matcher.matches()) {
255: String group = matcher.group(0);
256: int[] offlen;
257: try {
258: offlen = getOffsetOfElement(bundle,
259: header, group);
260: } catch (CoreException e) {
261: offlen = new int[] {
262: header.getOffset(),
263: header.getLength() };
264: }
265: fSearchRequestor.reportMatch(new Match(
266: bundle.getModel()
267: .getUnderlyingResource(),
268: Match.UNIT_CHARACTER, offlen[0],
269: offlen[1]));
270: break; // only one package will be listed per header
271: }
272: }
273: } catch (BundleException e) {
274: }
275: }
276: }
277: }
278:
279: private Matcher getMatcher(String value) {
280: return fSearchPattern.matcher(value.subSequence(0, value
281: .length()));
282: }
283:
284: private int[] getOffsetOfElement(IBundle bundle,
285: IManifestHeader header, String value) throws CoreException {
286: int[] offlen = new int[] { 0, 0 };
287: IBundleModel model = bundle.getModel();
288: if (model instanceof IEditingModel) {
289: IDocument pDoc = ((IEditingModel) model).getDocument();
290: int headerOffset = header.getOffset()
291: + header.getName().length();
292: try {
293: String headerString = pDoc.get(headerOffset, header
294: .getLength()
295: - header.getName().length());
296: int internalOffset = headerString.indexOf(value);
297: if (internalOffset != -1)
298: offlen[0] = headerOffset + internalOffset;
299: else
300: offlen[0] = headerOffset
301: + header.getName().length()
302: + header.getValue().indexOf(value);
303: offlen[1] = value.length();
304: } catch (BadLocationException e) {
305: }
306: }
307: return offlen;
308: }
309:
310: private String getProperValue(String value) {
311: return fSearchFor == S_FOR_TYPES ? extractType(value)
312: : extractPackage(value);
313: }
314:
315: private String extractType(String value) {
316: int index = value.lastIndexOf("."); //$NON-NLS-1$
317: if (index == -1 || index == value.length() - 1)
318: return value;
319: return value.substring(index + 1);
320: }
321:
322: private String extractPackage(String value) {
323: int index = value.lastIndexOf("."); //$NON-NLS-1$
324: if (index == -1 || index == value.length() - 1)
325: return value;
326: char afterPeriod = value.charAt(index + 1);
327: if (afterPeriod >= 'A' && afterPeriod <= 'Z')
328: return value.substring(0, index);
329: return value;
330: }
331:
332: public int estimateTicks(QuerySpecification specification) {
333: return 100;
334: }
335:
336: public IMatchPresentation getUIParticipant() {
337: return null;
338: }
339: }
|