Bug 459831 - [launching] Support attaching external annotations to a JRE
container

Change-Id: I1dfb8290158144a19dc1a86b7056353dc135c3d6
Signed-off-by:  Frits Jalvingh <jal@etc.to>
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath/ClasspathEntry.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath/ClasspathEntry.java
index fd43ef1..4f0a747 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath/ClasspathEntry.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/classpath/ClasspathEntry.java
@@ -7,6 +7,8 @@
  * 
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 
 package org.eclipse.jdt.internal.debug.ui.classpath;
@@ -131,7 +133,22 @@
 	@Override
 	public void setSourceAttachmentRootPath(IPath path) {
 		entry.setSourceAttachmentRootPath(path);
-		
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry#getExternalAnnotationsPath()
+	 */
+	@Override
+	public IPath getExternalAnnotationsPath() {
+		return entry.getExternalAnnotationsPath();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry#setExternalAnnotationsPath(org.eclipse.core.runtime.IPath)
+	 */
+	@Override
+	public void setExternalAnnotationsPath(IPath path) {
+		entry.setExternalAnnotationsPath(path);
 	}
 
 	/* (non-Javadoc)
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.java
index 8d3071b..5a79880 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- *  Copyright (c) 2000, 2014 IBM Corporation and others.
+ *  Copyright (c) 2000, 2015 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
@@ -7,6 +7,8 @@
  * 
  *  Contributors:
  *  IBM - Initial API and implementation
+ *  Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.ui.jres;
 
@@ -147,6 +149,10 @@
 	public static String VMLibraryBlock_9;
 	public static String VMLibraryBlock_10;
 	public static String VMLibraryBlock_11;
+	public static String VMExternalAnnsBlock_1;
+	public static String VMExternalAnnsBlock_2;
+	public static String VMExternalAnnsBlock_3;
+	public static String VMExternalAnnsBlock_4;
 
 	static {
 		// load message values from bundle file
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.properties b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.properties
index 00937e8..9becfbc 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.properties
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-#  Copyright (c) 2000, 2014 IBM Corporation and others.
+#  Copyright (c) 2000, 2015 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
@@ -7,6 +7,8 @@
 # 
 #  Contributors:
 #     IBM Corporation - initial API and implementation
+#     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+#     	external annotations to a JRE container
 ###############################################################################
 
 AbstractJavaCommandTab_1=Java executable:
@@ -130,3 +132,8 @@
 VMTypePage_1=Select the type of JRE to add to the workspace.
 VMTypePage_2=JRE Type
 VMTypePage_3=Installed JRE &Types:
+
+VMExternalAnnsBlock_1=External annotations:
+VMExternalAnnsBlock_2=(none)
+VMExternalAnnsBlock_3=E&xternal annotations...
+VMExternalAnnsBlock_4=Select to add the external annotations file or directory to the selected library
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryContentProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryContentProvider.java
index 115d393..117a428 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryContentProvider.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryContentProvider.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.ui.jres;
 
@@ -46,6 +47,7 @@
 		
 		public static final int JAVADOC_URL= 1;
 		public static final int SOURCE_PATH= 2;
+		public static final int EXTERNAL_ANNOTATIONS_PATH = 3;
 		
 		private LibraryStandin fParent;
 		private int fType;
@@ -71,6 +73,10 @@
 				case SOURCE_PATH:
 					fParent.setSystemLibrarySourcePath(Path.EMPTY);
 					break;
+				case EXTERNAL_ANNOTATIONS_PATH:
+					fParent.setExternalAnnotationsPath(Path.EMPTY);
+					break;
+
 			}
 		}
 	}
@@ -112,7 +118,8 @@
 			LibraryStandin standin= (LibraryStandin) parentElement;
 			Object[] children= fChildren.get(standin);
 			if (children == null) {
-				children= new Object[] {new SubElement(standin, SubElement.SOURCE_PATH), new SubElement(standin, SubElement.JAVADOC_URL)};
+				children = new Object[] { new SubElement(standin, SubElement.SOURCE_PATH), new SubElement(standin, SubElement.JAVADOC_URL),
+						new SubElement(standin, SubElement.EXTERNAL_ANNOTATIONS_PATH) };
 				fChildren.put(standin, children);
 			}
 			return children;
@@ -320,6 +327,29 @@
 	}
 	
 	/**
+	 * Set the given paths as the annotations path for the libraries contained in the given selection.
+	 *
+	 * @param annotationsAttachmentPath
+	 *            the path of the new attachment
+	 * @param annotationsAttachmentRootPath
+	 *            the root path of the new attachment
+	 * @param selection
+	 *            the selection of libraries to set the new paths in
+	 */
+	public void setAnnotationsPath(IPath annotationsAttachmentPath, IStructuredSelection selection) {
+		Set<Object> libraries = getSelectedLibraries(selection);
+		if (annotationsAttachmentPath == null) {
+			annotationsAttachmentPath = Path.EMPTY;
+		}
+		Iterator<Object> iterator = libraries.iterator();
+		while (iterator.hasNext()) {
+			LibraryStandin standin = (LibraryStandin) iterator.next();
+			standin.setExternalAnnotationsPath(annotationsAttachmentPath);
+		}
+		fViewer.refresh();
+	}
+
+	/**
 	 * Returns the stand-in libraries being edited.
 	 * 
 	 * @return stand-ins
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryLabelProvider.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryLabelProvider.java
index 1335caa..21fdefe 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryLabelProvider.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryLabelProvider.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * Copyright (c) 2006, 2015 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
@@ -7,6 +7,8 @@
  * 
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.ui.jres;
 
@@ -50,11 +52,19 @@
 			status = library.validate();
 			
 		} else if (element instanceof SubElement) {
-			if (((SubElement)element).getType() == SubElement.SOURCE_PATH) {
-				key = ISharedImages.IMG_OBJS_JAR_WITH_SOURCE;
-			}
-			else {
-				key = ISharedImages.IMG_OBJS_JAVADOCTAG;
+			switch(((SubElement)element).getType()) {
+				case SubElement.SOURCE_PATH:
+					key = ISharedImages.IMG_OBJS_JAR_WITH_SOURCE;
+					break;
+					
+				case SubElement.EXTERNAL_ANNOTATIONS_PATH:
+					key = ISharedImages.IMG_OBJS_EXTERNAL_ANNOTATIONS;
+					break;
+
+				default:
+				case SubElement.JAVADOC_URL:
+					key = ISharedImages.IMG_OBJS_JAVADOCTAG;
+					break;
 			}
 		}
 		if(key != null) {
@@ -94,6 +104,14 @@
 				} else {
 					text.append(JREMessages.VMLibraryBlock_1);
 				}
+			} else if (subElement.getType() == SubElement.EXTERNAL_ANNOTATIONS_PATH) {
+				text.append(JREMessages.VMExternalAnnsBlock_1);
+				IPath externalAnnotationsPath = subElement.getParent().getExternalAnnotationsPath();
+				if (externalAnnotationsPath != null && !Path.EMPTY.equals(externalAnnotationsPath)) {
+					text.append(externalAnnotationsPath.toOSString());
+				} else {
+					text.append(JREMessages.VMExternalAnnsBlock_2);
+				}
 			}
 			return text.toString();
 		}
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryStandin.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryStandin.java
index 8168a20..1ecdb97 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryStandin.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/LibraryStandin.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -7,6 +7,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.ui.jres;
 
@@ -28,6 +30,7 @@
 public final class LibraryStandin {
 	private IPath fSystemLibrary;
 	private IPath fSystemLibrarySource;
+	private IPath fExternalAnnotations;
 	private IPath fPackageRootPath;
 	private URL fJavadocLocation;
 	
@@ -39,6 +42,7 @@
 		setSystemLibrarySourcePath(libraryLocation.getSystemLibrarySourcePath());
 		setPackageRootPath(libraryLocation.getPackageRootPath());
 		setJavadocLocation(libraryLocation.getJavadocLocation());
+		setExternalAnnotationsPath(libraryLocation.getExternalAnnotationsPath());
 	}		
 		
 	/**
@@ -69,6 +73,19 @@
 	}
 	
 	/**
+	 * Returns the path to the external annotations.
+	 *
+	 * @return
+	 */
+	public IPath getExternalAnnotationsPath() {
+		return fExternalAnnotations;
+	}
+
+	void setExternalAnnotationsPath(IPath path) {
+		fExternalAnnotations = path;
+	}
+
+	/**
 	 * Returns the path to the default package in the sources zip file
 	 * 
 	 * @return The path to the default package in the sources zip file.
@@ -96,6 +113,7 @@
 			return getSystemLibraryPath().equals(lib.getSystemLibraryPath()) 
 				&& equals(getSystemLibrarySourcePath(), lib.getSystemLibrarySourcePath())
 				&& equals(getPackageRootPath(), lib.getPackageRootPath())
+				&& equals(getExternalAnnotationsPath(), lib.getExternalAnnotationsPath())
 				&& equalURLs(getJavadocLocation(), lib.getJavadocLocation());
 		} 
 		return false;
@@ -179,7 +197,7 @@
 	 * @return library location
 	 */
 	LibraryLocation toLibraryLocation() {
-		return new LibraryLocation(getSystemLibraryPath(), getSystemLibrarySourcePath(), getPackageRootPath(), getJavadocLocation());
+		return new LibraryLocation(getSystemLibraryPath(), getSystemLibrarySourcePath(), getPackageRootPath(), getJavadocLocation(), null, getExternalAnnotationsPath());
 	}
 	
 	/**
@@ -201,6 +219,17 @@
 				}
 			}
 		}
+		path = getExternalAnnotationsPath();
+		if (path != null && !path.isEmpty()) {
+			if (!path.toFile().exists()) {
+				// check for workspace resource
+				IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+				if (resource == null || !resource.exists()) {
+					return new Status(IStatus.ERROR, IJavaDebugUIConstants.PLUGIN_ID, IJavaDebugUIConstants.INTERNAL_ERROR, "External Annotations file does not exist: " + path.toOSString(), null); //$NON-NLS-1$
+				}
+			}
+		}
+
 		return Status.OK_STATUS;
 	}
 	
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/VMLibraryBlock.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/VMLibraryBlock.java
index e0afe02..ca71a0a 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/VMLibraryBlock.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/VMLibraryBlock.java
@@ -7,6 +7,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.ui.jres;
 
@@ -21,6 +23,7 @@
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.debug.internal.ui.SWTFactory;
+import org.eclipse.jdt.core.IClasspathAttribute;
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
 import org.eclipse.jdt.debug.ui.launchConfigurations.AbstractVMInstallPage;
@@ -83,6 +86,8 @@
 	
 	private IStatus[] fLibStatus;
 	
+	private Button fAnnotationsButton;
+
 	/**
 	 * Constructs a new wizard page with the given name.
 	 * 
@@ -118,9 +123,15 @@
 		fJavadocButton = SWTFactory.createPushButton(pathButtonComp, JREMessages.VMLibraryBlock_3, JREMessages.VMLibraryBlock_17, null); 
 		fJavadocButton.setEnabled(false);
 		fJavadocButton.addSelectionListener(this);
+
 		fSourceButton =  SWTFactory.createPushButton(pathButtonComp, JREMessages.VMLibraryBlock_11, JREMessages.VMLibraryBlock_18, null);
 		fSourceButton.setEnabled(false);
 		fSourceButton.addSelectionListener(this);
+
+		fAnnotationsButton = SWTFactory.createPushButton(pathButtonComp, JREMessages.VMExternalAnnsBlock_3, JREMessages.VMExternalAnnsBlock_4, null);
+		fAnnotationsButton.setEnabled(false);
+		fAnnotationsButton.addSelectionListener(this);
+
 		fLibraryViewer.addDoubleClickListener(new IDoubleClickListener() {
 			@Override
 			public void doubleClick(DoubleClickEvent event) {
@@ -263,6 +274,9 @@
 		else if(source == fSourceButton) {
 			edit((IStructuredSelection) fLibraryViewer.getSelection(), SubElement.SOURCE_PATH);
 		}
+		else if (source == fAnnotationsButton) {
+			edit((IStructuredSelection) fLibraryViewer.getSelection(), SubElement.EXTERNAL_ANNOTATIONS_PATH);
+		}
 		else if (source == fDefaultButton) {
 			restoreDefaultLibraries();
 		}
@@ -340,9 +354,27 @@
 					fLibraryContentProvider.setSourcePath(classpathEntry.getSourceAttachmentPath(), classpathEntry.getSourceAttachmentRootPath(), selection);
 				}
 			}
+			else if(type == SubElement.EXTERNAL_ANNOTATIONS_PATH) {
+				IRuntimeClasspathEntry entry = JavaRuntime.newArchiveRuntimeClasspathEntry(library.getSystemLibraryPath());
+				entry.setExternalAnnotationsPath(library.getExternalAnnotationsPath());
+				IClasspathAttribute[] extraAttributes = entry.getClasspathEntry().getExtraAttributes();
+				String annotationPathString = findClasspathAttribute(extraAttributes, IClasspathAttribute.EXTERNAL_ANNOTATION_PATH);
+				IPath annotationPath = null == annotationPathString ? null : new Path(annotationPathString);
+
+				IPath newPath = BuildPathDialogAccess.configureExternalAnnotationsAttachment(fLibraryViewer.getControl().getShell(), annotationPath);
+				fLibraryContentProvider.setAnnotationsPath(newPath, selection);
+			}
 		}
 	}
 
+	private static String findClasspathAttribute(IClasspathAttribute[] attributes, String name) {
+		for(int i = attributes.length; --i >= 0;) {
+			if(name.equals(attributes[i].getName())) {
+				return attributes[i].getValue();
+			}
+		}
+		return null;
+	}
 	/* (non-Javadoc)
 	 * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
 	 */
@@ -361,6 +393,7 @@
 				enableDown = true, 
 				allSource = true, 
 				allJavadoc = true,
+				allAnnotations = true,
 				allRoots = true;
 		Object[] libraries = fLibraryContentProvider.getElements(null);
 		if (selection.isEmpty() || libraries.length == 0) {
@@ -376,15 +409,25 @@
 					allRoots = false;
 					SubElement subElement= (SubElement)element;
 					lib = (subElement).getParent().toLibraryLocation();
-					if (subElement.getType() == SubElement.JAVADOC_URL) {
-						allSource = false;
-					} else {
-						allJavadoc = false;
+					switch (subElement.getType()) {
+						case SubElement.JAVADOC_URL:
+							allSource = false;
+							allAnnotations = false;
+							break;
+						case SubElement.SOURCE_PATH:
+							allAnnotations = false;
+							allJavadoc = false;
+							break;
+						case SubElement.EXTERNAL_ANNOTATIONS_PATH:
+							allJavadoc = false;
+							allSource = false;
+							break;
 					}
 				} else {
 					lib = element;
 					allSource = false;
 					allJavadoc = false;
+					allAnnotations = false;
 				}
 				if (lib == first) {
 					enableUp = false;
@@ -398,6 +441,7 @@
 		fDownButton.setEnabled(enableDown);
 		fJavadocButton.setEnabled(!selection.isEmpty() && (allJavadoc || allRoots));
 		fSourceButton.setEnabled(!selection.isEmpty() && (allSource || allRoots));
+		fAnnotationsButton.setEnabled(!selection.isEmpty() && (allAnnotations || allRoots));
 	}
 
 	/* (non-Javadoc)
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/AbstractRuntimeClasspathEntry.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/AbstractRuntimeClasspathEntry.java
index 86ab083..10c64fb 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/AbstractRuntimeClasspathEntry.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/AbstractRuntimeClasspathEntry.java
@@ -7,6 +7,8 @@
  * 
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.launching;
 
@@ -37,6 +39,8 @@
 	
 	private IPath sourceAttachmentPath = null;
 	private IPath rootSourcePath = null;
+	private IPath externalAnnotationsPath = null;
+
 	private int classpathProperty = IRuntimeClasspathEntry.USER_CLASSES;
 	/**
 	 * Associated Java project, or <code>null</code>
@@ -162,6 +166,16 @@
 	public void setSourceAttachmentRootPath(IPath path) {
 		rootSourcePath = path;
 	}
+	@Override
+	public IPath getExternalAnnotationsPath() {
+		return externalAnnotationsPath;
+	}
+
+	@Override
+	public void setExternalAnnotationsPath(IPath path) {
+		externalAnnotationsPath = path;
+	}
+
 	/* (non-Javadoc)
 	 * @see org.eclipse.jdt.launching.IRuntimeClasspathEntry#getClasspathProperty()
 	 */
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JREContainer.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JREContainer.java
index 53ae6be..e1d780a 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JREContainer.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JREContainer.java
@@ -7,6 +7,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.launching;
 
@@ -358,6 +360,12 @@
 			IClasspathAttribute indexCPLocation = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, indexLocation.toExternalForm());
 			classpathAttributes.add(indexCPLocation);
 		}
+		IPath annotationsPath = lib.getExternalAnnotationsPath();
+		if (null != annotationsPath && !annotationsPath.isEmpty()) {
+			IClasspathAttribute xAnnLocation = JavaCore.newClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, annotationsPath.toPortableString());
+			classpathAttributes.add(xAnnLocation);
+		}
+
 		return classpathAttributes.toArray(new IClasspathAttribute[classpathAttributes.size()]);
 	}
 	
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JREContainerInitializer.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JREContainerInitializer.java
index 4bf36eb..f7ff8f0 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JREContainerInitializer.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JREContainerInitializer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -7,6 +7,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.launching;
 
