1 /*******************************************************************************
2  * Copyright (c) 2000, 2016 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.corext.util;
15 
16 import java.io.File;
17 import java.net.URI;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 
23 import org.eclipse.core.filesystem.EFS;
24 
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IPath;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.MultiStatus;
29 import org.eclipse.core.runtime.Status;
30 
31 import org.eclipse.core.resources.IFile;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.resources.IResourceStatus;
34 import org.eclipse.core.resources.ResourceAttributes;
35 import org.eclipse.core.resources.ResourcesPlugin;
36 
37 import org.eclipse.jdt.core.manipulation.JavaManipulation;
38 
39 import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
40 import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
41 import org.eclipse.jdt.internal.corext.CorextMessages;
42 
43 import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
44 import org.eclipse.jdt.internal.ui.JavaUIStatus;
45 
46 public class Resources {
47 
Resources()48 	private Resources() {
49 	}
50 
51 	/**
52 	 * Checks if the given resource is in sync with the underlying file system.
53 	 *
54 	 * @param resource the resource to be checked
55 	 * @return IStatus status describing the check's result. If <code>status.
56 	 * isOK()</code> returns <code>true</code> then the resource is in sync
57 	 */
checkInSync(IResource resource)58 	public static IStatus checkInSync(IResource resource) {
59 		return checkInSync(new IResource[] {resource});
60 	}
61 
62 	/**
63 	 * Checks if the given resources are in sync with the underlying file
64 	 * system.
65 	 *
66 	 * @param resources the resources to be checked
67 	 * @return IStatus status describing the check's result. If <code>status.
68 	 *  isOK() </code> returns <code>true</code> then the resources are in sync
69 	 */
checkInSync(IResource[] resources)70 	public static IStatus checkInSync(IResource[] resources) {
71 		IStatus result= null;
72 		for (IResource resource : resources) {
73 			if (!resource.isSynchronized(IResource.DEPTH_INFINITE)) {
74 				result= addOutOfSync(result, resource);
75 			}
76 		}
77 		if (result != null)
78 			return result;
79 		return Status.OK_STATUS;
80 	}
81 
82 	/**
83 	 * Makes the given resource committable. Committable means that it is
84 	 * writeable and that its content hasn't changed by calling
85 	 * <code>validateEdit</code> for the given resource on <tt>IWorkspace</tt>.
86 	 *
87 	 * @param resource the resource to be checked
88 	 * @param context the context passed to <code>validateEdit</code>
89 	 * @return status describing the method's result. If <code>status.isOK()</code> returns <code>true</code> then the resources are committable.
90 	 *
91 	 * @see org.eclipse.core.resources.IWorkspace#validateEdit(org.eclipse.core.resources.IFile[], java.lang.Object)
92 	 */
makeCommittable(IResource resource, Object context)93 	public static IStatus makeCommittable(IResource resource, Object context) {
94 		return makeCommittable(new IResource[] { resource }, context);
95 	}
96 
97 	/**
98 	 * Makes the given resources committable. Committable means that all
99 	 * resources are writeable and that the content of the resources hasn't
100 	 * changed by calling <code>validateEdit</code> for a given file on
101 	 * <tt>IWorkspace</tt>.
102 	 *
103 	 * @param resources the resources to be checked
104 	 * @param context the context passed to <code>validateEdit</code>
105 	 * @return IStatus status describing the method's result. If <code>status.
106 	 * isOK()</code> returns <code>true</code> then the add resources are
107 	 * committable
108 	 *
109 	 * @see org.eclipse.core.resources.IWorkspace#validateEdit(org.eclipse.core.resources.IFile[], java.lang.Object)
110 	 */
makeCommittable(IResource[] resources, Object context)111 	public static IStatus makeCommittable(IResource[] resources, Object context) {
112 		List<IResource> readOnlyFiles= new ArrayList<>();
113 		for (IResource resource : resources) {
114 			if (resource.getType() == IResource.FILE && isReadOnly(resource))
115 				readOnlyFiles.add(resource);
116 		}
117 		if (readOnlyFiles.isEmpty())
118 			return Status.OK_STATUS;
119 
120 		Map<IFile, Long> oldTimeStamps= createModificationStampMap(readOnlyFiles);
121 		IStatus status= ResourcesPlugin.getWorkspace().validateEdit(
122 			readOnlyFiles.toArray(new IFile[readOnlyFiles.size()]), context);
123 		if (!status.isOK())
124 			return status;
125 
126 		IStatus modified= null;
127 		Map<IFile, Long> newTimeStamps= createModificationStampMap(readOnlyFiles);
128 		for (IFile file : oldTimeStamps.keySet()) {
129 			if (!oldTimeStamps.get(file).equals(newTimeStamps.get(file)))
130 				modified= addModified(modified, file);
131 		}
132 		if (modified != null)
133 			return modified;
134 		return Status.OK_STATUS;
135 	}
136 
createModificationStampMap(List<IResource> files)137 	private static Map<IFile, Long> createModificationStampMap(List<IResource> files){
138 		Map<IFile, Long> map= new HashMap<>();
139 		for (IResource iResource : files) {
140 			IFile file= (IFile)iResource;
141 			map.put(file, Long.valueOf(file.getModificationStamp()));
142 		}
143 		return map;
144 	}
145 
addModified(IStatus status, IFile file)146 	private static IStatus addModified(IStatus status, IFile file) {
147 		IStatus entry= JavaUIStatus.createError(
148 			IJavaStatusConstants.VALIDATE_EDIT_CHANGED_CONTENT,
149 			Messages.format(CorextMessages.Resources_fileModified, BasicElementLabels.getPathLabel(file.getFullPath(), false)),
150 			null);
151 		if (status == null) {
152 			return entry;
153 		} else if (status.isMultiStatus()) {
154 			((MultiStatus)status).add(entry);
155 			return status;
156 		} else {
157 			MultiStatus result= new MultiStatus(JavaManipulation.getPreferenceNodeId(),
158 				IJavaStatusConstants.VALIDATE_EDIT_CHANGED_CONTENT,
159 				CorextMessages.Resources_modifiedResources, null);
160 			result.add(status);
161 			result.add(entry);
162 			return result;
163 		}
164 	}
165 
addOutOfSync(IStatus status, IResource resource)166 	private static IStatus addOutOfSync(IStatus status, IResource resource) {
167 		IStatus entry= new Status(
168 			IStatus.ERROR,
169 			ResourcesPlugin.PI_RESOURCES,
170 			IResourceStatus.OUT_OF_SYNC_LOCAL,
171 			Messages.format(CorextMessages.Resources_outOfSync, BasicElementLabels.getPathLabel(resource.getFullPath(), false)),
172 			null);
173 		if (status == null) {
174 			return entry;
175 		} else if (status.isMultiStatus()) {
176 			((MultiStatus)status).add(entry);
177 			return status;
178 		} else {
179 			MultiStatus result= new MultiStatus(
180 				ResourcesPlugin.PI_RESOURCES,
181 				IResourceStatus.OUT_OF_SYNC_LOCAL,
182 				CorextMessages.Resources_outOfSyncResources, null);
183 			result.add(status);
184 			result.add(entry);
185 			return result;
186 		}
187 	}
188 
189 	/**
190 	 * This method is used to generate a list of local locations to
191 	 * be used in DnD for file transfers.
192 	 *
193 	 * @param resources the array of resources to get the local
194 	 *  locations for
195 	 * @return the local locations
196 	 */
getLocationOSStrings(IResource[] resources)197 	public static String[] getLocationOSStrings(IResource[] resources) {
198 		List<String> result= new ArrayList<>(resources.length);
199 		for (IResource resource : resources) {
200 			IPath location= resource.getLocation();
201 			if (location != null)
202 				result.add(location.toOSString());
203 		}
204 		return result.toArray(new String[result.size()]);
205 	}
206 
207 	/**
208 	 * Returns the location of the given resource. For local
209 	 * resources this is the OS path in the local file system. For
210 	 * remote resource this is the URI.
211 	 *
212 	 * @param resource the resource
213 	 * @return the location string or <code>null</code> if the
214 	 *  location URI of the resource is <code>null</code>
215 	 */
getLocationString(IResource resource)216 	public static String getLocationString(IResource resource) {
217 		URI uri= resource.getLocationURI();
218 		if (uri == null)
219 			return null;
220 		return EFS.SCHEME_FILE.equalsIgnoreCase(uri.getScheme())
221 			? new File(uri).getAbsolutePath()
222 			: uri.toString();
223 	}
224 
isReadOnly(IResource resource)225 	public static boolean isReadOnly(IResource resource) {
226 		ResourceAttributes resourceAttributes = resource.getResourceAttributes();
227 		if (resourceAttributes == null)  // not supported on this platform for this resource
228 			return false;
229 		return resourceAttributes.isReadOnly();
230 	}
231 
setReadOnly(IResource resource, boolean readOnly)232 	static void setReadOnly(IResource resource, boolean readOnly) {
233 		ResourceAttributes resourceAttributes = resource.getResourceAttributes();
234 		if (resourceAttributes == null) // not supported on this platform for this resource
235 			return;
236 
237 		resourceAttributes.setReadOnly(readOnly);
238 		try {
239 			resource.setResourceAttributes(resourceAttributes);
240 		} catch (CoreException e) {
241 			JavaManipulationPlugin.log(e);
242 		}
243 	}
244 }
245