1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 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.core;
15 
16 import org.eclipse.core.resources.ResourcesPlugin;
17 import org.eclipse.core.runtime.CoreException;
18 import org.eclipse.core.runtime.IPath;
19 import org.eclipse.core.runtime.OperationCanceledException;
20 import org.eclipse.jdt.core.IClasspathContainer;
21 import org.eclipse.jdt.core.IClasspathEntry;
22 import org.eclipse.jdt.core.IJavaElement;
23 import org.eclipse.jdt.core.IJavaModelStatusConstants;
24 import org.eclipse.jdt.core.IJavaProject;
25 import org.eclipse.jdt.core.JavaModelException;
26 import org.eclipse.jdt.internal.core.util.Util;
27 
28 public class SetContainerOperation extends ChangeClasspathOperation {
29 
30 	IPath containerPath;
31 	IJavaProject[] affectedProjects;
32 	IClasspathContainer[] respectiveContainers;
33 
34 	/*
35 	 * Creates a new SetContainerOperation.
36 	 */
SetContainerOperation(IPath containerPath, IJavaProject[] affectedProjects, IClasspathContainer[] respectiveContainers)37 	public SetContainerOperation(IPath containerPath, IJavaProject[] affectedProjects, IClasspathContainer[] respectiveContainers) {
38 		super(new IJavaElement[] {JavaModelManager.getJavaModelManager().getJavaModel()}, !ResourcesPlugin.getWorkspace().isTreeLocked());
39 		this.containerPath = containerPath;
40 		this.affectedProjects = affectedProjects;
41 		this.respectiveContainers = respectiveContainers;
42 	}
43 
44 	@Override
executeOperation()45 	protected void executeOperation() throws JavaModelException {
46 		checkCanceled();
47 		try {
48 			beginTask("", 1); //$NON-NLS-1$
49 			if (JavaModelManager.CP_RESOLVE_VERBOSE)
50 				verbose_set_container();
51 			if (JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED)
52 				verbose_set_container_invocation_trace();
53 
54 			JavaModelManager manager = JavaModelManager.getJavaModelManager();
55 			if (manager.containerPutIfInitializingWithSameEntries(this.containerPath, this.affectedProjects, this.respectiveContainers))
56 				return;
57 
58 			final int projectLength = this.affectedProjects.length;
59 			final IJavaProject[] modifiedProjects;
60 			System.arraycopy(this.affectedProjects, 0, modifiedProjects = new IJavaProject[projectLength], 0, projectLength);
61 
62 			// filter out unmodified project containers
63 			int remaining = 0;
64 			for (int i = 0; i < projectLength; i++){
65 				if (isCanceled())
66 					return;
67 				JavaProject affectedProject = (JavaProject) this.affectedProjects[i];
68 				IClasspathContainer newContainer = this.respectiveContainers[i];
69 				if (newContainer == null) newContainer = JavaModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS; // 30920 - prevent infinite loop
70 				boolean found = false;
71 				if (JavaProject.hasJavaNature(affectedProject.getProject())){
72 					IClasspathEntry[] rawClasspath = affectedProject.getRawClasspath();
73 					for (int j = 0, cpLength = rawClasspath.length; j <cpLength; j++) {
74 						IClasspathEntry entry = rawClasspath[j];
75 						if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER && entry.getPath().equals(this.containerPath)){
76 							found = true;
77 							break;
78 						}
79 					}
80 				}
81 				if (!found) {
82 					modifiedProjects[i] = null; // filter out this project - does not reference the container path, or isnt't yet Java project
83 					manager.containerPut(affectedProject, this.containerPath, newContainer);
84 					continue;
85 				}
86 				IClasspathContainer oldContainer = manager.containerGet(affectedProject, this.containerPath);
87 				if (oldContainer == JavaModelManager.CONTAINER_INITIALIZATION_IN_PROGRESS) {
88 					oldContainer = null;
89 				}
90 				if ((oldContainer != null && oldContainer.equals(this.respectiveContainers[i]))
91 						|| (oldContainer == this.respectiveContainers[i])/*handle case where old and new containers are null (see bug 149043*/) {
92 					modifiedProjects[i] = null; // filter out this project - container did not change
93 					continue;
94 				}
95 				remaining++;
96 				manager.containerPut(affectedProject, this.containerPath, newContainer);
97 			}
98 
99 			if (remaining == 0) return;
100 
101 			// trigger model refresh
102 			try {
103 				for(int i = 0; i < projectLength; i++){
104 					this.progressMonitor.setWorkRemaining(projectLength - i);
105 					if (isCanceled())
106 						return;
107 
108 					JavaProject affectedProject = (JavaProject)modifiedProjects[i];
109 					if (affectedProject == null) continue; // was filtered out
110 					if (JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED)
111 						verbose_update_project(affectedProject);
112 
113 					// force resolved classpath to be recomputed
114 					ClasspathChange classpathChange = affectedProject.getPerProjectInfo().resetResolvedClasspath();
115 
116 					// if needed, generate delta, update project ref, create markers, ...
117 					classpathChanged(classpathChange, i==0/*refresh external linked folder only once*/);
118 
119 					if (this.canChangeResources) {
120 						// touch project to force a build if needed
121 						try {
122 							affectedProject.getProject().touch(this.progressMonitor.split(1));
123 						} catch (CoreException e) {
124 							// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=148970
125 							if (!ExternalJavaProject.EXTERNAL_PROJECT_NAME.equals(affectedProject.getElementName()))
126 								throw e;
127 						} catch (OperationCanceledException ex) {
128 							throw new JavaModelException(ex, IJavaModelStatusConstants.EVALUATION_ERROR);
129 						}
130 					}
131 				}
132 			} catch(CoreException e) {
133 				if (JavaModelManager.CP_RESOLVE_VERBOSE || JavaModelManager.CP_RESOLVE_VERBOSE_FAILURE)
134 					verbose_failure(e);
135 				if (e instanceof JavaModelException) {
136 					throw (JavaModelException)e;
137 				} else {
138 					throw new JavaModelException(e);
139 				}
140 			} finally {
141 				for (int i = 0; i < projectLength; i++) {
142 					if (this.respectiveContainers[i] == null) {
143 						manager.containerPut(this.affectedProjects[i], this.containerPath, null); // reset init in progress marker
144 					}
145 				}
146 			}
147 		} finally {
148 			done();
149 		}
150 	}
151 
verbose_failure(CoreException e)152 	private void verbose_failure(CoreException e) {
153 		Util.verbose(
154 			"CPContainer SET  - FAILED DUE TO EXCEPTION\n" + //$NON-NLS-1$
155 			"	container path: " + this.containerPath, //$NON-NLS-1$
156 			System.err);
157 		e.printStackTrace();
158 	}
159 
verbose_update_project(JavaProject affectedProject)160 	private void verbose_update_project(JavaProject affectedProject) {
161 		Util.verbose(
162 			"CPContainer SET  - updating affected project due to setting container\n" + //$NON-NLS-1$
163 			"	project: " + affectedProject.getElementName() + '\n' + //$NON-NLS-1$
164 			"	container path: " + this.containerPath); //$NON-NLS-1$
165 	}
166 
verbose_set_container()167 	private void verbose_set_container() {
168 		Util.verbose(
169 			"CPContainer SET  - setting container\n" + //$NON-NLS-1$
170 			"	container path: " + this.containerPath + '\n' + //$NON-NLS-1$
171 			"	projects: {" +//$NON-NLS-1$
172 			org.eclipse.jdt.internal.compiler.util.Util.toString(
173 				this.affectedProjects,
174 				new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
175 					@Override
176 					public String displayString(Object o) { return ((IJavaProject) o).getElementName(); }
177 				}) +
178 			"}\n	values: {\n"  +//$NON-NLS-1$
179 			org.eclipse.jdt.internal.compiler.util.Util.toString(
180 				this.respectiveContainers,
181 				new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
182 					@Override
183 					public String displayString(Object o) {
184 						StringBuffer buffer = new StringBuffer("		"); //$NON-NLS-1$
185 						if (o == null) {
186 							buffer.append("<null>"); //$NON-NLS-1$
187 							return buffer.toString();
188 						}
189 						IClasspathContainer container = (IClasspathContainer) o;
190 						buffer.append(container.getDescription());
191 						buffer.append(" {\n"); //$NON-NLS-1$
192 						IClasspathEntry[] entries = container.getClasspathEntries();
193 						if (entries != null){
194 							for (int i = 0; i < entries.length; i++){
195 								buffer.append(" 			"); //$NON-NLS-1$
196 								buffer.append(entries[i]);
197 								buffer.append('\n');
198 							}
199 						}
200 						buffer.append(" 		}"); //$NON-NLS-1$
201 						return buffer.toString();
202 					}
203 				}) +
204 			"\n	}");//$NON-NLS-1$
205 	}
206 
verbose_set_container_invocation_trace()207 	private void verbose_set_container_invocation_trace() {
208 		Util.verbose(
209 			"CPContainer SET  - setting container\n" + //$NON-NLS-1$
210 			"	invocation stack trace:"); //$NON-NLS-1$
211 			new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
212 	}
213 
214 }
215