/* * 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); } } }