How Can I Correctly Implement Change Directory Sup

2019-09-25 05:27发布

I am writing a script-interpreter/shell and I need change directory support to do this effectively.

As of right now, I am using a File object reference as my current directory, and resolving all relative paths using that file object. This works, but the problem is that every file reference the interpreter makes use of will have an absolute path. This doesn't work so well for things like getting a relative URI or when an error happens, the error messages won't reflect the path that was supplied to the script engine, and this will make script debugging very "non-intuitive" (a correct error message is verbatim with respect to its errant inputs).

If it's not possible to change the current directory, Java's File Object is basically OBSOLETE. Of course, if java would let me specify what my current directory is on the file objects this wouldn't be an issue, but I don't know of a way to do that except to extend the File class with such feature support.

To further compound the LOGICAL-FALLACY of this lack of correct feature support, I am basically facing the same problems with running a process using the process builder.

I am aware that I am already "getting it done", but it is very important to my project that i "get it done CORRECTLY" which means TRANSPARENTLY.

I have seen stack-overflow-answers which say: modify the system property: user.dir, but that is flat out wrong and doesn't work with my JVM.

I don't care at all what the JVM specs say I should or shouldn't do in this particular case (because I don't subscribe to logical fallacies), I just want to change the current directory in a cross-platform-compatible-way. Every major operating system supports the feature but Java doesn't? (HORSERADISH!)

How Can I Correctly Implement Change Directory Support in Java 8+?

After a little digging, I figured it out! This is not possible. Apparently, the JDK team back in the day couldn't understand the concept of current directory (WHICH HAS NOTHING TO DO WITH CHANGING THE CURRENT DIRECTORY OF OTHER PROCESSES AND THREADS).

For all intents and purposes, change directory is for the currently executing thread (process), and all future RELATIVE file system access operations, from within that thread or its children (quote-this). Some operating systems and maybe even all specifcations may have a different notion of its effect, but this reasoning (quote-that) is what change directory does from the perspective of the end user. Being completely system compatible is not the use case for change directory, but how each future file operation IS affected IS the use case hence the word: CHANGE.

Java is basically a system emulation layer, and all Filesystem IO operations are bound within that layer. Neglecting to provide change directory emulation was, and always will be Java's biggest failure in foresight, until they decide to neglect some other pertinent system standard.

I am no stranger to writing emulation layers. It is not about the underlying implementation. That is a core unix philosphy principle. It IS about providing the capability to get the job done. That being said, it is not impossible or diffficult to implement a JDK change directory feature, from the JDK side of development.

If you look at my answer, its as simple as providing a thread safe static variable, which all file operations will access in the case of a relative path. UBER FAIL.

标签: java
1条回答
贼婆χ
2楼-- · 2019-09-25 06:00

So here is my "example" which provides limited support. I don't accept this as the answer because it does not fix all of the issues. Stream IO for example. But for lightweight projects which only need java.io.File support for changing the current directory with correct error outputs, this SHOULD do.

I say that it should do, because I have not tested it. This code is not complete. I have never worked with file filters in java, so I have no idea how to patch those entry points. The methods are marked deprecated and will throw unsupported operation exceptions if called.

Using this tool, you can set and get your current directory value, and all of your file methods will work with the path you supplied, unless there is a disk access; in that case, it will fallback to the path of the real file. If an exception is raised, the code will attempt (again, not tested) to rectify the error message with the path you supplied.

The people at java really screwed up on this one, undoubtedly; you basically have to patch/hack/mock the entire Java IO Platform just to enable support for changing directories. That my friends is what we call a serious case of neglect; an amatuer coding mistake. There is no coder on this earth worth his salt, who thinks it would be a smart idea to abrogate a computing coding standard older than he or she is, when there is no problem with that standard to begin with.

A disk operating system, with no change directory? You're pretty lucky you don't work for me. You would be getting your termination papers in the mail. Because I would be ready to strangle you if you walked into my office with a "fix" like this=(no change directory support) for something that isn't broken.

import java.io.*;
import java.net.URI;
import java.nio.file.Path;

public class HSFile extends java.io.File {

  /*
    For this class to work as intended, we need to intervene in any
    method which transposes the file's name, accesses the disk,
    or throws exceptions.
  */

  private final File realFile;
  private final String realDirectory;

  public static String getCurrentDirectory() {
    return currentDirectory.getPath();
  }

  public static void setCurrentDirectory(String currentDirectory) throws
    FileNotFoundException {
    File target = new File(currentDirectory);
    if (! target.exists() )
      throw new FileNotFoundException("target directory: `"+target+"'; does not exist");
    if (! target.isDirectory())
      throw new UnsupportedOperationException("target directory: `"+target+"'; is not a directory");
    HSFile.currentDirectory = target;
  }

  /**
   * The global current directory property
   */
  private static java.io.File currentDirectory =
    new File(java.lang.System.getProperty("user.dir"));

  public File getRealFile() {
    if (realFile != null) return realFile;
    if (! isAbsolute()) return new File(getCurrentDirectory(), getPath());
    return new File(this.getPath());
  }

  public String getRealPath() { return realFile.getPath(); }

  public HSFile(String s) {
    super(s);
    realFile = getRealFile();
    if (! isAbsolute()) {
      realDirectory = getCurrentDirectory() + File.separator;
    } else realDirectory = realFile.getParent() + File.separator;
  }

  public HSFile(String s, String s1) {
    super(s, s1);
    realFile = getRealFile();
    realDirectory = s + File.separator;
  }

  public HSFile(File file, String s) {
    super(file, s);
    realFile = getRealFile();
    realDirectory = file.getPath() + File.separator;
  }

  public HSFile(URI uri) {
    super(uri);
    realFile = getRealFile();
    if (! isAbsolute()) {
      realDirectory = getCurrentDirectory() + File.separator;
    } else realDirectory = realFile.getParent() + File.separator;
  }

  @Override
  public boolean canRead() {
    return realFile.canRead();
  }

  @Override
  public boolean canWrite() {
    return realFile.canWrite();
  }

  @Override
  public boolean exists() {return realFile.exists();}

  @Override
  public boolean isDirectory() {return realFile.isDirectory();}

  @Override
  public boolean isFile() {return realFile.isFile();}

  @Override
  public boolean isHidden() {return realFile.isHidden();}

  @Override
  public long lastModified() {return realFile.lastModified();}

  @Override
  public long length() {return realFile.length();}

  @Override
  public boolean createNewFile() throws IOException {
    return realFile.createNewFile();
  }

  @Override
  public boolean delete() {return realFile.delete();}

  @Override
  public void deleteOnExit() {realFile.deleteOnExit();}

  @Override
  public boolean mkdir() {return realFile.mkdir();}

  @Override
  public boolean mkdirs() {return realFile.mkdirs();}

  @Override
  public boolean setLastModified(long l) {return realFile.setLastModified(l);}

  @Override
  public boolean setWritable(boolean b, boolean b1) {
    return realFile.setWritable(b, b1);
  }

  @Override
  public boolean setWritable(boolean b) {return realFile.setWritable(b);}

  @Override
  public boolean setReadable(boolean b, boolean b1) {
    return realFile.setReadable(b, b1);
  }

  @Override
  public boolean setReadable(boolean b) {return realFile.setReadable(b);}

  @Override
  public boolean setExecutable(boolean b, boolean b1) {
    return realFile.setExecutable(b, b1);
  }

  @Override
  public boolean setExecutable(boolean b) {return realFile.setExecutable(b);}

  @Override
  public boolean canExecute() {
    return realFile.canExecute();
  }

  @Override
  public long getTotalSpace() {return realFile.getTotalSpace();}

  @Override
  public long getFreeSpace() {return realFile.getFreeSpace();}

  @Override
  public long getUsableSpace() {return realFile.getUsableSpace();}

  @Override
  public String getAbsolutePath() {
    return realFile.getAbsolutePath();
  }

  @Override
  public HSFile getAbsoluteFile() {
    return new HSFile(getAbsolutePath());
  }

  @Override
  public String getCanonicalPath() throws IOException {
    return realFile.getCanonicalPath();
  }

  @Override
  public HSFile getCanonicalFile() throws IOException {
    return new HSFile(realFile.getCanonicalPath());
  }

  @Override
  public boolean setReadOnly() {
    return realFile.setReadOnly();
  }

  @Override
  public Path toPath() {
    return realFile.toPath();
  }

  @Override
  public HSFile getParentFile() {
    return new HSFile(getParent());
  }

  @Override
  public boolean renameTo(File file) {
    HSFile target = file instanceof HSFile ?
      (HSFile)file : new HSFile(file.getPath());
    return realFile.renameTo(target.realFile);
  }

  @Override
  public int compareTo(File file) {
    HSFile target = file instanceof HSFile ?
      (HSFile)file : new HSFile(file.getPath());
    return realFile.compareTo(target.realFile);
  }

  @Override
  public String[] list() {return  realFile.list();}

  @Override
  public HSFile[] listFiles() {
    File[] files = realFile.listFiles();
    if (files == null) return null;
    HSFile[] xFiles = new HSFile[files.length];
    int item;
    for (item = 0; item < files.length; item++)
      xFiles[item] = new HSFile(files[item].getPath());
    return xFiles;
  }

  @Override @Deprecated
  public HSFile[] listFiles(FilenameFilter filenameFilter) {
    // fill this in if you need it
    throw new UnsupportedOperationException("this operation is beyond the scope of this class");
  }

  @Override @Deprecated
  public String[] list(FilenameFilter filenameFilter) {
    // fill this in if you need it
    throw new UnsupportedOperationException("this operation is beyond the scope of this class");
  }

  @Override @Deprecated
  public HSFile[] listFiles(FileFilter fileFilter) {
    // fill this in if you need it
    throw new UnsupportedOperationException("this operation is beyond the scope of this class");
  }

}

--UPDATE-- The current directory for each file must be cached, or subsequent changes will refrence the wrong file path. It is unknown at this time, if the error message fixes will be viable in their current state.

I have removed the file name changing in the errors. So that this example reflects only change current directory support. The package name is also nixed.

查看更多
登录 后发表回答