@@ -312,6 +314,9 @@
 		if (attributeKey.equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) {
 			return Status.OK_STATUS;
 		}
+		if (attributeKey.equals(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH)) {
+			return Status.OK_STATUS;
+		}
 		if (attributeKey.equals(JavaRuntime.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY)) {
 			return Status.OK_STATUS;
 		}
@@ -347,6 +352,7 @@
 						rootPath = Path.EMPTY;
 					}
 					URL javadocLocation = null;
+					IPath externalAnnotations = null;
 					IClasspathAttribute[] extraAttributes = entry.getExtraAttributes();
 					for (int j = 0; j < extraAttributes.length; j++) {
 						IClasspathAttribute attribute = extraAttributes[j];
@@ -359,9 +365,18 @@
 									LaunchingPlugin.log(e);
 								}
 							}
+						} else if (attribute.getName().equals(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH)) {
+							String xpath = attribute.getValue();
+							if (null != xpath && xpath.trim().length() > 0) {
+								try {
+									externalAnnotations = Path.fromPortableString(xpath);
+								} catch (Exception x) {
+									LaunchingPlugin.log(x);
+								}
+							}
 						}
 					}
-					libs[i] = new LibraryLocation(path, srcPath, rootPath, javadocLocation);
+					libs[i] = new LibraryLocation(path, srcPath, rootPath, javadocLocation, null, externalAnnotations);
 				} else {
 					IStatus status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR, NLS.bind(LaunchingMessages.JREContainerInitializer_Classpath_entry__0__does_not_refer_to_an_existing_library__2, new String[]{entry.getPath().toString()}), null); 
 					throw new CoreException(status);
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/RuntimeClasspathEntry.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/RuntimeClasspathEntry.java
index c46920b..66df845 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/RuntimeClasspathEntry.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/RuntimeClasspathEntry.java
@@ -8,6 +8,8 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     BEA - Daniel R Somerfield - Bug 88939
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.launching;
 
