| /******************************************************************************* |
| * Copyright (c) 2008, 2015 Freescale Semiconductor and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Serge Beauchamp (Freescale Semiconductor) - initial API and implementation |
| * IBM Corporation - ongoing development |
| * James Blackburn (Broadcom Corp.) - ongoing development |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 |
| *******************************************************************************/ |
| package org.eclipse.core.internal.resources; |
| |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.*; |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.internal.resources.ProjectVariableProviderManager.Descriptor; |
| import org.eclipse.core.internal.utils.Messages; |
| import org.eclipse.core.resources.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * The {@link IPathVariableManager} for a single project |
| * @see IProject#getPathVariableManager() |
| */ |
| public class ProjectPathVariableManager implements IPathVariableManager, IManager { |
| |
| private Resource resource; |
| private ProjectVariableProviderManager.Descriptor variableProviders[] = null; |
| |
| /** |
| * Constructor for the class. |
| */ |
| public ProjectPathVariableManager(Resource resource) { |
| this.resource = resource; |
| variableProviders = ProjectVariableProviderManager.getDefault().getDescriptors(); |
| } |
| |
| PathVariableManager getWorkspaceManager() { |
| return (PathVariableManager) resource.getWorkspace().getPathVariableManager(); |
| } |
| |
| /** |
| * Throws a runtime exception if the given name is not valid as a path |
| * variable name. |
| */ |
| private void checkIsValidName(String name) throws CoreException { |
| IStatus status = validateName(name); |
| if (!status.isOK()) |
| throw new CoreException(status); |
| } |
| |
| /** |
| * Throws an exception if the given path is not valid as a path variable |
| * value. |
| */ |
| private void checkIsValidValue(URI newValue) throws CoreException { |
| IStatus status = validateValue(newValue); |
| if (!status.isOK()) |
| throw new CoreException(status); |
| } |
| |
| /** |
| * @see org.eclipse.core.resources.IPathVariableManager#getPathVariableNames() |
| */ |
| @Override |
| public String[] getPathVariableNames() { |
| List<String> result = new LinkedList<>(); |
| HashMap<String, VariableDescription> map; |
| try { |
| map = ((ProjectDescription) resource.getProject().getDescription()).getVariables(); |
| } catch (CoreException e) { |
| return new String[0]; |
| } |
| for (Descriptor variableProvider : variableProviders) { |
| String[] variableHints = variableProvider.getVariableNames(variableProvider.getName(), resource); |
| if (variableHints != null && variableHints.length > 0) |
| for (int k = 0; k < variableHints.length; k++) |
| result.add(variableProvider.getVariableNames(variableProvider.getName(), resource)[k]); |
| } |
| if (map != null) |
| result.addAll(map.keySet()); |
| result.addAll(Arrays.asList(getWorkspaceManager().getPathVariableNames())); |
| return result.toArray(new String[0]); |
| } |
| |
| /** |
| * @deprecated use {@link #getURIValue(String)} instead. |
| */ |
| @Deprecated |
| @Override |
| public IPath getValue(String varName) { |
| URI uri = getURIValue(varName); |
| if (uri != null) |
| return URIUtil.toPath(uri); |
| return null; |
| } |
| |
| /** |
| * If the variable is not listed in the project description, we fall back on |
| * the workspace variables. |
| * |
| * @see org.eclipse.core.resources.IPathVariableManager#getURIValue(String) |
| */ |
| @Override |
| public URI getURIValue(String varName) { |
| String value = internalGetValue(varName); |
| if (value != null) { |
| if (value.indexOf("..") != -1) { //$NON-NLS-1$ |
| // if the path is 'reducible', lets resolve it first. |
| int index = value.indexOf(IPath.SEPARATOR); |
| if (index > 0) { // if its the first character, its an |
| // absolute path on unix, so we don't |
| // resolve it |
| URI resolved = resolveVariable(value); |
| if (resolved != null) |
| return resolved; |
| } |
| } |
| try { |
| return URI.create(value); |
| } catch (IllegalArgumentException e) { |
| IPath path = Path.fromPortableString(value); |
| return URIUtil.toURI(path); |
| } |
| } |
| return getWorkspaceManager().getURIValue(varName); |
| } |
| |
| public String internalGetValue(String varName) { |
| HashMap<String, VariableDescription> map; |
| try { |
| map = ((ProjectDescription) resource.getProject().getDescription()).getVariables(); |
| } catch (CoreException e) { |
| return null; |
| } |
| if (map != null && map.containsKey(varName)) |
| return map.get(varName).getValue(); |
| |
| String name; |
| int index = varName.indexOf('-'); |
| if (index != -1) |
| name = varName.substring(0, index); |
| else |
| name = varName; |
| for (Descriptor variableProvider : variableProviders) { |
| if (variableProvider.getName().equals(name)) |
| return variableProvider.getValue(varName, resource); |
| } |
| for (Descriptor variableProvider : variableProviders) { |
| if (name.startsWith(variableProvider.getName())) |
| return variableProvider.getValue(varName, resource); |
| } |
| return null; |
| } |
| |
| /** |
| * @see org.eclipse.core.resources.IPathVariableManager#isDefined(String) |
| */ |
| @Override |
| public boolean isDefined(String varName) { |
| for (Descriptor variableProvider : variableProviders) { |
| // if (variableProviders[i].getName().equals(varName)) |
| // return true; |
| |
| if (varName.startsWith(variableProvider.getName())) |
| return true; |
| } |
| |
| try { |
| HashMap<String, VariableDescription> map = ((ProjectDescription) resource.getProject().getDescription()).getVariables(); |
| if (map != null) { |
| Iterator<String> it = map.keySet().iterator(); |
| while (it.hasNext()) { |
| String name = it.next(); |
| if (name.equals(varName)) |
| return true; |
| } |
| } |
| } catch (CoreException e) { |
| return false; |
| } |
| boolean value = getWorkspaceManager().isDefined(varName); |
| if (!value) { |
| // this is to handle variables with encoded arguments |
| int index = varName.indexOf('-'); |
| if (index != -1) { |
| String newVarName = varName.substring(0, index); |
| value = isDefined(newVarName); |
| } |
| } |
| return value; |
| } |
| |
| /** |
| * @deprecated use {@link #resolveURI(URI)} instead. |
| */ |
| @Override |
| @Deprecated |
| public IPath resolvePath(IPath path) { |
| if (path == null || path.segmentCount() == 0 || path.isAbsolute() || path.getDevice() != null) |
| return path; |
| URI value = resolveURI(URIUtil.toURI(path)); |
| return value == null ? path : URIUtil.toPath(value); |
| } |
| |
| public URI resolveVariable(String variable) { |
| LinkedList<String> variableStack = new LinkedList<>(); |
| |
| String value = resolveVariable(variable, variableStack); |
| if (value != null) { |
| try { |
| return URI.create(value); |
| } catch (IllegalArgumentException e) { |
| return URIUtil.toURI(Path.fromPortableString(value)); |
| } |
| } |
| return null; |
| } |
| |
| public String resolveVariable(String value, LinkedList<String> variableStack) { |
| if (variableStack == null) |
| variableStack = new LinkedList<>(); |
| |
| String tmp = internalGetValue(value); |
| if (tmp == null) { |
| URI result = getWorkspaceManager().getURIValue(value); |
| if (result != null) |
| return result.toASCIIString(); |
| } else |
| value = tmp; |
| |
| while (true) { |
| String stringValue; |
| try { |
| URI uri = URI.create(value); |
| if (uri != null) { |
| IPath path = URIUtil.toPath(uri); |
| if (path != null) |
| stringValue = path.toPortableString(); |
| else |
| stringValue = value; |
| } else |
| stringValue = value; |
| } catch (IllegalArgumentException e) { |
| stringValue = value; |
| } |
| // we check if the value contains referenced variables with ${VAR} |
| int index = stringValue.indexOf("${"); //$NON-NLS-1$ |
| if (index != -1) { |
| int endIndex = PathVariableUtil.getMatchingBrace(stringValue, index); |
| String macro = stringValue.substring(index + 2, endIndex); |
| String resolvedMacro = ""; //$NON-NLS-1$ |
| if (!variableStack.contains(macro)) { |
| variableStack.add(macro); |
| resolvedMacro = resolveVariable(macro, variableStack); |
| if (resolvedMacro == null) |
| resolvedMacro = ""; //$NON-NLS-1$ |
| } |
| if (stringValue.length() > endIndex) |
| stringValue = stringValue.substring(0, index) + resolvedMacro + stringValue.substring(endIndex + 1); |
| else |
| stringValue = resolvedMacro; |
| value = stringValue; |
| } else |
| break; |
| } |
| return value; |
| } |
| |
| @Override |
| public URI resolveURI(URI uri) { |
| if (uri == null || uri.isAbsolute()) { |
| return uri; |
| } |
| String schemeSpecificPart = uri.getSchemeSpecificPart(); |
| if (schemeSpecificPart == null || schemeSpecificPart.isEmpty()) { |
| return uri; |
| } |
| IPath raw = new Path(schemeSpecificPart); |
| if (raw == null || raw.segmentCount() == 0 || raw.isAbsolute() || raw.getDevice() != null) |
| return URIUtil.toURI(raw); |
| URI value = resolveVariable(raw.segment(0)); |
| if (value == null) |
| return uri; |
| |
| String path = value.getPath(); |
| if (path != null) { |
| IPath p = Path.fromPortableString(path); |
| p = p.append(raw.removeFirstSegments(1)); |
| try { |
| value = new URI(value.getScheme(), value.getHost(), p.toPortableString(), value.getFragment()); |
| } catch (URISyntaxException e) { |
| return uri; |
| } |
| return value; |
| } |
| return uri; |
| } |
| |
| /** |
| * @deprecated use {@link #setURIValue(String, URI)} instead. |
| */ |
| @Deprecated |
| @Override |
| public void setValue(String varName, IPath newValue) throws CoreException { |
| if (newValue == null) |
| setURIValue(varName, (URI) null); |
| else |
| setURIValue(varName, URIUtil.toURI(newValue)); |
| } |
| |
| /** |
| * @see org.eclipse.core.resources.IPathVariableManager#setValue(String, |
| * IPath) |
| */ |
| @Override |
| public void setURIValue(String varName, URI newValue) throws CoreException { |
| checkIsValidName(varName); |
| checkIsValidValue(newValue); |
| // read previous value and set new value atomically in order to generate |
| // the right event |
| boolean changeWorkspaceValue = false; |
| Project project = (Project) resource.getProject(); |
| int eventType = 0; |
| synchronized (this) { |
| String value = internalGetValue(varName); |
| URI currentValue = null; |
| if (value == null) |
| currentValue = getWorkspaceManager().getURIValue(varName); |
| else { |
| try { |
| currentValue = URI.create(value); |
| } catch (IllegalArgumentException e) { |
| currentValue = null; |
| } |
| } |
| boolean variableExists = currentValue != null; |
| if (!variableExists && newValue == null) |
| return; |
| if (variableExists && currentValue.equals(newValue)) |
| return; |
| |
| for (Descriptor variableProvider : variableProviders) { |
| // if (variableProviders[i].getName().equals(varName)) |
| // return; |
| |
| if (varName.startsWith(variableProvider.getName())) |
| return; |
| } |
| |
| if (value == null && variableExists) |
| changeWorkspaceValue = true; |
| else { |
| IProgressMonitor monitor = new NullProgressMonitor(); |
| final ISchedulingRule rule = resource.getProject(); // project.workspace.getRuleFactory().modifyRule(project); |
| try { |
| project.workspace.prepareOperation(rule, monitor); |
| project.workspace.beginOperation(true); |
| // save the location in the project description |
| ProjectDescription description = project.internalGetDescription(); |
| if (newValue == null) { |
| description.setVariableDescription(varName, null); |
| eventType = IPathVariableChangeEvent.VARIABLE_DELETED; |
| } else { |
| description.setVariableDescription(varName, new VariableDescription(varName, newValue.toASCIIString())); |
| eventType = variableExists ? IPathVariableChangeEvent.VARIABLE_CHANGED : IPathVariableChangeEvent.VARIABLE_CREATED; |
| } |
| project.writeDescription(IResource.NONE); |
| } finally { |
| project.workspace.endOperation(rule, true); |
| } |
| } |
| } |
| if (changeWorkspaceValue) |
| getWorkspaceManager().setURIValue(varName, newValue); |
| else { |
| // notify listeners from outside the synchronized block to avoid deadlocks |
| getWorkspaceManager().fireVariableChangeEvent(project, varName, newValue != null ? URIUtil.toPath(newValue) : null, eventType); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.core.internal.resources.IManager#shutdown(IProgressMonitor) |
| */ |
| @Override |
| public void shutdown(IProgressMonitor monitor) { |
| // nothing to do here |
| } |
| |
| /** |
| * @see org.eclipse.core.internal.resources.IManager#startup(IProgressMonitor) |
| */ |
| @Override |
| public void startup(IProgressMonitor monitor) { |
| // nothing to do here |
| } |
| |
| /** |
| * @see org.eclipse.core.resources.IPathVariableManager#validateName(String) |
| */ |
| @Override |
| public IStatus validateName(String name) { |
| String message = null; |
| if (name.length() == 0) { |
| message = Messages.pathvar_length; |
| return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); |
| } |
| |
| char first = name.charAt(0); |
| if (!Character.isLetter(first) && first != '_') { |
| message = NLS.bind(Messages.pathvar_beginLetter, String.valueOf(first)); |
| return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); |
| } |
| |
| for (int i = 1; i < name.length(); i++) { |
| char following = name.charAt(i); |
| if (Character.isWhitespace(following)) |
| return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, Messages.pathvar_whitespace); |
| if (!Character.isLetter(following) && !Character.isDigit(following) && following != '_') { |
| message = NLS.bind(Messages.pathvar_invalidChar, String.valueOf(following)); |
| return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); |
| } |
| } |
| // check |
| |
| return Status.OK_STATUS; |
| } |
| |
| /** |
| * @see IPathVariableManager#validateValue(IPath) |
| */ |
| @Override |
| public IStatus validateValue(IPath value) { |
| // accept any format |
| return Status.OK_STATUS; |
| } |
| |
| /** |
| * @see IPathVariableManager#validateValue(URI) |
| */ |
| @Override |
| public IStatus validateValue(URI value) { |
| // accept any format |
| return Status.OK_STATUS; |
| } |
| |
| /** |
| * @throws CoreException |
| * @see IPathVariableManager#convertToRelative(URI, boolean, String) |
| */ |
| @Override |
| public URI convertToRelative(URI path, boolean force, String variableHint) throws CoreException { |
| return PathVariableUtil.convertToRelative(this, path, resource, force, variableHint); |
| } |
| |
| /** |
| * @see IPathVariableManager#convertToUserEditableFormat(String, boolean) |
| */ |
| @Override |
| public String convertToUserEditableFormat(String value, boolean locationFormat) { |
| return PathVariableUtil.convertToUserEditableFormatInternal(value, locationFormat); |
| } |
| |
| @Override |
| public String convertFromUserEditableFormat(String userFormat, boolean locationFormat) { |
| return PathVariableUtil.convertFromUserEditableFormatInternal(this, userFormat, locationFormat); |
| } |
| |
| @Override |
| public void addChangeListener(IPathVariableChangeListener listener) { |
| getWorkspaceManager().addChangeListener(listener, resource.getProject()); |
| } |
| |
| @Override |
| public void removeChangeListener(IPathVariableChangeListener listener) { |
| getWorkspaceManager().removeChangeListener(listener, resource.getProject()); |
| } |
| |
| @Override |
| public URI getVariableRelativePathLocation(URI location) { |
| try { |
| URI result = convertToRelative(location, false, null); |
| if (!result.equals(location)) |
| return result; |
| } catch (CoreException e) { |
| // handled by returning null |
| } |
| return null; |
| } |
| |
| /* |
| * Return the resource of this manager. |
| */ |
| public IResource getResource() { |
| return resource; |
| } |
| |
| @Override |
| public boolean isUserDefined(String name) { |
| return ProjectVariableProviderManager.getDefault().findDescriptor(name) == null; |
| } |
| } |