    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.ui;
    020
    021import org.apache.wiki.api.core.Command;
    022import org.apache.wiki.api.core.Page;
    023import org.apache.wiki.api.exceptions.ProviderException;
    024
    025import javax.servlet.http.HttpServletRequest;
    026import java.util.Arrays;
    027
    028
    029/**
    030 * <p>Resolves special pages, JSPs and Commands on behalf of a Engine. CommandResolver will automatically resolve page names
    031 * with singular/plural variants. It can also detect the correct Command based on parameters supplied in an HTTP request, or due to the
    032 * JSP being accessed.</p>
    033 * <p>
    034 * <p>CommandResolver's static {@link #findCommand(String)} method is the simplest method; it looks up and returns the Command matching
    035 * a supplied wiki context. For example, looking up the request context <code>view</code> returns {@link PageCommand#VIEW}. Use this method
    036 * to obtain static Command instances that aren't targeted at a particular page or group.</p>
    037 * <p>For more complex lookups in which the caller supplies an HTTP request, {@link #findCommand(HttpServletRequest, String)} will
    038 * look up and return the correct Command. The String parameter <code>defaultContext</code> supplies the request context to use
    039 * if it cannot be detected. However, note that the default wiki context may be overridden if the request was for a "special page."</p>
    040 * <p>For example, suppose the Engine's properties specify a special page called <code>UserPrefs</code> that redirects to
    041 * <code>UserPreferences.jsp</code>. The ordinary lookup method {@linkplain #findCommand(String)} using a supplied context <code>view</code>
    042 * would return {@link PageCommand#VIEW}. But the {@linkplain #findCommand(HttpServletRequest, String)} method, when passed the same context
    043 * (<code>view</code>) and an HTTP request containing the page parameter value <code>UserPrefs</code>, will instead return
    044 * {@link WikiCommand#PREFS}.</p>
    045 *
    046 * @since 2.4.22
    047 */
    048public interface CommandResolver {
    049
    050    /** Prefix in jspwiki.properties signifying special page keys. */
    051    String PROP_SPECIALPAGE = "jspwiki.specialPage.";
    052
    053    /**
    054     * Attempts to locate a wiki command for a supplied request context. The resolution technique is simple: we examine the list of
    055     * Commands returned by {@link AllCommands#get()} and return the one whose <code>requestContext</code> matches the
    056     * supplied context. If the supplied context does not resolve to a known Command, this method throws an {@link IllegalArgumentException}.
    057     *
    058     * @param context the request context
    059     * @return the resolved context
    060     */
    061    static Command findCommand( final String context ) {
    062        return Arrays.stream( AllCommands.get() )
    063                     .filter( c -> c.getRequestContext().equals( context ) )
    064                     .findFirst()
    065                     .orElseThrow( () -> new IllegalArgumentException( "Unsupported wiki context: " + context + "." ) );
    066    }
    067
    068    /**
    069     * <p>Attempts to locate a Command for a supplied wiki context and HTTP request, incorporating the correct WikiPage into the command
    070     * if required. This method will first determine what page the user requested by delegating to {@link #extractPageFromParameter(String, HttpServletRequest)}.
    071     * If this page equates to a special page, we return the Command corresponding to that page. Otherwise, this method simply returns the
    072     * Command for the supplied request context.</p>
    073     * <p>The reason this method attempts to resolve against special pages is because some of them resolve to contexts that may be different
    074     * from the one supplied. For example, a VIEW request context for the special page "UserPreferences" should return a PREFS context instead.</p>
    075     * <p>When the caller supplies a request context and HTTP request that specifies an actual wiki page (rather than a special page),
    076     * this method will return a "targeted" Command that includes the resolved WikiPage as the target. (See {@link #resolvePage(HttpServletRequest, String)}
    077     * for the resolution algorithm). Specifically, the Command will return a non-<code>null</code> value for
    078     * its {@link AbstractCommand#getTarget()} method.</p>
    079     * <p><em>Note: if this method determines that the Command is the VIEW PageCommand, then the Command returned will always be targeted to
    080     * the front page.</em></p>
    081     *
    082     * @param request the HTTP request; if <code>null</code>, delegates to {@link #findCommand(String)}
    083     * @param defaultContext the request context to use by default
    084     * @return the resolved wiki command
    085     */
    086    Command findCommand( HttpServletRequest request, String defaultContext );
    087
    088    /**
    089     * <p>Returns the correct page name, or <code>null</code>, if no such page can be found. Aliases are considered.</p>
    090     * <p>In some cases, page names can refer to other pages. For example, when you have matchEnglishPlurals set, then a page name
    091     * "Foobars" will be transformed into "Foobar", should a page "Foobars" not exist, but the page "Foobar" would. This method gives
    092     * you the correct page name to refer to. </p>
    093     * <p>This facility can also be used to rewrite any page name, for example, by using aliases. It can also be used to check the
    094     * existence of any page.</p>
    095     *
    096     * @since 2.4.20
    097     * @param page the page name.
    098     * @return The rewritten page name, or <code>null</code>, if the page does not exist.
    099     * @throws ProviderException if the underlyng page provider that locates pages throws an exception
    100     */
    101    String getFinalPageName( String page ) throws ProviderException;
    102
    103    /**
    104     * <p>If the page is a special page, this method returns a direct URL to that page; otherwise, it returns <code>null</code>.</p>
    105     * <p>Special pages are non-existant references to other pages. For example, you could define a special page reference "RecentChanges"
    106     * which would always be redirected to "RecentChanges.jsp" instead of trying to find a Wiki page called "RecentChanges".</p>
    107     *
    108     * @param page the page name ro search for
    109     * @return the URL of the special page, if the supplied page is one, or <code>null</code>
    110     */
    111    String getSpecialPageReference( final String page );
    112
    113    /**
    114     * Determines the correct wiki page based on a supplied request context and HTTP request. This method attempts to determine the page
    115     * requested by a user, taking into acccount special pages. The resolution algorithm will:
    116     * <ul>
    117     * <li>Extract the page name from the URL according to the rules for the current {@link org.apache.wiki.url.URLConstructor}. If a
    118     * page name was passed in the request, return the correct name after taking into account potential plural matches.</li>
    119     * <li>If the extracted page name is <code>null</code>, attempt to see if a "special page" was intended by examining the servlet path.
    120     * For example, the request path "/UserPreferences.jsp" will resolve to "UserPreferences."</li>
    121     * <li>If neither of these methods work, this method returns <code>null</code></li>
    122     * </ul>
    123     *
    124     * @param requestContext the request context
    125     * @param request the HTTP request
    126     * @return the resolved page name
    127     */
    128    String extractPageFromParameter( String requestContext, HttpServletRequest request );
    129
    130    /**
    131     * Looks up and returns the correct, versioned WikiPage based on a supplied page name and optional <code>version</code> parameter
    132     * passed in an HTTP request. If the <code>version</code> parameter does not exist in the request, the latest version is returned.
    133     *
    134     * @param request the HTTP request
    135     * @param page the name of the page to look up; this page <em>must</em> exist
    136     * @return the wiki page
    137     */
    138    Page resolvePage( HttpServletRequest request, String page );
    139
    140}