View Javadoc
1   package org.argeo.jcr.fs;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.net.URI;
6   import java.net.URISyntaxException;
7   import java.nio.file.FileSystem;
8   import java.nio.file.LinkOption;
9   import java.nio.file.Path;
10  import java.nio.file.WatchEvent.Kind;
11  import java.nio.file.WatchEvent.Modifier;
12  import java.nio.file.WatchKey;
13  import java.nio.file.WatchService;
14  import java.util.Arrays;
15  import java.util.Iterator;
16  import java.util.NoSuchElementException;
17  
18  import javax.jcr.Node;
19  import javax.jcr.RepositoryException;
20  
21  /** A {@link Path} which contains a reference to a JCR {@link Node}. */
22  public class JcrPath implements Path {
23  	final static String separator = "/";
24  	final static char separatorChar = '/';
25  
26  	private final JcrFileSystem fs;
27  	/** null for non absolute paths */
28  	private final WorkspaceFileStore fileStore;
29  	private final String[] path;// null means root
30  	private final boolean absolute;
31  
32  	// optim
33  	private final int hashCode;
34  
35  	public JcrPath(JcrFileSystem filesSystem, String path) {
36  		this.fs = filesSystem;
37  		if (path == null)
38  			throw new JcrFsException("Path cannot be null");
39  		if (path.equals(separator)) {// root
40  			this.path = null;
41  			this.absolute = true;
42  			this.hashCode = 0;
43  			this.fileStore = fs.getBaseFileStore();
44  			return;
45  		} else if (path.equals("")) {// empty path
46  			this.path = new String[] { "" };
47  			this.absolute = false;
48  			this.fileStore = null;
49  			this.hashCode = "".hashCode();
50  			return;
51  		}
52  
53  		if (path.equals("~")) {// home
54  			path = filesSystem.getUserHomePath();
55  			if (path == null)
56  				throw new JcrFsException("No home directory available");
57  		}
58  
59  		this.absolute = path.charAt(0) == separatorChar ? true : false;
60  
61  		this.fileStore = absolute ? fs.getFileStore(path) : null;
62  
63  		String trimmedPath = path.substring(absolute ? 1 : 0,
64  				path.charAt(path.length() - 1) == separatorChar ? path.length() - 1 : path.length());
65  		this.path = trimmedPath.split(separator);
66  		for (int i = 0; i < this.path.length; i++) {
67  			this.path[i] = Text.unescapeIllegalJcrChars(this.path[i]);
68  		}
69  		this.hashCode = this.path[this.path.length - 1].hashCode();
70  		assert !(absolute && fileStore == null);
71  	}
72  
73  	public JcrPath(JcrFileSystem filesSystem, Node node) throws RepositoryException {
74  		this(filesSystem, filesSystem.getFileStore(node).toFsPath(node));
75  	}
76  
77  	/** Internal optimisation */
78  	private JcrPath(JcrFileSystem filesSystem, WorkspaceFileStore fileStore, String[] path, boolean absolute) {
79  		this.fs = filesSystem;
80  		this.path = path;
81  		this.absolute = path == null ? true : absolute;
82  		if (this.absolute && fileStore == null)
83  			throw new IllegalArgumentException("Absolute path requires a file store");
84  		if (!this.absolute && fileStore != null)
85  			throw new IllegalArgumentException("A file store should not be provided for a relative path");
86  		this.fileStore = fileStore;
87  		this.hashCode = path == null ? 0 : path[path.length - 1].hashCode();
88  		assert !(absolute && fileStore == null);
89  	}
90  
91  	@Override
92  	public FileSystem getFileSystem() {
93  		return fs;
94  	}
95  
96  	@Override
97  	public boolean isAbsolute() {
98  		return absolute;
99  	}
100 
101 	@Override
102 	public Path getRoot() {
103 		if (path == null)
104 			return this;
105 		return new JcrPath(fs, separator);
106 	}
107 
108 	@Override
109 	public String toString() {
110 		return toFsPath(path);
111 	}
112 
113 	private String toFsPath(String[] path) {
114 		if (path == null)
115 			return "/";
116 		StringBuilder sb = new StringBuilder();
117 		if (isAbsolute())
118 			sb.append('/');
119 		for (int i = 0; i < path.length; i++) {
120 			if (i != 0)
121 				sb.append('/');
122 			sb.append(path[i]);
123 		}
124 		return sb.toString();
125 	}
126 
127 //	@Deprecated
128 //	private String toJcrPath() {
129 //		return toJcrPath(path);
130 //	}
131 //
132 //	@Deprecated
133 //	private String toJcrPath(String[] path) {
134 //		if (path == null)
135 //			return "/";
136 //		StringBuilder sb = new StringBuilder();
137 //		if (isAbsolute())
138 //			sb.append('/');
139 //		for (int i = 0; i < path.length; i++) {
140 //			if (i != 0)
141 //				sb.append('/');
142 //			sb.append(Text.escapeIllegalJcrChars(path[i]));
143 //		}
144 //		return sb.toString();
145 //	}
146 
147 	@Override
148 	public Path getFileName() {
149 		if (path == null)
150 			return null;
151 		return new JcrPath(fs, path[path.length - 1]);
152 	}
153 
154 	@Override
155 	public Path getParent() {
156 		if (path == null)
157 			return null;
158 		if (path.length == 1)// root
159 			return new JcrPath(fs, separator);
160 		String[] parentPath = Arrays.copyOfRange(path, 0, path.length - 1);
161 		if (!absolute)
162 			return new JcrPath(fs, null, parentPath, absolute);
163 		else
164 			return new JcrPath(fs, toFsPath(parentPath));
165 	}
166 
167 	@Override
168 	public int getNameCount() {
169 		if (path == null)
170 			return 0;
171 		return path.length;
172 	}
173 
174 	@Override
175 	public Path getName(int index) {
176 		if (path == null)
177 			return null;
178 		return new JcrPath(fs, path[index]);
179 	}
180 
181 	@Override
182 	public Path subpath(int beginIndex, int endIndex) {
183 		if (path == null)
184 			return null;
185 		String[] parentPath = Arrays.copyOfRange(path, beginIndex, endIndex);
186 		return new JcrPath(fs, null, parentPath, false);
187 	}
188 
189 	@Override
190 	public boolean startsWith(Path other) {
191 		return toString().startsWith(other.toString());
192 	}
193 
194 	@Override
195 	public boolean startsWith(String other) {
196 		return toString().startsWith(other);
197 	}
198 
199 	@Override
200 	public boolean endsWith(Path other) {
201 		return toString().endsWith(other.toString());
202 	}
203 
204 	@Override
205 	public boolean endsWith(String other) {
206 		return toString().endsWith(other);
207 	}
208 
209 	@Override
210 	public Path normalize() {
211 		// always normalized
212 		return this;
213 	}
214 
215 	@Override
216 	public Path resolve(Path other) {
217 		JcrPath otherPath = (JcrPath) other;
218 		if (otherPath.isAbsolute())
219 			return other;
220 		String[] newPath;
221 		if (path == null) {
222 			newPath = new String[otherPath.path.length];
223 			System.arraycopy(otherPath.path, 0, newPath, 0, otherPath.path.length);
224 		} else {
225 			newPath = new String[path.length + otherPath.path.length];
226 			System.arraycopy(path, 0, newPath, 0, path.length);
227 			System.arraycopy(otherPath.path, 0, newPath, path.length, otherPath.path.length);
228 		}
229 		if (!absolute)
230 			return new JcrPath(fs, null, newPath, absolute);
231 		else {
232 			return new JcrPath(fs, toFsPath(newPath));
233 		}
234 	}
235 
236 	@Override
237 	public final Path resolve(String other) {
238 		return resolve(getFileSystem().getPath(other));
239 	}
240 
241 	@Override
242 	public final Path resolveSibling(Path other) {
243 		if (other == null)
244 			throw new NullPointerException();
245 		Path parent = getParent();
246 		return (parent == null) ? other : parent.resolve(other);
247 	}
248 
249 	@Override
250 	public final Path resolveSibling(String other) {
251 		return resolveSibling(getFileSystem().getPath(other));
252 	}
253 
254 	@Override
255 	public final Iterator<Path> iterator() {
256 		return new Iterator<Path>() {
257 			private int i = 0;
258 
259 			@Override
260 			public boolean hasNext() {
261 				return (i < getNameCount());
262 			}
263 
264 			@Override
265 			public Path next() {
266 				if (i < getNameCount()) {
267 					Path result = getName(i);
268 					i++;
269 					return result;
270 				} else {
271 					throw new NoSuchElementException();
272 				}
273 			}
274 
275 			@Override
276 			public void remove() {
277 				throw new UnsupportedOperationException();
278 			}
279 		};
280 	}
281 
282 	@Override
283 	public Path relativize(Path other) {
284 		if (equals(other))
285 			return new JcrPath(fs, "");
286 		if (other.startsWith(this)) {
287 			String p1 = toString();
288 			String p2 = other.toString();
289 			String relative = p2.substring(p1.length(), p2.length());
290 			if (relative.charAt(0) == '/')
291 				relative = relative.substring(1);
292 			return new JcrPath(fs, relative);
293 		}
294 		throw new IllegalArgumentException(other + " cannot be relativized against " + this);
295 	}
296 
297 	@Override
298 	public URI toUri() {
299 		try {
300 			return new URI(fs.provider().getScheme(), toString(), null);
301 		} catch (URISyntaxException e) {
302 			throw new JcrFsException("Cannot create URI for " + toString(), e);
303 		}
304 	}
305 
306 	@Override
307 	public Path toAbsolutePath() {
308 		if (isAbsolute())
309 			return this;
310 		return new JcrPath(fs, fileStore, path, true);
311 	}
312 
313 	@Override
314 	public Path toRealPath(LinkOption... options) throws IOException {
315 		return this;
316 	}
317 
318 	@Override
319 	public File toFile() {
320 		throw new UnsupportedOperationException();
321 	}
322 
323 	@Override
324 	public WatchKey register(WatchService watcher, Kind<?>[] events, Modifier... modifiers) throws IOException {
325 		// TODO Auto-generated method stub
326 		return null;
327 	}
328 
329 	@Override
330 	public WatchKey register(WatchService watcher, Kind<?>... events) throws IOException {
331 		// TODO Auto-generated method stub
332 		return null;
333 	}
334 
335 	@Override
336 	public int compareTo(Path other) {
337 		return toString().compareTo(other.toString());
338 	}
339 
340 	public Node getNode() throws RepositoryException {
341 		if (!isAbsolute())// TODO default dir
342 			throw new JcrFsException("Cannot get a JCR node from a relative path");
343 		assert fileStore != null;
344 		return fileStore.toNode(path);
345 //		String pathStr = toJcrPath();
346 //		Session session = fs.getSession();
347 //		// TODO synchronize on the session ?
348 //		if (!session.itemExists(pathStr))
349 //			return null;
350 //		return session.getNode(pathStr);
351 	}
352 
353 	@Override
354 	public boolean equals(Object obj) {
355 		if (!(obj instanceof JcrPath))
356 			return false;
357 		JcrPath other = (JcrPath) obj;
358 
359 		if (path == null) {// root
360 			if (other.path == null)// root
361 				return true;
362 			else
363 				return false;
364 		} else {
365 			if (other.path == null)// root
366 				return false;
367 		}
368 		// non root
369 		if (path.length != other.path.length)
370 			return false;
371 		for (int i = 0; i < path.length; i++) {
372 			if (!path[i].equals(other.path[i]))
373 				return false;
374 		}
375 		return true;
376 	}
377 
378 	@Override
379 	public int hashCode() {
380 		return hashCode;
381 	}
382 
383 	@Override
384 	protected Object clone() throws CloneNotSupportedException {
385 		return new JcrPath(fs, toString());
386 	}
387 
388 	@Override
389 	protected void finalize() throws Throwable {
390 		Arrays.fill(path, null);
391 	}
392 
393 }