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 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    }