blob: a2074ecce6a12b328ae6e45162e925c2c28f04e2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sling.resourceresolver.impl.params;
import java.util.Collections;
import java.util.Map;
class PathParser {
/**
* List of states. V1 and V2 prefixes means variant 1 and 2. In V1, the parameters are added after
* selectors and extension: {@code /content/test.sel.html;v=1.0}. In V2 parameters are added before
* selectors and extension: {@code /content/test;v='1.0'.sel.html}
*/
private enum ParserState {
INIT, V1_EXTENSION, V1_PARAMS, V2_PARAMS, V2_EXTENSION, SUFFIX, INVALID
}
private String rawPath;
private String parametersString;
private Map<String, String> parameters;
/**
* @return Path with no parameters.
*/
public String getPath() {
return rawPath;
}
/**
* @return Path's substring containing parameters
*/
public String getParametersString() {
return parametersString;
}
/**
* @return Parsed parameters.
*/
public Map<String, String> getParameters() {
return parameters;
}
/**
* Parses path containing parameters. Results will be available in {@link #rawPath} and {@link parameters}.
*
* @param path
*/
public void parse(String path) {
this.rawPath = path;
this.parameters = Collections.emptyMap();
if (path == null) {
return;
}
// indexOf shortcut for the most common case
final int si = path.indexOf(';');
if (si == -1) {
return;
}
final int di = path.indexOf('.');
final char[] chars = path.toCharArray();
final ParametersParser parametersParser = new ParametersParser();
ParserState state = ParserState.INIT;
int paramsStart = -1, paramsEnd = -1;
int i = (di != -1) ? ((si != -1) ? Math.min(di, si) : di) : si;
for (; i <= chars.length; i++) {
final char c;
if (i == chars.length) {
c = 0;
} else {
c = chars[i];
}
switch (state) {
case INIT:
if (c == '.') {
state = ParserState.V1_EXTENSION;
} else if (c == ';') {
paramsStart = i;
i = parametersParser.parseParameters(chars, i, false);
paramsEnd = i--;
state = parametersParser.isInvalid() ? ParserState.INVALID : ParserState.V2_PARAMS;
}
break;
case V1_EXTENSION:
if (c == '/') {
state = ParserState.SUFFIX;
} else if (c == ';') {
paramsStart = i;
i = parametersParser.parseParameters(chars, i, true);
paramsEnd = i--;
state = parametersParser.isInvalid() ? ParserState.INVALID : ParserState.V1_PARAMS;
}
break;
case V1_PARAMS:
if (c == '/') {
state = ParserState.SUFFIX;
} else if (c == '.') {
state = ParserState.INVALID; // no dots after params
}
break;
case V2_PARAMS:
if (c == '/') {
state = ParserState.INVALID; // there was no extension, so no suffix is allowed
} else if (c == '.') {
state = ParserState.V2_EXTENSION;
}
break;
case V2_EXTENSION:
if (c == '/') {
state = ParserState.SUFFIX;
}
break;
case SUFFIX:
case INVALID:
break;
}
}
if (state == ParserState.INVALID) {
paramsStart = paramsEnd = -1;
} else {
cutPath(path, paramsStart, paramsEnd);
parameters = Collections.unmodifiableMap(parametersParser.getParameters());
}
}
private void cutPath(String path, int from, int to) {
if (from == -1) {
rawPath = path;
parametersString = null;
} else if (to == -1) {
rawPath = path.substring(0, from);
parametersString = path.substring(from);
} else {
rawPath = path.substring(0, from) + path.substring(to);
parametersString = path.substring(from, to);
}
}
}