1 /*******************************************************************************
2  * Copyright (c) 2006, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.jdt.internal.junit.buildpath;
15 
16 import java.util.ArrayList;
17 import java.util.List;
18 
19 import org.eclipse.jdt.junit.JUnitCore;
20 
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IPath;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.Status;
25 import org.eclipse.core.runtime.preferences.DefaultScope;
26 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
27 import org.eclipse.core.runtime.preferences.InstanceScope;
28 
29 import org.eclipse.jdt.core.ClasspathContainerInitializer;
30 import org.eclipse.jdt.core.IClasspathAttribute;
31 import org.eclipse.jdt.core.IClasspathContainer;
32 import org.eclipse.jdt.core.IClasspathEntry;
33 import org.eclipse.jdt.core.IJavaModel;
34 import org.eclipse.jdt.core.IJavaProject;
35 import org.eclipse.jdt.core.JavaCore;
36 import org.eclipse.jdt.core.JavaModelException;
37 
38 import org.eclipse.jdt.internal.junit.JUnitCorePlugin;
39 import org.eclipse.jdt.internal.junit.JUnitMessages;
40 import org.eclipse.jdt.internal.junit.JUnitPreferencesConstants;
41 
42 public class JUnitContainerInitializer extends ClasspathContainerInitializer {
43 
44 	private static final IStatus NOT_SUPPORTED= new Status(IStatus.ERROR, JUnitCorePlugin.CORE_PLUGIN_ID, ClasspathContainerInitializer.ATTRIBUTE_NOT_SUPPORTED, new String(), null);
45 	private static final IStatus READ_ONLY= new Status(IStatus.ERROR, JUnitCorePlugin.CORE_PLUGIN_ID, ClasspathContainerInitializer.ATTRIBUTE_READ_ONLY, new String(), null);
46 
47 	/**
48 	 * @deprecated just for compatibility
49 	 */
50 	@Deprecated
51 	private final static String JUNIT3_8_1= "3.8.1"; //$NON-NLS-1$
52 	private final static String JUNIT3= "3"; //$NON-NLS-1$
53 	private final static String JUNIT4= "4"; //$NON-NLS-1$
54 	private final static String JUNIT5= "5"; //$NON-NLS-1$
55 
56 	private static class JUnitContainer implements IClasspathContainer {
57 
58 		private final IClasspathEntry[] fEntries;
59 		private final IPath fPath;
60 
JUnitContainer(IPath path, IClasspathEntry[] entries)61 		public JUnitContainer(IPath path, IClasspathEntry[] entries) {
62 			fPath= path;
63 			fEntries= entries;
64 		}
65 
66 		@Override
getClasspathEntries()67 		public IClasspathEntry[] getClasspathEntries() {
68 			return fEntries;
69 		}
70 
71 		@Override
getDescription()72 		public String getDescription() {
73 			if (JUnitCore.JUNIT5_CONTAINER_PATH.equals(fPath)) {
74 				return JUnitMessages.JUnitContainerInitializer_description_junit5;
75 			}
76 			if (JUnitCore.JUNIT4_CONTAINER_PATH.equals(fPath)) {
77 				return JUnitMessages.JUnitContainerInitializer_description_junit4;
78 			}
79 			return JUnitMessages.JUnitContainerInitializer_description_junit3;
80 		}
81 
82 		@Override
getKind()83 		public int getKind() {
84 			return IClasspathContainer.K_APPLICATION;
85 		}
86 
87 		@Override
getPath()88 		public IPath getPath() {
89 			return fPath;
90 		}
91 
92 	}
93 
94 
JUnitContainerInitializer()95 	public JUnitContainerInitializer() {
96 	}
97 
98 	@Override
initialize(IPath containerPath, IJavaProject project)99 	public void initialize(IPath containerPath, IJavaProject project) throws CoreException {
100 		if (isValidJUnitContainerPath(containerPath)) {
101 			JUnitContainer container= getNewContainer(containerPath);
102 			JavaCore.setClasspathContainer(containerPath, new IJavaProject[] { project }, 	new IClasspathContainer[] { container }, null);
103 		}
104 
105 	}
106 
getNewContainer(IPath containerPath)107 	private static JUnitContainer getNewContainer(IPath containerPath) {
108 		List<IClasspathEntry> entriesList= new ArrayList<>();
109 		IClasspathEntry entry= null;
110 		IClasspathEntry entry2= null;
111 		String version= containerPath.segment(1);
112 		if (null != version) switch (version) {
113 		case JUNIT3_8_1:
114 		case JUNIT3:
115 			entry= BuildPathSupport.getJUnit3LibraryEntry();
116 			if (entry == null) { // JUnit 4 includes most of JUnit 3, so let's cheat
117 				entry= BuildPathSupport.getJUnit4as3LibraryEntry();
118 			}
119 			break;
120 		case JUNIT4:
121 			entry= BuildPathSupport.getJUnit4LibraryEntry();
122 			entry2= BuildPathSupport.getHamcrestCoreLibraryEntry();
123 			break;
124 		case JUNIT5:
125 			entriesList.add(BuildPathSupport.getJUnitJupiterApiLibraryEntry());
126 			entriesList.add(BuildPathSupport.getJUnitJupiterEngineLibraryEntry());
127 			entriesList.add(BuildPathSupport.getJUnitJupiterMigrationSupportLibraryEntry());
128 			entriesList.add(BuildPathSupport.getJUnitJupiterParamsLibraryEntry());
129 			entriesList.add(BuildPathSupport.getJUnitPlatformCommonsLibraryEntry());
130 			entriesList.add(BuildPathSupport.getJUnitPlatformEngineLibraryEntry());
131 			entriesList.add(BuildPathSupport.getJUnitPlatformLauncherLibraryEntry());
132 			entriesList.add(BuildPathSupport.getJUnitPlatformRunnerLibraryEntry());
133 			entriesList.add(BuildPathSupport.getJUnitPlatformSuiteApiLibraryEntry());
134 			entriesList.add(BuildPathSupport.getJUnitVintageEngineLibraryEntry());
135 			entriesList.add(BuildPathSupport.getJUnitOpentest4jLibraryEntry());
136 			entriesList.add(BuildPathSupport.getJUnitApiGuardianLibraryEntry());
137 			entriesList.add(BuildPathSupport.getJUnit4LibraryEntry());
138 			entriesList.add(BuildPathSupport.getHamcrestCoreLibraryEntry());
139 			break;
140 		default:
141 			break;
142 		}
143 		IClasspathEntry[] entries;
144 		if (!entriesList.isEmpty() ) {
145 			entries= entriesList.toArray(new IClasspathEntry[entriesList.size()]);
146 		} else if (entry == null) {
147 			entries= new IClasspathEntry[] { };
148 		} else if (entry2 == null) {
149 			entries= new IClasspathEntry[] { entry };
150 		} else {
151 			entries= new IClasspathEntry[] { entry, entry2 };
152 		}
153 		return new JUnitContainer(containerPath, entries);
154 	}
155 
156 
isValidJUnitContainerPath(IPath path)157 	private static boolean isValidJUnitContainerPath(IPath path) {
158 		return path != null && path.segmentCount() == 2 && JUnitCore.JUNIT_CONTAINER_ID.equals(path.segment(0));
159 	}
160 
161 	@Override
canUpdateClasspathContainer(IPath containerPath, IJavaProject project)162 	public boolean canUpdateClasspathContainer(IPath containerPath, IJavaProject project) {
163 		return true;
164 	}
165 
166 	@Override
getAccessRulesStatus(IPath containerPath, IJavaProject project)167 	public IStatus getAccessRulesStatus(IPath containerPath, IJavaProject project) {
168 		return READ_ONLY;
169 	}
170 
171 	@Override
getSourceAttachmentStatus(IPath containerPath, IJavaProject project)172 	public IStatus getSourceAttachmentStatus(IPath containerPath, IJavaProject project) {
173 		return READ_ONLY;
174 	}
175 
176 	@Override
getAttributeStatus(IPath containerPath, IJavaProject project, String attributeKey)177 	public IStatus getAttributeStatus(IPath containerPath, IJavaProject project, String attributeKey) {
178 		if (attributeKey.equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) {
179 			return Status.OK_STATUS;
180 		}
181 		return NOT_SUPPORTED;
182 	}
183 
184 
185 	@Override
requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion)186 	public void requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion) throws CoreException {
187 		IEclipsePreferences preferences= InstanceScope.INSTANCE.getNode(JUnitCorePlugin.CORE_PLUGIN_ID);
188 
189 		IClasspathEntry[] entries= containerSuggestion.getClasspathEntries();
190 		if (entries.length >= 1 && isValidJUnitContainerPath(containerPath)) {
191 			String version= containerPath.segment(1);
192 
193 			// only modifiable entry is Javadoc location
194 			for (IClasspathEntry entry : entries) {
195 				String preferenceKey= getPreferenceKey(entry, version);
196 				IClasspathAttribute[] extraAttributes= entry.getExtraAttributes();
197 				if (extraAttributes.length == 0) {
198 					// Revert to default
199 					String defaultValue= DefaultScope.INSTANCE.getNode(JUnitCorePlugin.CORE_PLUGIN_ID).get(preferenceKey, ""); //$NON-NLS-1$
200 					if (!defaultValue.equals(preferences.get(preferenceKey, defaultValue))) {
201 						preferences.put(preferenceKey, defaultValue);
202 					}
203 
204 					/*
205 					* The following would be correct, but would not allow to revert to the default.
206 					* There's no concept of "default value" for a classpath attribute, see
207 					* org.eclipse.jdt.internal.ui.preferences.JavadocConfigurationBlock.performDefaults()
208 					*/
209 					// preferenceStore.setValue(preferenceKey, "");
210 				} else {
211 					for (IClasspathAttribute attrib : extraAttributes) {
212 						if (attrib.getName().equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) {
213 							if (preferenceKey != null) {
214 								preferences.put(preferenceKey, attrib.getValue());
215 							}
216 							break;
217 						}
218 					}
219 				}
220 			}
221 			rebindClasspathEntries(project.getJavaModel(), containerPath);
222 		}
223 	}
224 
getPreferenceKey(IClasspathEntry entry, String version)225 	private String getPreferenceKey(IClasspathEntry entry, String version) {
226 		if (JUNIT3.equals(version)) {
227 			return JUnitPreferencesConstants.JUNIT3_JAVADOC;
228 		} else {
229 			String lastSegment= entry.getPath().lastSegment();
230 			if (JUNIT4.equals(version)) {
231 				if (lastSegment.contains("junit")) { //$NON-NLS-1$
232 					return JUnitPreferencesConstants.JUNIT4_JAVADOC;
233 				} else {
234 					return JUnitPreferencesConstants.HAMCREST_CORE_JAVADOC;
235 				}
236 			} else if (JUNIT5.equals(version)) {
237 				if (lastSegment.contains("jupiter.api")) { //$NON-NLS-1$
238 					return JUnitPreferencesConstants.JUNIT_JUPITER_API_JAVADOC;
239 				} else if (lastSegment.contains("jupiter.engine")) { //$NON-NLS-1$
240 					return JUnitPreferencesConstants.JUNIT_JUPITER_ENGINE_JAVADOC;
241 				} else if (lastSegment.contains("jupiter.migrationsupport")) { //$NON-NLS-1$
242 					return JUnitPreferencesConstants.JUNIT_JUPITER_MIGRATIONSUPPORT_JAVADOC;
243 				} else if (lastSegment.contains("jupiter.params")) { //$NON-NLS-1$
244 					return JUnitPreferencesConstants.JUNIT_JUPITER_PARAMS_JAVADOC;
245 				} else if (lastSegment.contains("platform.commons")) { //$NON-NLS-1$
246 					return JUnitPreferencesConstants.JUNIT_PLATFORM_COMMONS_JAVADOC;
247 				} else if (lastSegment.contains("platform.engine")) { //$NON-NLS-1$
248 					return JUnitPreferencesConstants.JUNIT_PLATFORM_ENGINE_JAVADOC;
249 				} else if (lastSegment.contains("platform.launcher")) { //$NON-NLS-1$
250 					return JUnitPreferencesConstants.JUNIT_PLATFORM_LAUNCHER_JAVADOC;
251 				} else if (lastSegment.contains("platform.runner")) { //$NON-NLS-1$
252 					return JUnitPreferencesConstants.JUNIT_PLATFORM_RUNNER_JAVADOC;
253 				} else if (lastSegment.contains("platform.suite.api")) { //$NON-NLS-1$
254 					return JUnitPreferencesConstants.JUNIT_PLATFORM_SUITE_API_JAVADOC;
255 				} else if (lastSegment.contains("vintage.engine")) { //$NON-NLS-1$
256 					return JUnitPreferencesConstants.JUNIT_VINTAGE_ENGINE_JAVADOC;
257 				} else if (lastSegment.contains("opentest4j")) { //$NON-NLS-1$
258 					return JUnitPreferencesConstants.JUNIT_OPENTEST4J_JAVADOC;
259 				} else if (lastSegment.contains("apiguardian")) { //$NON-NLS-1$
260 					return JUnitPreferencesConstants.JUNIT_APIGUARDIAN_JAVADOC;
261 				} else if (lastSegment.contains("junit")) { //$NON-NLS-1$
262 					return JUnitPreferencesConstants.JUNIT4_JAVADOC;
263 				} else {
264 					return JUnitPreferencesConstants.HAMCREST_CORE_JAVADOC;
265 				}
266 			}
267 		}
268 		return null;
269 	}
270 
rebindClasspathEntries(IJavaModel model, IPath containerPath)271 	private static void rebindClasspathEntries(IJavaModel model, IPath containerPath) throws JavaModelException {
272 		ArrayList<IJavaProject> affectedProjects= new ArrayList<>();
273 
274 		IJavaProject[] projects= model.getJavaProjects();
275 		for (IJavaProject project : projects) {
276 			IClasspathEntry[] entries= project.getRawClasspath();
277 			for (IClasspathEntry curr : entries) {
278 				if (curr.getEntryKind() == IClasspathEntry.CPE_CONTAINER && containerPath.equals(curr.getPath())) {
279 					affectedProjects.add(project);
280 				}
281 			}
282 		}
283 		if (!affectedProjects.isEmpty()) {
284 			IJavaProject[] affected= affectedProjects.toArray(new IJavaProject[affectedProjects.size()]);
285 			IClasspathContainer[] containers= new IClasspathContainer[affected.length];
286 			for (int i= 0; i < containers.length; i++) {
287 				containers[i]= getNewContainer(containerPath);
288 			}
289 			JavaCore.setClasspathContainer(containerPath, affected, containers, null);
290 		}
291 	}
292 
293 	/**
294 	 * @see org.eclipse.jdt.core.ClasspathContainerInitializer#getDescription(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject)
295 	 */
296 	@Override
getDescription(IPath containerPath, IJavaProject project)297 	public String getDescription(IPath containerPath, IJavaProject project) {
298 		if (isValidJUnitContainerPath(containerPath)) {
299 			String version= containerPath.segment(1);
300 			if (null != version) switch (version) {
301 			case JUNIT3_8_1:
302 			case JUNIT3:
303 				return JUnitMessages.JUnitContainerInitializer_description_initializer_junit3;
304 			case JUNIT4:
305 				return JUnitMessages.JUnitContainerInitializer_description_initializer_junit4;
306 			case JUNIT5:
307 				return JUnitMessages.JUnitContainerInitializer_description_initializer_junit5;
308 			default:
309 				break;
310 			}
311 		}
312 		return JUnitMessages.JUnitContainerInitializer_description_initializer_unresolved;
313 	}
314 
315 	@Override
getComparisonID(IPath containerPath, IJavaProject project)316 	public Object getComparisonID(IPath containerPath, IJavaProject project) {
317 		return containerPath;
318 	}
319 
320 }
321