ar.com.zauber.commons.web.uri.factory.ExpressionMapUriFactory.java Source code

Java tutorial

Introduction

Here is the source code for ar.com.zauber.commons.web.uri.factory.ExpressionMapUriFactory.java

Source

/**
 * Copyright (c) 2005-2012 Zauber S.A. <http://www.zaubersoftware.com/>
 *
 * 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 ar.com.zauber.commons.web.uri.factory;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.UnhandledException;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.web.util.UriTemplate;

/**
 * Factory para generar uris.
 * 
 * <p>
 * Cada uri tiene una clave, y una expresin que al ser evaluada construye la
 * URI. La expresin se evalua con un {@link ExpressionParser}. Un ejemplo de
 * este es el {@link SpelExpressionParser}.
 * 
 * <p>
 * Las expresiones son similares a las del UriTemplate de Spring. Algunos
 * ejemplos con <em>SpEl</em>:
 * 
 * <ul>
 * <li> <code>/users/{#root[0]}/inbox</code></li>
 * <li> <code>/users/{#root[0]}/questions/{#root[1].id}</code></li>
 * <li> <code>/users/{#root[0].username}/</code></li>
 * </ul>
 * 
 * <p>
 * El contenido entre los <code>{}</code> es evaluado por el lenguaje. La
 * variable <code>#root</code> refiere al array de argumentos que se pasan al
 * mtodo {@link ExpressionMapUriFactory#buildUri(String, Object...)}.
 * Adems puede invocarse a la funcin #encode que llama a URLEncoder#encode(...)
 * para encoding utf-8.
 * 
 * @author Mariano Cortesi
 * @author Juan F. Codagnone (uri template support)
 * @since Jan 29, 2010
 */
public class ExpressionMapUriFactory implements UriFactory {

    private final Map<String, UriExpression> uriMap;
    private static final Method ENCODE_METHOD;
    static {
        try {
            ENCODE_METHOD = ExpressionMapUriFactory.class.getDeclaredMethod("encodeUtf8", String.class);
        } catch (final NoSuchMethodException e) {
            throw new UnhandledException(e);
        }
    }

    /** tipo  de expressin */
    public static interface ExpressionTemplate {
        /** @return an human representation for the expression */
        String getExpressionString();

        /** @return a resolved uri */
        String resolveUri(Object... params);
    }

    /** crea expressiones en base a strings */
    public static interface ExpressionTemplateFactory {
        /** crea expresiones */
        ExpressionTemplate create(String expression);
    }

    /** @see Expression */
    public static class SpringExpressionTemplate implements ExpressionTemplate {
        private final Expression expression;

        /** Creates the ExpressionMapUriFactory.SpringExpressionTemplate. */
        public SpringExpressionTemplate(final Expression expression) {
            Validate.notNull(expression);

            this.expression = expression;
        }

        public final String getExpressionString() {
            return expression.getExpressionString();
        }

        /** @see ExpressionMapUriFactory.ExpressionTemplate#resolveUri(Object[]) */
        public final String resolveUri(final Object... expArgs) {
            final StandardEvaluationContext ctx = new StandardEvaluationContext(expArgs);
            ctx.registerFunction("encode", ENCODE_METHOD);
            return expression.getValue(ctx, String.class);
        }
    }

    /** create {@link ExpressionTemplateFactory} */
    public static class SpringExpressionTemplateFactory implements ExpressionTemplateFactory {
        private final ExpressionParser parser;
        private final TemplateParserContext ctx = new TemplateParserContext("{", "}");

        /** */
        public SpringExpressionTemplateFactory(final ExpressionParser parser) {
            Validate.notNull(parser);

            this.parser = parser;

        }

        /** @see ExpressionTemplateFactory#create(String) */
        public final ExpressionTemplate create(final String expr) {
            return new SpringExpressionTemplate(parser.parseExpression(expr, ctx));
        }
    }

    /** Utiliza {@link UriTemplate} para hidratar las uris */
    public static class UriTemplateExpression implements ExpressionTemplate {
        private final UriTemplate uriTemplate;

        /** Creates the ExpressionMapUriFactory.UriTemplateExpression. */
        public UriTemplateExpression(final UriTemplate uriTemplate) {
            Validate.notNull(uriTemplate);

            this.uriTemplate = uriTemplate;
        }