@@ -25,6 +27,7 @@
 import org.eclipse.core.runtime.Status;
 import org.eclipse.debug.core.DebugPlugin;
 import org.eclipse.jdt.core.ClasspathContainerInitializer;
+import org.eclipse.jdt.core.IClasspathAttribute;
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
@@ -291,6 +294,9 @@
 		if (getSourceAttachmentRootPath() != null) {
 			node.setAttribute("sourceRootPath", getSourceAttachmentRootPath().toString()); //$NON-NLS-1$
 		}
+		if (getExternalAnnotationsPath() != null) {
+			node.setAttribute("externalAnnotationsPath", getExternalAnnotationsPath().toString()); //$NON-NLS-1$
+		}
 		if (getJavaProject() != null) {
 			node.setAttribute("javaProject", getJavaProject().getElementName()); //$NON-NLS-1$
 		}
@@ -367,7 +373,36 @@
 		if (path != null && path.isEmpty()) {
 			path = null;
 		}
-		updateClasspathEntry(getPath(), path, getSourceAttachmentRootPath());
+		updateClasspathEntry(getPath(), path, getSourceAttachmentRootPath(), getExternalAnnotationsPath());
+	}
+
+	@Override
+	public IPath getExternalAnnotationsPath() {
+		IClasspathEntry entry = getClasspathEntry();
+		if (null != entry) {
+			String s = findClasspathAttribute(entry.getExtraAttributes(), IClasspathAttribute.EXTERNAL_ANNOTATION_PATH);
+			if (null != s) {
+				return new Path(s);
+			}
+		}
+		return null;
+	}
+
+	private static String findClasspathAttribute(IClasspathAttribute[] attributes, String name) {
+		for(int i = attributes.length; --i >= 0;) {
+			if(name.equals(attributes[i].getName())) {
+				return attributes[i].getValue();
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public void setExternalAnnotationsPath(IPath path) {
+		if (path != null && path.isEmpty()) {
+			path = null;
+		}
+		updateClasspathEntry(getPath(), getSourceAttachmentPath(), getSourceAttachmentRootPath(), path);
 	}
 	
 	/**
@@ -391,7 +426,7 @@
 		if (path != null && path.isEmpty()) {
 			path = null;
 		}
-		updateClasspathEntry(getPath(), getSourceAttachmentPath(), path);		
+		updateClasspathEntry(getPath(), getSourceAttachmentPath(), path, getExternalAnnotationsPath());
 	}
 	
 	/**
@@ -605,12 +640,17 @@
 	 * @param sourcePath the source path
 	 * @param rootPath the root path
 	 */
-	protected void updateClasspathEntry(IPath path, IPath sourcePath, IPath rootPath) {
+	protected void updateClasspathEntry(IPath path, IPath sourcePath, IPath rootPath, IPath annotationsPath) {
 		IClasspathEntry entry = null;
 		IClasspathEntry original = getClasspathEntry();
 		switch (getType()) {
 			case ARCHIVE:
-				entry = JavaCore.newLibraryEntry(path, sourcePath, rootPath, original.getAccessRules(), original.getExtraAttributes(), original.isExported());
+				IClasspathAttribute[] extraAttributes = original.getExtraAttributes();
+				if (annotationsPath != null) {
+					extraAttributes = setClasspathAttribute(extraAttributes, IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, annotationsPath.toPortableString());
+				}
+
+				entry = JavaCore.newLibraryEntry(path, sourcePath, rootPath, original.getAccessRules(), extraAttributes, original.isExported());
 				break;
 			case VARIABLE:
 				entry = JavaCore.newVariableEntry(path, sourcePath, rootPath);
@@ -621,6 +661,21 @@
 		setClasspathEntry(entry);		
 	}
 	
+	private static IClasspathAttribute[] setClasspathAttribute(IClasspathAttribute[] attributes, String name, String value) {
+		for (int i = attributes.length; --i >= 0;) {
+			if (name.equals(attributes[i].getName())) {
+				IClasspathAttribute[] nw = new IClasspathAttribute[attributes.length];
+				System.arraycopy(nw, 0, attributes, 0, attributes.length);
+				nw[i] = JavaCore.newClasspathAttribute(name, value);
+				return nw;
+			}
+		}
+		IClasspathAttribute[] nw = new IClasspathAttribute[attributes.length + 1];
+		System.arraycopy(nw, 0, attributes, 0, attributes.length);
+		nw[attributes.length] = JavaCore.newClasspathAttribute(name, value);
+		return nw;
+	}
+
 	/**
 	 * Returns the resolved classpath entry associated with this runtime
 	 * entry, resolving if required.
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/VMDefinitionsContainer.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/VMDefinitionsContainer.java
index e9cd46e..baaef1b 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/VMDefinitionsContainer.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/VMDefinitionsContainer.java
@@ -7,6 +7,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.internal.launching;
 
@@ -382,6 +384,10 @@
 			Element element = doc.createElement("libraryLocation");  //$NON-NLS-1$
 			element.setAttribute("jreJar", locations[i].getSystemLibraryPath().toString()); //$NON-NLS-1$
 			element.setAttribute("jreSrc", locations[i].getSystemLibrarySourcePath().toString()); //$NON-NLS-1$
+			IPath annotationsPath = locations[i].getExternalAnnotationsPath();
+			if (null != annotationsPath && !annotationsPath.isEmpty()) {
+				element.setAttribute("jreExternalAnns", annotationsPath.toString()); //$NON-NLS-1$
+			}
 			
 			IPath packageRootPath = locations[i].getPackageRootPath();
             if (packageRootPath != null) {
@@ -629,6 +635,7 @@
 		String pkgRoot= libLocationElement.getAttribute("pkgRoot"); //$NON-NLS-1$
 		String jreJavadoc= libLocationElement.getAttribute("jreJavadoc"); //$NON-NLS-1$
 		String jreIndex= libLocationElement.getAttribute("jreIndex"); //$NON-NLS-1$
+		String externalAnns = libLocationElement.getAttribute("jreExternalAnns"); //$NON-NLS-1$
 		// javadoc URL
 		URL javadocURL= null;
 		if (jreJavadoc.length() == 0) {
@@ -652,7 +659,8 @@
 			}
 		}		
 		if (jreJar != null && jreSrc != null && pkgRoot != null) {
-			return new LibraryLocation(new Path(jreJar), new Path(jreSrc), new Path(pkgRoot), javadocURL, indexURL);
+			return new LibraryLocation(new Path(jreJar), new Path(jreSrc), new Path(pkgRoot), javadocURL, indexURL
+					, externalAnns == null ? null : new Path(externalAnns));
 		}
 		LaunchingPlugin.log("Library location element is specified incorrectly.");  //$NON-NLS-1$
 		return null;
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/IRuntimeClasspathEntry.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/IRuntimeClasspathEntry.java
index 986e22a..498d76a 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/IRuntimeClasspathEntry.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/IRuntimeClasspathEntry.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -7,6 +7,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.launching;
 
@@ -233,6 +235,25 @@
 	public void setSourceAttachmentRootPath(IPath path);
 	
 	/**
+	 * Returns the path to the external annotations file or directory, or <code>null</code> 
+	 * if no external annotations are associated with this classpath entry.
+	 * 
+	 * @since 3.8
+	 * @return The path to the external annotations file or directory, or <code>null</code> 
+	 * 		if not present.
+	 */
+	public IPath getExternalAnnotationsPath();
+
+	/**
+	 * Sets the path to the external annotations file or directory. It should be set to
+	 * <code>null</code> if no annotations are associated with this entry. 
+	 * 
+	 * @since 3.8
+	 * @param path The file or directory holding external annotations.
+	 */
+	public void setExternalAnnotationsPath(IPath path);
+
+	/**
 	 * Returns a constant indicating where this entry should appear on the 
 	 * runtime classpath by default.
 	 * The value returned is one of the following:
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java
index bf48210..5fa7402 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -7,6 +7,8 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation 
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.launching;
 
@@ -1686,6 +1688,7 @@
 		IPath[] libraryPaths;
 		IPath[] sourcePaths;
 		IPath[] sourceRootPaths;
+		IPath[] annotationPaths;
 		URL[] javadocLocations;
 		URL[] indexes;
 		LibraryLocation[] locations= vm.getLibraryLocations();
@@ -1701,6 +1704,7 @@
 			sourceRootPaths = new IPath[dflts.length];
 			javadocLocations= new URL[dflts.length];
 			indexes = new URL[dflts.length];
+			annotationPaths = new IPath[dflts.length];
 			for (int i = 0; i < dflts.length; i++) {
 				libraryPaths[i]= dflts[i].getSystemLibraryPath();
                 if (defJavaDocLocation == null) {
@@ -1713,6 +1717,8 @@
 					libraryPaths[i]= Path.EMPTY;
 				}
 				
+				annotationPaths[i] = Path.EMPTY;
+
 				sourcePaths[i]= dflts[i].getSystemLibrarySourcePath();
 				if (sourcePaths[i].toFile().isFile()) {
 					sourceRootPaths[i]= dflts[i].getPackageRootPath();
@@ -1727,17 +1733,19 @@
 			sourceRootPaths = new IPath[locations.length];
 			javadocLocations= new URL[locations.length];
 			indexes = new URL[locations.length];
+			annotationPaths = new IPath[locations.length];
 			for (int i = 0; i < locations.length; i++) {
 				libraryPaths[i]= locations[i].getSystemLibraryPath();
 				sourcePaths[i]= locations[i].getSystemLibrarySourcePath();
 				sourceRootPaths[i]= locations[i].getPackageRootPath();
 				javadocLocations[i]= locations[i].getJavadocLocation();
+				annotationPaths[i] = locations[i].getExternalAnnotationsPath();
 				indexes[i] = locations[i].getIndexLocation();
 			}
 		}
 		locations = new LibraryLocation[sourcePaths.length];
 		for (int i = 0; i < sourcePaths.length; i++) {
-			locations[i] = new LibraryLocation(libraryPaths[i], sourcePaths[i], sourceRootPaths[i], javadocLocations[i], indexes[i]);
+			locations[i] = new LibraryLocation(libraryPaths[i], sourcePaths[i], sourceRootPaths[i], javadocLocations[i], indexes[i], annotationPaths[i]);
 		}
 		return locations;
 	}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/LibraryLocation.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/LibraryLocation.java
index c2cc1f1..6e0850a 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/LibraryLocation.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/LibraryLocation.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2013 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -7,12 +7,15 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 
+ *     	external annotations to a JRE container
  *******************************************************************************/
 package org.eclipse.jdt.launching;
 
 import java.net.URL;
 
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
 import org.eclipse.jdt.internal.launching.LaunchingMessages;
 import org.eclipse.jdt.internal.launching.LaunchingPlugin;
 
@@ -26,6 +29,7 @@
 public final class LibraryLocation {
 	private IPath fSystemLibrary;
 	private IPath fSystemLibrarySource;
+	private IPath fExternalAnnotations;
 	private IPath fPackageRootPath;
 	private URL fJavadocLocation;
 	private URL fIndexLocation;
@@ -65,8 +69,8 @@
 	 * @since 3.1
 	 */	
 	public LibraryLocation(IPath libraryPath, IPath sourcePath, IPath packageRoot, URL javadocLocation) {
-		this(libraryPath, sourcePath, packageRoot, javadocLocation, null);
-	}		
+		this(libraryPath, sourcePath, packageRoot, javadocLocation, null, null);
+	}
 
 	/**
 	 * Creates a new library location.
@@ -86,6 +90,28 @@
 	 * @since 3.7
 	 */
 	public LibraryLocation(IPath libraryPath, IPath sourcePath, IPath packageRoot, URL javadocLocation, URL indexLocation) {
+		this(libraryPath, sourcePath, packageRoot, javadocLocation, indexLocation, null);
+	}		
+
+	/**
+	 * Creates a new library location.
+	 * 
+	 * @param libraryPath	The location of the JAR containing java.lang.Object
+	 * 					Must not be <code>null</code>.
+	 * @param sourcePath	The location of the zip file containing the sources for <code>library</code>
+	 * 					Must not be <code>null</code> (Use Path.EMPTY instead)
+	 * @param packageRoot The path inside the <code>source</code> zip file where packages names
+	 * 					  begin. If the source for java.lang.Object source is found at 
+	 * 					  "src/java/lang/Object.java" in the zip file, the 
+	 * 					  packageRoot should be "src"
+	 * 					  Must not be <code>null</code>. (Use Path.EMPTY or IPath.ROOT)
+	 * @param javadocLocation The location of the javadoc for <code>library</code>
+	 * @param indexLocation The location of the index for <code>library</code>
+	 * @param externalAnnotations The file or directory containing external annotations, or <code>null</code> if not applicable.
+	 * @throws IllegalArgumentException If the library path is <code>null</code>.
+	 * @since 3.8
+	 */
+	public LibraryLocation(IPath libraryPath, IPath sourcePath, IPath packageRoot, URL javadocLocation, URL indexLocation, IPath externalAnnotations) {
 		if (libraryPath == null) {
 			throw new IllegalArgumentException(LaunchingMessages.libraryLocation_assert_libraryNotNull); 
 		}
@@ -94,6 +120,7 @@
 		fPackageRootPath= packageRoot;
 		fJavadocLocation= javadocLocation;
 		fIndexLocation = indexLocation;
+		fExternalAnnotations = externalAnnotations == null ? Path.EMPTY : externalAnnotations;
 	}
 	
 	/**
@@ -115,6 +142,16 @@
 	}	
 	
 	/**
+	 * Return the JRE library external annotations location.
+	 * 
+	 * @since 3.8
+	 * @return The file or directory holding external annotations, or Path.EMPTY if not applicable. This will never be null.
+	 */
+	public IPath getExternalAnnotationsPath() {
+		return fExternalAnnotations;
+	}
+
+	/**
 	 * Returns the path to the default package in the sources zip file
 	 * 
 	 * @return The path to the default package in the sources zip file.
@@ -154,6 +191,7 @@
 			LibraryLocation lib = (LibraryLocation)obj;
 			return getSystemLibraryPath().equals(lib.getSystemLibraryPath()) 
 				&& equals(getSystemLibrarySourcePath(), lib.getSystemLibrarySourcePath())
+				&& equals(getExternalAnnotationsPath(), lib.getExternalAnnotationsPath())
 				&& equals(getPackageRootPath(), lib.getPackageRootPath())
 				&& LaunchingPlugin.sameURL(getJavadocLocation(), lib.getJavadocLocation());
 		}