/*
* Copyright (C) 2015 Sebastian Daschner, sebastian-daschner.com
*
* Licensed 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/LICENSE2.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 com.sebastian_daschner.jaxrs_analyzer.analysis.results;
import com.sebastian_daschner.jaxrs_analyzer.model.results.ClassResult;
import com.sebastian_daschner.jaxrs_analyzer.model.results.MethodResult;
import com.sebastian_daschner.jaxrs_analyzer.utils.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
/**
* Normalizes the JAX-RS paths.
*
* @author Sebastian Daschner
*/
final class PathNormalizer {
private PathNormalizer() {
throw new UnsupportedOperationException();
}
/**
* Returns the normalized application path found in any of the given class results.
*
* @return The base URI of the application
*/
static String getApplicationPath(final Set<ClassResult> classResults) {
return classResults.stream().map(ClassResult::getApplicationPath).filter(Objects::nonNull)
.map(PathNormalizer::normalize).findAny().orElse("");
}
/**
* Returns the normalized path (without forward-slashes at the beginning or the end) of the given method result
* including all parent class resources.
*
* @param methodResult The method result
* @return The normalized full path of the method
*/
static String getPath(final MethodResult methodResult) {
final List<String> paths = determinePaths(methodResult);
return paths.stream().map(PathNormalizer::normalize).collect(Collectors.joining("/"));
}
/**
* Determines all single paths of the method result recursively. All parent class and method results are analyzed as well.
*
* @param methodResult The method result
* @return All single path pieces
*/
private static List<String> determinePaths(final MethodResult methodResult) {
final List<String> paths = new LinkedList<>();
MethodResult currentMethod = methodResult;
while (true) {
addNonBlank(currentMethod.getPath(), paths);
final ClassResult parentClass = currentMethod.getParentResource();
if (parentClass == null)
break;
currentMethod = parentClass.getParentSubResourceLocator();
if (currentMethod == null) {
addNonBlank(parentClass.getResourcePath(), paths);
break;
}
}
Collections.reverse(paths);
return paths;
}
/**
* Adds the string to the list if it is not blank.
*
* @param string The string to add
* @param strings The list
*/
private static void addNonBlank(final String string, final List<String> strings) {
if (!StringUtils.isBlank(string) && !"/".equals(string))
strings.add(string);
}
/**
* Normalizes the given path, i.e. trims leading or trailing forward-slashes and removes path parameter matchers.
*
* @param path The path to normalize
* @return The normalized path
*/
private static String normalize(final String path) {
final StringBuilder builder = new StringBuilder(path);
int index = 0;
int colonIndex = -1;
char current = 0;
char last;
while (index < builder.length()) {
last = current;
current = builder.charAt(index);
switch (current) {
case '}':
if (last == '\\')
break;
if (colonIndex != -1) {
builder.delete(colonIndex, index);
index = colonIndex;
colonIndex = -1;
}
break;
case ':':
if (colonIndex == -1) {
colonIndex = index;
}
break;
}
if ((index == 0 || index == builder.length() - 1) && current == '/') {
builder.deleteCharAt(index);
if (index == builder.length())
// check again for path end
index--;
} else
index++;
}
return builder.toString();
}
}