/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.vfs;
import com.google.common.base.Joiner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import static com.google.common.base.Strings.isNullOrEmpty;
/**
* Path of VirtualFile.
*
* @author andrew00x
*/
public final class Path {
/** Create new path. */
public static Path of(String path) {
final String[] segments = splitToSegments(path);
if (segments.length == 0) {
return ROOT;
}
return new Path(path.charAt(0) == '/', normalizePathSegments(EMPTY_PATH, segments));
}
private static final String[] EMPTY_PATH = new String[0];
private static final Pattern PATH_SPLITTER = Pattern.compile("/");
public static final Path ROOT = new Path(true);
private final String[] elements;
private final boolean absolute;
private volatile int hashCode;
private volatile String asString;
private Path(boolean absolute, String... elements) {
this.absolute = absolute;
this.elements = elements;
}
public boolean isAbsolute() {
return absolute;
}
public Path getParent() {
return isRoot() ? null : elements.length == 1 ? ROOT : subPath(0, elements.length - 1);
}
public Path subPath(Path parent) {
return subPath(parent.length(), elements.length);
}
public Path subPath(int beginIndex) {
return subPath(beginIndex, elements.length);
}
public Path subPath(int beginIndex, int endIndex) {
if (beginIndex < 0 || beginIndex >= elements.length || endIndex > elements.length || beginIndex >= endIndex) {
throw new IllegalArgumentException("Invalid end or begin index. ");
}
final String[] subPathElements = Arrays.copyOfRange(elements, beginIndex, endIndex);
return new Path(absolute && beginIndex == 0, subPathElements);
}
public String getName() {
return isRoot() ? "" : element(elements.length - 1);
}
public String[] elements() {
return Arrays.copyOf(elements, elements.length);
}
public int length() {
return elements.length;
}
public String element(int index) {
if (index < 0 || index >= elements.length) {
throw new IllegalArgumentException("Invalid index. ");
}
return elements[index];
}
public boolean isRoot() {
return absolute && elements.length == 0;
}
public boolean isChild(Path parent) {
if (parent.elements.length >= this.elements.length) {
return false;
}
for (int i = 0, parentLength = parent.elements.length; i < parentLength; i++) {
if (!parent.elements[i].equals(this.elements[i])) {
return false;
}
}
return true;
}
public Path newPath(String relative) {
return newPath(splitToSegments(relative));
}
private static String[] splitToSegments(String rawPath) {
return (isNullOrEmpty(rawPath) || ((rawPath.length() == 1) && (rawPath.charAt(0) == '/')))
? EMPTY_PATH : PATH_SPLITTER.split(rawPath.charAt(0) == '/' ? rawPath.substring(1) : rawPath);
}
public Path newPath(String... relative) {
if (relative.length == 0) {
return this;
}
return new Path(absolute, normalizePathSegments(elements, relative));
}
private static String[] normalizePathSegments(String[] parent, String[] relative) {
List<String> segmentsList = new ArrayList<>(parent.length + relative.length);
Collections.addAll(segmentsList, parent);
for (String segment : relative) {
if ("..".equals(segment)) {
int size = segmentsList.size();
if (size == 0) {
throw new IllegalArgumentException(String.format("Invalid path '%s', '..' on root. ", Joiner.on('/').join(relative)));
}
segmentsList.remove(size - 1);
} else if (!(".".equals(segment))) {
segmentsList.add(segment);
}
}
if (segmentsList.isEmpty()) {
return EMPTY_PATH;
}
return segmentsList.toArray(new String[segmentsList.size()]);
}
public Path newPath(Path relative) {
final String[] newPath = new String[elements.length + relative.elements.length];
System.arraycopy(elements, 0, newPath, 0, elements.length);
System.arraycopy(relative.elements, 0, newPath, elements.length, relative.elements.length);
return new Path(absolute, newPath);
}
public String join(char separator) {
StringBuilder builder = new StringBuilder();
if (absolute) {
builder.append(separator);
}
Joiner.on(separator).appendTo(builder, elements);
return builder.toString();
}
/* ==================================================== */
@Override
public String toString() {
if (asString == null) {
if (isRoot()) {
asString = "/";
} else {
asString = join('/');
}
}
return asString;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof Path) {
Path path = (Path)o;
return Arrays.equals(elements, path.elements);
}
return false;
}
@Override
public int hashCode() {
if (hashCode == 0) {
hashCode = Arrays.hashCode(elements);
}
return hashCode;
}
}