/* * Copyright 2015 Petr Bouda * * 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/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.joyrest.routing; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.function.Function; import org.joyrest.exception.type.InvalidConfigurationException; import org.joyrest.extractor.param.IntegerVariable; import org.joyrest.extractor.param.LongVariable; import org.joyrest.extractor.param.StringVariable; import org.joyrest.extractor.param.VariableType; import org.joyrest.model.RoutePart; /** * Contains a logic for parsing data from the part of the given route * * @author pbouda */ public class ParamParser implements Function<String, RoutePart<?>> { public static final String NAME_TYPE_DELIMITER = ":"; /* This character determines whether the given part is PARAM or not */ private static final String START_PARAM = "{"; private static final String END_PARAM = "}"; private static final int START_PARAM_LENGTH = START_PARAM.length(); private static final int END_PARAM_LENGTH = END_PARAM.length(); /* All path param types which are available for a creation of route */ private final static Map<String, VariableType<?>> PATH_TYPES; static { PATH_TYPES = new HashMap<>(); PATH_TYPES.put(StringVariable.NAME, StringVariable.INSTANCE); PATH_TYPES.put(IntegerVariable.NAME, IntegerVariable.INSTANCE); PATH_TYPES.put(LongVariable.NAME, LongVariable.INSTANCE); } private final Map<String, RoutePart<?>> pathParams = new HashMap<>(); private final String path; public ParamParser(String path) { this.path = path; } private static boolean isPathParam(String part) { return part.startsWith(START_PARAM) && part.endsWith(END_PARAM); } private static String getPathParam(String part) { if (part.startsWith(START_PARAM) && part.endsWith(END_PARAM)) { return part.substring(START_PARAM_LENGTH, part.length() - END_PARAM_LENGTH); } throw new InvalidConfigurationException(String.format( "Invalid path param configurer '%s'", part)); } @Override public RoutePart<?> apply(String part) { if (isPathParam(part)) { /* Split a name and a type of the param */ String param = getPathParam(part); int paramIndex = param.indexOf(NAME_TYPE_DELIMITER); char[] paramChars = param.toCharArray(); /* param index == 1 -> there is no param type definition */ String paramName = paramIndex == -1 ? param : new String(paramChars, 0, paramIndex); /* Duplication param-name in one route */ if (pathParams.containsKey(paramName)) { throw new InvalidConfigurationException(String.format( "Route '%s' contains more path params with the same name '%s'.", path, paramName)); } VariableType<?> variableType; /* param without a definition of a type - String default */ if (paramIndex == -1) { variableType = StringVariable.INSTANCE; } /* param with a definition of a type */ else { String paramType = new String(paramChars, paramIndex + 1, paramChars.length - paramIndex - 1); Optional<VariableType<?>> optPathType = Optional.ofNullable(PATH_TYPES.get(paramType)); variableType = optPathType.orElseThrow(() -> new InvalidConfigurationException(String.format( "Missing a path type for param '%s' and type '%s' in the route '%s'.", paramName, paramType, path))); } RoutePart<?> routePart = new RoutePart<>(RoutePart.Type.PARAM, paramName, variableType); pathParams.put(paramName, routePart); return routePart; } return new RoutePart<>(RoutePart.Type.PATH, part, StringVariable.INSTANCE); } }