package de.wiv.tapestryportlet2.services; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; 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.internal.services.ComponentEventLinkEncoderImpl; import org.apache.tapestry5.internal.services.LinkImpl; import org.apache.tapestry5.internal.services.RequestPathOptimizer; import org.apache.tapestry5.internal.services.RequestSecurityManager; import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.internal.util.InternalUtils; import org.apache.tapestry5.runtime.Component; import org.apache.tapestry5.services.ComponentClassResolver; import org.apache.tapestry5.services.ComponentEventLinkEncoder; import org.apache.tapestry5.services.ComponentEventRequestParameters; import org.apache.tapestry5.services.ComponentSource; import org.apache.tapestry5.services.ContextPathEncoder; import org.apache.tapestry5.services.LocalizationSetter; import org.apache.tapestry5.services.PageRenderRequestParameters; import org.apache.tapestry5.services.PersistentLocale; import org.apache.tapestry5.services.Request; import org.apache.tapestry5.services.Response; public class ComponentEventLinkEncoderImplTest 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 ComponentSource componentSource; 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 ComponentEventLinkEncoderImplTest(ComponentClassResolver componentClassResolver, ContextPathEncoder contextPathEncoder, LocalizationSetter localizationSetter, Request request, Response response, RequestSecurityManager requestSecurityManager, RequestPathOptimizer optimizer, PersistentLocale persistentLocale, ComponentSource componentSource, @Symbol(SymbolConstants.ENCODE_LOCALE_INTO_PATH) boolean encodeLocaleIntoPath) { this.componentSource = componentSource; 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.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); String encodedPageName = encodePageName(activePageName); builder.append(encodedPageName); appendContext(encodedPageName.length() > 0, parameters.getActivationContext(), builder); return new LinkImpl(builder.toString(), baseURL == null, false, response, optimizer); } private String encodePageName(String pageName) { if (pageName.equalsIgnoreCase("index")) return ""; String encoded = pageName.toLowerCase(); if (!encoded.endsWith("/index")) return encoded; return encoded.substring(0, encoded.length() - 6); } 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) { System.out.println("createcomponentlink"); System.out.println(parameters.getContainingPageName()); System.out.println(parameters.getNestedComponentId()); System.out.println(componentSource.getComponent(parameters.getContainingPageName() + ":" + parameters.getNestedComponentId())); Component component = componentSource.getComponent(parameters.getContainingPageName() + ":" + parameters.getNestedComponentId()); if (component.getComponentResources().isBound("zone")){ System.out.println("true"); } // for (String p : params){ // System.out.println(p); // } 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(encodePageName(eventType)); } appendContext(true, 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(true, 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, encodePageName(containingPageName)); 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(boolean seperatorRequired, EventContext context, StringBuilder builder) { String encoded = contextPathEncoder.encodeIntoPath(context); if (encoded.length() > 0) { if (seperatorRequired) builder.append(SLASH); builder.append(encoded); } } }