/******************************************************************************* * Copyright (c) 2009 SpringSource, a divison of VMware, Inc. * 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: * SpringSource, a division of VMware, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.virgo.ide.ui.editors.text; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.StringTokenizer; import org.eclipse.core.resources.IProject; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.contentassist.ContentAssistEvent; import org.eclipse.jface.text.contentassist.ICompletionListener; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.osgi.service.resolver.BundleDescription; import org.eclipse.osgi.service.resolver.ExportPackageDescription; import org.eclipse.osgi.util.ManifestElement; import org.eclipse.pde.core.IBaseModel; import org.eclipse.pde.core.plugin.IPluginModelBase; import org.eclipse.pde.core.plugin.ModelEntry; import org.eclipse.pde.core.plugin.PluginRegistry; import org.eclipse.pde.internal.core.ICoreConstants; import org.eclipse.pde.internal.core.ibundle.IBundleModel; import org.eclipse.pde.internal.core.util.HeaderMap; import org.eclipse.pde.internal.core.util.PDEJavaHelper; import org.eclipse.pde.internal.ui.PDEPluginImages; import org.eclipse.pde.internal.ui.editor.PDEFormEditor; import org.eclipse.pde.internal.ui.editor.PDESourcePage; import org.eclipse.pde.internal.ui.editor.contentassist.TypePackageCompletionProcessor; import org.eclipse.pde.internal.ui.editor.plugin.ManifestEditor; import org.eclipse.pde.internal.ui.util.ImageOverlayIcon; import org.eclipse.pde.internal.ui.util.PDEJavaHelperUI; import org.eclipse.swt.graphics.Image; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.Version; /** * @author Christian Dupuis */ public class AbstractPdeManifestContentAssistProcessor extends TypePackageCompletionProcessor implements ICompletionListener { protected PDESourcePage fSourcePage; private IJavaProject fJP; // if we order the headers alphabetically in the array, there is no need to // sort and we can save time protected static String[] fHeader = { Constants.BUNDLE_ACTIVATOR, Constants.BUNDLE_ACTIVATIONPOLICY, Constants.BUNDLE_CATEGORY, Constants.BUNDLE_CLASSPATH, Constants.BUNDLE_CONTACTADDRESS, Constants.BUNDLE_COPYRIGHT, Constants.BUNDLE_DESCRIPTION, Constants.BUNDLE_DOCURL, Constants.BUNDLE_LOCALIZATION, Constants.BUNDLE_MANIFESTVERSION, Constants.BUNDLE_NAME, Constants.BUNDLE_NATIVECODE, Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_UPDATELOCATION, Constants.BUNDLE_VENDOR, Constants.BUNDLE_VERSION, Constants.DYNAMICIMPORT_PACKAGE, ICoreConstants.ECLIPSE_BUDDY_POLICY, ICoreConstants.ECLIPSE_GENERIC_CAPABILITY, ICoreConstants.ECLIPSE_GENERIC_REQUIRED, ICoreConstants.ECLIPSE_LAZYSTART, Constants.EXPORT_PACKAGE, ICoreConstants.PLATFORM_FILTER, ICoreConstants.ECLIPSE_REGISTER_BUDDY, ICoreConstants.EXPORT_SERVICE, Constants.IMPORT_PACKAGE, ICoreConstants.IMPORT_SERVICE, Constants.REQUIRE_BUNDLE, Constants.FRAGMENT_HOST }; private static final String BAUMAN = "Brian Bauman"; //$NON-NLS-1$ private static final String ANISZCZYK = "Chris Aniszczyk"; //$NON-NLS-1$ private static final String LASOCKI_BICZYSKO = "Janek Lasocki-Biczysko"; //$NON-NLS-1$ private static final String PAWLOWSKI = "Mike Pawlowski"; //$NON-NLS-1$ private static final String MELHEM = "Wassim Melhem"; //$NON-NLS-1$ private static final String[] fNames = { BAUMAN, ANISZCZYK, LASOCKI_BICZYSKO, PAWLOWSKI, MELHEM }; protected static final short F_TYPE_HEADER = 0, // header proposal F_TYPE_PKG = 1, // package proposal F_TYPE_BUNDLE = 2, // bundle proposal F_TYPE_CLASS = 3, // class proposal F_TYPE_DIRECTIVE = 4, // directive proposal F_TYPE_ATTRIBUTE = 5, // attribute proposal F_TYPE_VALUE = 6, // value of attribute or directive proposal F_TYPE_EXEC_ENV = 7, // value of execution env., added since we // use a unique icon for exec envs. F_TYPE_LIB = 8, F_TOTAL_TYPES = 9; protected final Image[] fImages = new Image[F_TOTAL_TYPES]; private static final String[] fExecEnvs; static { IExecutionEnvironment[] envs = JavaRuntime.getExecutionEnvironmentsManager().getExecutionEnvironments(); fExecEnvs = new String[envs.length]; for (int i = 0; i < envs.length; i++) { fExecEnvs[i] = envs[i].getId(); } Arrays.sort(fExecEnvs, new Comparator() { public int compare(Object o1, Object o2) { return ((String) o1).compareToIgnoreCase((String) o2); } }); } protected Map fHeaders; public AbstractPdeManifestContentAssistProcessor(PDESourcePage sourcePage) { fSourcePage = sourcePage; } @Override public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { IDocument doc = fSourcePage.getDocumentProvider().getDocument(fSourcePage.getInputContext().getInput()); if (fHeaders == null) { parseDocument(doc); } try { int lineNum = doc.getLineOfOffset(offset); int lineStart = doc.getLineOffset(lineNum); return computeCompletionProposals(doc, lineStart, offset); } catch (BadLocationException e) { } return null; } protected final void parseDocument(IDocument doc) { fHeaders = new HeaderMap(); int numLines = doc.getNumberOfLines(); int offset = 0; for (int i = 0; i < numLines; i++) { try { IRegion line = doc.getLineInformation(i); String value = doc.get(offset, line.getOffset() + line.getLength() - offset); if (value.indexOf(':') != value.lastIndexOf(':') || i == (numLines - 1)) { value = doc.get(offset, line.getOffset() - offset - 1).trim(); int index = value.indexOf(':'); String header = (index == -1) ? value : value.substring(0, index); try { if (value.endsWith(",")) { value = value.substring(0, value.length() - 1); } ManifestElement[] elems = ManifestElement.parseHeader(header, value.substring(index + 1)); if (shouldStoreSet(header)) { HashSet set = new HashSet((4 / 3) * elems.length + 1); for (ManifestElement element : elems) { set.add(element.getValue()); } fHeaders.put(header, set); } else { fHeaders.put(header, elems); } } catch (BundleException e) { } offset = line.getOffset(); } } catch (BadLocationException e) { } } } protected boolean shouldStoreSet(String header) { return header.equalsIgnoreCase(Constants.IMPORT_PACKAGE) || header.equalsIgnoreCase(Constants.EXPORT_PACKAGE) || header.equalsIgnoreCase(Constants.REQUIRE_BUNDLE) || header.equalsIgnoreCase(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT); } protected ICompletionProposal[] computeCompletionProposals(IDocument doc, int startOffset, int offset) { try { if (!isHeader(doc, startOffset, offset)) { return computeValue(doc, startOffset, offset); } return computeHeader(doc.get(startOffset, offset - startOffset), startOffset, offset); } catch (BadLocationException e) { } return new ICompletionProposal[0]; } protected final boolean isHeader(IDocument doc, int startOffset, int offset) throws BadLocationException { String value = doc.get(startOffset, offset - startOffset); if (value.indexOf(':') != -1) { return false; } for (--startOffset; startOffset >= 0; --startOffset) { char ch = doc.getChar(startOffset); if (!Character.isWhitespace(ch)) { return ch != ',' && ch != ':' && ch != ';'; } } return true; } protected ICompletionProposal[] computeHeader(String currentValue, int startOffset, int offset) { ArrayList completions = new ArrayList(); IBaseModel model = fSourcePage.getInputContext().getModel(); int length = fHeader.length; if (model instanceof IBundleModel && !((IBundleModel) model).isFragmentModel()) { --length; } for (String element : fHeader) { if (element.regionMatches(true, 0, currentValue, 0, currentValue.length()) && fHeaders.get(element) == null) { BundleTypeCompletionProposal proposal = new BundleTypeCompletionProposal( element + ": ", getImage(F_TYPE_HEADER), //$NON-NLS-1$ element, startOffset, currentValue.length()); proposal.setAdditionalProposalInfo(getJavaDoc(element)); completions.add(proposal); } } return (ICompletionProposal[]) completions.toArray(new ICompletionProposal[completions.size()]); } protected ICompletionProposal[] computeValue(IDocument doc, int startOffset, int offset) throws BadLocationException { String value = doc.get(startOffset, offset - startOffset); int lineNum = doc.getLineOfOffset(startOffset) - 1; int index; while ((index = value.indexOf(':')) == -1 || ((value.length() - 1 != index) && (value.charAt(index + 1) == '='))) { int startLine = doc.getLineOffset(lineNum); value = doc.get(startLine, offset - startLine); lineNum--; } int length = value.length(); if (value.regionMatches(true, 0, Constants.IMPORT_PACKAGE, 0, Math.min(length, Constants.IMPORT_PACKAGE .length()))) { return handleImportPackageCompletion(value.substring(Constants.IMPORT_PACKAGE.length() + 1), offset); } if (value .regionMatches(true, 0, Constants.FRAGMENT_HOST, 0, Math.min(length, Constants.FRAGMENT_HOST.length()))) { return handleFragmentHostCompletion(value.substring(Constants.FRAGMENT_HOST.length() + 1), offset); } if (value.regionMatches(true, 0, Constants.REQUIRE_BUNDLE, 0, Math.min(length, Constants.REQUIRE_BUNDLE .length()))) { return handleRequireBundleCompletion(value.substring(Constants.REQUIRE_BUNDLE.length() + 1), offset); } if (value.regionMatches(true, 0, Constants.EXPORT_PACKAGE, 0, Math.min(length, Constants.EXPORT_PACKAGE .length()))) { return handleExportPackageCompletion(value.substring(Constants.EXPORT_PACKAGE.length() + 1), offset); } if (value.regionMatches(true, 0, Constants.BUNDLE_ACTIVATOR, 0, Math.min(length, Constants.BUNDLE_ACTIVATOR .length()))) { return handleBundleActivatorCompletion(removeLeadingSpaces(value.substring(Constants.BUNDLE_ACTIVATOR .length() + 1)), offset); } if (value.regionMatches(true, 0, Constants.BUNDLE_SYMBOLICNAME, 0, Math.min(length, Constants.BUNDLE_SYMBOLICNAME.length()))) { return handleBundleSymbolicNameCompletion(value.substring(Constants.BUNDLE_SYMBOLICNAME.length() + 1), offset); } if (value.regionMatches(true, 0, Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, 0, Math.min(length, Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT.length()))) { return handleRequiredExecEnv(value.substring(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT.length() + 1), offset); } if (value.regionMatches(true, 0, ICoreConstants.ECLIPSE_LAZYSTART, 0, Math.min(length, ICoreConstants.ECLIPSE_LAZYSTART.length()))) { return handleTrueFalseValue(value.substring(ICoreConstants.ECLIPSE_LAZYSTART.length() + 1), offset); } if (value.regionMatches(true, 0, Constants.BUNDLE_NAME, 0, Math.min(length, Constants.BUNDLE_NAME.length()))) { return handleBundleNameCompletion(value.substring(Constants.BUNDLE_NAME.length() + 1), offset); } if (value.regionMatches(true, 0, Constants.BUNDLE_ACTIVATIONPOLICY, 0, Math.min(length, Constants.BUNDLE_ACTIVATIONPOLICY.length()))) { return handleBundleActivationPolicyCompletion(value .substring(Constants.BUNDLE_ACTIVATIONPOLICY.length() + 1), offset); } if (value.regionMatches(true, 0, ICoreConstants.ECLIPSE_BUDDY_POLICY, 0, Math.min(length, ICoreConstants.ECLIPSE_BUDDY_POLICY.length()))) { return handleBuddyPolicyCompletion(value.substring(ICoreConstants.ECLIPSE_BUDDY_POLICY.length() + 1), offset); } return new ICompletionProposal[0]; } /* * Easter Egg */ protected ICompletionProposal[] handleBundleNameCompletion(String currentValue, int offset) { currentValue = removeLeadingSpaces(currentValue); int length = currentValue.length(); // only show when there is no bundle name if (length == 0) { return new ICompletionProposal[] { new BundleTypeCompletionProposal(BAUMAN, null, BAUMAN, offset - length, length), new BundleTypeCompletionProposal(ANISZCZYK, null, ANISZCZYK, offset - length, length), new BundleTypeCompletionProposal(LASOCKI_BICZYSKO, null, LASOCKI_BICZYSKO, offset - length, length), new BundleTypeCompletionProposal(PAWLOWSKI, null, PAWLOWSKI, offset - length, length), new BundleTypeCompletionProposal(MELHEM, null, MELHEM, offset - length, length) }; } // only show when we are trying to complete a name for (String element : fNames) { StringTokenizer tokenizer = new StringTokenizer(currentValue, " "); //$NON-NLS-1$ while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (element.regionMatches(true, 0, token, 0, token.length())) { return new ICompletionProposal[] { new BundleTypeCompletionProposal(element, null, element, offset - token.length(), token.length()) }; } } } return new ICompletionProposal[0]; } protected ICompletionProposal[] handleImportPackageCompletion(String currentValue, int offset) { int comma = currentValue.lastIndexOf(','); int semicolon = currentValue.lastIndexOf(';'); String value = comma != -1 ? currentValue.substring(comma + 1) : currentValue; if (comma > semicolon || comma == semicolon) { HashSet set = (HashSet) fHeaders.get(Constants.IMPORT_PACKAGE); if (set == null) { set = parseHeaderForValues(currentValue, offset); } HashSet importedBundles = (HashSet) fHeaders.get(Constants.REQUIRE_BUNDLE); if (importedBundles == null) { importedBundles = new HashSet(0); } value = removeLeadingSpaces(value); int length = value.length(); set.remove(value); ArrayList completions = new ArrayList(); IPluginModelBase[] bases = PluginRegistry.getActiveModels(); for (IPluginModelBase element : bases) { // Remove any packages // already imported // through // Require-Bundle BundleDescription desc = element.getBundleDescription(); if (desc == null || importedBundles.contains(desc.getSymbolicName())) { continue; } ExportPackageDescription[] expPkgs = desc.getExportPackages(); for (ExportPackageDescription element2 : expPkgs) { String pkgName = element2.getName(); if (pkgName.regionMatches(true, 0, value, 0, length) && !set.contains(pkgName)) { completions.add(new BundleTypeCompletionProposal(pkgName, getImage(F_TYPE_PKG), pkgName, offset - length, length)); set.add(pkgName); } } } ICompletionProposal[] proposals = (ICompletionProposal[]) completions .toArray(new ICompletionProposal[completions.size()]); sortCompletions(proposals); return proposals; } int equals = currentValue.lastIndexOf('='); if (equals == -1 || semicolon > equals) { String[] validAtts = new String[] { Constants.RESOLUTION_DIRECTIVE, Constants.VERSION_ATTRIBUTE }; Integer[] validTypes = new Integer[] { new Integer(F_TYPE_DIRECTIVE), new Integer(F_TYPE_ATTRIBUTE) }; return handleAttrsAndDirectives(value, initializeNewList(validAtts), initializeNewList(validTypes), offset); } String attributeValue = removeLeadingSpaces(currentValue.substring(semicolon + 1)); if (Constants.RESOLUTION_DIRECTIVE.regionMatches(true, 0, attributeValue, 0, Constants.RESOLUTION_DIRECTIVE .length())) { return matchValueCompletion(currentValue.substring(equals + 1), new String[] { Constants.RESOLUTION_MANDATORY, Constants.RESOLUTION_OPTIONAL }, new int[] { F_TYPE_VALUE, F_TYPE_VALUE }, offset, "RESOLUTION_"); //$NON-NLS-1$ } if (Constants.VERSION_ATTRIBUTE.regionMatches(true, 0, attributeValue, 0, Constants.VERSION_ATTRIBUTE.length())) { value = removeLeadingSpaces(currentValue.substring(equals + 1)); if (value.length() == 0) { return new ICompletionProposal[] { new BundleTypeCompletionProposal( "\"\"", getImage(F_TYPE_VALUE), "\"\"", offset, 0) }; //$NON-NLS-1$ //$NON-NLS-2$ } } return new ICompletionProposal[0]; } protected ICompletionProposal[] handleXFriendsCompletion(String value, final int offset) { ManifestElement[] elems = (ManifestElement[]) fHeaders.get(Constants.BUNDLE_SYMBOLICNAME); HashSet set = new HashSet(); if (elems != null && elems.length > 0) { set.add(elems[0].getValue()); } value = removeLeadingSpaces(value); if (value.length() == 0) { return new ICompletionProposal[] { new BundleTypeCompletionProposal( "\"\"", getImage(F_TYPE_VALUE), "\"\"", offset, 0) }; //$NON-NLS-1$ //$NON-NLS-2$ } if (value.charAt(0) == '"') { value = value.substring(1); } int index = value.lastIndexOf(','); StringTokenizer tokenizer = new StringTokenizer(value, ","); //$NON-NLS-1$ while (tokenizer.hasMoreTokens()) { set.add(tokenizer.nextToken()); } return handleBundleCompletions(value.substring((index == -1) ? 0 : index + 1), set, F_TYPE_VALUE, offset, true); } protected ICompletionProposal[] handleFragmentHostCompletion(String currentValue, int offset) { int index = currentValue.lastIndexOf(';'); if (index == -1) { HashMap completions = new HashMap(); IPluginModelBase base = PluginRegistry.findModel(((ManifestEditor) fSourcePage.getEditor()) .getCommonProject()); BundleDescription desc = base.getBundleDescription(); String currentId = desc != null ? desc.getSymbolicName() : null; String pluginStart = removeLeadingSpaces(currentValue); int length = pluginStart.length(); IPluginModelBase[] bases = PluginRegistry.getActiveModels(); for (IPluginModelBase element : bases) { desc = element.getBundleDescription(); if (desc != null && desc.getHost() == null) { String pluginID = element.getBundleDescription().getSymbolicName(); if (!completions.containsKey(pluginID) && pluginID.regionMatches(true, 0, pluginStart, 0, length) && !pluginID.equals(currentId)) { completions.put(pluginID, new BundleTypeCompletionProposal(pluginID, getImage(F_TYPE_BUNDLE), pluginID, offset - length, length)); } } } return (ICompletionProposal[]) completions.values().toArray(new ICompletionProposal[completions.size()]); } int equals = currentValue.lastIndexOf('='); if (equals == -1 || index > equals) { return matchValueCompletion(removeLeadingSpaces(currentValue.substring(index + 1)), new String[] { Constants.BUNDLE_VERSION_ATTRIBUTE }, new int[] { F_TYPE_ATTRIBUTE }, offset); } String attributeValue = removeLeadingSpaces(currentValue.substring(index + 1)); if (Constants.BUNDLE_VERSION_ATTRIBUTE.regionMatches(true, 0, attributeValue, 0, Constants.BUNDLE_VERSION_ATTRIBUTE.length())) { return getBundleVersionCompletions(currentValue.substring(0, index).trim(), removeLeadingSpaces(currentValue.substring(equals + 1)), offset); } return new ICompletionProposal[0]; } protected ICompletionProposal[] handleRequireBundleCompletion(String currentValue, int offset) { int comma = currentValue.lastIndexOf(','); int semicolon = currentValue.lastIndexOf(';'); String value = comma != -1 ? currentValue.substring(comma + 1) : currentValue; if (comma > semicolon || comma == semicolon) { HashSet set = (HashSet) fHeaders.get(Constants.REQUIRE_BUNDLE); if (set == null) { set = parseHeaderForValues(currentValue, offset); } return handleBundleCompletions(value, set, F_TYPE_BUNDLE, offset, false); } int equals = currentValue.lastIndexOf('='); if (equals == -1 || semicolon > equals) { String[] validAttrs = new String[] { Constants.BUNDLE_VERSION_ATTRIBUTE, Constants.RESOLUTION_DIRECTIVE, Constants.VISIBILITY_DIRECTIVE }; Integer[] validTypes = new Integer[] { new Integer(F_TYPE_ATTRIBUTE), new Integer(F_TYPE_DIRECTIVE), new Integer(F_TYPE_DIRECTIVE) }; return handleAttrsAndDirectives(value, initializeNewList(validAttrs), initializeNewList(validTypes), offset); } String attributeValue = removeLeadingSpaces(currentValue.substring(semicolon + 1)); if (Constants.VISIBILITY_DIRECTIVE.regionMatches(true, 0, attributeValue, 0, Constants.VISIBILITY_DIRECTIVE .length())) { return matchValueCompletion(currentValue.substring(equals + 1), new String[] { Constants.VISIBILITY_PRIVATE, Constants.VISIBILITY_REEXPORT }, new int[] { F_TYPE_VALUE, F_TYPE_VALUE }, offset, "VISIBILITY_"); //$NON-NLS-1$ } if (Constants.RESOLUTION_DIRECTIVE.regionMatches(true, 0, attributeValue, 0, Constants.RESOLUTION_DIRECTIVE .length())) { return matchValueCompletion(currentValue.substring(equals + 1), new String[] { Constants.RESOLUTION_MANDATORY, Constants.RESOLUTION_OPTIONAL }, new int[] { F_TYPE_VALUE, F_TYPE_VALUE }, offset, "RESOLUTION_"); //$NON-NLS-1$ } if (Constants.BUNDLE_VERSION_ATTRIBUTE.regionMatches(true, 0, attributeValue, 0, Constants.RESOLUTION_DIRECTIVE .length())) { String pluginId = removeLeadingSpaces(currentValue.substring((comma == -1) ? 0 : comma + 1, semicolon)); return getBundleVersionCompletions(pluginId, removeLeadingSpaces(currentValue.substring(equals + 1)), offset); } return new ICompletionProposal[0]; } protected ICompletionProposal[] getBundleVersionCompletions(String pluginID, String existingValue, int offset) { ModelEntry entry = PluginRegistry.findEntry(pluginID); if (entry != null) { IPluginModelBase[] hosts = entry.getActiveModels(); ArrayList proposals = new ArrayList(hosts.length); for (IPluginModelBase element : hosts) { String proposalValue = getVersionProposal(element); if (proposalValue.regionMatches(0, existingValue, 0, existingValue.length())) { proposals.add(new BundleTypeCompletionProposal(proposalValue.substring(existingValue.length()), getImage(F_TYPE_VALUE), proposalValue, offset, 0)); } } return (ICompletionProposal[]) proposals.toArray(new ICompletionProposal[proposals.size()]); } else if (existingValue.length() == 0) { return new ICompletionProposal[] { new BundleTypeCompletionProposal( "\"\"", getImage(F_TYPE_VALUE), "\"\"", offset, 0) }; //$NON-NLS-1$ //$NON-NLS-2$ } return new ICompletionProposal[0]; } protected String getVersionProposal(IPluginModelBase base) { StringBuffer buffer = new StringBuffer("\""); //$NON-NLS-1$ BundleDescription desc = base.getBundleDescription(); if (desc != null) { Version version = desc.getVersion(); buffer.append(version.getMajor()); buffer.append('.'); buffer.append(version.getMinor()); buffer.append('.'); buffer.append(version.getMicro()); } else { char[] chars = base.getPluginBase().getVersion().toCharArray(); int periodCount = 0; for (char element : chars) { if (element == '.') { if (periodCount == 2) { break; } ++periodCount; } buffer.append(element); } } return buffer.append('\"').toString(); } protected ICompletionProposal[] handleBundleCompletions(String value, Collection doNotInclude, int type, int offset, boolean includeFragments) { value = removeLeadingSpaces(value); int length = value.length(); doNotInclude.remove(value); ArrayList completions = new ArrayList(); IPluginModelBase[] bases = PluginRegistry.getActiveModels(); for (IPluginModelBase element : bases) { BundleDescription desc = element.getBundleDescription(); if (desc != null) { if (!includeFragments && desc.getHost() != null) { continue; } String bundleId = desc.getSymbolicName(); if (bundleId.regionMatches(true, 0, value, 0, value.length()) && !doNotInclude.contains(bundleId)) { completions.add(new BundleTypeCompletionProposal(bundleId, getImage(type), bundleId, offset - length, length)); } } } return (ICompletionProposal[]) completions.toArray(new ICompletionProposal[completions.size()]); } protected ICompletionProposal[] handleExportPackageCompletion(String currentValue, int offset) { int comma = currentValue.lastIndexOf(','); int semicolon = currentValue.lastIndexOf(';'); ArrayList list = new ArrayList(); if (!insideQuotes(currentValue) && comma > semicolon || comma == semicolon) { String value = comma != -1 ? currentValue.substring(comma + 1) : currentValue; HashSet set = (HashSet) fHeaders.get(Constants.EXPORT_PACKAGE); if (set == null) { set = parseHeaderForValues(currentValue, offset); } value = removeLeadingSpaces(value); int length = value.length(); IProject proj = ((PDEFormEditor) fSourcePage.getEditor()).getCommonProject(); if (proj != null) { IJavaProject jp = JavaCore.create(proj); IPackageFragment[] frags = PDEJavaHelper.getPackageFragments(jp, set, false); for (IPackageFragment element : frags) { String name = element.getElementName(); if (name.regionMatches(true, 0, value, 0, length)) { list.add(new BundleTypeCompletionProposal(name, getImage(F_TYPE_PKG), name, offset - length, length)); } } } } else { String value = currentValue; if (comma > 0) { do { String prefix = currentValue.substring(0, comma); if (!insideQuotes(prefix)) { value = currentValue.substring(comma + 1); break; } comma = currentValue.lastIndexOf(',', comma - 1); } while (comma > 0); } int equals = currentValue.lastIndexOf('='); if (equals == -1 || semicolon > equals) { String[] validAttrs = new String[] { Constants.VERSION_ATTRIBUTE, ICoreConstants.INTERNAL_DIRECTIVE, ICoreConstants.FRIENDS_DIRECTIVE }; Integer[] validTypes = new Integer[] { new Integer(F_TYPE_ATTRIBUTE), new Integer(F_TYPE_DIRECTIVE), new Integer(F_TYPE_DIRECTIVE) }; return handleAttrsAndDirectives(value, initializeNewList(validAttrs), initializeNewList(validTypes), offset); } String attributeValue = removeLeadingSpaces(currentValue.substring(semicolon + 1)); if (ICoreConstants.FRIENDS_DIRECTIVE.regionMatches(true, 0, attributeValue, 0, ICoreConstants.FRIENDS_DIRECTIVE.length())) { return handleXFriendsCompletion(currentValue.substring(equals + 1), offset); } if (ICoreConstants.INTERNAL_DIRECTIVE.regionMatches(true, 0, attributeValue, 0, ICoreConstants.INTERNAL_DIRECTIVE.length())) { return handleTrueFalseValue(currentValue.substring(equals + 1), offset); } if (Constants.VERSION_ATTRIBUTE.regionMatches(true, 0, attributeValue, 0, Constants.VERSION_ATTRIBUTE .length())) { value = removeLeadingSpaces(currentValue.substring(equals + 1)); if (value.length() == 0) { return new ICompletionProposal[] { new BundleTypeCompletionProposal( "\"\"", getImage(F_TYPE_VALUE), "\"\"", offset, 0) }; //$NON-NLS-1$ //$NON-NLS-2$ } } } return (ICompletionProposal[]) list.toArray(new ICompletionProposal[list.size()]); } protected ICompletionProposal[] handleBundleActivatorCompletion(final String currentValue, final int offset) { ArrayList completions = new ArrayList(); IProject project = ((PDEFormEditor) fSourcePage.getEditor()).getCommonProject(); int startOffset = offset - currentValue.length(); generateTypePackageProposals(currentValue, project, completions, startOffset, IJavaSearchConstants.CLASS); ICompletionProposal[] proposals = (ICompletionProposal[]) completions .toArray(new ICompletionProposal[completions.size()]); sortCompletions(proposals); return proposals; } protected ICompletionProposal[] handleBundleSymbolicNameCompletion(String currentValue, int offset) { int semicolon = currentValue.indexOf(';'); if (semicolon != -1) { int equals = currentValue.indexOf('='); if (equals == -1) { String attribute = currentValue.substring(semicolon + 1); attribute = removeLeadingSpaces(attribute); Object o = fHeaders.get(Constants.BUNDLE_MANIFESTVERSION); int type = (o == null || o.toString().equals("1")) ? F_TYPE_ATTRIBUTE : F_TYPE_DIRECTIVE;//$NON-NLS-1$ if (Constants.SINGLETON_DIRECTIVE.regionMatches(true, 0, attribute, 0, attribute.length())) { int length = attribute.length(); BundleTypeCompletionProposal proposal = new BundleTypeCompletionProposal( Constants.SINGLETON_DIRECTIVE + ":=", //$NON-NLS-1$ getImage(type), Constants.SINGLETON_DIRECTIVE, offset - length, length); proposal.setAdditionalProposalInfo(getJavaDoc("SINGLETON_DIRECTIVE")); //$NON-NLS-1$ return new ICompletionProposal[] { proposal }; } } else if (equals > semicolon) { return handleTrueFalseValue(currentValue.substring(equals + 1), offset); } } return new ICompletionProposal[0]; } protected ICompletionProposal[] handleBundleActivationPolicyCompletion(final String currentValue, final int offset) { int comma = currentValue.lastIndexOf(','); int semicolon = currentValue.lastIndexOf(';'); if (!insideQuotes(currentValue) && comma > semicolon || comma == semicolon) { String value = removeLeadingSpaces(currentValue); String lazyValue = "lazy"; //$NON-NLS-1$ int length = value.length(); if (lazyValue.regionMatches(0, value, 0, length)) { return new ICompletionProposal[] { new BundleTypeCompletionProposal(lazyValue, null, lazyValue, offset - length, length) }; } } return new ICompletionProposal[0]; } protected ICompletionProposal[] handleBuddyPolicyCompletion(String currentValue, int offset) { String value = removeLeadingSpaces(currentValue); // values from bug 178517 comment #7 ArrayList validValues = initializeNewList(new String[] { "dependent", "global", "registered", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "app", "ext", "boot", "parent" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ ArrayList types = initializeNewList(new Object[] { new Integer(F_TYPE_VALUE), new Integer(F_TYPE_VALUE), new Integer(F_TYPE_VALUE), new Integer(F_TYPE_VALUE), new Integer(F_TYPE_VALUE), new Integer(F_TYPE_VALUE), new Integer(F_TYPE_VALUE) }); return handleAttrsAndDirectives(value, validValues, types, offset); } protected ICompletionProposal[] handleRequiredExecEnv(String currentValue, int offset) { int comma = currentValue.lastIndexOf(','); if (comma != -1) { currentValue = currentValue.substring(comma + 1); } currentValue = removeLeadingSpaces(currentValue); ArrayList completions = new ArrayList(); HashSet set = (HashSet) fHeaders.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT); if (set == null) { set = new HashSet(0); } int length = currentValue.length(); for (int i = 0; i < fExecEnvs.length; i++) { if (fExecEnvs[i].regionMatches(true, 0, currentValue, 0, length) && !set.contains(fExecEnvs[i])) { completions.add(new BundleTypeCompletionProposal(fExecEnvs[i], getImage(F_TYPE_EXEC_ENV), fExecEnvs[i], offset - length, length)); } } return (ICompletionProposal[]) completions.toArray(new ICompletionProposal[completions.size()]); } protected ICompletionProposal[] handleTrueFalseValue(String currentValue, int offset) { currentValue = removeLeadingSpaces(currentValue); int length = currentValue.length(); if (length == 0) { return new ICompletionProposal[] { new BundleTypeCompletionProposal("true", getImage(F_TYPE_VALUE), "true", offset, 0), //$NON-NLS-1$ //$NON-NLS-2$ new BundleTypeCompletionProposal("false", getImage(F_TYPE_VALUE), "false", offset, 0) //$NON-NLS-1$ //$NON-NLS-2$ }; } else if (length < 5 && "true".regionMatches(true, 0, currentValue, 0, length)) { return new ICompletionProposal[] { new BundleTypeCompletionProposal( "true", getImage(F_TYPE_VALUE), "true", offset - length, length) //$NON-NLS-1$ //$NON-NLS-2$ }; } else if (length < 6 && "false".regionMatches(true, 0, currentValue, 0, length)) { return new ICompletionProposal[] { new BundleTypeCompletionProposal( "false", getImage(F_TYPE_VALUE), "false", offset - length, length) //$NON-NLS-1$ //$NON-NLS-2$ }; } return new ICompletionProposal[0]; } protected ICompletionProposal[] matchValueCompletion(String value, String[] attrs, int[] types, int offset) { return matchValueCompletion(value, attrs, types, offset, ""); //$NON-NLS-1$ } protected ICompletionProposal[] matchValueCompletion(String value, String[] attrs, int[] types, int offset, String prefixCostant) { ArrayList list = new ArrayList(); int length = value.length(); BundleTypeCompletionProposal proposal = null; for (int i = 0; i < attrs.length; i++) { if (attrs[i].regionMatches(true, 0, value, 0, length)) { if (types[i] == F_TYPE_ATTRIBUTE) { proposal = new BundleTypeCompletionProposal( attrs[i] + "=", getImage(F_TYPE_ATTRIBUTE), attrs[i], offset - length, length); //$NON-NLS-1$ proposal.setAdditionalProposalInfo(getJavaDoc(attrs[i] + "_ATTRIBUTE")); //$NON-NLS-1$ } else if (types[i] == F_TYPE_DIRECTIVE) { proposal = new BundleTypeCompletionProposal( attrs[i] + ":=", getImage(F_TYPE_DIRECTIVE), attrs[i], offset - length, length); //$NON-NLS-1$ proposal.setAdditionalProposalInfo(getJavaDoc(attrs[i] + "_DIRECTIVE")); //$NON-NLS-1$ } else { proposal = new BundleTypeCompletionProposal(attrs[i], getImage(types[i]), attrs[i], offset - length, length); proposal.setAdditionalProposalInfo(getJavaDoc(prefixCostant + attrs[i])); } list.add(proposal); } } return (ICompletionProposal[]) list.toArray(new ICompletionProposal[list.size()]); } protected ICompletionProposal[] handleAttrsAndDirectives(String value, ArrayList attrs, ArrayList types, int offset) { String fullValue = findFullLine(value, offset, false); int semicolon = value.lastIndexOf(';'); value = removeLeadingSpaces(value.substring(semicolon + 1)); StringTokenizer tokenizer = new StringTokenizer(fullValue, ";"); //$NON-NLS-1$ tokenizer.nextToken(); while (tokenizer.hasMoreTokens()) { String tokenValue = removeLeadingSpaces(tokenizer.nextToken()); int index = tokenValue.indexOf('='); if (index == -1) { continue; } if (tokenValue.charAt(index - 1) == ':') { --index; } tokenValue = tokenValue.substring(0, index); int indexOfObject = attrs.indexOf(tokenValue); if (indexOfObject >= 0) { attrs.remove(indexOfObject); types.remove(indexOfObject); } } return matchValueCompletion(value, (String[]) attrs.toArray(new String[attrs.size()]), toIntArray(types), offset); } protected HashSet parseHeaderForValues(String currentValue, int offset) { HashSet set = new HashSet(); String fullValue = findFullLine(currentValue, offset, true); StringTokenizer tokenizer = new StringTokenizer(fullValue, ","); //$NON-NLS-1$ while (tokenizer.hasMoreTokens()) { String pkgValue = tokenizer.nextToken(); int index = pkgValue.indexOf(';'); set.add(index == -1 ? pkgValue.trim() : pkgValue.substring(0, index).trim()); } return set; } protected String findFullLine(String value, int offset, boolean entireHeader) { IDocument doc = fSourcePage.getDocumentProvider().getDocument(fSourcePage.getInputContext().getInput()); try { int line = doc.getLineOfOffset(offset); String newValue = ""; //$NON-NLS-1$ int startOfLine = 0; int colon = -1; do { startOfLine = doc.getLineOffset(line); newValue = doc.get(offset, doc.getLineLength(line) - offset + startOfLine); ++line; colon = newValue.lastIndexOf(':'); } while ((colon == -1 || (newValue.length() > colon && newValue.charAt(colon + 1) == '=')) && (entireHeader || newValue.indexOf(',') == -1) && !(doc.getNumberOfLines() == line)); int firstColon = newValue.indexOf(':'); if (colon > 0 && newValue.charAt(colon + 1) != '=') { newValue = doc.get(offset, startOfLine - 1 - offset); } else if (firstColon > 0 && newValue.charAt(firstColon + 1) == ' ') { newValue = doc.get(offset, startOfLine - 1 - offset); } else { // break on the comma to find our element, but only break on a // comma that is not enclosed in parenthesis int comma = newValue.indexOf(','); int parenthesis = newValue.indexOf('"'); if (!(parenthesis < comma && newValue.indexOf('"', parenthesis + 1) > comma)) { newValue = (comma != -1) ? newValue.substring(0, comma) : newValue; } } return value.concat(newValue); } catch (BadLocationException e) { } return ""; //$NON-NLS-1$ } protected int[] toIntArray(ArrayList list) { int[] result = new int[list.size()]; int i = -1; while (++i < result.length) { Object o = list.get(i); if (!(o instanceof Integer)) { return new int[0]; } result[i] = ((Integer) o).intValue(); } return result; } // if you use java.util.Arrays.asList(), we get an UnsupportedOperation // later in the code protected final ArrayList initializeNewList(Object[] values) { ArrayList list = new ArrayList(values.length); for (Object element : values) { list.add(element); } return list; } protected boolean insideQuotes(String value) { char[] chars = value.toCharArray(); int numOfQuotes = 0; for (char element : chars) { if (element == '\"') { ++numOfQuotes; } } int j = numOfQuotes % 2; return j == 1; } public void assistSessionEnded(ContentAssistEvent event) { fHeaders = null; } public void assistSessionStarted(ContentAssistEvent event) { } public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) { } public Image getImage(int type) { if (type >= 0 && type < F_TOTAL_TYPES) { if (fImages[type] == null) { switch (type) { case F_TYPE_HEADER: return fImages[type] = PDEPluginImages.DESC_BUILD_VAR_OBJ.createImage(); case F_TYPE_PKG: return PDEPluginImages.get(PDEPluginImages.OBJ_DESC_PACKAGE); case F_TYPE_BUNDLE: return fImages[type] = PDEPluginImages.DESC_PLUGIN_OBJ.createImage(); case F_TYPE_CLASS: return PDEPluginImages.get(PDEPluginImages.OBJ_DESC_GENERATE_CLASS); case F_TYPE_ATTRIBUTE: return fImages[type] = PDEPluginImages.DESC_ATT_URI_OBJ.createImage(); case F_TYPE_DIRECTIVE: fImages[F_TYPE_ATTRIBUTE] = PDEPluginImages.DESC_ATT_URI_OBJ.createImage(); ImageOverlayIcon icon = new ImageOverlayIcon(fImages[F_TYPE_ATTRIBUTE], new ImageDescriptor[][] { new ImageDescriptor[] { PDEPluginImages.DESC_DOC_CO }, null, null, null }); return fImages[type] = icon.createImage(); case F_TYPE_EXEC_ENV: return fImages[type] = PDEPluginImages.DESC_JAVA_LIB_OBJ.createImage(); case F_TYPE_VALUE: return null; } } else { return fImages[type]; } } return null; } protected void dispose() { for (int i = 0; i < fImages.length; i++) { if (fImages[i] != null && !fImages[i].isDisposed()) { fImages[i].dispose(); } } } protected String getJavaDoc(String constant) { if (fJP == null) { IProject project = ((PDEFormEditor) fSourcePage.getEditor()).getCommonProject(); fJP = JavaCore.create(project); } return PDEJavaHelperUI.getOSGIConstantJavaDoc(constant, fJP); } }