/*
* Copyright 2003-2017 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;
import jetbrains.mps.util.annotation.ToRemove;
import jetbrains.mps.vfs.path.Path;
import jetbrains.mps.vfs.path.UniPath;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.util.ProgressMonitor;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
/**
* AP:
* An abstraction of path names similar to the {@link java.io.File}.
* Represents an abstract absolute path (or location) on disk (might be non-existent).
* Moreover might represents a path to some archive + path within the archive (unlike the {@link java.io.File}).
*
* FIXME
// * Another difference from the {@link java.io.File} is the one that this abstraction is supposed to be
// * platform-independent (sic!) meaning that the path
* @see #getPath() for an example
* FIXME
*
* Also it is an MPS abstraction around the IDEA platform file system {@link com.intellij.openapi.vfs.VirtualFile}.
* IDEA provides an intelligent caching mechanism which might boost up the file system traversal (comparing to the {@link java.io.File}).
* @see CachingFile
*
* It has no fallback implementation base on {@link java.io.File},
* however there is a special singleton class {@link jetbrains.mps.vfs.FileSystem} with
* method #getFileByPath which creates a suitable instance of IFile.
*
* These mechanism nevertheless is deprecated and must not be used.
*
* From 3.4 you are supposed to use the direct implementing classes of IFile.
*
* //TODO continue
*
* AP: I suggest to continue using this interface because dropping it might cause too much pain.
* As long as it is (technically speaking) our internal api, we are more or less free to change it.
*
* This class mixes two different issues (as well as the {@link java.io.File}): it works with paths (which are strings) and
* also it accesses the physical fs from time to time. I'd rather split it up.
*
* FIXME IFile is mutable
* (as well as the java.io.File) I think it is unacceptable.
* we define it as a pathname abstraction. That means that we cannot rename the IFile, we can only rename something
* that lies at this pathname on disk. The IFile itself must not be touched in any way. Otherwise it is cumbersome.
* The alternative is to reconsider the IFile contract.
*
* AP
*/
public interface IFile {
/**
* @return the file system which this file belongs to
*/
@NotNull jetbrains.mps.vfs.openapi.FileSystem getFileSystem();
/**
* @return simply the last name of the file (the furthest one)
*/
@NotNull String getName();
/**
* @return the whole path to the abstract location.
* The current contract:
* the resulting path will be canonical, absolute
* and the folders are separated with {@link Path#UNIX_SEPARATOR_CHAR} // todo explain more
*
* @see File#getCanonicalPath()
* @see Path
*/
@ToRemove(version = 3.5)
/*@Deprecated*/
@NotNull String getPath();
/**
* @return the comprised path corresponding to this file
*/
@NotNull
UniPath toPath();
URL getUrl() throws MalformedURLException;
/**
* @return null iff the instance is root and has no parent, the parent folder otherwise
*/
@Nullable IFile getParent();
/**
* shortcut to {@link #toPath()} {@link UniPath#isArchive()}
* @return whether the underlying pathname points exactly to an archive file
*/
boolean isArchive();
/**
* shortcut to {@link #toPath()} {@link UniPath#isInArchive()}
* @return whether the underlying pathname points to an archive file or some of its contents
*/
boolean isInArchive();
/**
* @deprecated use {@link #isArchive()} or {@link #isInArchive()}
*/
@ToRemove(version = 3.4)
@Deprecated
default boolean isPackaged() {
return isInArchive();
}
/**
* @deprecated use {@link CachingFile#refresh(CachingContext)}
*/
@ToRemove(version = 3.4)
@Deprecated
default void refresh() {
if (this instanceof CachingFile) {
CachingFile me = (CachingFile) this;
me.refresh(new DefaultCachingContext(true, false));
}
}
/**
* @return the jar or folder which contains this file
* @deprecated use {@link #toPath()} and extract the path you need
*/
@Deprecated
@ToRemove(version = 3.4)
IFile getBundleHome();
// accessing physical fs
boolean isDirectory();
boolean isReadOnly();
/**
* TODO will be like this in some implementations of these two methods after 3.4:
// * the files in the archive root in the case when {@link #isArchive()} is true.
// *
// * Thus the client of this method need not to bother to expand the archives on his own: the implementing class
// * must do it automatically. Probably cosy recursive processing also will be provided.
* TODO
* FIXME please document whether suffix may include path separators (i.e. if one could query folder.getDescendant("my/relative/path/to/file")
* @return the file which has this file's path + suffix
*/
@NotNull IFile getDescendant(@NotNull String suffix);
/**
* @return the children of this file in case when it is a folder,
* null iff it is a file and therefore has no children
*/
@Nullable List<IFile> getChildren();
default void addListener(@NotNull FileListener listener) {
getFileSystem().addListener(new FileListenerAdapter(this, listener));
}
default void removeListener(@NotNull FileListener listener) {
getFileSystem().removeListener(new FileListenerAdapter(this, listener));
}
/**
* fixme if it is the same as in java.io.File then we need to enforce it
*/
long lastModified();
long length();
boolean exists();
boolean setTimeStamp(long time);
/**
* creates a physical file
* @return whether it is a success
*/
boolean createNewFile();
boolean mkdirs();
/**
* FIXME document what happens if one deletes non-empty folder. IoFile seems to force deletion. Is it the contract?
* FIXME document what happens if this file doesn't exist (false == exists()).
*/
boolean delete();
boolean rename(String newName);
boolean move(IFile newParent);
InputStream openInputStream() throws IOException;
OutputStream openOutputStream() throws IOException;
}