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

org.glassfish.jersey.uri.internal.TemplateVariable Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.9
Show newest version
/*
 * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */
package org.glassfish.jersey.uri.internal;

import org.glassfish.jersey.uri.UriComponent;

import java.util.Collection;
import java.util.Map;

/**
 * The Reserved Expansion template variable representation as per RFC6570.
 */
/* package */ class TemplateVariable extends UriPart {

    protected final Position position;
    protected int len = -1; // unlimited
    protected boolean star = false;

    TemplateVariable(String part, Position position) {
        super(part);
        this.position = position;
    }

    /**
     * Choose the template variable type. The
     * @param type Type of the template
     * @param part the template content
     * @param position the position of the variable in the template.
     * @return Subclass of Templatevariable to represent the variable and allowing expansion based on the type of the variable
     */
    static TemplateVariable createTemplateVariable(char type, String part, Position position) {
        TemplateVariable newType;
        switch (type) {
            case '+':
                newType = new TemplateVariable(part, position);
                break;
            case '-': // Not supported by RFC
                newType = new MinusTemplateVariable(part, position);
                break;
            case '#':
                newType = new HashTemplateVariable(part, position);
                break;
            case '.':
                newType = new DotTemplateVariable(part, position);
                break;
            case '/':
                newType = new SlashTemplateVariable(part, position);
                break;
            case ';':
                newType = new MatrixTemplateVariable(part, position);
                break;
            case '?':
                newType = new QueryTemplateVariable(part, position);
                break;
            case '&':
                newType = new QueryContinuationTemplateVariable(part, position);
                break;
            default:
                //'p'
                newType = new PathTemplateVariable(part, position);
                break;
        }
        return newType;
    }

    @Override
    public boolean isTemplate() {
        return true;
    }

    @Override
    public String getGroup() {
        StringBuilder sb = new StringBuilder();
        if (position.isFirst()) {
            sb.append('{');
        } else {
            sb.append(',');
        }
        sb.append(getPart());
        if (position.isLast()) {
            sb.append('}');
        }
        return sb.toString();
    }

    @Override
    public String resolve(Object value, UriComponent.Type type, boolean encode) {
        if (value == null) {
            return "";
        }
        return position.isFirst()
                ? plainResolve(value, type, encode)
                : separator() + plainResolve(value, type, encode);
    }

    protected char separator() {
        return ',';
    }

    protected char keyValueSeparator() {
        return star ? '=' : ',';
    }

    protected String plainResolve(Object value, UriComponent.Type componentType, boolean encode) {
        if (Collection.class.isInstance(value)) {
            return ((Collection) value).stream()
                    .map(a -> plainResolve(a, componentType, encode))
                    .reduce("", (a, b) -> a + (a.isEmpty() ? b : separator() + b));
        } else if (Map.class.isInstance(value)) {
            return ((Map) value).entrySet().stream()
                    .map(e -> plainResolve(e.getKey(), componentType, encode)
                            + keyValueSeparator()
                            + plainResolve(e.getValue(), componentType, encode))
                    .reduce("", (a, b) -> a + (a.isEmpty() ? b : separator() + b));
        } else {
            return plainResolve(value.toString(), componentType, encode);
        }
    }

    protected String plainResolve(String value, UriComponent.Type componentType, boolean encode) {
        String val = len == -1 ? value : value.substring(0, Math.min(value.length(), len));
        return encode(val, componentType, encode);
    }

    protected String encode(String toEncode, UriComponent.Type componentType, boolean encode) {
        if (componentType == null) {
            componentType = getDefaultType();
        }
        return UriPart.percentEncode(toEncode, componentType, encode);
    }

    protected UriComponent.Type getDefaultType() {
        return UriComponent.Type.PATH;
    }

    void setLength(int len) {
        this.len = len;
    }

    void setStar(boolean b) {
        star = b;
    }

    /**
     * The default UriBuilder template
     */
    private static class PathTemplateVariable extends TemplateVariable {
        protected PathTemplateVariable(String part, Position position) {
            super(part, position);
        }

        @Override
        public boolean throwWhenNoTemplateArg() {
            return true; // The default UriBuilder behaviour
        }

        @Override
        protected UriComponent.Type getDefaultType() {
            return UriComponent.Type.PATH;
        }
    }

    /**
     * The template that works according to RFC 6570, Section 3.2.2.
     * The default Path works as described in Section 3.2.3, as described by RFC 3986.
     */
    private static class MinusTemplateVariable extends TemplateVariable {
        protected MinusTemplateVariable(String part, Position position) {
            super(part, position);
        }

        @Override
        protected String encode(String toEncode, UriComponent.Type componentType, boolean encode) {
            return super.encode(toEncode, UriComponent.Type.QUERY, encode); //Query has the same encoding as Section 3.2.3
        }

        @Override
        protected UriComponent.Type getDefaultType() {
            return UriComponent.Type.QUERY;
        }
    }


    /**
     * Section 3.2.5
     */
    private static class DotTemplateVariable extends MinusTemplateVariable {
        protected DotTemplateVariable(String part, Position position) {
            super(part, position);
        }

        @Override
        public String resolve(Object value, UriComponent.Type type, boolean encode) {
            if (value == null) {
                return "";
            }
            return '.' + plainResolve(value, type, encode);
        }

        @Override
        protected char separator() {
            return star ? '.' : super.separator();
        }
    }

    /**
     * Section 3.2.6
     */
    private static class SlashTemplateVariable extends MinusTemplateVariable {
        protected SlashTemplateVariable(String part, Position position) {
            super(part, position);
        }

        @Override
        public String resolve(Object value, UriComponent.Type type, boolean encode) {
            if (value == null) {
                return "";
            }
            return '/' + plainResolve(value, type, encode);
        }

        @Override
        protected char separator() {
            return star ? '/' : super.separator();
        }
    }

    /**
     * Section 3.2.4
     */
    private static class HashTemplateVariable extends TemplateVariable {
        protected HashTemplateVariable(String part, Position position) {
            super(part, position);
        }

        @Override
        public String resolve(Object value, UriComponent.Type type, boolean encode) {
            return (value == null || !position.isFirst() ? "" : "#") + super.resolve(value, type, encode);
        }

        @Override
        protected UriComponent.Type getDefaultType() {
            return UriComponent.Type.PATH;
        }
    }


    private abstract static class ExtendedVariable extends TemplateVariable {

        private final Character firstSymbol;
        private final char separator;
        protected final boolean appendEmpty;

        protected ExtendedVariable(String part, Position position, Character firstSymbol, char separator, boolean appendEmpty) {
            super(part, position);
            this.firstSymbol = firstSymbol;
            this.separator = separator;
            this.appendEmpty = appendEmpty;
        }

        @Override
        public String resolve(Object value, UriComponent.Type componentType, boolean encode) {
            if (value == null) { // RFC 6570
                return "";
            }
            String sValue = super.plainResolve(value, componentType, encode);
            StringBuilder sb = new StringBuilder();

            if (position.isFirst()) {
                sb.append(firstSymbol);
            } else {
                sb.append(separator);
            }

            if (!star) {
                sb.append(getPart());
                if (appendEmpty || !sValue.isEmpty()) {
                    sb.append('=').append(sValue);
                }
            } else if (!Map.class.isInstance(value)) {
                String[] split = sValue.split(String.valueOf(separator()));
                for (int i = 0; i != split.length; i++) {
                    sb.append(getPart());
                    sb.append('=').append(split[i]);
                    if (i != split.length - 1) {
                        sb.append(separator);
                    }
                }
            } else if (Map.class.isInstance(value)) {
                sb.append(sValue);
            }
            return sb.toString();
        }

        @Override
        protected char separator() {
            return star ? separator : super.separator();
        }
    }

    /**
     * Section 3.2.7
     */
    private static class MatrixTemplateVariable extends ExtendedVariable {
        protected MatrixTemplateVariable(String part, Position position) {
            super(part, position, ';', ';', false);
        }

        @Override
        protected UriComponent.Type getDefaultType() {
            return UriComponent.Type.QUERY; // For matrix, use query encoding per 6570
        }

        @Override
        public String resolve(Object value, UriComponent.Type componentType, boolean encode) {
            return super.resolve(value, getDefaultType(), encode);
        }
    }

    /**
     * Section 3.2.8
     */
    private static class QueryTemplateVariable extends ExtendedVariable {
        protected QueryTemplateVariable(String part, Position position) {
            super(part, position, '?', '&', true);
        }
    }

    /**
     * Section 3.2.9
     */
    private static class QueryContinuationTemplateVariable extends ExtendedVariable {
        protected QueryContinuationTemplateVariable(String part, Position position) {
            super(part, position, '&', '&', true);
        }

        @Override
        protected UriComponent.Type getDefaultType() {
            return UriComponent.Type.QUERY;
        }

        @Override
        public String resolve(Object value, UriComponent.Type componentType, boolean encode) {
            return super.resolve(value, getDefaultType(), encode);
        }
    }

    /**
     * 

* Position of the template variable. For instance, template {@code {first, middle, last}} would have three arguments, on * {@link Position#FIRST}, {@link Position#MIDDLE}, and {@link Position#LAST} positions. * If only a single argument is in template (most common) e.g. {@code {single}}, the position is {@link Position#SINGLE}. *

*

* {@link Position#SINGLE} is first (see {@link Position#isFirst()}) and last (see {@link Position#isLast()}) at the same time. *

*/ /* package */ static enum Position { FIRST((byte) 0b1100), MIDDLE((byte) 0b1010), LAST((byte) 0b1001), SINGLE((byte) 0b1111); final byte val; Position(byte val) { this.val = val; } /** * Informs whether the position of the argument is the last in the argument group. * @return true when the argument is the last. */ boolean isLast() { return (val & LAST.val) == LAST.val; } /** * Informs whether the position of the argument is the first in the argument group. * @return true when the argument is the first. */ boolean isFirst() { return (val & FIRST.val) == FIRST.val; } } }