1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 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.*;
17 import org.eclipse.core.runtime.CoreException;
18 import org.eclipse.core.runtime.IPath;
19 import org.eclipse.jdt.core.*;
20 import org.eclipse.jdt.internal.core.util.Messages;
21 
22 public class CopyPackageFragmentRootOperation extends JavaModelOperation {
23 	IPath destination;
24 	int updateResourceFlags;
25 	int updateModelFlags;
26 	IClasspathEntry sibling;
27 
CopyPackageFragmentRootOperation( IPackageFragmentRoot root, IPath destination, int updateResourceFlags, int updateModelFlags, IClasspathEntry sibling)28 	public CopyPackageFragmentRootOperation(
29 		IPackageFragmentRoot root,
30 		IPath destination,
31 		int updateResourceFlags,
32 		int updateModelFlags,
33 		IClasspathEntry sibling) {
34 
35 		super(root);
36 		this.destination = destination;
37 		this.updateResourceFlags = updateResourceFlags;
38 		this.updateModelFlags = updateModelFlags;
39 		this.sibling = sibling;
40 	}
41 	@Override
executeOperation()42 	protected void executeOperation() throws JavaModelException {
43 
44 		IPackageFragmentRoot root = (IPackageFragmentRoot)getElementToProcess();
45 		IClasspathEntry rootEntry = root.getRawClasspathEntry();
46 		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
47 
48 		// copy resource
49 		if (!root.isExternal() && (this.updateModelFlags & IPackageFragmentRoot.NO_RESOURCE_MODIFICATION) == 0) {
50 			copyResource(root, rootEntry, workspaceRoot);
51 		}
52 
53 		// update classpath if needed
54 		if ((this.updateModelFlags & IPackageFragmentRoot.DESTINATION_PROJECT_CLASSPATH) != 0) {
55 			addEntryToClasspath(rootEntry, workspaceRoot);
56 		}
57 	}
copyResource( IPackageFragmentRoot root, IClasspathEntry rootEntry, final IWorkspaceRoot workspaceRoot)58 	protected void copyResource(
59 		IPackageFragmentRoot root,
60 		IClasspathEntry rootEntry,
61 		final IWorkspaceRoot workspaceRoot)
62 		throws JavaModelException {
63 		final char[][] exclusionPatterns = ((ClasspathEntry)rootEntry).fullExclusionPatternChars();
64 		IResource rootResource = ((JavaElement) root).resource();
65 		if (root.getKind() == IPackageFragmentRoot.K_BINARY || exclusionPatterns == null) {
66 			try {
67 				IResource destRes;
68 				if ((this.updateModelFlags & IPackageFragmentRoot.REPLACE) != 0) {
69 					if (rootEntry.getPath().equals(this.destination)) return;
70 					if ((destRes = workspaceRoot.findMember(this.destination)) != null) {
71 						destRes.delete(this.updateResourceFlags, this.progressMonitor);
72 					}
73 				}
74 				rootResource.copy(this.destination, this.updateResourceFlags, this.progressMonitor);
75 			} catch (CoreException e) {
76 				throw new JavaModelException(e);
77 			}
78 		} else {
79 			final int sourceSegmentCount = rootEntry.getPath().segmentCount();
80 			final IFolder destFolder = workspaceRoot.getFolder(this.destination);
81 			final IPath[] nestedFolders = getNestedFolders(root);
82 			IResourceProxyVisitor visitor = new IResourceProxyVisitor() {
83 				@Override
84 				public boolean visit(IResourceProxy proxy) throws CoreException {
85 					if (proxy.getType() == IResource.FOLDER) {
86 						IPath path = proxy.requestFullPath();
87 						if (prefixesOneOf(path, nestedFolders)) {
88 							if (equalsOneOf(path, nestedFolders)) {
89 								// nested source folder
90 								return false;
91 							} else {
92 								// folder containing nested source folder
93 								IFolder folder = destFolder.getFolder(path.removeFirstSegments(sourceSegmentCount));
94 								if ((CopyPackageFragmentRootOperation.this.updateModelFlags & IPackageFragmentRoot.REPLACE) != 0
95 										&& folder.exists()) {
96 									return true;
97 								}
98 								folder.create(CopyPackageFragmentRootOperation.this.updateResourceFlags, true, CopyPackageFragmentRootOperation.this.progressMonitor);
99 								return true;
100 							}
101 						} else {
102 							// subtree doesn't contain any nested source folders
103 							IPath destPath = CopyPackageFragmentRootOperation.this.destination.append(path.removeFirstSegments(sourceSegmentCount));
104 							IResource destRes;
105 							if ((CopyPackageFragmentRootOperation.this.updateModelFlags & IPackageFragmentRoot.REPLACE) != 0
106 									&& (destRes = workspaceRoot.findMember(destPath)) != null) {
107 								destRes.delete(CopyPackageFragmentRootOperation.this.updateResourceFlags, CopyPackageFragmentRootOperation.this.progressMonitor);
108 							}
109 							proxy.requestResource().copy(destPath, CopyPackageFragmentRootOperation.this.updateResourceFlags, CopyPackageFragmentRootOperation.this.progressMonitor);
110 							return false;
111 						}
112 					} else {
113 						IPath path = proxy.requestFullPath();
114 						IPath destPath = CopyPackageFragmentRootOperation.this.destination.append(path.removeFirstSegments(sourceSegmentCount));
115 						IResource destRes;
116 						if ((CopyPackageFragmentRootOperation.this.updateModelFlags & IPackageFragmentRoot.REPLACE) != 0
117 								&& (destRes = workspaceRoot.findMember(destPath)) != null) {
118 							destRes.delete(CopyPackageFragmentRootOperation.this.updateResourceFlags, CopyPackageFragmentRootOperation.this.progressMonitor);
119 						}
120 						proxy.requestResource().copy(destPath, CopyPackageFragmentRootOperation.this.updateResourceFlags, CopyPackageFragmentRootOperation.this.progressMonitor);
121 						return false;
122 					}
123 				}
124 			};
125 			try {
126 				rootResource.accept(visitor, IResource.NONE);
127 			} catch (CoreException e) {
128 				throw new JavaModelException(e);
129 			}
130 		}
131 		setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
132 	}
addEntryToClasspath(IClasspathEntry rootEntry, IWorkspaceRoot workspaceRoot)133 	protected void addEntryToClasspath(IClasspathEntry rootEntry, IWorkspaceRoot workspaceRoot) throws JavaModelException {
134 
135 		IProject destProject = workspaceRoot.getProject(this.destination.segment(0));
136 		IJavaProject jProject = JavaCore.create(destProject);
137 		IClasspathEntry[] classpath = jProject.getRawClasspath();
138 		int length = classpath.length;
139 		IClasspathEntry[] newClasspath;
140 
141 		// case of existing entry and REPLACE was specified
142 		if ((this.updateModelFlags & IPackageFragmentRoot.REPLACE) != 0) {
143 			// find existing entry
144 			for (int i = 0; i < length; i++) {
145 				if (this.destination.equals(classpath[i].getPath())) {
146 					newClasspath = new IClasspathEntry[length];
147 					System.arraycopy(classpath, 0, newClasspath, 0, length);
148 					newClasspath[i] = copy(rootEntry);
149 					jProject.setRawClasspath(newClasspath, this.progressMonitor);
150 					return;
151 				}
152 			}
153 		}
154 
155 		// other cases
156 		int position;
157 		if (this.sibling == null) {
158 			// insert at the end
159 			position = length;
160 		} else {
161 			// insert before sibling
162 			position = -1;
163 			for (int i = 0; i < length; i++) {
164 				if (this.sibling.equals(classpath[i])) {
165 					position = i;
166 					break;
167 				}
168 			}
169 		}
170 		if (position == -1) {
171 			throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_SIBLING, this.sibling.toString()));
172 		}
173 		newClasspath = new IClasspathEntry[length+1];
174 		if (position != 0) {
175 			System.arraycopy(classpath, 0, newClasspath, 0, position);
176 		}
177 		if (position != length) {
178 			System.arraycopy(classpath, position, newClasspath, position+1, length-position);
179 		}
180 		IClasspathEntry newEntry = copy(rootEntry);
181 		newClasspath[position] = newEntry;
182 		jProject.setRawClasspath(newClasspath, this.progressMonitor);
183 	}
184 	/*
185 	 * Copies the given classpath entry replacing its path with the destination path
186 	 * if it is a source folder or a library.
187 	 */
copy(IClasspathEntry entry)188 	protected IClasspathEntry copy(IClasspathEntry entry) throws JavaModelException {
189 		switch (entry.getEntryKind()) {
190 			case IClasspathEntry.CPE_CONTAINER:
191 				return JavaCore.newContainerEntry(entry.getPath(), entry.getAccessRules(), entry.getExtraAttributes(), entry.isExported());
192 			case IClasspathEntry.CPE_LIBRARY:
193 				try {
194 					return JavaCore.newLibraryEntry(this.destination, entry.getSourceAttachmentPath(), entry.getSourceAttachmentRootPath(), entry.getAccessRules(), entry.getExtraAttributes(), entry.isExported());
195 				} catch (ClasspathEntry.AssertionFailedException e) {
196 					IJavaModelStatus status = new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, e.getMessage());
197 					throw new JavaModelException(status);
198 				}
199 			case IClasspathEntry.CPE_PROJECT:
200 				return JavaCore.newProjectEntry(entry.getPath(), entry.getAccessRules(), entry.combineAccessRules(), entry.getExtraAttributes(), entry.isExported());
201 			case IClasspathEntry.CPE_SOURCE:
202 				return JavaCore.newSourceEntry(this.destination, entry.getInclusionPatterns(), entry.getExclusionPatterns(), entry.getOutputLocation(), entry.getExtraAttributes());
203 			case IClasspathEntry.CPE_VARIABLE:
204 				try {
205 					return JavaCore.newVariableEntry(entry.getPath(), entry.getSourceAttachmentPath(), entry.getSourceAttachmentRootPath(), entry.getAccessRules(), entry.getExtraAttributes(), entry.isExported());
206 				} catch (ClasspathEntry.AssertionFailedException e) {
207 					IJavaModelStatus status = new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, e.getMessage());
208 					throw new JavaModelException(status);
209 				}
210 			default:
211 				throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, getElementToProcess()));
212 		}
213 	}
214 	@Override
verify()215 	public IJavaModelStatus verify() {
216 		IJavaModelStatus status = super.verify();
217 		if (!status.isOK()) {
218 			return status;
219 		}
220 		PackageFragmentRoot root = (PackageFragmentRoot)getElementToProcess();
221 		if (root == null || !root.exists()) {
222 			return new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, root);
223 		}
224 
225 		IResource resource = root.resource();
226 		if (resource instanceof IFolder) {
227 			if (resource.isLinked()) {
228 				return new JavaModelStatus(IJavaModelStatusConstants.INVALID_RESOURCE, root);
229 			}
230 		}
231 
232 		if ((this.updateModelFlags & IPackageFragmentRoot.DESTINATION_PROJECT_CLASSPATH) != 0) {
233 			String destProjectName = this.destination.segment(0);
234 			IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(destProjectName);
235 			if (JavaProject.hasJavaNature(project)) {
236 				try {
237 					IJavaProject destProject = JavaCore.create(project);
238 					IClasspathEntry[] destClasspath = destProject.getRawClasspath();
239 					boolean foundSibling = false;
240 					boolean foundExistingEntry = false;
241 					for (int i = 0, length = destClasspath.length; i < length; i++) {
242 						IClasspathEntry entry = destClasspath[i];
243 						if (entry.equals(this.sibling)) {
244 							foundSibling = true;
245 							break;
246 						}
247 						if (entry.getPath().equals(this.destination)) {
248 							foundExistingEntry = true;
249 						}
250 					}
251 					if (this.sibling != null && !foundSibling) {
252 						return new JavaModelStatus(IJavaModelStatusConstants.INVALID_SIBLING, this.sibling.toString());
253 					}
254 					if (foundExistingEntry && (this.updateModelFlags & IPackageFragmentRoot.REPLACE) == 0) {
255 						return new JavaModelStatus(
256 							IJavaModelStatusConstants.NAME_COLLISION,
257 							Messages.bind(Messages.status_nameCollision, new String[] {this.destination.toString()}));
258 					}
259 				} catch (JavaModelException e) {
260 					return e.getJavaModelStatus();
261 				}
262 			}
263 		}
264 
265 		return JavaModelStatus.VERIFIED_OK;
266 	}
267 }
268