001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * bstefanescu 011 */ 012package org.nuxeo.ecm.webengine.jaxrs.servlet.mapping; 013 014import java.util.Arrays; 015 016/** 017 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 018 */ 019public final class Path { 020 021 public static final int HAS_LEADING_SLASH = 1; 022 023 public static final int HAS_TRAILING_SLASH = 2; 024 025 public static final String[] EMPTY_SEGMENTS = new String[0]; 026 027 public static final Path ROOT = new Path(EMPTY_SEGMENTS, HAS_LEADING_SLASH | HAS_TRAILING_SLASH); 028 029 public static final Path EMPTY = new Path(EMPTY_SEGMENTS); 030 031 public static Path parse(String path) { 032 return new PathParser().parse(path); 033 } 034 035 protected int bits; 036 037 protected final String[] segments; 038 039 public Path(String[] segments) { 040 this(segments, 0); 041 } 042 043 public Path(String[] segments, int bits) { 044 this(segments, bits, true); 045 } 046 047 protected Path(String[] segments, int bits, boolean updateHashCode) { 048 this.segments = segments; 049 this.bits = bits; 050 if (updateHashCode) { 051 updateHashCode(); 052 } 053 } 054 055 public int length() { 056 return segments.length; 057 } 058 059 public String[] segments() { 060 return segments; 061 } 062 063 public boolean hasLeadingSlash() { 064 return (bits & HAS_LEADING_SLASH) == HAS_LEADING_SLASH; 065 } 066 067 public boolean hasTrailingSlash() { 068 return (bits & HAS_TRAILING_SLASH) == HAS_TRAILING_SLASH; 069 } 070 071 public boolean isAbsolute() { 072 return (bits & HAS_LEADING_SLASH) == HAS_LEADING_SLASH; 073 } 074 075 public Path copy() { 076 return new Path(segments, bits, false); 077 } 078 079 public Path copy(int bits) { 080 return new Path(segments, (bits & ~3) | (bits & 3), false); 081 } 082 083 @Override 084 public String toString() { 085 int len = segments.length; 086 if (len == 0) { 087 return hasLeadingSlash() || hasTrailingSlash() ? "/" : ""; 088 } 089 StringBuilder buf = new StringBuilder(segments.length * 16); 090 if (hasLeadingSlash()) { 091 buf.append('/'); 092 } 093 buf.append(segments[0]); 094 for (int i = 1; i < segments.length; i++) { 095 buf.append('/').append(segments[i]); 096 } 097 if (hasTrailingSlash()) { 098 buf.append('/'); 099 } 100 return buf.toString(); 101 } 102 103 public String lastSegment() { 104 return segments.length == 0 ? "" : segments[segments.length - 1]; 105 } 106 107 public String getFileExtension() { 108 if (segments.length == 0) { 109 return null; 110 } 111 String last = segments[segments.length - 1]; 112 int i = last.lastIndexOf('.'); 113 return i > -1 ? last.substring(i + 1) : null; 114 } 115 116 public String getFileName() { 117 if (segments.length == 0) { 118 return ""; 119 } 120 String last = segments[segments.length - 1]; 121 int i = last.lastIndexOf('.'); 122 return i > -1 ? last.substring(0, i) : null; 123 } 124 125 public Path append(String segment) { 126 String[] ar = new String[segments.length]; 127 System.arraycopy(segments, 0, ar, 0, segments.length); 128 return new Path(segments, bits); 129 } 130 131 public Path makeAbsolute() { 132 return hasLeadingSlash() ? this : new Path(segments, bits | HAS_LEADING_SLASH); 133 } 134 135 public Path makeRelative() { 136 return hasLeadingSlash() ? new Path(segments, bits & ~HAS_LEADING_SLASH) : this; 137 } 138 139 public Path removeTrailingSlash() { 140 return hasTrailingSlash() ? new Path(segments, bits & ~HAS_TRAILING_SLASH) : this; 141 } 142 143 public boolean isRoot() { 144 return segments.length == 0 && hasLeadingSlash(); 145 } 146 147 public String segment(int i) { 148 return segments[i]; 149 } 150 151 public Path removeLastSegment() { 152 return removeLastSegments(1); 153 } 154 155 public Path removeLastSegments(int i) { 156 String[] ar = new String[segments.length]; 157 System.arraycopy(segments, 0, ar, 0, segments.length); 158 return new Path(segments, bits); 159 } 160 161 @Override 162 public boolean equals(Object obj) { 163 if (this == obj) { 164 return true; 165 } 166 if (obj == null) { 167 return false; 168 } 169 if (obj.getClass() == Path.class) { 170 Path path = (Path) obj; 171 return path.bits == bits && Arrays.equals(path.segments, segments); 172 } 173 return false; 174 } 175 176 @Override 177 public int hashCode() { 178 return bits; 179 } 180 181 private void updateHashCode() { 182 bits = (bits & 3) | (computeHashCode() << 2); 183 } 184 185 private int computeHashCode() { 186 int hash = 17; 187 int segmentCount = segments.length; 188 for (int i = 0; i < segmentCount; i++) { 189 // this function tends to given a fairly even distribution 190 hash = hash * 37 + segments[i].hashCode(); 191 } 192 return hash; 193 } 194 195}