/*
* Copyright 2003-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.vfs.path;
import jetbrains.mps.vfs.Watchable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* Alike to the {@link java.nio.file.Path}.
* Might be absolute or relative. The path is split up into so-called name components. Let us call
* the first name component the <em>root</em> component.
*
* The aim of this class is to get rid of working with the file paths (simply Strings) in the client code.
* This comprises working with separators as well as working with archives.
*
* Created by apyshkin on 6/17/16.
*/
public interface Path extends Comparable<Path>, /*AP: do I want this?*/ Watchable, PathUtil {
/**
* current system defaults
*/
char SYSTEM_SEPARATOR_CHAR = File.separatorChar;
String SYSTEM_SEPARATOR = File.separator;
/**
* any path one can get from this API must use this separator
* default separator is UNIX-style
*/
char UNIX_SEPARATOR_CHAR = '/'; // this is used for so-called independent paths
String UNIX_SEPARATOR = String.valueOf(UNIX_SEPARATOR_CHAR);
char WIN_SEPARATOR_CHAR = '\\'; // this is used for so-called dependent paths
String WIN_SEPARATOR = String.valueOf(WIN_SEPARATOR_CHAR);
String ZIP = "zip";
String DOT_ZIP = "." + ZIP;
String JAR = "jar";
String DOT_JAR = "." + JAR;
/**
* e.g /Users/ap/foo/bar/abc/my-archive.jar!/my/path/within/jar/module.xml
*
* NB: might be any ZIP as well
*/
String ARCHIVE_SEPARATOR = "!/";
/**
* @return whether the given path is relative or absolute
*/
boolean isRelative();
/**
* @return the separator of this path
*/
char getSeparator();
/**
* @return null iff it is a root folder, the parent Path instance otherwise
* note that this method will not eliminate special path parts like '..' and '.'
* please
* @see #toNormal()
* @see #toCanonical()
*/
@Nullable Path getParent();
/**
* simply a shortcut to the last element of the {@link #getNames()}
* if the list is empty then this method returns null
*
* @return the actual file name (the last in the whole path)
*/
@Nullable String getFileName();
/**
* @return an immutable list of names of folder/file(s)
*/
@NotNull List<String> getNames();
/**
* Returns the root component of this path as a {@code Path} object,
* or {@code null} if this path does not have a root component.
*
* @return a path representing the root component of this path,
* or {@code null}
*/
@Nullable Path getRoot();
/**
* Separator becomes {@link #UNIX_SEPARATOR_CHAR}, replacing drive letter is up to implementation
*/
@NotNull Path toIndependentPath();
/**
* Separator becomes {@link #SYSTEM_SEPARATOR_CHAR}, replacing drive letter is up to implementation
*/
@NotNull Path toSystemPath();
/**
* Tests if this path ends with the given path.
*
* If the given path has <em>N</em> elements, and it is relative
* and this path has <em>N</em> or more elements, then this path ends with
* the given path if the last <em>N</em> elements of each path, starting at
* the element farthest from the root, are equal.
*
* If the given path is absolute then this path ends with the
* given path if the root component of this path ends with the root
* component of the given path, and the corresponding elements of both paths
* are equal.
* If this path does not have a root component and the given path has a root component
* then this path does not end with the given path.
*/
boolean endsWith(@NotNull String other);
boolean endsWith(@NotNull Path other);
/**
* Tests if this path starts with the given path.
*
* The logic is absolutely the same as in the {@link #endsWith(String)}
*
* Whether or not the root component of this path starts with the root
* component of the given path is file system specific. If this path does
* not have a root component and the given path has a root component then
* this path does not start with the given path.
*/
boolean startsWith(@NotNull Path other);
boolean startsWith(@NotNull String other);
/**
* Constructs a relative path between this path and a given path.
*
* p2.relativize(p1.resolve(p2)) must be equal to p2
*
* <p> Relativization is the inverse of {@link #resolve(Path) resolution}.
* This method attempts to construct a {@link #isRelative()} relative} path
* that when {@link #resolve(Path) resolved} against this path, yields a
* path that locates the same file as the given path. For example, on UNIX,
* if this path is {@code "/a/b"} and the given path is {@code "/a/b/c/d"}
* then the resulting relative path would be {@code "c/d"}.
* When either paths are relative then a relative path also can be constructed (as if we are looking from the current directory).
* A relative path cannot be constructed if only one of the paths {@link #isRelative()}.
* If this path and the given path are * {@link #equals equal} then an <i>empty path</i> is returned.
*
* @return the resulting relative path, or an empty path if both paths are
* equal
*
* @throws IllegalArgumentException
* if {@code other} is not a {@code Path} that can be relativized
* against this path
*/
@NotNull Path relativize(@NotNull Path other); // TODO
@NotNull Path resolve(@NotNull Path other); // TODO
@NotNull Path resolve(@NotNull String other);
/**
* @return the path, which is equals to this one but not the same
*/
@NotNull Path copy();
/**
* Returns a {@code Path} object representing the absolute path of this
* path.
*
* <p> If this path is not {@link Path#isRelative()} relative} then it is absolute and this
* method simply returns this path. Otherwise, this method resolves the path
* in an implementation dependent manner, typically by resolving the path
* against a file system default directory. Depending on the implementation,
* this method may throw an I/O error if the file system is not accessible.
*
* @return a {@code Path} object representing the absolute path
*
* @throws java.io.IOError
* if an I/O error occurs
*/
@NotNull Path toAbsolute();
/**
* it tries to do the same that the <code>#toCanonical</code>
* without actual access to the file system.
* That means resolving all "." and ".." symbols.
* Note that in the case there are symlinks in the path string we might end
* with the quite different path.
*
* @see #toCanonical()
*/
@NotNull Path toNormal();
/**
* Returns the canonical pathname string of this abstract pathname.
*
* A canonical pathname is both absolute and unique. The precise
* definition of canonical form is system-dependent. This method first
* converts this pathname to absolute form if necessary, as if by invoking the
* {@link #toAbsolute()} method, and then maps it to its unique form in a
* system-dependent way. This typically involves removing redundant names
* such as "." and ".." from the pathname, resolving
* symbolic links (on UNIX platforms), and converting drive letters to a
* standard case (on Microsoft Windows platforms).
*
* Every pathname that denotes an existing file or directory has a
* unique canonical form. Every pathname that denotes a nonexistent file
* or directory also has a unique canonical form. The canonical form of
* the pathname of a nonexistent file or directory may be different from
* the canonical form of the same pathname after the file or directory is
* created. Similarly, the canonical form of the pathname of an existing
* file or directory may be different from the canonical form of the same
* pathname after the file or directory is deleted.
*
* @return The canonical pathname string denoting the same file or
* directory as this abstract pathname
*
* @throws IOException
* If an I/O error occurs, which is possible because the
* construction of the canonical pathname may require
* filesystem queries
*
* @see File#getCanonicalFile()
*/
@NotNull Path toCanonical() throws IOException;
// @NotNull Path subpath(int beginIndex, int endIndex);
//
//
// Path resolveSibling(Path other);
//
// Path resolveSibling(String other);
}