        /** @see ExpressionMapUriFactory.ExpressionTemplate#getExpressionString() */
        public final String getExpressionString() {
            return uriTemplate.toString();
        }

        /** @see ExpressionTemplate#resolveUri(Object[]) */
        public final String resolveUri(final Object... params) {
            return uriTemplate.expand(params).toString();
        }
    }

    /** crea {@link UriTemplateExpression} */
    public static class UriTemplateExpressionFactory implements ExpressionTemplateFactory {
        /** @see ExpressionTemplateFactory#create(String) */
        public final ExpressionTemplate create(final String expression) {
            return new UriTemplateExpression(new UriTemplate(expression));
        }
    }

    /** Describe una expresin */
    public static class UriExpression {
        /** expresion */
        public final ExpressionTemplate expression;
        /** descripcion del recurso al que apunta la expresin (opcional) */
        public final String description;

        /** Creates the ExpressionMapUriFactory.UriExpression. */
        public UriExpression(final ExpressionTemplate expression) {
            Validate.notNull(expression);

            this.expression = expression;
            this.description = null;
        }

        /** Creates the ExpressionMapUriFactory.UriExpression. */
        public UriExpression(final ExpressionTemplate expression, final String description) {
            Validate.notNull(expression);

            this.expression = expression;
            this.description = description;
        }

        /** @see Object#toString() */
        @Override
        public final String toString() {
            return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
                    .append("expr", expression.getExpressionString()).append("description", description).toString();
        }
    }

    /**************** CONSTRUCTORES ***********************************/
    /** tipo de expresion a construir  */
    public enum Type {
        /** spring el */
        SPEL,
        /** uritemplate */
        URITEMPLATE
    }

    /** Construye un UriFactor utilizando {@link UriTemplate}  */
    public ExpressionMapUriFactory(final Map<String, String> uris, final Type type) {
        this(createUriExpression(uris, type));
    }

    /** Construye un UriFactor utilizando Spring expression Language */
    public ExpressionMapUriFactory(final ExpressionParser parser, final Map<String, String> uris) {
        this(createUriExpression(new SpringExpressionTemplateFactory(parser), uris));
    }

    /** Construye un UriFactor */
    public ExpressionMapUriFactory(final Map<String, UriExpression> uris) {
        Validate.notNull(uris);
        this.uriMap = uris;
    }

    /** construye en base a string {@link UriExpression} */
    private static Map<String, UriExpression> createUriExpression(final Map<String, String> uris, final Type type) {
        return createUriExpression(
                type == Type.SPEL ? new SpringExpressionTemplateFactory(new SpelExpressionParser())
                        : new UriTemplateExpressionFactory(),
                uris);
    }

    /** create expression map */
    private static Map<String, UriExpression> createUriExpression(final ExpressionTemplateFactory factory,
            final Map<String, String> uris) {
        final Map<String, UriExpression> map = new HashMap<String, UriExpression>();
        for (final Map.Entry<String, String> uriConf : uris.entrySet()) {
            map.put(uriConf.getKey(), new UriExpression(factory.create(uriConf.getValue())));
        }
        return map;
    }

    /****************** METODO DE SERVICIO ***********************************/

    /** @see UriFactory#buildUri(String, Object) */
    public final String buildUri(final String uriKey, final Object... expArgs) {
        Validate.notNull(uriKey);
        Validate.isTrue(uriMap.containsKey(uriKey), "No key named `" + uriKey + "'");
        Validate.noNullElements(expArgs);

        return uriMap.get(uriKey).expression.resolveUri(expArgs);
    }

    /**
     * Esta funcin se usa para ser registrada en el contexto de evaluacin por
     * reflexin.
     * @return la url codificada usando utf-8.
     * @throws UnsupportedEncodingException 
     */
    public static String encodeUtf8(final String url) throws UnsupportedEncodingException {
        final String ret = URLEncoder.encode(url, "utf-8");

        return ret.replace("+", "%20");
    }

    public final Map<String, UriExpression> getUriMap() {
        return Collections.unmodifiableMap(uriMap);
    }
}