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

gw.fs.ResourcePath Maven / Gradle / Ivy

There is a newer version: 1.18.2
Show newest version
/*
 * Copyright 2014 Guidewire Software, Inc.
 */

package gw.fs;

import gw.lang.UnstableAPI;
import gw.util.DynamicArray;
import gw.util.GosuObjectUtil;
import gw.util.GosuStringUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

@UnstableAPI
public class ResourcePath {

  public static final String WINDOWS_NETWORK_ROOT = "\\\\";

  protected final ResourcePath _parent;
  protected final String _name;

  protected ResourcePath(ResourcePath parent, String name) {
    if (name == null) {
      throw new IllegalArgumentException("The name argument to the ResourcePath constructor cannot be null");
    }
    _parent = parent;
    _name = name;
  }

  public static ResourcePath parse(String pathString) {
    String rootElement;
    int lastIndex;
    if (pathString.startsWith(WINDOWS_NETWORK_ROOT)) {
      rootElement = WINDOWS_NETWORK_ROOT;
      lastIndex = 2;
    } else if (pathString.startsWith("/")) {
      rootElement = "";
      lastIndex = 1;
    } else {
      char first = pathString.charAt(0);
      if (pathString.length() > 1 && pathString.charAt(1) == ':' && Character.isLetter(first)) {
        rootElement = Character.toUpperCase(first) + ":";
        lastIndex = 2;
        if (pathString.length() > 2 && (pathString.charAt(2) == '/' || pathString.charAt(2) == '\\')) {
          lastIndex = 3;
        }
      } else {
        throw new IllegalArgumentException("The path string [" + pathString + "] is not an absolute path starting at a drive root");
      }
    }

    DynamicArray results = tokenizePathFragment(pathString, lastIndex);

    results = normalizePath(results);

    return construct(rootElement, results, results.size() - 1);
  }

  private static DynamicArray tokenizePathFragment(String pathString, int lastIndex) {
    DynamicArray results = new DynamicArray();
    for (int i = lastIndex; i < pathString.length(); i++) {
      char c = pathString.charAt(i);
      if (c == '/' || c == '\\') {
        results.add(pathString.substring(lastIndex, i));
        lastIndex = i + 1;
      }
    }

    pathString = pathString.substring(lastIndex);
    results.add(pathString);
    return results;
  }

  private static DynamicArray normalizePath(DynamicArray pathElements) {
    DynamicArray results = new DynamicArray(pathElements.size());
    for (int i = 0; i < pathElements.size; i++) {
      String s = (String) pathElements.data[i];
      if (s.equals(".") || s.equals("/") || s.equals("\\") || s.equals("")) {
        // no-op
      } else if (s.equals("..")) {
        // TODO - Throw if no more elements
        results.remove(results.size() - 1);
      } else {
        results.add(s);
      }
    }

    return results;
  }

  private static ResourcePath construct(String headElement, List fullPath, int nameIndex) {
    if (nameIndex == -1) {
      if (headElement == null) {
        return null;
      } else {
        return new ResourcePathHead(headElement);
      }
    } else {
      return new ResourcePath(construct(headElement, fullPath, nameIndex - 1), fullPath.get(nameIndex));
    }
  }

  /**
   * Returns the leaf name of this resource path.  If this path object represents the path "/usr/local/bin", then
   * the getName() method will return the String "bin".  In the case of the root path element, this method will return
   * the empty string for the unix filesystem root and a windows drive letter, normalized to upper case, with no
   * trailing path separator for a windows path root.
   *
   * This method will never return a null value.
   *
   * @return the name of the last element of the path.
   */
  public String getName() {
    return _name;
  }

  /**
   * Returns the parent of this resource path.  If this path object represents the root of the filesystem,
   * this method will return null.
   *
   * @return the parent of this ResourcePath
   */
  public ResourcePath getParent() {
    return _parent;
  }

  /**
   * Returns the path string for this path using the / path separator.
   *
   * @return the path string for this path using the / path separator.
   * @see #getPathString(String)
   */
  public String getPathString() {
    return getPathString("/");
  }

  /**
   * Returns the path string for this path using the default file system separator,
   * as defined by the File.separator property.
   *
   * @return the path string for this path using the default file system separator
   * @see #getPathString(String)
   */
  public String getFileSystemPathString() {
    return getPathString(File.separator);
  }

