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