/* * Copyright (C) 2011 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.web.controller.router; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.gatein.common.io.UndeclaredIOException; /** * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */ class Path { static Path parse(String path) { try { Data data = new Data(path); return new Path(data, 0); } catch (IOException e) { throw new UndeclaredIOException(e); } } /** Constant. */ public static final Path SLASH = Path.parse("/"); private static final class Data { private int hex(char c) { if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'A' && c <= 'F') { return c + 10 - 'A'; } else if (c >= 'a' && c <= 'f') { return c + 10 - 'a'; } else { throw new IllegalArgumentException("Invalid hex code in " + rawValue); } } /** . */ private final String rawValue; /** . */ private final String value; /** . */ private final int[] mapping; private Data(String rawValue) throws IOException, IllegalArgumentException { this.rawValue = rawValue; // int len = rawValue.length(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int[] mapping = new int[len]; int count = 0; // int i = 0; while (i < len) { char c = rawValue.charAt(i); mapping[count++] = i; // if (PercentEncoding.PATH_SEGMENT.accept(c)) { baos.write((int) c); i++; } else if (c == '%') { if (i + 2 >= len) { throw new IllegalArgumentException("Invalid percent escape in " + rawValue); } int h = (hex(rawValue.charAt(i + 1)) << 4) + hex(rawValue.charAt(i + 2)); baos.write(h); i += 3; // Compute the number of bytes to read for this char int size = 0; for (int j = h; (j & 0x80) != 0; j = j << 1) { size++; } if (size == 0) { size = 1; } else if (size > 6) { throw new IllegalArgumentException("Invalid percent escape in " + rawValue); } // Compute the offset we need to read those bytes int to = i + (size - 1) * 3; if (to > len) { throw new IllegalArgumentException("Invalid percent escape in " + rawValue); } // Read what we need while (i < to) { if (rawValue.charAt(i) != '%') { throw new IllegalArgumentException("Invalid percent escape in " + rawValue); } h = (hex(rawValue.charAt(i + 1)) << 4) + hex(rawValue.charAt(i + 2)); baos.write(h); i += 3; } } else if (c == '/') { baos.write('/'); i++; } else { throw new IllegalArgumentException("Unsupported char value in path " + (int) c + " / " + c); } } // this.value = baos.toString("UTF-8"); this.mapping = mapping; } int getRawStart(int index) { if (index < 0) { throw new IndexOutOfBoundsException("No negative index accepted"); } if (index >= value.length()) { throw new IndexOutOfBoundsException("Index can't be greater than length"); } return mapping[index]; } int getRawEnd(int index) { if (index < 0) { throw new IndexOutOfBoundsException("No negative index accepted"); } if (index >= value.length()) { throw new IndexOutOfBoundsException("Index can't be greater than length"); } index++; if (index == value.length()) { return rawValue.length(); } else { return mapping[index]; } } } /** . */ private final Data data; /** . */ private final int offset; /** . */ private final String value; private Path(Data data, int offset) { this.data = data; this.offset = offset; this.value = data.value.substring(offset); } String getValue() { return value; } int getRawStart(int index) { return data.getRawStart(innerIndex(index)) - data.getRawStart(offset); } int getRawEnd(int index) { return data.getRawEnd(innerIndex(index)) - data.getRawStart(offset); } int getRawLength(int index) { return getRawEnd(index) - getRawStart(index); } char charAt(int pos) { return value.charAt(pos); } int length() { return value.length(); } int indexOf(int c, int index) { return value.indexOf(c, index); } Path subPath(int index) { if (index < 0) { throw new IndexOutOfBoundsException("No negative index accepted"); } else if (index == 0) { return this; } else { int i = offset + index; if (i >= data.value.length()) { throw new IndexOutOfBoundsException("Index can't be greater than length"); } return new Path(data, i); } } /** * Convert the specified index to the internal index. * * @param outterIndex the outter index * @return the inner index value * @throws IndexOutOfBoundsException if the outter index is not correct */ private int innerIndex(int outterIndex) throws IndexOutOfBoundsException { if (outterIndex < 0) { throw new IndexOutOfBoundsException("No negative index accepted"); } int pos = offset + outterIndex; if (pos > data.value.length()) { throw new IndexOutOfBoundsException("Index can't be greater than length"); } return pos; } }