View Javadoc

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   /** Immutable
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  //                case ' ':
51  //                    // ignore
52  //                    break;
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      * @return - the first part of the path. ie a/b/c returns a
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      * Add the path components of the given path to this one.
227      *
228      * Eg "a/b/c" + "/d/e/f" = "a/b/c/d/e/f"
229      *
230      * @param p
231      * @return
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 }