net.contextfw.web.application.internal.servlet.UriMappingFactory.java Source code

Java tutorial

Introduction

Here is the source code for net.contextfw.web.application.internal.servlet.UriMappingFactory.java

Source

/**
 * Copyright 2010 Marko Lavikainen
 *
 * 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 net.contextfw.web.application.internal.servlet;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.contextfw.web.application.PropertyProvider;
import net.contextfw.web.application.WebApplicationException;
import net.contextfw.web.application.component.Component;
import net.contextfw.web.application.internal.initializer.InitializerProvider;
import net.contextfw.web.application.internal.service.InitHandler;
import net.contextfw.web.application.lifecycle.RequestInvocationFilter;
import net.contextfw.web.application.lifecycle.View;

import org.apache.commons.lang.StringUtils;

public class UriMappingFactory {

    public static class Split {

        private final String value;

        private final String variableName;

        public Split(String value, String variableName) {
            this.value = value;
            this.variableName = variableName;
        }

        public String getValue() {
            return value;
        }

        public String getVariableName() {
            return variableName;
        }
    }

    private static final String SPLITTER_STR = "\\<[^<>]+>|[^<>]+";
    private static final Pattern PATH_VERIFIER = Pattern.compile("(" + SPLITTER_STR + ")+");
    private static final Pattern REGEX_VARIABLE_VERIFIER = Pattern.compile("^<\\w+(:.+)?>$");
    private static final Pattern PATH_VARIABLE_VERIFIER = Pattern.compile("^<\\w+>$");
    private static final Pattern PATH_SPLITTER = Pattern.compile(SPLITTER_STR);

    private static final String REGEX = "regex:";

    public UriMapping getMapping(Class<? extends Component> viewClass, InitServlet initServlet, String path) {
        if (path.startsWith(REGEX)) {
            return getRegexMapping(viewClass, initServlet, path.substring(REGEX.length()));
        } else {
            return getPathStyleMapping(viewClass, initServlet, path);
        }
    }

    private String toEscapedRegex(String path) {
        return path.replaceAll("\\(", "\\\\(").replaceAll("\\)", "\\\\)").replaceAll("\\{", "\\\\{")
                .replaceAll("\\}", "\\\\}").replaceAll("\\[", "\\\\[").replaceAll("\\]", "\\\\]");
    }

    private UriMapping getPathStyleMapping(Class<? extends Component> viewClass, InitServlet initServlet,
            String path) {
        List<Split> variableSplits = splitByVariables(toEscapedRegex(path), PATH_VARIABLE_VERIFIER);
        List<Split> pathSplits = splitByVariables(path, PATH_VARIABLE_VERIFIER);
        StringBuilder constructedPath = new StringBuilder();
        for (Split split : pathSplits) {
            if (split.getVariableName() != null) {
                constructedPath.append("*");
            } else {
                constructedPath.append(split.getValue());
            }
        }
        return new PathStyleUriMapping(viewClass, constructedPath.toString(), initServlet,
                getVariables(variableSplits));
    }

    private Map<String, Pattern> getVariables(List<Split> splits) {
        Map<String, Pattern> variables = new HashMap<String, Pattern>();
        for (int i = 0; i < splits.size(); i++) {
            if (splits.get(i).getVariableName() != null) {
                variables.put(splits.get(i).getVariableName(), getVariableMatcher(splits, i));
            }
        }
        return variables;
    }

    private Pattern getVariableMatcher(List<Split> splits, int pos) {
        StringBuilder before = new StringBuilder("");
        StringBuilder after = new StringBuilder("");
        for (int i = 0; i < pos; i++) {
            before.append(splits.get(i).getValue());
        }
        for (int i = pos + 1; i < splits.size(); i++) {
            after.append(splits.get(i).getValue());
        }

        return Pattern.compile(toNonCapturingMode(before.toString()) + "(" + splits.get(pos).getValue() + ")"
                + toNonCapturingMode(after.toString()));
    }

    private String toNonCapturingMode(String part) {
        StringBuilder sb = new StringBuilder();
        String[] splits = part.split("\\\\\\(");
        String delim = "";
        for (String split : splits) {
            sb.append(delim).append(split.replaceAll("\\(", "(?:"));
            delim = "\\(";
        }
        return sb.toString();
    }

    private UriMapping getRegexMapping(Class<? extends Component> viewClass, InitServlet initServlet, String path) {
        List<Split> splits = splitByVariables(path, REGEX_VARIABLE_VERIFIER);

        StringBuilder constructedPath = new StringBuilder();
        for (Split split : splits) {
            constructedPath.append(split.getValue());
        }
        return new RegexUriMapping(viewClass, constructedPath.toString(), initServlet, getVariables(splits));
    }

    private List<Split> splitByVariables(String path, Pattern variableVerifier) {
        if (!PATH_VERIFIER.matcher(path).matches()) {
            throw new WebApplicationException("Path '" + path + "' is not valid");
        }

        List<Split> splits = new ArrayList<Split>();

        Matcher matcher = PATH_SPLITTER.matcher(path);
        while (matcher.find()) {
            String group = matcher.group();
            if (group.startsWith("<")) {
                splits.add(getVariableSplit(path, group, variableVerifier));
            } else {
                splits.add(new Split(group, null));
            }
        }
        return splits;
    }

    private Split getVariableSplit(String path, String split, Pattern verifier) {
        if (!verifier.matcher(split).matches()) {
            throw new WebApplicationException("Variable '" + split + "' in path '" + path + "' is not valid.");
        }
        String def = split.substring(1, split.length() - 1);
        String name = StringUtils.substringBefore(def, ":");
        String value = StringUtils.substringAfter(def, ":");
        if (StringUtils.isBlank(value)) {
            value = "[^/]+";
        }

        return new Split(value, name);
    }

    @SuppressWarnings("unchecked")
    public SortedSet<UriMapping> createMappings(Collection<Class<?>> origClasses, ClassLoader classLoader,
            InitializerProvider initializerProvider, InitHandler initHandler, PropertyProvider properties,
            RequestInvocationFilter filter) {

        // Note: This process creates some phantom chains from
        // views that do not have any url. Those chains are
        // however ingnored and are not such problem.

        SortedSet<UriMapping> mappings = new TreeSet<UriMapping>();

        try {
            for (Class<?> origClass : origClasses) {
                Class<?> cl = classLoader.loadClass(origClass.getCanonicalName());
                View annotation = cl.getAnnotation(View.class);
                if (annotation != null) {

                    if (!Component.class.isAssignableFrom(cl)) {
                        throw new WebApplicationException(
                                "Class " + cl.getName() + " annotated with @View does " + "not extend Component");
                    }

                    List<Class<? extends Component>> chain = initializerProvider.getInitializerChain(cl);

                    InitServlet servlet = new InitServlet(initHandler, chain, filter);

                    for (String url : annotation.url()) {
                        if (!"".equals(url)) {
                            mappings.add(this.getMapping((Class<? extends Component>) cl, servlet, url));
                        }
                    }

                    for (String property : annotation.property()) {
                        if (!"".equals(property)) {
                            if (!properties.get().containsKey(property)) {
                                throw new WebApplicationException("No url bound to property: " + property);
                            }

                            String url = properties.get().getProperty(property);

                            if (url != null && !"".equals(url)) {
                                mappings.add(this.getMapping((Class<? extends Component>) cl, servlet, url));
                            } else {
                                throw new WebApplicationException("No url bound to view component. (class="
                                        + cl.getSimpleName() + ", property=" + property + ")");
                            }
                        }
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            throw new WebApplicationException(e);
        }

        return mappings;
    }
}