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

org.apache.wink.common.internal.uritemplate.JaxRsUriTemplateProcessor Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * 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.wink.common.internal.uritemplate;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.ws.rs.core.MultivaluedMap;

import org.apache.wink.common.internal.i18n.Messages;
import org.apache.wink.common.internal.uri.UriEncoder;

/**
 * JAX-RS style template processor for compiling, matching and expanding URI
 * templates
 */
public class JaxRsUriTemplateProcessor extends UriTemplateProcessor {

    /*
     * From the JAX-RS API specification: param = "{"WSP nameWSP [ ":"WSP
     * regexWSP ] "}" name = (ALPHA / DIGIT / "_")(ALPHA / DIGIT / "." / "_" /
     * "-" ) ; \w[\w\.-] regex =( nonbrace / "{" nonbrace "}" ) ; where nonbrace
     * is any char other than "{" and "}"
     */
    private static final String  JAXRS_VARIABLE_PATTERN_WSP      = "[ \\t]*"; //$NON-NLS-1$
    private static final String  JAXRS_VARIABLE_PATTERN_NAME     = "(\\w[\\w\\.-]*)"; //$NON-NLS-1$
    private static final String  JAXRS_VARIABLE_PATTERN_NONBRACE = "[^{}]"; //$NON-NLS-1$
    private static final String  JAXRS_VARIABLE_PATTERN_REGEX    =
                                                                     "((?:(?:" + JAXRS_VARIABLE_PATTERN_NONBRACE //$NON-NLS-1$
                                                                         + ")|(?:\\{" //$NON-NLS-1$
                                                                         + JAXRS_VARIABLE_PATTERN_NONBRACE
                                                                         + "*\\}))*)"; //$NON-NLS-1$
    private static final String  JAXRS_VARIABLE_PATTERN_PARAM    =
                                                                     "\\{" + JAXRS_VARIABLE_PATTERN_WSP //$NON-NLS-1$
                                                                         + JAXRS_VARIABLE_PATTERN_NAME
                                                                         + JAXRS_VARIABLE_PATTERN_WSP
                                                                         + "(?::" //$NON-NLS-1$
                                                                         + JAXRS_VARIABLE_PATTERN_WSP
                                                                         + JAXRS_VARIABLE_PATTERN_REGEX
                                                                         + JAXRS_VARIABLE_PATTERN_WSP
                                                                         + ")?\\}"; //$NON-NLS-1$
    private static final Pattern JAXRS_VARIABLE_PATTERN          =
                                                                     Pattern
                                                                         .compile(JAXRS_VARIABLE_PATTERN_PARAM);

    protected int                numOfNonDefaultRegexes;

    /**
     * Create a processor without a template
     */
    public JaxRsUriTemplateProcessor() {
        super();
        numOfNonDefaultRegexes = 0;
    }

    /**
     * Create an processor with the provided template. The
     * {@link #compile(String)} method is called on the provided template.
     * 
     * @param uriTemplate the template that this processor is associated with
     */
    public JaxRsUriTemplateProcessor(String template) {
        this();
        compile(template);
    }

    @Override
    protected void reset() {
        super.reset();
        numOfNonDefaultRegexes = 0;
    }

    @Override
    public final void compile(String template) {
        compile(template, new JaxRsPatternBuilder(this));
    }

    /**
     * Compile the provided uri template and pass compilation events to the
     * provided {@link JaxRsCompilationHandler}.
     * 
     * @param template the template to compile
     * @param handler the {@link JaxRsCompilationHandler} that will receive
     *            compilation events
     */
    public static void compile(String template, JaxRsCompilationHandler handler) {
        if (template == null) {
            throw new NullPointerException("uriTemplate"); //$NON-NLS-1$
        }
        if (handler == null) {
            throw new NullPointerException("handler"); //$NON-NLS-1$
        }

        int start = 0;
        String literal = ""; //$NON-NLS-1$

        // fire start
        handler.startCompile(template);

        // search for JAX-RS style variables
        Matcher matcher = JAXRS_VARIABLE_PATTERN.matcher(template);
        while (matcher.find()) {
            // get the literal part which is the characters up to the variable
            literal = template.substring(start, matcher.start());
            start = matcher.end();
            // fire literal
            handler.literal(literal);

            // capturing group 1 is the variable name
            String variable = matcher.group(1);
            // capturing group 2 is the regular expression (will be null if it
            // doesn't exist)
            String regex = matcher.group(2);
            // fire variable
            handler.variable(variable, regex);
        }
        // get the trailing literal part
        literal = template.substring(start);

        // fire end
        handler.endCompile(literal);
    }

    /**
     * Expand the provided template using the provided values. Regular
     * expressions of variables in the template are ignored. All variables
     * defined in the template must have a value.
     * 
     * @param template the uri template to expand
     * @param values a map with the values of the variables
     * @return an expanded uri using the supplied variable values
     */
    public static String expand(String template, MultivaluedMap values) {
        if (template == null) {
            return null;
        }
        StringBuilder result = new StringBuilder();
        expand(template, values, result);
        return result.toString();
    }

    /**
     * Expand the provided template using the provided values. Regular
     * expressions of variables in the template are ignored. All variables
     * defined in the template must have a value.
     * 
     * @param uriTemplate the uri template to expand
     * @param values a map with the values of the variables
     * @param out the output for the expansion
     */
    public static void expand(String template,
                              MultivaluedMap values,
                              StringBuilder out) {
        if (template == null) {
            return;
        }
        JaxRsTemplateExpander expander = new JaxRsTemplateExpander(values, out);
        compile(template, expander);
    }

    @Override
    public int compareTo(UriTemplateProcessor other) {
        int result = super.compareTo(other);
        if (result != 0) {
            return result;
        }
        if (!(other instanceof JaxRsUriTemplateProcessor)) {
            return result;
        }

        return compareNumOfNonDefaultRegexes((JaxRsUriTemplateProcessor)other);
    }

    private int compareNumOfNonDefaultRegexes(JaxRsUriTemplateProcessor jaxRsProcessor) {
        return (numOfNonDefaultRegexes - jaxRsProcessor.numOfNonDefaultRegexes);
    }

    /**
     * Factory method for normalized uri-templates.
     * 
     * @param uriTemplate uri-template specification
     * @return instance representing (normalized) uri-template
     * @see UriTemplateProcessor#normalizeUri(String)
     */
    public static UriTemplateProcessor newNormalizedInstance(String uriTemplate) {
        return new JaxRsUriTemplateProcessor(UriTemplateProcessor.normalizeUri(uriTemplate));
    }

    /**
     * This interface is used for receiving events during the compilation of a
     * JAX-RS uri template.
     * 
     * @see {@link JaxRsUriTemplateProcessor#compile(String, JaxRsCompilationHandler)}
     */
    public static interface JaxRsCompilationHandler extends BaseCompilationHandler {
        /**
         * Variable event.
         * 
         * @param name the name of the variable
         * @param regex the regex as it appears in the template being compiled,
         *            or null if no regex appeared in the template.
         */
        public void variable(String name, String regex);

    }

    /**
     * This compilation handler handles the compilation events for compiling the
     * uri template of a processor instance. It creates the regex pattern to use
     * for matching and the variables used for matching and expansion, and then
     * sets them on the processor instance.
     */
    private static class JaxRsPatternBuilder extends AbstractPatternBuilder implements
        JaxRsCompilationHandler {

        public JaxRsPatternBuilder(JaxRsUriTemplateProcessor processor) {
            super(processor);
        }

        @Override
        public void literal(String literal) {
            super.literal(UriEncoder.encodeUriTemplate(literal, true));
        }

        public void variable(String name, String regex) {
            // create a new variable
            CapturingGroup variable = createVariable(name, regex, null);
            // save it to the map of capturing variables for use during matching
            processor.variables.add(name, variable);
            // save it for use during expansion
            processor.expanders.add(variable);
            if (regex != null) {
                ((JaxRsUriTemplateProcessor)processor).numOfNonDefaultRegexes += 1;
            }
        }

        @Override
        protected void createTail() {
            // overriding the tail creation to handle the special case
            // where the complete path template is empty (i.e. it was either
            // @Path("") or @Path("/")).
            // we need this to be able to handle resources with @Path("/") that 
            // have sub-resource methods or locators.
            // if we don't do this and there's a resource with @Path("/") and a 
            // sub-resource @Path("hello"), then a request to "/hello" will not
            // be picked up becuase the default tail is "(/.*)?"
            if (processor.template.equals("")) { //$NON-NLS-1$
                // let the tail catch all characters, whether there is a / or not
                processor.tail = createVariable(TEMPLATE_TAIL_NAME, "(.*)?", null); //$NON-NLS-1$
            } else {
                // normal behavior
                super.createTail();
            }
        }
    }

    /**
     * This compilation handler is used for creating an expansion string by
     * replacing all variables with their values from the provided map.
     */
    private static class JaxRsTemplateExpander extends AbstractTemplateExpander implements
        JaxRsCompilationHandler {

        public JaxRsTemplateExpander(MultivaluedMap values, StringBuilder out) {
            super(values, out);
        }

        public void variable(String name, String regex) {
            if (values == null) {
                throw new NullPointerException(Messages.getMessage("variableNotSuppliedAValue", name)); //$NON-NLS-1$
            }
            String valueStr = values.getFirst(name);
            if (valueStr == null) {
                throw new NullPointerException(Messages.getMessage("variableNotSuppliedAValue", name)); //$NON-NLS-1$
            }
            out.append(valueStr);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy