Bug 217772 - Provide advanced content assist for Bundle-ActivationPolicy
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/contentassist/ManifestContentAssistProcessor.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/contentassist/ManifestContentAssistProcessor.java
index 23eba1e..092e9d6 100644
--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/contentassist/ManifestContentAssistProcessor.java
+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/contentassist/ManifestContentAssistProcessor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2007 IBM Corporation and others.
+ * Copyright (c) 2006, 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
@@ -171,7 +171,7 @@
 				TypeCompletionProposal proposal = new TypeCompletionProposal(fHeader[i] + ": ", getImage(F_TYPE_HEADER), //$NON-NLS-1$
 						fHeader[i], startOffset, currentValue.length());
 				proposal.setAdditionalProposalInfo(getJavaDoc(fHeader[i]));
-				completions.add(proposal); //$NON-NLS-1$
+				completions.add(proposal);
 			}
 		}
 		return (ICompletionProposal[]) completions.toArray(new ICompletionProposal[completions.size()]);
@@ -435,17 +435,7 @@
 			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 (int i = 0; i < frags.length; i++) {
-					String name = frags[i].getElementName();
-					if (name.regionMatches(true, 0, value, 0, length))
-						list.add(new TypeCompletionProposal(name, getImage(F_TYPE_PKG), name, offset - length, length));
-				}
-			}
+			addPackageCompletions(value, set, offset, list);
 		} else {
 			String value = currentValue;
 			if (comma > 0) {
@@ -511,14 +501,45 @@
 	}
 
 	protected ICompletionProposal[] handleBundleActivationPolicyCompletion(final String currentValue, final int offset) {
-		int comma = currentValue.lastIndexOf(',');
 		int semicolon = currentValue.lastIndexOf(';');
-		if (!insideQuotes(currentValue) && comma > semicolon || comma == semicolon) {
+		if (semicolon == -1) {
+			// we know there are no directives, therefore we are looking for a header value
 			String value = removeLeadingSpaces(currentValue);
-			String lazyValue = "lazy"; //$NON-NLS-1$
+			String lazyValue = Constants.ACTIVATION_LAZY;
 			int length = value.length();
 			if (lazyValue.regionMatches(0, value, 0, length))
 				return new ICompletionProposal[] {new TypeCompletionProposal(lazyValue, null, lazyValue, offset - length, length)};
+		} else {
+			int equals = currentValue.lastIndexOf('=');
+			if (semicolon > equals) {
+				// we know we are looking for a directive
+				String[] validDirectives = new String[] {Constants.EXCLUDE_DIRECTIVE, Constants.INCLUDE_DIRECTIVE};
+				Integer[] validTypes = new Integer[] {new Integer(F_TYPE_DIRECTIVE), new Integer(F_TYPE_DIRECTIVE)};
+				return handleAttrsAndDirectives(currentValue, initializeNewList(validDirectives), initializeNewList(validTypes), offset);
+			}
+			int quote = currentValue.lastIndexOf('"');
+			if (!insideQuotes(currentValue)) {
+				// if quote > equals, that means the cursor is after a closing quote for the directive
+				if (equals > quote)
+					return new ICompletionProposal[] {new TypeCompletionProposal("\"\"", getImage(F_TYPE_VALUE), "\"\"", offset, 0)}; //$NON-NLS-1$ //$NON-NLS-2$
+			} else {
+				// We know we are looking for a completion inside the quote of a directive
+				String value = currentValue.substring(quote + 1);
+				// find existing packages 
+				StringTokenizer parser = new StringTokenizer(value, ","); //$NON-NLS-1$
+				HashSet set = new HashSet();
+				while (parser.hasMoreTokens()) {
+					set.add(parser.nextToken().trim());
+				}
+				// find the value of the package we are trying to find the completion for
+				int comma = value.lastIndexOf(',');
+				if (comma > -1)
+					value = removeLeadingSpaces(value.substring(comma + 1));
+				// find proposals
+				ArrayList proposals = new ArrayList();
+				addPackageCompletions(value, set, offset, proposals);
+				return (ICompletionProposal[]) proposals.toArray(new ICompletionProposal[proposals.size()]);
+			}
 		}
 		return new ICompletionProposal[0];
 	}
@@ -594,6 +615,7 @@
 		int semicolon = value.lastIndexOf(';');
 		value = removeLeadingSpaces(value.substring(semicolon + 1));
 		StringTokenizer tokenizer = new StringTokenizer(fullValue, ";"); //$NON-NLS-1$
+		// remove value part of the String, the rest of the tokens will be attributes/directives
 		tokenizer.nextToken();
 		while (tokenizer.hasMoreTokens()) {
 			String tokenValue = removeLeadingSpaces(tokenizer.nextToken());
@@ -612,6 +634,28 @@
 		return matchValueCompletion(value, (String[]) attrs.toArray(new String[attrs.size()]), toIntArray(types), offset);
 	}
 
+	/**
+	 * Adds completions to the proposals list which represent packages in source folders found in the current project
+	 * 
+	 * @param value the current incomplete package value (without any leading spaces)
+	 * @param currentPackages a Set containing the packages already specified by the Manifest Header
+	 * @param offset the offset of the current completion proposal
+	 * @param proposals the list to which new completion proposals will be added
+	 */
+	private void addPackageCompletions(String value, Set currentPackages, int offset, ArrayList proposals) {
+		int length = value.length();
+		IProject proj = ((PDEFormEditor) fSourcePage.getEditor()).getCommonProject();
+		if (proj != null) {
+			IJavaProject jp = JavaCore.create(proj);
+			IPackageFragment[] frags = PDEJavaHelper.getPackageFragments(jp, currentPackages, false);
+			for (int i = 0; i < frags.length; i++) {
+				String name = frags[i].getElementName();
+				if (name.regionMatches(true, 0, value, 0, length))
+					proposals.add(new TypeCompletionProposal(name, getImage(F_TYPE_PKG), name, offset - length, length));
+			}
+		}
+	}
+
 	private HashSet parseHeaderForValues(String currentValue, int offset) {
 		HashSet set = new HashSet();
 		String fullValue = findFullLine(currentValue, offset, true);
@@ -637,11 +681,16 @@
 				++line;
 				colon = newValue.lastIndexOf(':');
 			} while ((colon == -1 || (newValue.length() > colon && newValue.charAt(colon + 1) == '=')) && (entireHeader || newValue.indexOf(',') == -1) && !(doc.getNumberOfLines() == line));
+
 			if (colon > 0 && newValue.charAt(colon + 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(',');
-				newValue = (comma != -1) ? newValue.substring(0, comma) : newValue;
+				int parenthesis = newValue.indexOf('"');
+				int test = newValue.indexOf('"', parenthesis + 1);
+				if (!(parenthesis < comma && newValue.indexOf('"', parenthesis + 1) > comma))
+					newValue = (comma != -1) ? newValue.substring(0, comma) : newValue;
 			}
 			return value.concat(newValue);
 		} catch (BadLocationException e) {