All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micronaut.openapi.visitor.UrlUtils Maven / Gradle / Ivy

/*
 * Copyright 2017-2024 original authors
 *
 * 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
 *
 * https://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 io.micronaut.openapi.visitor;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.util.StringUtils;
import io.micronaut.inject.visitor.VisitorContext;

import java.util.ArrayList;
import java.util.List;

import static io.micronaut.openapi.visitor.StringUtil.CLOSE_BRACE;
import static io.micronaut.openapi.visitor.StringUtil.DOLLAR;
import static io.micronaut.openapi.visitor.StringUtil.OPEN_BRACE;
import static io.micronaut.openapi.visitor.StringUtil.SLASH;
import static io.micronaut.openapi.visitor.StringUtil.SLASH_CHAR;
import static io.micronaut.openapi.visitor.UrlUtils.SegmentType.CONST;
import static io.micronaut.openapi.visitor.UrlUtils.SegmentType.OPT_VAR;
import static io.micronaut.openapi.visitor.UrlUtils.SegmentType.PLACEHOLDER;
import static io.micronaut.openapi.visitor.UrlUtils.SegmentType.REQ_VAR;

/**
 * URL and URL paths util methods.
 *
 * @since 6.12.0
 */
@Internal
public final class UrlUtils {

    private UrlUtils() {
    }

    /**
     * Construct all possible URL variants by parsed segments.
     *
     * @param segments url template segments
     * @param context visitor context
     * @return all possible URL variants by parsed segments.
     */
    public static List buildUrls(List segments, VisitorContext context) {

        var results = new ArrayList();

        Segment prevSegment = null;
        for (var segment : segments) {
            appendSegment(segment, prevSegment, results);
            prevSegment = segment;
        }

        String contextPath = ConfigUtils.getServerContextPath(context);
        if (StringUtils.isNotEmpty(contextPath)) {
            if (!contextPath.startsWith(SLASH) && !contextPath.startsWith(DOLLAR)) {
                contextPath = SLASH + contextPath;
            }
            if (contextPath.endsWith(SLASH)) {
                contextPath = contextPath.substring(0, contextPath.length() - 1);
            }
        }

        var resultStrings = new ArrayList();
        for (var res : results) {
            var url = res.toString();
            if (url.endsWith(SLASH) && url.length() > 1) {
                url = url.substring(0, url.length() - SLASH.length());
            } else if (!url.startsWith(SLASH) && !url.startsWith(DOLLAR)) {
                url = SLASH + url;
            } else if (url.startsWith(SLASH + DOLLAR)) {
                url = url.substring(1);
            }

            if (StringUtils.isNotEmpty(contextPath)) {
                url = contextPath + url;
            }

            if (!resultStrings.contains(url)) {
                resultStrings.add(url);
            }
        }

        return resultStrings;
    }

    private static void appendSegment(Segment segment, Segment prevSegment, List results) {
        var type = segment.type;
        var value = segment.value;
        if (results.isEmpty()) {
            if (type == PLACEHOLDER) {
                results.add(new StringBuilder(value));
                return;
            }
            var builder = new StringBuilder();
            builder.append(value);
            results.add(builder);
            if (type == OPT_VAR) {
                results.add(new StringBuilder());
            }
            return;
        }
        if (type == CONST || type == REQ_VAR || type == PLACEHOLDER) {
            for (var result : results) {
                result.append(value);
            }
            return;
        }

        var newResults = new ArrayList();
        for (var result : results) {
            newResults.add(new StringBuilder(result));
        }
        for (var result : results) {
            if (prevSegment.type == OPT_VAR && result.indexOf(prevSegment.value) < 0) {
                continue;
            }
            result.append(SLASH_CHAR).append(value);
        }
        results.addAll(newResults);
    }

    /**
     * Parse path string to list of segments.
     *
     * @param pathString path string
     * @return list of segments
     */
    public static List parsePathSegments(String pathString) {

        var segments = new ArrayList();

        var startPos = 0;

        for (; ; ) {

            var varStartPos = pathString.indexOf('{', startPos);
            if (varStartPos < 0) {
                addConstValue(pathString.substring(startPos), segments);
                break;
            }

            var varEndPos = pathString.indexOf('}', varStartPos);

            var constSegment = pathString.substring(startPos, varStartPos);
            var nextChar = pathString.charAt(varStartPos + 1);

            // skip non path vars
            if (nextChar == '?' || nextChar == '.' || nextChar == '+' || nextChar == '0') {
                addConstValue(constSegment, segments);
                startPos = varEndPos + 1;
                continue;
            }

            // process placeholders
            if (varStartPos >= 1 && pathString.charAt(varStartPos - 1) == '$') {
                if (!constSegment.isEmpty()) {
                    addConstValue(constSegment.substring(0, constSegment.length() - 1), segments);
                }
                segments.add(new Segment(PLACEHOLDER, pathString.substring(varStartPos - 1, varEndPos + 1)));
                startPos = varEndPos + 1;
                continue;
            }

            SegmentType type = nextChar == '/' ? OPT_VAR : REQ_VAR;

            if (!constSegment.isEmpty()) {
                addConstValue(constSegment, segments);
            }

            var startBlockPos = varStartPos;
            if (pathString.charAt(startBlockPos + 1) == '/') {
                startBlockPos++;
            }
            for (; ; ) {
                var dotPos = pathString.indexOf(',', startBlockPos + 1);
                var dotPos2 = pathString.indexOf(':', startBlockPos + 1);
                var minEndPos = dotPos > 0 && dotPos < varEndPos ? dotPos : varEndPos;
                minEndPos = dotPos2 > 0 && dotPos2 < minEndPos ? dotPos2 : minEndPos;
                var varName = pathString.substring(startBlockPos + 1, minEndPos);
                segments.add(new Segment(type, OPEN_BRACE + varName + CLOSE_BRACE));
                if (minEndPos != dotPos) {
                    break;
                }
                startBlockPos = minEndPos;
            }
            startPos = varEndPos + 1;
        }

        if (segments.isEmpty()) {
            segments.add(new Segment(CONST, SLASH));
        }

        return segments;
    }

    private static void addConstValue(String constValue, List segments) {
        if (!constValue.isEmpty()) {
            segments.add(new Segment(CONST, constValue));
        }
    }

    /**
     * Segment of urlTemplate.
     *
     * @param type segment type
     * @param value value
     */
    public record Segment(
        SegmentType type,
        String value
    ) {
    }

    /**
     * Type of segment.
     */
    public enum SegmentType {
        REQ_VAR,
        OPT_VAR,
        CONST,
        PLACEHOLDER,
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy