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
021/**
022 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
023 */
024public class PathParser {
025
026    protected String[] array;
027
028    protected int count;
029
030    protected char[] buf;
031
032    protected int bufCount;
033
034    public PathParser() {
035        reset();
036    }
037
038    public void reset() {
039        array = new String[16];
040        buf = new char[16];
041        count = 0;
042        bufCount = 0;
043    }
044
045    public Path parse(String path) {
046        return parse(path, -1);
047    }
048
049    public Path parse(String path, int userBits) {
050        char[] chars = path.toCharArray();
051        if (chars.length == 0) {
052            return Path.EMPTY;
053        }
054        if (chars.length == 1 && chars[0] == '/') {
055            return Path.ROOT;
056        }
057
058        int i = 0;
059        int len = chars.length;
060        int bits = 0;
061        if (chars[chars.length - 1] == '/') {
062            bits |= Path.HAS_TRAILING_SLASH;
063            len--;
064        }
065        if (chars[0] == '/') {
066            bits |= Path.HAS_LEADING_SLASH;
067            i++;
068        }
069
070        for (; i < len; i++) {
071            char c = chars[i];
072            if (c == '/') {
073                if (hasSegment()) {
074                    addSegment(currentSegment());
075                    resetBuf();
076                } // else -> duplicate / - it will be ignored
077            } else {
078                append(c);
079            }
080        }
081        if (hasSegment()) {
082            addSegment(currentSegment());
083        }
084
085        return new Path(getSegments(), userBits == -1 ? bits : userBits);
086    }
087
088    public void back() {
089        if (count == 0) {
090            add("..");
091        } else {
092            count--;
093        }
094    }
095
096    public void addSegment(String segment) {
097        if ("..".equals(segment)) {
098            back();
099        } else if (!".".equals(segment)) {
100            add(segment);
101        }
102    }
103
104    public String[] getSegments() {
105        String[] result = new String[count];
106        System.arraycopy(array, 0, result, 0, count);
107        return result;
108    }
109
110    private final void add(String segment) {
111        if (count + 1 == array.length) {
112            String[] result = new String[count + 16];
113            System.arraycopy(array, 0, result, 0, count);
114            array = result;
115        }
116        array[count++] = segment;
117    }
118
119    private final void append(char c) {
120        if (bufCount + 1 == buf.length) {
121            char[] result = new char[bufCount + 16];
122            System.arraycopy(buf, 0, result, 0, bufCount);
123            buf = result;
124        }
125        buf[bufCount++] = c;
126    }
127
128    private final String currentSegment() {
129        return new String(buf, 0, bufCount);
130    }
131
132    private final boolean hasSegment() {
133        return bufCount > 0;
134    }
135
136    private final void resetBuf() {
137        bufCount = 0;
138    }
139
140}