1 package com.bradmcevoy.common;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.Comparator;
6 import java.util.List;
7
8
9
10 public class Path implements Serializable {
11
12 private static final long serialVersionUID = -8411900835514833454L;
13 private final Path parent;
14 private final String name;
15 public static final Path root = new Path();
16 private int hash;
17 private final int length;
18 public static final LengthComparator LENGTH_COMPARATOR = new LengthComparator();
19
20
21 public static Path path( Path parent, String path ) {
22 if( path == null )
23 throw new NullPointerException( "The path parameter may not be null" );
24 return split( parent, path );
25 }
26
27 public static Path path( String path ) {
28 if( path == null || path.length() == 0 ) return root;
29 return split( null, path );
30 }
31
32 private static Path split( Path startFrom, String s ) {
33 Path parent = startFrom;
34 StringBuilder sb = null;
35 for( int i = 0; i < s.length(); i++ ) {
36 char c = s.charAt( i );
37 switch( c ) {
38 case '/':
39 if( sb == null ) {
40 parent = root;
41 } else {
42 if( sb.length() > 0 ) {
43 String ss = sb.toString();
44 if( parent != null ) parent = parent.child( ss );
45 else parent = new Path( null, ss );
46 }
47 sb = null;
48 }
49 break;
50
51
52
53 default:
54 if( sb == null ) sb = new StringBuilder();
55 sb.append( c );
56 }
57 }
58 if( sb != null ) {
59 if( sb.length() > 0 ) {
60 String ss = sb.toString();
61 if( parent != null ) parent = parent.child( ss );
62 else parent = new Path( null, ss );
63 }
64 }
65
66 return parent;
67 }
68
69 private Path() {
70 this.parent = null;
71 this.name = null;
72 length = 0;
73 }
74
75 private Path( Path parent, String name ) {
76 if( name == null )
77 throw new IllegalArgumentException( "name may not be null" );
78 this.parent = parent;
79 this.name = name;
80 if( this.parent != null ) {
81 this.length = this.parent.length + 1;
82 } else {
83 this.length = 1;
84 }
85 }
86
87
88 public int getLength() {
89 return length;
90 }
91
92 public String[] getParts() {
93 String[] arr = new String[length];
94 Path p = this;
95 int i = length;
96 while( i > 0 ) {
97 arr[--i] = p.getName();
98 p = p.getParent();
99 }
100 return arr;
101 }
102
103
104
105
106
107 public String getFirst() {
108 Path p = this;
109 while( p.getParent() != null ) {
110 Path next = p.getParent();
111 if( next.getName() == null ) return p.getName();
112 p = next;
113 }
114 if( p != null ) return p.getName();
115 return null;
116 }
117
118 public List<String> getAfterFirst() {
119 List<String> afterFirst = new ArrayList<String>();
120 Path p = this;
121 while( p != null && p.getParent() != null && !p.getParent().isRoot() ) {
122 afterFirst.add( 0, p.getName() );
123 p = p.getParent();
124 if( p == null ) break;
125 }
126 return afterFirst;
127 }
128
129 public Path getStripFirst() {
130 return stripFirst( this );
131 }
132
133 Path stripFirst( Path p ) {
134 Path pParent = p.getParent();
135 if( pParent == null || pParent.isRoot() ) return root;
136 pParent = stripFirst( pParent );
137 return new Path( pParent, p.getName() );
138 }
139
140 public String getName() {
141 return name;
142 }
143
144 public Path getParent() {
145 return parent;
146 }
147
148 public boolean isRoot() {
149 return ( ( parent == null ) && ( name == null ) );
150 }
151
152 public String toPath() {
153 if( isRoot() ) return "";
154 if( parent == null ) return name;
155 return parent.toString() + '/' + name;
156 }
157
158 @Override
159 public String toString() {
160 return toPath();
161 }
162
163 public String toString( String delimiter ) {
164 if( parent == null ) return "";
165 if( parent == null ) return name;
166 return parent.toString( delimiter ) + delimiter + name;
167 }
168
169 public static Path root() {
170 return root;
171 }
172
173 @Override
174 public int hashCode() {
175 if( hash == 0 ) {
176 if( parent == null ) {
177 hash = 158;
178 } else {
179 hash = parent.hashCode() ^ name.hashCode();
180 }
181 }
182 return hash;
183 }
184
185 @Override
186 public boolean equals( Object obj ) {
187 if( obj == null ) return false;
188 if( obj instanceof Path ) {
189 Path p2 = (Path) obj;
190 if( this.isRoot() ) {
191 return p2.isRoot();
192 } else {
193 if( parentEquals( this, p2 ) ) {
194 return this.name.equals( p2.name );
195 } else {
196 return false;
197 }
198 }
199 } else {
200 return false;
201 }
202 }
203
204 private static boolean parentEquals( Path p1, Path p2 ) {
205 if( p2.parent == null ) {
206 return p1.parent == null;
207 } else {
208 return p2.parent.equals( p1.parent );
209 }
210 }
211
212 public Path child( String name ) {
213 Path ch = new Path( this, name );
214 return ch;
215 }
216
217 public boolean isRelative() {
218 if( parent == null ) {
219 return !isRoot();
220 } else {
221 return parent.isRelative();
222 }
223 }
224
225
226
227
228
229
230
231
232
233 public Path add(Path p) {
234 Path x = this;
235 for(String s : p.getParts()) {
236 x = x.child(s);
237 }
238 return x;
239 }
240
241 public static class LengthComparator implements Comparator<Path> {
242
243 public int compare( Path o1, Path o2 ) {
244 Integer i1 = o1.getLength();
245 Integer i2 = o2.getLength();
246 return i1.compareTo( i2 );
247 }
248 }
249 }