Package org.apache.tapestry5.internal.services

Source Code of org.apache.tapestry5.internal.services.ComponentEventLinkEncoderImpl

// Copyright 2009 The Apache Software Foundation
//
// 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 org.apache.tapestry5.internal.services;

import org.apache.tapestry5.EventConstants;
import org.apache.tapestry5.EventContext;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.SymbolConstants;
import org.apache.tapestry5.internal.InternalConstants;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.services.*;

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

public class ComponentEventLinkEncoderImpl implements ComponentEventLinkEncoder
{
    private final ComponentClassResolver componentClassResolver;

    private final ContextPathEncoder contextPathEncoder;

    private final LocalizationSetter localizationSetter;

    private final Request request;

    private final Response response;

    private final RequestSecurityManager requestSecurityManager;

    private final RequestPathOptimizer optimizer;

    private final PersistentLocale persistentLocale;

    private final ContextValueEncoder valueEncoder;

    private final URLEncoder urlEncoder;

    private final boolean encodeLocaleIntoPath;

    private static final int BUFFER_SIZE = 100;

    private static final char SLASH = '/';

    // A beast that recognizes all the elements of a path in a single go.
    // We skip the leading slash, then take the next few terms (until a dot or a colon)
    // as the page name.  Then there's a sequence that sees a dot
    // and recognizes the nested component id (which may be missing), which ends
    // at the colon, or at the slash (or the end of the string).  The colon identifies
    // the event name (the event name is also optional).  A valid path will always have
    // a nested component id or an event name (or both) ... when both are missing, then the
    // path is most likely a page render request.  After the optional event name,
    // the next piece is the action context, which is the remainder of the path.

    private final Pattern PATH_PATTERN = Pattern.compile(

            "^/" +      // The leading slash is recognized but skipped
                    "(((\\w+)/)*(\\w+))" + // A series of folder names leading up to the page name, forming the logical page name
                    "(\\.(\\w+(\\.\\w+)*))?" + // The first dot separates the page name from the nested component id
                    "(\\:(\\w+))?" + // A colon, then the event type
                    "(/(.*))?", //  A slash, then the action context
            Pattern.COMMENTS);

    // Constants for the match groups in the above pattern.
    private static final int LOGICAL_PAGE_NAME = 1;
    private static final int NESTED_ID = 6;
    private static final int EVENT_NAME = 9;
    private static final int CONTEXT = 11;

    public ComponentEventLinkEncoderImpl(ComponentClassResolver componentClassResolver,
                                         ContextPathEncoder contextPathEncoder, LocalizationSetter localizationSetter,
                                         Request request, Response response,
                                         RequestSecurityManager requestSecurityManager, RequestPathOptimizer optimizer,
                                         PersistentLocale persistentLocale, ContextValueEncoder valueEncoder,
                                         URLEncoder urlEncoder,
                                         @Symbol(SymbolConstants.ENCODE_LOCALE_INTO_PATH)
                                         boolean encodeLocaleIntoPath)
    {
        this.componentClassResolver = componentClassResolver;
        this.contextPathEncoder = contextPathEncoder;
        this.localizationSetter = localizationSetter;
        this.request = request;
        this.response = response;
        this.requestSecurityManager = requestSecurityManager;
        this.optimizer = optimizer;
        this.persistentLocale = persistentLocale;
        this.valueEncoder = valueEncoder;
        this.urlEncoder = urlEncoder;
        this.encodeLocaleIntoPath = encodeLocaleIntoPath;
    }

    public Link createPageRenderLink(PageRenderRequestParameters parameters)
    {
        StringBuilder builder = new StringBuilder(BUFFER_SIZE);

        // Build up the absolute URI.

        String activePageName = parameters.getLogicalPageName();

        String baseURL = requestSecurityManager.getBaseURL(activePageName);

        if (baseURL != null)
            builder.append(baseURL);

        builder.append(request.getContextPath());

        encodeLocale(builder);

        builder.append(SLASH);
        builder.append(activePageName.toLowerCase());

        appendContext(parameters.getActivationContext(), builder);

        return new LinkImpl(builder.toString(), baseURL == null, false, response, optimizer);
    }

    private void encodeLocale(StringBuilder builder)
    {
        if (encodeLocaleIntoPath)
        {
            Locale locale = persistentLocale.get();

            if (locale != null)
            {
                builder.append(SLASH);
                builder.append(locale.toString());
            }
        }
    }

