| /******************************************************************************* |
| * Copyright (c) 2008, 2016 Freescale Semiconductor and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Serge Beauchamp (Freescale Semiconductor) - initial API and implementation |
| * James Blackburn (Broadcom Corp.) - ongoing development |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 |
| * Mickael Istria (Red Hat Inc.) - Bug 488937 |
| *******************************************************************************/ |
| package org.eclipse.core.internal.resources; |
| |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.internal.resources.projectvariables.*; |
| import org.eclipse.core.resources.IPathVariableManager; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.*; |
| |
| public class PathVariableUtil { |
| |
| static public String getUniqueVariableName(String variable, IResource resource) { |
| int index = 1; |
| variable = getValidVariableName(variable); |
| String destVariable = variable; |
| |
| IPathVariableManager pathVariableManager = resource.getPathVariableManager(); |
| if (destVariable.startsWith(ParentVariableResolver.NAME) || destVariable.startsWith(ProjectLocationVariableResolver.NAME)) |
| destVariable = "copy_" + destVariable; //$NON-NLS-1$ |
| |
| while (pathVariableManager.isDefined(destVariable)) { |
| destVariable = destVariable + index; |
| index++; |
| } |
| return destVariable; |
| } |
| |
| public static String getValidVariableName(String variable) { |
| // remove the argument part if the variable is of the form ${VAR-ARG} |
| int argumentIndex = variable.indexOf('-'); |
| if (argumentIndex != -1) |
| variable = variable.substring(0, argumentIndex); |
| |
| variable = variable.trim(); |
| char first = variable.charAt(0); |
| if (!Character.isLetter(first) && first != '_') { |
| variable = 'A' + variable; |
| } |
| |
| StringBuilder builder = new StringBuilder(); |
| for (int i = 0; i < variable.length(); i++) { |
| char c = variable.charAt(i); |
| if ((Character.isLetter(c) || Character.isDigit(c) || c == '_') && !Character.isWhitespace(c)) |
| builder.append(c); |
| } |
| variable = builder.toString(); |
| return variable; |
| } |
| |
| public static IPath convertToPathRelativeMacro(IPathVariableManager pathVariableManager, IPath originalPath, IResource resource, boolean force, String variableHint) throws CoreException { |
| return convertToRelative(pathVariableManager, originalPath, resource, force, variableHint, true, true); |
| } |
| |
| static public IPath convertToRelative(IPathVariableManager pathVariableManager, IPath originalPath, IResource resource, boolean force, String variableHint) throws CoreException { |
| return convertToRelative(pathVariableManager, originalPath, resource, force, variableHint, true, false); |
| } |
| |
| static public URI convertToRelative(IPathVariableManager pathVariableManager, URI originalPath, IResource resource, boolean force, String variableHint) throws CoreException { |
| return URIUtil.toURI(convertToRelative(pathVariableManager, URIUtil.toPath(originalPath), resource, force, variableHint, true, false)); |
| } |
| |
| static public URI convertToRelative(IPathVariableManager pathVariableManager, URI originalPath, IResource resource, boolean force, String variableHint, boolean skipWorkspace, boolean generateMacro) throws CoreException { |
| return URIUtil.toURI(convertToRelative(pathVariableManager, URIUtil.toPath(originalPath), resource, force, variableHint)); |
| } |
| |
| static private IPath convertToRelative(IPathVariableManager pathVariableManager, IPath originalPath, IResource resource, boolean force, String variableHint, boolean skipWorkspace, boolean generateMacro) throws CoreException { |
| if (variableHint != null && pathVariableManager.isDefined(variableHint)) { |
| IPath value = URIUtil.toPath(pathVariableManager.getURIValue(variableHint)); |
| if (value != null) |
| return wrapInProperFormat(makeRelativeToVariable(pathVariableManager, originalPath, resource, force, variableHint, generateMacro), generateMacro); |
| } |
| IPath path = convertToProperCase(originalPath); |
| IPath newPath = null; |
| int maxMatchLength = -1; |
| String[] existingVariables = pathVariableManager.getPathVariableNames(); |
| for (int i = 0; i < existingVariables.length; i++) { |
| String variable = existingVariables[i]; |
| if (skipWorkspace) { |
| // Variables relative to the workspace are not portable, and defeat the purpose of having linked resource locations, |
| // so they should not automatically be created relative to the workspace. |
| if (variable.equals(WorkspaceLocationVariableResolver.NAME)) |
| continue; |
| } |
| if (variable.equals(WorkspaceParentLocationVariableResolver.NAME)) |
| continue; |
| if (variable.equals(ParentVariableResolver.NAME)) |
| continue; |
| // find closest path to the original path |
| IPath value = URIUtil.toPath(pathVariableManager.getURIValue(variable)); |
| if (value != null) { |
| value = convertToProperCase(URIUtil.toPath(pathVariableManager.resolveURI(URIUtil.toURI(value)))); |
| if (value.isPrefixOf(path)) { |
| int matchLength = value.segmentCount(); |
| if (matchLength > maxMatchLength) { |
| maxMatchLength = matchLength; |
| newPath = makeRelativeToVariable(pathVariableManager, originalPath, resource, force, variable, generateMacro); |
| } |
| } |
| } |
| } |
| if (newPath != null) |
| return wrapInProperFormat(newPath, generateMacro); |
| |
| if (force) { |
| int originalSegmentCount = originalPath.segmentCount(); |
| for (int j = 0; j <= originalSegmentCount; j++) { |
| IPath matchingPath = path.removeLastSegments(j); |
| int minDifference = Integer.MAX_VALUE; |
| for (int k = 0; k < existingVariables.length; k++) { |
| String variable = existingVariables[k]; |
| if (skipWorkspace) { |
| if (variable.equals(WorkspaceLocationVariableResolver.NAME)) |
| continue; |
| } |
| if (variable.equals(WorkspaceParentLocationVariableResolver.NAME)) |
| continue; |
| if (variable.equals(ParentVariableResolver.NAME)) |
| continue; |
| IPath value = URIUtil.toPath(pathVariableManager.getURIValue(variable)); |
| if (value != null) { |
| value = convertToProperCase(URIUtil.toPath(pathVariableManager.resolveURI(URIUtil.toURI(value)))); |
| if (matchingPath.isPrefixOf(value)) { |
| int difference = value.segmentCount() - originalSegmentCount; |
| if (difference < minDifference) { |
| minDifference = difference; |
| newPath = makeRelativeToVariable(pathVariableManager, originalPath, resource, force, variable, generateMacro); |
| } |
| } |
| } |
| } |
| if (newPath != null) |
| return wrapInProperFormat(newPath, generateMacro); |
| } |
| if (originalSegmentCount == 0) { |
| String variable = ProjectLocationVariableResolver.NAME; |
| IPath value = URIUtil.toPath(pathVariableManager.getURIValue(variable)); |
| value = convertToProperCase(URIUtil.toPath(pathVariableManager.resolveURI(URIUtil.toURI(value)))); |
| if (originalPath.isPrefixOf(value)) |
| newPath = makeRelativeToVariable(pathVariableManager, originalPath, resource, force, variable, generateMacro); |
| if (newPath != null) |
| return wrapInProperFormat(newPath, generateMacro); |
| } |
| } |
| |
| if (skipWorkspace) |
| return convertToRelative(pathVariableManager, originalPath, resource, force, variableHint, false, generateMacro); |
| return originalPath; |
| } |
| |
| private static IPath wrapInProperFormat(IPath newPath, boolean generateMacro) { |
| if (generateMacro) |
| newPath = PathVariableUtil.buildVariableMacro(newPath); |
| return newPath; |
| } |
| |
| private static IPath makeRelativeToVariable(IPathVariableManager pathVariableManager, IPath originalPath, IResource resource, boolean force, String variableHint, boolean generateMacro) { |
| IPath path = convertToProperCase(originalPath); |
| IPath value = URIUtil.toPath(pathVariableManager.getURIValue(variableHint)); |
| value = convertToProperCase(URIUtil.toPath(pathVariableManager.resolveURI(URIUtil.toURI(value)))); |
| int valueSegmentCount = value.segmentCount(); |
| if (value.isPrefixOf(path)) { |
| // transform "c:/foo/bar" into "FOO/bar" |
| IPath tmp = Path.fromOSString(variableHint); |
| for (int j = valueSegmentCount; j < originalPath.segmentCount(); j++) { |
| tmp = tmp.append(originalPath.segment(j)); |
| } |
| return tmp; |
| } |
| |
| if (force) { |
| if (devicesAreCompatible(path, value)) { |
| // transform "c:/foo/bar/other_child/file.txt" into "${PARENT-1-BAR_CHILD}/other_child/file.txt" |
| int matchingFirstSegments = path.matchingFirstSegments(value); |
| if (matchingFirstSegments >= 0) { |
| String originalName = buildParentPathVariable(variableHint, valueSegmentCount - matchingFirstSegments, true); |
| IPath tmp = Path.fromOSString(originalName); |
| for (int j = matchingFirstSegments; j < originalPath.segmentCount(); j++) { |
| tmp = tmp.append(originalPath.segment(j)); |
| } |
| return tmp; |
| } |
| } |
| } |
| return originalPath; |
| } |
| |
| private static boolean devicesAreCompatible(IPath path, IPath value) { |
| return (path.getDevice() != null && value.getDevice() != null) ? (path.getDevice().equals(value.getDevice())) : (path.getDevice() == value.getDevice()); |
| } |
| |
| static private IPath convertToProperCase(IPath path) { |
| if (Platform.getOS().equals(Platform.OS_WIN32)) |
| return Path.fromPortableString(path.toPortableString().toLowerCase()); |
| return path; |
| } |
| |
| static public boolean isParentVariable(String variableString) { |
| return variableString.startsWith(ParentVariableResolver.NAME + '-'); |
| } |
| |
| // the format is PARENT-COUNT-ARGUMENT |
| static public int getParentVariableCount(String variableString) { |
| String items[] = variableString.split("-"); //$NON-NLS-1$ |
| if (items.length == 3) { |
| try { |
| Integer count = Integer.valueOf(items[1]); |
| return count.intValue(); |
| } catch (NumberFormatException e) { |
| // nothing |
| } |
| } |
| return -1; |
| } |
| |
| // the format is PARENT-COUNT-ARGUMENT |
| static public String getParentVariableArgument(String variableString) { |
| String items[] = variableString.split("-"); //$NON-NLS-1$ |
| if (items.length == 3) |
| return items[2]; |
| return null; |
| } |
| |
| static public String buildParentPathVariable(String variable, int difference, boolean generateMacro) { |
| String newString = ParentVariableResolver.NAME + "-" + difference + "-" + variable; //$NON-NLS-1$//$NON-NLS-2$ |
| |
| if (!generateMacro) |
| newString = "${" + newString + "}"; //$NON-NLS-1$//$NON-NLS-2$ |
| return newString; |
| } |
| |
| public static IPath buildVariableMacro(IPath relativeSrcValue) { |
| String variable = relativeSrcValue.segment(0); |
| variable = "${" + variable + "}"; //$NON-NLS-1$//$NON-NLS-2$ |
| return Path.fromOSString(variable).append(relativeSrcValue.removeFirstSegments(1)); |
| } |
| |
| public static String convertFromUserEditableFormatInternal(IPathVariableManager manager, String userFormat, boolean locationFormat) { |
| char pathPrefix = 0; |
| if ((userFormat.length() > 0) && (userFormat.charAt(0) == '/' || userFormat.charAt(0) == '\\')) |
| pathPrefix = userFormat.charAt(0); |
| String components[] = splitPathComponents(userFormat); |
| for (int i = 0; i < components.length; i++) { |
| if (components[i] == null) |
| continue; |
| if (isDotDot(components[i])) { |
| int parentCount = 1; |
| components[i] = null; |
| for (int j = i + 1; j < components.length; j++) { |
| if (components[j] != null) { |
| if (isDotDot(components[j])) { |
| parentCount++; |
| components[j] = null; |
| } else |
| break; |
| } |
| } |
| if (i == 0) // this means the value is implicitly relative to the project location |
| components[0] = PathVariableUtil.buildParentPathVariable(ProjectLocationVariableResolver.NAME, parentCount, false); |
| else { |
| for (int j = i - 1; j >= 0; j--) { |
| if (parentCount == 0) |
| break; |
| if (components[j] == null) |
| continue; |
| String variable = extractVariable(components[j]); |
| |
| boolean hasVariableWithMacroSyntax = true; |
| if (variable.length() == 0 && (locationFormat && j == 0)) { |
| variable = components[j]; |
| hasVariableWithMacroSyntax = false; |
| } |
| |
| try { |
| if (variable.length() > 0) { |
| String prefix = ""; //$NON-NLS-1$ |
| if (hasVariableWithMacroSyntax) { |
| int indexOfVariable = components[j].indexOf(variable) - "${".length(); //$NON-NLS-1$ |
| prefix = components[j].substring(0, indexOfVariable); |
| String suffix = components[j].substring(indexOfVariable + "${".length() + variable.length() + "}".length()); //$NON-NLS-1$ //$NON-NLS-2$ |
| if (suffix.length() != 0) { |
| // Create an intermediate variable, since a syntax of "${VAR}foo/../" |
| // can't be converted to a "${PARENT-1-VAR}foo" variable. |
| // So instead, an intermediate variable "VARFOO" will be created of value |
| // "${VAR}foo", and the string "${PARENT-1-VARFOO}" will be inserted. |
| String intermediateVariable = PathVariableUtil.getValidVariableName(variable + suffix); |
| IPath intermediateValue = Path.fromPortableString(components[j]); |
| int intermediateVariableIndex = 1; |
| String originalIntermediateVariableName = intermediateVariable; |
| while (manager.isDefined(intermediateVariable)) { |
| IPath tmpValue = URIUtil.toPath(manager.getURIValue(intermediateVariable)); |
| if (tmpValue.equals(intermediateValue)) |
| break; |
| intermediateVariable = originalIntermediateVariableName + intermediateVariableIndex; |
| } |
| if (!manager.isDefined(intermediateVariable)) |
| manager.setURIValue(intermediateVariable, URIUtil.toURI(intermediateValue)); |
| variable = intermediateVariable; |
| prefix = ""; //$NON-NLS-1$ |
| } |
| } |
| String newVariable = variable; |
| if (PathVariableUtil.isParentVariable(variable)) { |
| String argument = PathVariableUtil.getParentVariableArgument(variable); |
| int count = PathVariableUtil.getParentVariableCount(variable); |
| if (argument != null && count != -1) |
| newVariable = PathVariableUtil.buildParentPathVariable(argument, count + parentCount, locationFormat); |
| else |
| newVariable = PathVariableUtil.buildParentPathVariable(variable, parentCount, locationFormat); |
| } else |
| newVariable = PathVariableUtil.buildParentPathVariable(variable, parentCount, locationFormat); |
| components[j] = prefix + newVariable; |
| break; |
| } |
| components[j] = null; |
| parentCount--; |
| } catch (CoreException e) { |
| components[j] = null; |
| parentCount--; |
| } |
| } |
| } |
| } |
| } |
| StringBuilder buffer = new StringBuilder(); |
| if (pathPrefix != 0) |
| buffer.append(pathPrefix); |
| for (int i = 0; i < components.length; i++) { |
| if (components[i] != null) { |
| if (i > 0) |
| buffer.append(java.io.File.separator); |
| buffer.append(components[i]); |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| private static boolean isDotDot(String component) { |
| return component.equals(".."); //$NON-NLS-1$ |
| } |
| |
| private static String[] splitPathComponents(String userFormat) { |
| ArrayList<String> list = new ArrayList<>(); |
| StringBuilder buffer = new StringBuilder(); |
| for (int i = 0; i < userFormat.length(); i++) { |
| char c = userFormat.charAt(i); |
| if (c == '/' || c == '\\') { |
| if (buffer.length() > 0) |
| list.add(buffer.toString()); |
| buffer = new StringBuilder(); |
| } else |
| buffer.append(c); |
| } |
| if (buffer.length() > 0) |
| list.add(buffer.toString()); |
| return list.toArray(new String[0]); |
| } |
| |
| public static String convertToUserEditableFormatInternal(String value, boolean locationFormat) { |
| StringBuilder buffer = new StringBuilder(); |
| if (locationFormat) { |
| IPath path = Path.fromOSString(value); |
| if (path.isAbsolute()) |
| return path.toOSString(); |
| int index = value.indexOf(java.io.File.separator); |
| String variable = index != -1 ? value.substring(0, index) : value; |
| convertVariableToUserFormat(buffer, variable, variable, false); |
| if (index != -1) |
| buffer.append(value.substring(index)); |
| } else { |
| String components[] = splitVariablesAndContent(value); |
| for (int i = 0; i < components.length; i++) { |
| String variable = extractVariable(components[i]); |
| convertVariableToUserFormat(buffer, components[i], variable, true); |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| private static void convertVariableToUserFormat(StringBuilder buffer, String component, String variable, boolean generateMacro) { |
| if (PathVariableUtil.isParentVariable(variable)) { |
| String argument = PathVariableUtil.getParentVariableArgument(variable); |
| int count = PathVariableUtil.getParentVariableCount(variable); |
| if (argument != null && count != -1) { |
| buffer.append(generateMacro ? PathVariableUtil.buildVariableMacro(Path.fromOSString(argument)) : Path.fromOSString(argument)); |
| for (int j = 0; j < count; j++) { |
| buffer.append(java.io.File.separator + ".."); //$NON-NLS-1$ |
| } |
| } else |
| buffer.append(component); |
| } else |
| buffer.append(component); |
| } |
| |
| /* |
| * Splits a value (returned by this.getValue(variable) in an array of |
| * string, where the array is divided between the value content and the |
| * value variables. |
| * |
| * For example, if the value is "${ECLIPSE_HOME}/plugins", the value |
| * returned will be {"${ECLIPSE_HOME}" "/plugins"} |
| */ |
| static String[] splitVariablesAndContent(String value) { |
| LinkedList<String> result = new LinkedList<>(); |
| while (true) { |
| // we check if the value contains referenced variables with ${VAR} |
| int index = value.indexOf("${"); //$NON-NLS-1$ |
| if (index != -1) { |
| int endIndex = getMatchingBrace(value, index); |
| if (index > 0) |
| result.add(value.substring(0, index)); |
| result.add(value.substring(index, endIndex + 1)); |
| value = value.substring(endIndex + 1); |
| } else |
| break; |
| } |
| if (value.length() > 0) |
| result.add(value); |
| return result.toArray(new String[0]); |
| } |
| |
| /* |
| * Splits a value (returned by this.getValue(variable) in an array of |
| * string of the variables contained in the value. |
| * |
| * For example, if the value is "${ECLIPSE_HOME}/plugins", the value |
| * returned will be {"ECLIPSE_HOME"}. If the value is |
| * "${ECLIPSE_HOME}/${FOO}/plugins", the value returned will be |
| * {"ECLIPSE_HOME", "FOO"}. |
| */ |
| static String[] splitVariableNames(String value) { |
| LinkedList<String> result = new LinkedList<>(); |
| while (true) { |
| int index = value.indexOf("${"); //$NON-NLS-1$ |
| if (index != -1) { |
| int endIndex = getMatchingBrace(value, index); |
| result.add(value.substring(index + 2, endIndex)); |
| value = value.substring(endIndex + 1); |
| } else |
| break; |
| } |
| return result.toArray(new String[0]); |
| } |
| |
| /* |
| * Extracts the variable name from a variable segment. |
| * |
| * For example, if the value is "${ECLIPSE_HOME}", the value returned will |
| * be "ECLIPSE_HOME". If the segment doesn't contain any variable, the value |
| * returned will be "". |
| */ |
| static String extractVariable(String segment) { |
| int index = segment.indexOf("${"); //$NON-NLS-1$ |
| if (index != -1) { |
| int endIndex = getMatchingBrace(segment, index); |
| return segment.substring(index + 2, endIndex); |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| // getMatchingBrace("${FOO}/something") returns 5 |
| // getMatchingBrace("${${OTHER}}/something") returns 10 |
| // getMatchingBrace("${FOO") returns 5 |
| static int getMatchingBrace(String value, int index) { |
| int scope = 0; |
| for (int i = index + 1; i < value.length(); i++) { |
| char c = value.charAt(i); |
| if (c == '}') { |
| if (scope == 0) |
| return i; |
| scope--; |
| } |
| if (c == '$') { |
| if ((i + 1 < value.length()) && (value.charAt(i + 1) == '{')) |
| scope++; |
| } |
| } |
| return value.length(); |
| } |
| |
| /** |
| * Returns whether this variable is suited for programmatically determining |
| * which variable is the most appropriate when creating new linked resources. |
| * |
| * @return true if the path variable is preferred. |
| */ |
| static public boolean isPreferred(String variableName) { |
| return !(variableName.equals(WorkspaceLocationVariableResolver.NAME) || variableName.equals(WorkspaceParentLocationVariableResolver.NAME) || variableName.equals(ParentVariableResolver.NAME)); |
| } |
| } |