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.io.File;
025import java.util.Iterator;
026import java.util.NoSuchElementException;
027
028public abstract class Path implements Iterable<String> {
029
030  /** . */
031  private static final String[] EMPTY_STRING = new String[0];
032
033  /** . */
034  public static final Absolute ROOT = new Absolute(true, EMPTY_STRING);
035
036  /** . */
037  public static final Relative EMPTY = new Relative(true, EMPTY_STRING);
038
039  private static String[] path(java.io.File file, int size) {
040    String[] ret;
041    java.io.File parent = file.getParentFile();
042    if (parent != null && parent.isDirectory()) {
043      ret = path(parent, 1 + size);
044    } else {
045      ret = new String[1 + size];
046    }
047    ret[ret.length - size - 1] = file.getName();
048    return ret;
049  }
050
051  public static Path get(java.io.File file) {
052    String[] names = path(file, 0);
053    if (file.isAbsolute()) {
054      return new Absolute(file.isDirectory(), names);
055    } else {
056      return new Relative(file.isDirectory(), names);
057    }
058  }
059
060  public static Path get(String s) {
061    if (s.length() == 0) {
062      return EMPTY;
063    }
064
065    //
066    int start;
067    boolean absolute;
068    if (s.charAt(0) != '/') {
069      start = 0;
070      absolute = false;
071    } else {
072      if (s.length() == 1) {
073        return ROOT;
074      } else {
075        start = 1;
076        absolute = true;
077      }
078    }
079
080    //
081    boolean dir;
082    int end;
083    if (s.charAt(s.length() - 1) == '/' || s.charAt(s.length() - 1) == File.separatorChar) {
084      dir = true;
085      end = s.length() - 1;
086    } else {
087      dir = false;
088      end = s.length();
089    }
090
091    //
092    String[] names = parseNames(s, start, end, 0);
093
094    //
095    return absolute ? new Absolute(dir, names) : new Relative(dir, names);
096  }
097
098  private static String[] parseNames(final String s, final int prev, int end, final int count) {
099    int next = s.indexOf('/', prev);
100    if (next == -1) next = s.indexOf(File.separatorChar, prev);
101
102    if (next == -1 || next > end) {
103      if (prev < end) {
104        String[] ret = new String[count + 1];
105        ret[count] = s.substring(prev);
106        return ret;
107      } else {
108        return new String[count];
109      }
110    } else if (next - prev > 0) {
111      String[] ret = parseNames(s, next + 1, end, count + 1);
112      ret[count] = s.substring(prev, next);
113      return ret;
114    } else {
115      return parseNames(s, next + 1, end, count);
116    }
117  }
118
119  /** . */
120  protected final boolean dir;
121
122  /** . */
123  protected final String[] names;
124
125  /** . */
126  private String value;
127
128  private Path(boolean dir, String[] names) {
129    this.dir = dir;
130    this.names = names;
131  }
132
133  public Iterator<String> iterator() {
134    return new BaseIterator<String>() {
135      int index = 0;
136      public boolean hasNext() {
137        return index < names.length;
138      }
139      public String next() {
140        if (index < names.length) {
141          return names[index++];
142        } else {
143          throw new NoSuchElementException();
144        }
145      }
146    };
147  }
148
149  public Path append(String name, boolean dir) {
150    int length = names.length;
151    String[] names = new String[length + 1];
152    System.arraycopy(names, 0, names, 0, length);
153    names[length] = name;
154    return create(dir, names);
155  }
156
157  protected abstract Path create(boolean dir, String[] names);
158
159  public abstract boolean isAbsolute();
160
161  public abstract Absolute absolute();
162
163  public int getSize() {
164    return names.length;
165  }
166
167  public boolean isDir() {
168    return dir;
169  }
170
171  public String getName() {
172    return names.length > 0 ? names[names.length - 1] : "";
173  }
174
175  public String nameAt(int index) throws IndexOutOfBoundsException {
176    if (index < 0 || index >= names.length) {
177      throw new IndexOutOfBoundsException("Index out of bounds [0" + (names.length - 1) + "]" + index);
178    } else {
179      return names[index];
180    }
181  }
182
183  public boolean isChildOf(Path parent) {
184    if (parent.dir) {
185      int length = parent.names.length;
186      if (names.length == length + 1) {
187        for (int i = 0;i < length;i++) {
188          if (names[i].equals(parent.names[i])) {
189            return false;
190          }
191        }
192        return true;
193      }
194    }
195    return false;
196  }
197
198  @Override
199  public boolean equals(Object o) {
200    if (o == this) {
201      return true;
202    }
203    if (o instanceof Path) {
204      Path that = (Path)o;
205      int length = that.names.length;
206      if (names.length == length) {
207        for (int i = 0;i < length;i++) {
208          if (!names[i].equals(that.names[i])) {
209            return false;
210          }
211        }
212        return true;
213      }
214    }
215    return false;
216  }
217
218  @Override
219  public int hashCode() {
220    int hashCode = dir ? 1 : 0;
221    for (int i = names.length - 1;i >= 0;i--) {
222      hashCode = hashCode * 41 + names[i].hashCode();
223    }
224    return hashCode;
225  }
226
227  /**
228   * Returns the canonical path value.
229   *
230   * @return the value
231   */
232  public String getValue() {
233    if (value == null) {
234      if (names.length == 0) {
235        if (isAbsolute()) {
236          return "/";
237        } else {
238          return "";
239        }
240      } else {
241        StringBuilder sb = new StringBuilder(8 * names.length);
242        if (isAbsolute()) {
243          sb.append('/');
244        }
245        for (int i = 0;i < names.length;i++) {
246          if (i > 0) {
247            sb.append('/');
248          }
249          sb.append(names[i]);
250        }
251        if (dir) {
252          sb.append('/');
253        }
254        value = sb.toString();
255      }
256    }
257    return value;
258  }
259
260  public String toString() {
261    return "Path[value=" + getValue() + "]";
262  }
263
264  public static class Absolute extends Path {
265
266    private Absolute(boolean dir, String[] names) {
267      super(dir, names);
268    }
269
270    @Override
271    public Absolute append(String name, boolean dir) {
272      return (Absolute)super.append(name, dir);
273    }
274
275    @Override
276    protected Absolute create(boolean dir, String[] names) {
277      return new Absolute(dir, names);
278    }
279
280    @Override
281    public Absolute absolute() {
282      return this;
283    }
284
285    @Override
286    public boolean isAbsolute() {
287      return true;
288    }
289  }
290
291  public static class Relative extends Path {
292
293    private Relative(boolean dir, String[] names) {
294      super(dir, names);
295    }
296
297    @Override
298    public Relative append(String name, boolean dir) {
299      return (Relative)super.append(name, dir);
300    }
301
302    @Override
303    protected Relative create(boolean dir, String[] names) {
304      return new Relative(dir, names);
305    }
306
307    @Override
308    public Absolute absolute() {
309      return new Absolute(dir, names);
310    }
311
312    @Override
313    public boolean isAbsolute() {
314      return false;
315    }
316  }
317}