001/*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019
020package org.crsh.vfs;
021
022import org.crsh.util.BaseIterator;
023
024import java.util.Iterator;
025import java.util.NoSuchElementException;
026
027public final class Path implements Iterable<String> {
028
029  /** . */
030  private static final String[] EMPTY_STRING = new String[0];
031
032  /** . */
033  private final boolean dir;
034
035  /** . */
036  private final String[] names;
037
038  /** . */
039  private String value;
040
041  public static Path get(Path parent, String name, boolean dir) {
042    if (!parent.dir) {
043      throw new IllegalArgumentException("Not a dir");
044    }
045    int length = parent.names.length;
046    String[] names = new String[length + 1];
047    System.arraycopy(parent.names, 0, names, 0, length);
048    names[length] = name;
049    return new Path(dir, names);
050  }
051
052  public static Path get(String s) {
053    if (s.length() == 0) {
054      throw new IllegalArgumentException("No empty path");
055    }
056    if (s.charAt(0) != '/') {
057      throw new IllegalArgumentException("Path " + s + " must begin with a '/'");
058    }
059
060    //
061    int start = 0;
062    int end = s.length();
063    String[] names = EMPTY_STRING;
064    while (start < end) {
065      if (s.charAt(end - 1) == '/') {
066        end--;
067      } else if (s.charAt(start) == '/') {
068        start++;
069      } else {
070        names = parseNames(s, start, end, 0);
071        break;
072      }
073    }
074
075    //
076    return new Path(end < s.length(), names);
077  }
078
079  private static String[] parseNames(final String s, final int prev, int end, final int count) {
080    int next = s.indexOf('/', prev);
081    if (next == -1 || next > end) {
082      if (prev < end) {
083        String[] ret = new String[count + 1];
084        ret[count] = s.substring(prev);
085        return ret;
086      } else {
087        return new String[count];
088      }
089    } else if (next - prev > 0) {
090      String[] ret = parseNames(s, next + 1, end, count + 1);
091      ret[count] = s.substring(prev, next);
092      return ret;
093    } else {
094      return parseNames(s, next + 1, end, count);
095    }
096  }
097
098  private Path(boolean dir, String[] names) {
099    this.dir = dir;
100    this.names = names;
101  }
102
103  public Iterator<String> iterator() {
104    return new BaseIterator<String>() {
105      int index = 0;
106      public boolean hasNext() {
107        return index < names.length;
108      }
109      public String next() {
110        if (index < names.length) {
111          return names[index++];
112        } else {
113          throw new NoSuchElementException();
114        }
115      }
116    };
117  }
118
119  public int getSize() {
120    return names.length;
121  }
122
123  public boolean isDir() {
124    return dir;
125  }
126
127  public String getName() {
128    return names.length > 0 ? names[names.length - 1] : "";
129  }
130
131  public boolean isChildOf(Path parent) {
132    if (parent.dir) {
133      int length = parent.names.length;
134      if (names.length == length + 1) {
135        for (int i = 0;i < length;i++) {
136          if (names[i].equals(parent.names[i])) {
137            return false;
138          }
139        }
140        return true;
141      }
142    }
143    return false;
144  }
145
146  @Override
147  public boolean equals(Object o) {
148    if (o == this) {
149      return true;
150    }
151    if (o instanceof Path) {
152      Path that = (Path)o;
153      int length = that.names.length;
154      if (names.length == length) {
155        for (int i = 0;i < length;i++) {
156          if (!names[i].equals(that.names[i])) {
157            return false;
158          }
159        }
160        return true;
161      }
162    }
163    return false;
164  }
165
166  @Override
167  public int hashCode() {
168    int hashCode = dir ? 1 : 0;
169    for (int i = names.length - 1;i >= 0;i--) {
170      hashCode = hashCode * 41 + names[i].hashCode();
171    }
172    return hashCode;
173  }
174
175  /**
176   * Returns the canonical path value.
177   *
178   * @return the value
179   */
180  public String getValue() {
181    if (value == null) {
182      StringBuilder sb = new StringBuilder(8 * names.length);
183      for (String name : names) {
184        sb.append('/').append(name);
185      }
186      if (dir) {
187        sb.append('/');
188      }
189      value = sb.toString();
190    }
191    return value;
192  }
193
194  public String toString() {
195    return "Path[value=" + getValue() + "]";
196  }
197}