public String toString(InternalPortalUrl portalUrl) {
        final String serverName = ((PortalUrlImpl)portalUrl).getServerName();
        
        final Matcher serverNameMatcher = this.serverNamePattern.matcher(serverName);
        if (!serverNameMatcher.matches()) {
            throw new IllegalArgumentException("Configured serverNamePattern '" + this.serverNamePattern.pattern() + "' does not match server name '" + serverName + "'");
        }
        
        
        final int groupCount = serverNameMatcher.groupCount();
        final Object[] groupValues = new Object[groupCount + 1];
        for (int groupIndex = 0; groupIndex <= groupCount; groupIndex++) {
            groupValues[groupIndex] = serverNameMatcher.group(groupIndex);
        }
        
        final String protocol;
        final String redirectServerName;
        if (portalUrl.isPublic()) {
            protocol = this.publicRedirectProtocol;
            redirectServerName = MessageFormat.format(this.publicRedirectFormat, groupValues);
        }
        else {
            protocol = this.privateRedirectProtocol;
            redirectServerName = MessageFormat.format(this.privateRedirectFormat, groupValues);
        }
        
        final StringBuilder url = new StringBuilder();
        
        url.append(protocol);
        url.append("://");
        url.append(redirectServerName);
        
        url.append(this.path);
        
        /*
         * render.userLayoutRootNode.uP?uP_root=root&uP_sparam=activeTab&activeTab=1
         */
        
        final String targetPortlet = portalUrl.getTargetPortlet();
        final Integer tabIndex = portalUrl.getTabIndex();
        
        if (targetPortlet == null && tabIndex == null) {
            throw new IllegalArgumentException("Target portlet fname or portal tab index must be set on PortletUrl");
        }
        
        char paramJoin = '?';
        
        if (tabIndex != null) {
            url.append(paramJoin).append(this.layoutRootKey).append("=").append(this.layoutRootParam);
            paramJoin = '&';
            url.append(paramJoin).append(this.structParam).append("=").append(this.structTabIndexKey);
            url.append(paramJoin).append(this.structTabIndexKey).append("=").append(tabIndex);
        }
        
        if (targetPortlet != null) {
            url.append(paramJoin).append(this.fnameParam).append("=").append(targetPortlet);
            paramJoin = '&';
            url.append(paramJoin).append(this.requestTypeParam).append("=").append(portalUrl.getType());
            url.append(paramJoin).append(this.stateParam).append("=").append(portalUrl.getWindowState());
            url.append(paramJoin).append(this.modeParam).append("=").append(portalUrl.getPortletMode());
        }
        
        for (final Map.Entry<String, String[]> parameterEntry : portalUrl.getParameters().entrySet()) {
            final String name = parameterEntry.getKey();
            for (final String value : parameterEntry.getValue()) {
                url.append(paramJoin).append(this.portletParamPrefix).append(name).append("=").append(value);
            }
        }
        
        return url.toString();
    }