package com.bradmcevoy.common;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/** Immutable
*/
public class Path implements Serializable {
private static final long serialVersionUID = -8411900835514833454L;
private final Path parent;
private final String name;
public static final Path root = new Path();
private int hash;
private final int length;
public static final LengthComparator LENGTH_COMPARATOR = new LengthComparator();
public static Path path( Path parent, String path ) {
if( path == null )
throw new NullPointerException( "The path parameter may not be null" );
return split( parent, path );
}
public static Path path( String path ) {
if( path == null || path.length() == 0 ) return root;
return split( null, path );
}
private static Path split( Path startFrom, String s ) {
Path parent = startFrom;
StringBuilder sb = null;
for( int i = 0; i < s.length(); i++ ) {
char c = s.charAt( i );
switch( c ) {
case '/':
if( sb == null ) {
parent = root;
} else {
if( sb.length() > 0 ) {
String ss = sb.toString();
if( parent != null ) parent = parent.child( ss );
else parent = new Path( null, ss );
}
sb = null;
}
break;
// case ' ':
// // ignore
// break;
default:
if( sb == null ) sb = new StringBuilder();
sb.append( c );
}
}
if( sb != null ) {
if( sb.length() > 0 ) {
String ss = sb.toString();
if( parent != null ) parent = parent.child( ss );
else parent = new Path( null, ss );
}
}
return parent;
}
private Path() {
this.parent = null;
this.name = null;
length = 0;
}
private Path( Path parent, String name ) {
if( name == null )
throw new IllegalArgumentException( "name may not be null" );
this.parent = parent;
this.name = name;
if( this.parent != null ) {
this.length = this.parent.length + 1;
} else {
this.length = 1;
}
}
public int getLength() {
return length;
}
public String[] getParts() {
String[] arr = new String[length];
Path p = this;
int i = length;
while( i > 0 ) {
arr[--i] = p.getName();
p = p.getParent();
}
return arr;
}
/**
*
* @return - the first part of the path. ie a/b/c returns a
*/
public String getFirst() {
Path p = this;
while( p.getParent() != null ) {
Path next = p.getParent();
if( next.getName() == null ) return p.getName();
p = next;
}
if( p != null ) return p.getName();
return null;
}
public List<String> getAfterFirst() {
List<String> afterFirst = new ArrayList<String>();
Path p = this;
while( p != null && p.getParent() != null && !p.getParent().isRoot() ) {
afterFirst.add( 0, p.getName() );
p = p.getParent();
if( p == null ) break;
}
return afterFirst;
}
public Path getStripFirst() {
return stripFirst( this );
}
Path stripFirst( Path p ) {
Path pParent = p.getParent();
if( pParent == null || pParent.isRoot() ) return root;
pParent = stripFirst( pParent );
return new Path( pParent, p.getName() );
}
public String getName() {
return name;
}
public Path getParent() {
return parent;
}
public boolean isRoot() {
return ( ( parent == null ) && ( name == null ) );
}
public String toPath() {
if( isRoot() ) return "";
if( parent == null ) return name;
return parent.toString() + '/' + name;
}
@Override
public String toString() {
return toPath();
}
public String toString( String delimiter ) {
if( parent == null ) return "";
if( parent == null ) return name;
return parent.toString( delimiter ) + delimiter + name;
}
public static Path root() {
return root;
}
@Override
public int hashCode() {
if( hash == 0 ) {
if( parent == null ) {
hash = 158;
} else {
hash = parent.hashCode() ^ name.hashCode();
}
}
return hash;
}
@Override
public boolean equals( Object obj ) {
if( obj == null ) return false;
if( obj instanceof Path ) {
Path p2 = (Path) obj;
if( this.isRoot() ) {
return p2.isRoot();
} else {
if( parentEquals( this, p2 ) ) {
return this.name.equals( p2.name );
} else {
return false;
}
}
} else {
return false;
}
}
private static boolean parentEquals( Path p1, Path p2 ) {
if( p2.parent == null ) {
return p1.parent == null;
} else {
return p2.parent.equals( p1.parent );
}
}
public Path child( String name ) {
Path ch = new Path( this, name );
return ch;
}
public boolean isRelative() {
if( parent == null ) {
return !isRoot();
} else {
return parent.isRelative();
}
}
public static class LengthComparator implements Comparator<Path> {
public int compare( Path o1, Path o2 ) {
Integer i1 = o1.getLength();
Integer i2 = o2.getLength();
return i1.compareTo( i2 );
}
}
}