All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.core.runtime.Path Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2000, 2023 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *     Patrick Tasse - Add extra constructor to Path class (bug 454959)
 *******************************************************************************/
package org.eclipse.core.runtime;

import java.io.File;
import java.util.Arrays;

/** 
 * The standard implementation of the IPath interface.
 * Paths are always maintained in canonicalized form.  That is, parent
 * references (i.e., ../../) and duplicate separators are 
 * resolved.  For example,
 * 
     new Path("/a/b").append("../foo/bar")
* will yield the path *
     /a/foo/bar
*

* This class can be used without OSGi running. *

* This class is not intended to be subclassed by clients but * may be instantiated. *

* @see IPath * @noextend This class is not intended to be subclassed by clients. */ public final class Path implements IPath, Cloneable { /* masks for flag values: */ /** * if HAS_LEADING is set then Path starts with leading slash i.e. it is * absolute, but the slash is not included in segments. */ private static final int HAS_LEADING = 1; private static final int IS_UNC = 2; private static final int HAS_TRAILING = 4; private static final int IS_FOR_WINDOWS = 8; private static final int ALL_SEPARATORS = HAS_LEADING | IS_UNC | HAS_TRAILING; /** * Carrier that ensures that the contained constants are always initialized, * regardless of if the Path class or IPath interface is initialized fist. */ static class Constants { /** Constant value indicating if the current platform is Windows */ static final boolean RUNNING_ON_WINDOWS = java.io.File.separatorChar == '\\'; /** Constant value indicating no segments */ static final String[] NO_SEGMENTS = new String[0]; /** * We have cycle : Path implements IPath and IPath uses Path object instances in * interface constants. * * Constants and methods below are needed to resolve init order issues between * IPath and Path classes - depending on which is loaded first, constants * defined in one of the classes and pointing to other one will see not * initialized state. See https://github.com/eclipse-equinox/equinox/pull/279 */ private static Path empty = new Path(""); //$NON-NLS-1$ private static Path root = new Path("/"); //$NON-NLS-1$ static synchronized Path empty() { if (empty == null) { empty = new Path(""); //$NON-NLS-1$ } return empty; } static synchronized Path root() { if (root == null) { root = new Path("/"); //$NON-NLS-1$ } return root; } } /** * Constant value containing the empty path with no device on the local file * system. *

* Instead of referencing this constants it is recommended to use * {@link IPath#EMPTY} instead. *

* * @see IPath#EMPTY */ public static final Path EMPTY = Constants.empty(); /** * Constant value containing the root path with no device on the local file * system. *

* Instead of referencing this constants it is recommended to use * {@link IPath#ROOT} instead. *

* * @see IPath#ROOT */ public static final Path ROOT = Constants.root(); /** The device id string. May be null if there is no device. */ private final String device; //Private implementation note: the segments array and flag bitmap //are never modified, so that they can be shared between path instances /** The path segments */ private final String[] segments; /** cached hash code */ private int hash; /** flags indicating separators (has leading, is UNC, has trailing, is for Windows) */ private final byte flags; /** * Constructs a new path from the given string path. The string path must * represent a valid file system path on the local file system. The path is * canonicalized and double slashes are removed except at the beginning. (to * handle UNC paths). All forward slashes ('/') are treated as segment * delimiters, and any segment and device delimiters for the local file system * are also respected. *

* Instead of calling this method it is recommended to call * {@link IPath#fromOSString(String)} instead. *

* * @param pathString the operating-system specific string path * @return the IPath representing the given OS specific string path * @see IPath#toPortableString() * @see IPath#fromOSString(String) * @since 3.1 */ public static IPath fromOSString(String pathString) { return IPath.fromOSString(pathString); } /** * Constructs a new path from the given path string. The path string must have * been produced by a previous call to IPath.toPortableString. *

* Instead of calling this method it is recommended to call * {@link IPath#fromPortableString(String)} instead. *

* * @param pathString the portable path string * @return the IPath representing the given portable string path * @see IPath#toPortableString() * @see IPath#fromPortableString(String) * @since 3.1 */ public static IPath fromPortableString(String pathString) { return IPath.fromPortableString(pathString); } static IPath parsePortableString(String pathString) { int firstMatch = pathString.indexOf(DEVICE_SEPARATOR) + 1; //no extra work required if no device characters if (firstMatch <= 0) return new Path(null, pathString, Constants.RUNNING_ON_WINDOWS); //if we find a single colon, then the path has a device String devicePart = null; int pathLength = pathString.length(); if (firstMatch == pathLength || pathString.charAt(firstMatch) != DEVICE_SEPARATOR) { devicePart = pathString.substring(0, firstMatch); pathString = pathString.substring(firstMatch, pathLength); } //optimize for no colon literals if (pathString.indexOf(DEVICE_SEPARATOR) == -1) return new Path(devicePart, pathString, Constants.RUNNING_ON_WINDOWS); //contract colon literals char[] chars = pathString.toCharArray(); int readOffset = 0, writeOffset = 0, length = chars.length; while (readOffset < length) { if (chars[readOffset] == DEVICE_SEPARATOR && ++readOffset >= length) { break; } chars[writeOffset++] = chars[readOffset++]; } return new Path(devicePart, new String(chars, 0, writeOffset), Constants.RUNNING_ON_WINDOWS); } /** * Constructs a new POSIX path from the given string path. The string path must * represent a valid file system path on a POSIX file system. The path is * canonicalized and double slashes are removed except at the beginning (to * handle UNC paths). All forward slashes ('/') are treated as segment * delimiters. This factory method should be used if the string path is for a * POSIX file system. *

* Instead of calling this method it is recommended to call * {@link IPath#forPosix(String)} instead. *

* * @param fullPath the string path * @return the IPath representing the given POSIX string path * @see #isValidPosixPath(String) * @see IPath#forPosix(String) * @since 3.7 */ public static Path forPosix(String fullPath) { return (Path) IPath.forPosix(fullPath); } /** * Constructs a new Windows path from the given string path. The string path * must represent a valid file system path on the Windows file system. The path * is canonicalized and double slashes are removed except at the beginning (to * handle UNC paths). All forward slashes ('/') are treated as segment * delimiters, and any segment ('\') and device (':') delimiters for the Windows * file system are also respected. This factory method should be used if the * string path is for the Windows file system. *

* Instead of calling this method it is recommended to call * {@link IPath#forWindows(String)} instead. *

* * @param fullPath the string path * @return the IPath representing the given Windows string path * @see #isValidWindowsPath(String) * @see IPath#forWindows(String) * @since 3.7 */ public static Path forWindows(String fullPath) { return (Path) IPath.forWindows(fullPath); } /** * Constructs a new path from the given string path. * The string path must represent a valid file system path * on the local file system. * The path is canonicalized and double slashes are removed * except at the beginning. (to handle UNC paths). All forward * slashes ('/') are treated as segment delimiters, and any * segment and device delimiters for the local file system are * also respected (such as colon (':') and backslash ('\') on some file systems). * This constructor should be used if the string path if for the local file system. * * @param fullPath the string path * @see #isValidPath(String) */ public Path(String fullPath) { this(fullPath, Constants.RUNNING_ON_WINDOWS); } /** * Constructs a new path from the given device id and string path. * The given string path must be valid. * The path is canonicalized and double slashes are removed except * at the beginning (to handle UNC paths). All forward * slashes ('/') are treated as segment delimiters, and any * segment delimiters for the local file system are * also respected (such as backslash ('\') on some file systems). * * @param device the device id * @param path the string path * @see #isValidPath(String) * @see #setDevice(String) */ public Path(String device, String path) { this(device, backslashToForward(path, Constants.RUNNING_ON_WINDOWS), Constants.RUNNING_ON_WINDOWS); } private static String backslashToForward(String path, boolean forWindows) { if (forWindows) { //convert backslash to forward slash return path.replace('\\', SEPARATOR); } return path; } /** * Constructs a new path from the given string path. The string path must * represent a valid file system path on the specified file system. The path * is canonicalized and double slashes are removed except at the beginning * (to handle UNC paths). All forward slashes ('/') are treated as segment * delimiters, and any segment and device delimiters for the specified file * system are also respected (such as colon (':') and backslash ('\') on * Windows). * * @param fullPath the string path * @param forWindows true if the string path is for the Windows file system * @since 3.7 */ Path(String fullPath, boolean forWindows) { String devicePart = null; if (forWindows) { //convert backslash to forward slash fullPath = fullPath.replace('\\', SEPARATOR); //extract device int i = fullPath.indexOf(DEVICE_SEPARATOR); if (i != -1) { //remove leading slash from device part to handle output of URL.getFile() int start = fullPath.charAt(0) == SEPARATOR ? 1 : 0; devicePart = fullPath.substring(start, i + 1); fullPath = fullPath.substring(i + 1, fullPath.length()); } } // inlined Path(String devicePart, String fullPath, booleanforWindows) // because calling other constructor has to be first statement: Assert.isNotNull(fullPath); String collapsedPath = collapseSlashes(devicePart, fullPath); int flag = computeFlags(collapsedPath, forWindows); // compute segments and ensure canonical form String[] canonicalSegments = canonicalize((flag & HAS_LEADING) != 0, computeSegments(collapsedPath)); if (canonicalSegments.length == 0) { // paths of length 0 have no trailing separator flag &= ~HAS_TRAILING; } this.device = devicePart; this.segments = canonicalSegments; this.flags = (byte) flag; } /* (Intentionally not included in javadoc) * Private constructor. */ private Path(String device, String[] segments, int flags) { int flag = flags; if (segments.length == 0) { // paths of length 0 have no trailing separator flag &= ~HAS_TRAILING; } // no segment validations are done for performance reasons this.segments = segments; this.device = device; this.flags = (byte) flag; } /* (Intentionally not included in javadoc) * @see IPath#addFileExtension */ @Override public IPath addFileExtension(String extension) { if (isRoot() || isEmpty() || hasTrailingSeparator()) return this; int len = segments.length; String[] newSegments = new String[len]; System.arraycopy(segments, 0, newSegments, 0, len - 1); newSegments[len - 1] = segments[len - 1] + '.' + extension; return new Path(device, newSegments, flags); } /* (Intentionally not included in javadoc) * @see IPath#addTrailingSeparator */ @Override public IPath addTrailingSeparator() { if (hasTrailingSeparator() || isRoot()) { return this; } //XXX workaround, see 1GIGQ9V if (isEmpty()) { return new Path(device, segments, (flags & IS_FOR_WINDOWS) | HAS_LEADING); } return new Path(device, segments, flags | HAS_TRAILING); } /* (Intentionally not included in javadoc) * @see IPath#append(IPath) */ @Override public IPath append(IPath tail) { //optimize some easy cases if (tail == null || tail.segmentCount() == 0) return this; //these call chains look expensive, but in most cases they are no-ops //the tail must be for the same platform as this instance if (this.isEmpty() && ((flags & IS_FOR_WINDOWS) == 0) == tail.isValidSegment(":")) //$NON-NLS-1$ return tail.setDevice(device).makeRelative().makeUNC(isUNC()); if (this.isRoot() && ((flags & IS_FOR_WINDOWS) == 0) == tail.isValidSegment(":")) //$NON-NLS-1$ return tail.setDevice(device).makeAbsolute().makeUNC(isUNC()); //concatenate the two segment arrays int myLen = segments.length; int tailLen = tail.segmentCount(); String[] newSegments = new String[myLen + tailLen]; System.arraycopy(segments, 0, newSegments, 0, myLen); for (int i = 0; i < tailLen; i++) { newSegments[myLen + i] = tail.segment(i); } //use my leading separators and the tail's trailing separator String tailFirstSegment = newSegments[myLen]; if (tailFirstSegment.equals("..") || tailFirstSegment.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$ newSegments = canonicalize(isAbsolute(), newSegments); } return new Path(device, newSegments, (flags & (HAS_LEADING | IS_UNC | IS_FOR_WINDOWS)) | (tail.hasTrailingSeparator() ? HAS_TRAILING : 0)); } /* (Intentionally not included in javadoc) * @see IPath#append(java.lang.String) */ @Override public IPath append(String tail) { //optimize addition of a single segment if (tail.indexOf(SEPARATOR) == -1 && tail.indexOf('\\') == -1 && tail.indexOf(DEVICE_SEPARATOR) == -1) { int tailLength = tail.length(); if (tailLength < 3) { //some special cases if (tailLength == 0 || ".".equals(tail)) { //$NON-NLS-1$ return this; } if ("..".equals(tail)) //$NON-NLS-1$ return removeLastSegments(1); } //just add the segment int myLen = segments.length; String[] newSegments = new String[myLen + 1]; System.arraycopy(segments, 0, newSegments, 0, myLen); newSegments[myLen] = tail; return new Path(device, newSegments, flags & ~HAS_TRAILING); } //go with easy implementation return append(new Path(tail, (flags & IS_FOR_WINDOWS) != 0)); } /** * Destructively converts this path to its canonical form. *

* In its canonical form, a path does not have any * "." segments, and parent references ("..") are collapsed * where possible. *

* @return true if the path was modified, and false otherwise. */ private static String[] canonicalize(boolean isAbsolute, String[] segments) { //look for segments that need canonicalizing for (String segment : segments) { if (segment.charAt(0) == '.' && (segment.equals("..") || segment.equals("."))) { //$NON-NLS-1$ //$NON-NLS-2$ //path needs to be canonicalized return collapseParentReferences(isAbsolute, segments); } } return segments; } /* (Intentionally not included in javadoc) * Clones this object. */ @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { return null; } } /** * Destructively removes all occurrences of ".." segments from this path. */ private static String[] collapseParentReferences(boolean isAbsolute, String[] segments) { int segmentCount = segments.length; String[] stack = new String[segmentCount]; int stackPointer = 0; for (int i = 0; i < segmentCount; i++) { String segment = segments[i]; if (segment.equals("..")) { //$NON-NLS-1$ if (stackPointer == 0) { // if the stack is empty we are going out of our scope // so we need to accumulate segments. But only if the original // path is relative. If it is absolute then we can't go any higher than // root so simply toss the .. references. if (!isAbsolute) stack[stackPointer++] = segment; //stack push } else { // if the top is '..' then we are accumulating segments so don't pop if ("..".equals(stack[stackPointer - 1])) //$NON-NLS-1$ stack[stackPointer++] = ".."; //$NON-NLS-1$ else stackPointer--; //stack pop } //collapse current references } else if (!segment.equals(".") || segmentCount == 1) //$NON-NLS-1$ stack[stackPointer++] = segment; //stack push } //if the number of segments hasn't changed, then no modification needed if (stackPointer == segmentCount) return segments; //build the new segment array backwards by popping the stack String[] newSegments = new String[stackPointer]; System.arraycopy(stack, 0, newSegments, 0, stackPointer); return newSegments; } /** * Removes duplicate slashes from the given path, with the exception * of leading double slash which represents a UNC path. */ private static String collapseSlashes(String device, String path) { int length = path.length(); // if the path is only 0, 1 or 2 chars long then it could not possibly have illegal // duplicate slashes. if (length < 3) return path; // check for an occurrence of // in the path. Start at index 1 to ensure we skip leading UNC // // If there are no // then there is nothing to collapse so just return. if (path.indexOf("//", 1) == -1) //$NON-NLS-1$ return path; // We found an occurrence of // in the path so do the slow collapse. char[] result = new char[path.length()]; int count = 0; boolean hasPrevious = false; char[] characters = path.toCharArray(); for (int index = 0; index < characters.length; index++) { char c = characters[index]; if (c == SEPARATOR) { if (hasPrevious) { // skip double slashes, except for beginning of UNC. // note that a UNC path can't have a device. if (device == null && index == 1) { result[count] = c; count++; } } else { hasPrevious = true; result[count] = c; count++; } } else { hasPrevious = false; result[count] = c; count++; } } return new String(result, 0, count); } /* (Intentionally not included in javadoc) * Computes the hash code for this object. */ private static int computeHashCode(String device, String[] segments) { int hash = device == null ? 17 : device.hashCode(); int segmentCount = segments.length; for (int i = 0; i < segmentCount; i++) { //this function tends to given a fairly even distribution hash = hash * 37 + segments[i].hashCode(); } return hash; } /* (Intentionally not included in javadoc) * Returns the size of the string that will be created by toString or toOSString. */ private int computeLength() { int length = 0; if (device != null) length += device.length(); if ((flags & HAS_LEADING) != 0) length++; if ((flags & IS_UNC) != 0) length++; //add the segment lengths int max = segments.length; if (max > 0) { for (int i = 0; i < max; i++) { length += segments[i].length(); } //add the separator lengths length += max - 1; } if ((flags & HAS_TRAILING) != 0) length++; return length; } /* (Intentionally not included in javadoc) * Returns the number of segments in the given path */ private static int computeSegmentCount(String path) { int len = path.length(); if (len == 0 || (len == 1 && path.charAt(0) == SEPARATOR)) { return 0; } int count = 1; int prev = -1; int i; while ((i = path.indexOf(SEPARATOR, prev + 1)) != -1) { if (i != prev + 1 && i != len) { ++count; } prev = i; } if (path.charAt(len - 1) == SEPARATOR) { --count; } return count; } /** * Computes the segment array for the given canonicalized path. */ private static String[] computeSegments(String path) { // performance sensitive --- avoid creating garbage int segmentCount = computeSegmentCount(path); if (segmentCount == 0) return Constants.NO_SEGMENTS; String[] newSegments = new String[segmentCount]; int len = path.length(); // check for initial slash int firstPosition = (path.charAt(0) == SEPARATOR) ? 1 : 0; // check for UNC if (firstPosition == 1 && len > 1 && (path.charAt(1) == SEPARATOR)) firstPosition = 2; int lastPosition = (path.charAt(len - 1) != SEPARATOR) ? len - 1 : len - 2; // for non-empty paths, the number of segments is // the number of slashes plus 1, ignoring any leading // and trailing slashes int next = firstPosition; for (int i = 0; i < segmentCount; i++) { int start = next; int end = path.indexOf(SEPARATOR, next); if (end == -1) { newSegments[i] = path.substring(start, lastPosition + 1); } else { newSegments[i] = path.substring(start, end); } next = end + 1; } return newSegments; } /** * Returns the platform-neutral encoding of the given segment onto * the given string buffer. This escapes literal colon characters with double colons. */ private void encodeSegment(String string, StringBuilder buf) { int len = string.length(); for (int i = 0; i < len; i++) { char c = string.charAt(i); buf.append(c); if (c == DEVICE_SEPARATOR) buf.append(DEVICE_SEPARATOR); } } /* (Intentionally not included in javadoc) * Compares objects for equality. */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Path)) return false; Path target = (Path) obj; // check leading separators if ((flags & (HAS_LEADING | IS_UNC)) != (target.flags & (HAS_LEADING | IS_UNC))) { return false; } String[] targetSegments = target.segments; int i = segments.length; //check segment count if (i != targetSegments.length) return false; //check segments in reverse order - later segments more likely to differ while (--i >= 0) if (!segments[i].equals(targetSegments[i])) return false; //check device last (least likely to differ) return device == target.device || (device != null && device.equals(target.device)); } /* (Intentionally not included in javadoc) * @see IPath#getDevice */ @Override public String getDevice() { return device; } /* (Intentionally not included in javadoc) * @see IPath#getFileExtension */ @Override public String getFileExtension() { if (hasTrailingSeparator()) { return null; } String lastSegment = lastSegment(); if (lastSegment == null) { return null; } int index = lastSegment.lastIndexOf('.'); if (index == -1) { return null; } return lastSegment.substring(index + 1); } /* (Intentionally not included in javadoc) * Computes the hash code for this object. */ @Override public int hashCode() { int h = hash; if (h == 0) { hash = h = computeHashCode(device, segments); } return h; } /* (Intentionally not included in javadoc) * @see IPath#hasTrailingSeparator2 */ @Override public boolean hasTrailingSeparator() { return (flags & HAS_TRAILING) != 0; } /* * Initialize the current path with the given string. (Intentionally not * included in javadoc) Private constructor. */ private Path(String deviceString, String path, boolean forWindows) { Assert.isNotNull(path); String collapsedPath = collapseSlashes(deviceString, path); int flag = computeFlags(collapsedPath, forWindows); // compute segments and ensure canonical form String[] canonicalSegments = canonicalize((flag & HAS_LEADING) != 0, computeSegments(collapsedPath)); if (canonicalSegments.length == 0) { // paths of length 0 have no trailing separator flag &= ~HAS_TRAILING; } this.device = deviceString; this.segments = canonicalSegments; this.flags = (byte) flag; } private static int computeFlags(String path, boolean forWindows) { int len = path.length(); int flags; //compute the flags bitmap if (len < 2) { if (len == 1 && path.charAt(0) == SEPARATOR) { flags = HAS_LEADING; } else { flags = 0; } } else { boolean hasLeading = path.charAt(0) == SEPARATOR; boolean isUNC = hasLeading && path.charAt(1) == SEPARATOR; //UNC path of length two has no trailing separator boolean hasTrailing = !(isUNC && len == 2) && path.charAt(len - 1) == SEPARATOR; flags = hasLeading ? HAS_LEADING : 0; if (isUNC) flags |= IS_UNC; if (hasTrailing) flags |= HAS_TRAILING; } if (forWindows) { flags |= IS_FOR_WINDOWS; } return flags; } /* (Intentionally not included in javadoc) * @see IPath#isAbsolute */ @Override public boolean isAbsolute() { //it's absolute if it has a leading separator return (flags & HAS_LEADING) != 0; } /* (Intentionally not included in javadoc) * @see IPath#isEmpty */ @Override public boolean isEmpty() { //true if no segments and no leading prefix return segments.length == 0 && ((flags & ALL_SEPARATORS) != HAS_LEADING); } /* (Intentionally not included in javadoc) * @see IPath#isPrefixOf */ @Override public boolean isPrefixOf(IPath anotherPath) { if (device == null) { if (anotherPath.getDevice() != null) { return false; } } else { if (!device.equalsIgnoreCase(anotherPath.getDevice())) { return false; } } if (isEmpty() || (isRoot() && anotherPath.isAbsolute())) { return true; } int len = segments.length; if (len > anotherPath.segmentCount()) { return false; } for (int i = 0; i < len; i++) { if (!segments[i].equals(anotherPath.segment(i))) return false; } return true; } /* (Intentionally not included in javadoc) * @see IPath#isRoot */ @Override public boolean isRoot() { //must have no segments, a leading separator, and not be a UNC path. return this == ROOT || (segments.length == 0 && ((flags & ALL_SEPARATORS) == HAS_LEADING)); } /* (Intentionally not included in javadoc) * @see IPath#isUNC */ @Override public boolean isUNC() { if (device != null) return false; return (flags & IS_UNC) != 0; } /* (Intentionally not included in javadoc) * @see IPath#isValidPath(String) */ @Override public boolean isValidPath(String path) { return isValidPath(path, (flags & IS_FOR_WINDOWS) != 0); } /** * Returns whether the given string is syntactically correct as a path on a * POSIX file system. The path is correct if each of the segments in its * canonicalized form is valid. * * @param path the path to check * @return true if the given string is a valid path, * and false otherwise * @see #isValidPosixSegment(String) * @since 3.7 */ public static boolean isValidPosixPath(String path) { return isValidPath(path, false); } /** * Returns whether the given string is syntactically correct as a path on * the Windows file system. The device id is the prefix up to and including * the device separator (':'); the path proper is everything to the right of * it, or the entire string if there is no device separator. The device id * is not checked for validity; the path proper is correct if each of the * segments in its canonicalized form is valid. * * @param path the path to check * @return true if the given string is a valid path, * and false otherwise * @see #isValidWindowsSegment(String) * @since 3.7 */ public static boolean isValidWindowsPath(String path) { return isValidPath(path, true); } /** * Returns whether the given string is syntactically correct as a path on * the specified file system. The device id is the prefix up to and * including the device separator for the specified file system; the path * proper is everything to the right of it, or the entire string if there is * no device separator. When the specified platform is a file system with no * meaningful device separator, the entire string is treated as the path * proper. The device id is not checked for validity; the path proper is * correct if each of the segments in its canonicalized form is valid. * * @param path the path to check * @param forWindows true if the path is for the Windows file system * @return true if the given string is a valid path, * and false otherwise * @see #isValidSegment(String, boolean) * @since 3.7 */ private static boolean isValidPath(String path, boolean forWindows) { Path test = new Path(path, forWindows); for (int i = 0, max = test.segmentCount(); i < max; i++) if (!Path.isValidSegment(test.segment(i), forWindows)) return false; return true; } /* (Intentionally not included in javadoc) * @see IPath#isValidSegment(String) */ @Override public boolean isValidSegment(String segment) { return isValidSegment(segment, (flags & IS_FOR_WINDOWS) != 0); } /** * Returns whether the given string is valid as a segment in a path on a * POSIX file system. The rules for valid segments are as follows: *
    *
  • the empty string is not valid *
  • any string containing the slash character ('/') is not valid *
* * @param segment the path segment to check * @return true if the given path segment is valid, * and false otherwise * @since 3.7 */ public static boolean isValidPosixSegment(String segment) { return isValidSegment(segment, false); } /** * Returns whether the given string is valid as a segment in a path on the * Windows file system. The rules for valid segments are as follows: *
    *
  • the empty string is not valid *
  • any string containing the slash character ('/') is not valid *
  • any string containing segment ('\') or device (':') separator * characters is not valid *
* * @param segment the path segment to check * @return true if the given path segment is valid, * and false otherwise * @since 3.7 */ public static boolean isValidWindowsSegment(String segment) { return isValidSegment(segment, true); } /** * Returns whether the given string is valid as a segment in a path on the * specified file system. The rules for valid segments are as follows: *
    *
  • the empty string is not valid *
  • any string containing the slash character ('/') is not valid *
  • any string containing segment or device separator characters on the * specified file system, such as the backslash ('\') and colon (':') on * Windows, is not valid *
* * @param segment the path segment to check * @param forWindows true if the path is for the Windows file system * @return true if the given path segment is valid, * and false otherwise * @since 3.7 */ private static boolean isValidSegment(String segment, boolean forWindows) { int size = segment.length(); if (size == 0) return false; for (int i = 0; i < size; i++) { char c = segment.charAt(i); if (c == '/') return false; if (forWindows && (c == '\\' || c == ':')) return false; } return true; } /* (Intentionally not included in javadoc) * @see IPath#lastSegment() */ @Override public String lastSegment() { int len = segments.length; return len == 0 ? null : segments[len - 1]; } /* (Intentionally not included in javadoc) * @see IPath#makeAbsolute() */ @Override public IPath makeAbsolute() { if (isAbsolute()) { return this; } String[] newSegments = segments; //may need canonicalizing if it has leading ".." or "." segments if (segments.length > 0) { String first = segments[0]; if (first.equals("..") || first.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$ newSegments = canonicalize(true, segments); } } return new Path(device, newSegments, flags | HAS_LEADING); } /* (Intentionally not included in javadoc) * @see IPath#makeRelative() */ @Override public IPath makeRelative() { if (!isAbsolute()) { return this; } return new Path(device, segments, flags & (HAS_TRAILING | IS_FOR_WINDOWS)); } /** * {@inheritDoc} * @since org.eclipse.equinox.common 3.5 */ @Override public IPath makeRelativeTo(IPath base) { //can't make relative if devices are not equal if (device != base.getDevice() && (device == null || !device.equalsIgnoreCase(base.getDevice()))) return this; int commonLength = matchingFirstSegments(base); final int differenceLength = base.segmentCount() - commonLength; final int newSegmentLength = differenceLength + segmentCount() - commonLength; if (newSegmentLength == 0) return Path.EMPTY; String[] newSegments = new String[newSegmentLength]; //add parent references for each segment different from the base Arrays.fill(newSegments, 0, differenceLength, ".."); //$NON-NLS-1$ //append the segments of this path not in common with the base System.arraycopy(segments, commonLength, newSegments, differenceLength, newSegmentLength - differenceLength); return new Path(null, newSegments, flags & (HAS_TRAILING | IS_FOR_WINDOWS)); } /* (Intentionally not included in javadoc) * @see IPath#makeUNC(boolean) */ @Override public IPath makeUNC(boolean toUNC) { // if we are already in the right form then just return if (!(toUNC ^ isUNC())) return this; int newSeparators = this.flags; if (toUNC) { newSeparators |= HAS_LEADING | IS_UNC; } else { //mask out the UNC bit newSeparators &= HAS_LEADING | HAS_TRAILING | IS_FOR_WINDOWS; } return new Path(toUNC ? null : device, segments, newSeparators); } /* (Intentionally not included in javadoc) * @see IPath#matchingFirstSegments(IPath) */ @Override public int matchingFirstSegments(IPath anotherPath) { Assert.isNotNull(anotherPath); int anotherPathLen = anotherPath.segmentCount(); int max = Math.min(segments.length, anotherPathLen); int count = 0; for (int i = 0; i < max; i++) { if (!segments[i].equals(anotherPath.segment(i))) { return count; } count++; } return count; } /* (Intentionally not included in javadoc) * @see IPath#removeFileExtension() */ @Override public IPath removeFileExtension() { String extension = getFileExtension(); if (extension == null || extension.equals("")) { //$NON-NLS-1$ return this; } String lastSegment = lastSegment(); int index = lastSegment.lastIndexOf(extension) - 1; return removeLastSegments(1).append(lastSegment.substring(0, index)); } /* (Intentionally not included in javadoc) * @see IPath#removeFirstSegments(int) */ @Override public IPath removeFirstSegments(int count) { if (count == 0) return this; if (count >= segments.length) { return new Path(device, Constants.NO_SEGMENTS, flags & IS_FOR_WINDOWS); } Assert.isLegal(count > 0); int newSize = segments.length - count; String[] newSegments = new String[newSize]; System.arraycopy(this.segments, count, newSegments, 0, newSize); //result is always a relative path return new Path(device, newSegments, flags & (HAS_TRAILING | IS_FOR_WINDOWS)); } /* (Intentionally not included in javadoc) * @see IPath#removeLastSegments(int) */ @Override public IPath removeLastSegments(int count) { if (count == 0) return this; if (count >= segments.length) { //result will have no trailing separator return new Path(device, Constants.NO_SEGMENTS, flags & (HAS_LEADING | IS_UNC | IS_FOR_WINDOWS)); } Assert.isLegal(count > 0); int newSize = segments.length - count; String[] newSegments = new String[newSize]; System.arraycopy(this.segments, 0, newSegments, 0, newSize); return new Path(device, newSegments, flags); } /* (Intentionally not included in javadoc) * @see IPath#removeTrailingSeparator() */ @Override public IPath removeTrailingSeparator() { if (!hasTrailingSeparator()) { return this; } return new Path(device, segments, flags & (HAS_LEADING | IS_UNC | IS_FOR_WINDOWS)); } /* (Intentionally not included in javadoc) * @see IPath#segment(int) */ @Override public String segment(int index) { if (index >= segments.length) return null; return segments[index]; } /* (Intentionally not included in javadoc) * @see IPath#segmentCount() */ @Override public int segmentCount() { return segments.length; } /* (Intentionally not included in javadoc) * @see IPath#segments() */ @Override public String[] segments() { String[] segmentCopy = new String[segments.length]; System.arraycopy(segments, 0, segmentCopy, 0, segments.length); return segmentCopy; } /* (Intentionally not included in javadoc) * @see IPath#setDevice(String) */ @Override public IPath setDevice(String value) { if (value != null) { Assert.isTrue(value.indexOf(IPath.DEVICE_SEPARATOR) == (value.length() - 1), "Last character should be the device separator"); //$NON-NLS-1$ } //return the receiver if the device is the same if (value == device || (value != null && value.equals(device))) return this; return new Path(value, segments, flags); } /* (Intentionally not included in javadoc) * @see IPath#toFile() */ @Override public File toFile() { return new File(toOSString()); } /* (Intentionally not included in javadoc) * @see IPath#toOSString() */ @Override public String toOSString() { //Note that this method is identical to toString except //it uses the OS file separator instead of the path separator int resultSize = computeLength(); if (resultSize <= 0) return ""; //$NON-NLS-1$ char FILE_SEPARATOR = File.separatorChar; char[] result = new char[resultSize]; int offset = 0; if (device != null) { int size = device.length(); device.getChars(0, size, result, offset); offset += size; } if ((flags & HAS_LEADING) != 0) result[offset++] = FILE_SEPARATOR; if ((flags & IS_UNC) != 0) result[offset++] = FILE_SEPARATOR; int len = segments.length - 1; if (len >= 0) { //append all but the last segment, with file separators for (int i = 0; i < len; i++) { int size = segments[i].length(); segments[i].getChars(0, size, result, offset); offset += size; result[offset++] = FILE_SEPARATOR; } //append the last segment int size = segments[len].length(); segments[len].getChars(0, size, result, offset); offset += size; } if ((flags & HAS_TRAILING) != 0) result[offset++] = FILE_SEPARATOR; return new String(result); } /* (Intentionally not included in javadoc) * @see IPath#toPortableString() */ @Override public String toPortableString() { int resultSize = computeLength(); if (resultSize <= 0) return ""; //$NON-NLS-1$ StringBuilder result = new StringBuilder(resultSize); if (device != null) result.append(device); if ((flags & HAS_LEADING) != 0) result.append(SEPARATOR); if ((flags & IS_UNC) != 0) result.append(SEPARATOR); int len = segments.length; //append all segments with separators for (int i = 0; i < len; i++) { if (segments[i].indexOf(DEVICE_SEPARATOR) >= 0) encodeSegment(segments[i], result); else result.append(segments[i]); if (i < len - 1 || (flags & HAS_TRAILING) != 0) result.append(SEPARATOR); } return result.toString(); } /* (Intentionally not included in javadoc) * @see IPath#toString() */ @Override public String toString() { int resultSize = computeLength(); if (resultSize <= 0) return ""; //$NON-NLS-1$ char[] result = new char[resultSize]; int offset = 0; if (device != null) { int size = device.length(); device.getChars(0, size, result, offset); offset += size; } if ((flags & HAS_LEADING) != 0) result[offset++] = SEPARATOR; if ((flags & IS_UNC) != 0) result[offset++] = SEPARATOR; int len = segments.length - 1; if (len >= 0) { //append all but the last segment, with separators for (int i = 0; i < len; i++) { int size = segments[i].length(); segments[i].getChars(0, size, result, offset); offset += size; result[offset++] = SEPARATOR; } //append the last segment int size = segments[len].length(); segments[len].getChars(0, size, result, offset); offset += size; } if ((flags & HAS_TRAILING) != 0) result[offset++] = SEPARATOR; return new String(result); } /* (Intentionally not included in javadoc) * @see IPath#uptoSegment(int) */ @Override public IPath uptoSegment(int count) { if (count == 0) return new Path(device, Constants.NO_SEGMENTS, flags & (HAS_LEADING | IS_UNC | IS_FOR_WINDOWS)); if (count >= segments.length) return this; Assert.isTrue(count > 0, "Invalid parameter to Path.uptoSegment"); //$NON-NLS-1$ String[] newSegments = new String[count]; System.arraycopy(segments, 0, newSegments, 0, count); return new Path(device, newSegments, flags); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy