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