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}