    public Link createComponentEventLink(ComponentEventRequestParameters parameters, boolean forForm)
    {
        StringBuilder builder = new StringBuilder(BUFFER_SIZE);

        // Build up the absolute URI.

        String activePageName = parameters.getActivePageName();
        String containingPageName = parameters.getContainingPageName();
        String eventType = parameters.getEventType();

        String nestedComponentId = parameters.getNestedComponentId();
        boolean hasComponentId = InternalUtils.isNonBlank(nestedComponentId);

        String baseURL = requestSecurityManager.getBaseURL(activePageName);

        if (baseURL != null)
            builder.append(baseURL);

        builder.append(request.getContextPath());

        encodeLocale(builder);

        builder.append(SLASH);
        builder.append(activePageName.toLowerCase());

        if (hasComponentId)
        {
            builder.append('.');
            builder.append(nestedComponentId);
        }

        if (!hasComponentId || !eventType.equals(EventConstants.ACTION))
        {
            builder.append(":");
            builder.append(eventType.toLowerCase());
        }

        appendContext(parameters.getEventContext(), builder);

        Link result = new LinkImpl(builder.toString(), baseURL == null, forForm, response, optimizer);

        EventContext pageActivationContext = parameters.getPageActivationContext();

        if (pageActivationContext.getCount() != 0)
        {
            // Reuse the builder
            builder.setLength(0);
            appendContext(pageActivationContext, builder);

            // Omit that first slash
            result.addParameter(InternalConstants.PAGE_CONTEXT_NAME, builder.substring(1));
        }

        // TAPESTRY-2044: Sometimes the active page drags in components from another page and we
        // need to differentiate that.

        if (!containingPageName.equalsIgnoreCase(activePageName))
            result.addParameter(InternalConstants.CONTAINER_PAGE_NAME, containingPageName.toLowerCase());

        return result;
    }

    public ComponentEventRequestParameters decodeComponentEventRequest(Request request)
    {
        Matcher matcher = PATH_PATTERN.matcher(request.getPath());

        if (!matcher.matches()) return null;

        String nestedComponentId = matcher.group(NESTED_ID);

        String eventType = matcher.group(EVENT_NAME);

        if (nestedComponentId == null && eventType == null) return null;

        String activePageName = matcher.group(LOGICAL_PAGE_NAME);

        int slashx = activePageName.indexOf('/');

        String possibleLocaleName = slashx > 0
                                    ? activePageName.substring(0, slashx)
                                    : "";

        if (localizationSetter.setLocaleFromLocaleName(possibleLocaleName))
            activePageName = activePageName.substring(slashx + 1);

        if (!componentClassResolver.isPageName(activePageName)) return null;

        EventContext eventContext = contextPathEncoder.decodePath(matcher.group(CONTEXT));

        EventContext activationContext = contextPathEncoder.decodePath(
                request.getParameter(InternalConstants.PAGE_CONTEXT_NAME));

        // The event type is often omitted, and defaults to "action".

        if (eventType == null) eventType = EventConstants.ACTION;

        if (nestedComponentId == null) nestedComponentId = "";

        String containingPageName = request.getParameter(InternalConstants.CONTAINER_PAGE_NAME);

        if (containingPageName == null) containingPageName = activePageName;

        return new ComponentEventRequestParameters(activePageName,
                                                   containingPageName,
                                                   nestedComponentId,
                                                   eventType,
                                                   activationContext,
                                                   eventContext);
    }


    public PageRenderRequestParameters decodePageRenderRequest(Request request)
    {
        // The extended name may include a page activation context. The trick is
        // to figure out where the logical page name stops and where the
        // activation context begins. Further, strip out the leading slash.

        String path = request.getPath();

        // TAPESTRY-1343: Sometimes path is the empty string (it should always be at least a slash,
        // but Tomcat may return the empty string for a root context request).

        String extendedName = path.length() == 0 ? path : path.substring(1);

        // Ignore trailing slashes in the path.
        while (extendedName.endsWith("/"))
            extendedName = extendedName.substring(0, extendedName.length() - 1);

        int slashx = extendedName.indexOf('/');

        // So, what can we have left?
        // 1. A page name
        // 2. A locale followed by a page name
        // 3. A page name followed by activation context
        // 4. A locale name, page name, activation context
        // 5. Just activation context (for root Index page)
        // 6. A locale name followed by activation context

        String possibleLocaleName = slashx > 0
                                    ? extendedName.substring(0, slashx)
                                    : extendedName;

        if (localizationSetter.setLocaleFromLocaleName(possibleLocaleName))
        {
            extendedName = slashx > 0
                           ? extendedName.substring(slashx + 1)
                           : "";
        }

        slashx = extendedName.length();
        boolean atEnd = true;

        while (slashx > 0)
        {
            String pageName = extendedName.substring(0, slashx);
            String pageActivationContext = atEnd ? "" :
                                           extendedName.substring(slashx + 1);

            PageRenderRequestParameters parameters = checkIfPage(pageName, pageActivationContext);

            if (parameters != null)
                return parameters;

            // Work backwards, splitting at the next slash.
            slashx = extendedName.lastIndexOf('/', slashx - 1);

            atEnd = false;
        }

        // OK, maybe its all page activation context for the root Index page.

        return checkIfPage("", extendedName);
    }

    private PageRenderRequestParameters checkIfPage(String pageName, String pageActivationContext)
    {
        if (!componentClassResolver.isPageName(pageName)) return null;

        EventContext activationContext = contextPathEncoder.decodePath(pageActivationContext);

        return new PageRenderRequestParameters(pageName, activationContext);
    }

    public void appendContext(EventContext context, StringBuilder builder)
    {
        for (int i = 0; i < context.getCount(); i++)
        {
            Object raw = context.get(Object.class, i);

            String valueEncoded = raw == null ? null : valueEncoder.toClient(raw);
            String urlEncoded = urlEncoder.encode(valueEncoded);

            builder.append(SLASH);

            builder.append(urlEncoded);
        }
    }
}
TOP

Related Classes of org.apache.tapestry5.internal.services.ComponentEventLinkEncoderImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.