  /**
   * Returns the path string for this path using the specified path separator.  The path
   * constructed will begin with the root of the path, which will be one of:
   * 
    *
  • A windows drive letter, normalized to upper case, followed by : and the separator
  • *
  • The windows network path start \\
  • *
  • Just the separator, in the case of the unix root
  • *
* After the path root will follow all path components, separated with the given separator. The * separator will not be appended to the end of the path. Some example paths returned by this * method are: *
    *
  • C:\temp\downloads
  • *
  • /
  • *
  • \\files\documents
  • *
      * * @param separator the separator to use when constructing the path * @return the path string for this path using the specified path separator */ public String getPathString(String separator) { StringBuilder sb = new StringBuilder(); constructPathString(sb, separator); return sb.toString(); } private void constructPathString(StringBuilder sb, String separator) { if (_parent != null) { _parent.constructPathString(sb, separator); // We don't want to add the separator character after the windows network root; otherwise // we want to add the separator in before adding in our name if (!(_parent instanceof ResourcePathHead && _parent.getName().equals(WINDOWS_NETWORK_ROOT))) { sb.append(separator); } } sb.append(_name); } /** * Takes the specified path fragment and joins it with this ResourcePath to create a new ResourcePath. * The specified path can use either \ or / separator characters (or a mix of the two), and it can include * . or .. elements in the path, which will be traversed appropriately. The path can start and/or end with * a separator character, but it should not start with a windows drive letter or network root. The resulting * path will have the same root as this path. Some examples: *
        *
      • "/usr/local".join("lib/java") -> "/usr/local/lib/java"
      • *
      • "/usr/local".join("/..") -> "/usr"
      • *
      • "/usr/local".join("/.") -> "/usr/local
      • *
      * If .. path elements would lead to traversing backwards past the root element, an IllegalArgumentException will * be thrown. An IllegalArgumentException will also be thrown if otherPath is null. * * @param otherPath the path to join with this one * @return a ResourcePath that results from appending otherPath to this path and then normalizing the result */ public ResourcePath join(String otherPath) { if (otherPath == null) { throw new IllegalArgumentException("The join(String) method cannot be called with a null argument"); } DynamicArray components = tokenizePathFragment(otherPath, 0); ResourcePath result = this; for (int i = 0; i < components.size; i++) { String s = (String) components.data[i]; if (s.equals(".") || s.equals("/") || s.equals("\\") || s.equals("")) { // no-op } else if (s.equals("..")) { result = result.getParent(); if (result == null) { throw new IllegalArgumentException("Joining the path [" + otherPath + "] to the base path [" + getPathString() + "] resulted in traversing backwards past the root path element"); } } else { result = new ResourcePath(result, s); } } return result; } /** * Two ResourcePath objects are considered to be equal if they represent the same leaf path and if their parents * are equal. Note that currently the name matching is case-sensitive, even when this is being called on * a case-insensitive file system. * * @param obj the other object * @return true if the objects are equal, false otherwise */ @Override public boolean equals(Object obj) { if (obj instanceof ResourcePath) { ResourcePath otherPath = (ResourcePath) obj; return otherPath.getName().equals(getName()) && GosuObjectUtil.equals(getParent(), otherPath.getParent()); } else { return false; } } @Override public int hashCode() { int result = _parent != null ? _parent.hashCode() : 0; result = 31 * result + (_name != null ? _name.hashCode() : 0); return result; } public boolean isChild(ResourcePath path) { return GosuObjectUtil.equals(this, path.getParent()); } public boolean isDescendant(ResourcePath path) { ResourcePath pathToTest = path; while (pathToTest != null) { if (pathToTest.equals(this)) { return true; } pathToTest = pathToTest.getParent(); } return false; } public String relativePath(ResourcePath other) { return relativePath(other, File.separator); } public String relativePath(ResourcePath other, String separator) { List pathComponents = new ArrayList(); ResourcePath pathToTest = other; boolean success = false; while (pathToTest != null) { if (pathToTest.equals(this)) { success = true; break; } pathComponents.add(0, pathToTest.getName()); pathToTest = pathToTest.getParent(); } if (!success || pathComponents.isEmpty()) { return null; } else { return GosuStringUtil.join(pathComponents, separator); } } private static class ResourcePathHead extends ResourcePath { protected ResourcePathHead(String name) { super(null, name); } @Override public String getPathString(String separator) { // Special hack: the path string for the root element needs to // include the separator unless it's the windows network root symbol if (WINDOWS_NETWORK_ROOT.equals(_name)) { return _name; } else { return _name + separator; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy