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.util.TextUtil;
022
023
024/**
025 * Abstract, immutable Command implementation class. All of the fields in this
026 * class are <code>final</code>. This class is thread-safe.
027 * @since 2.4.22
028 */
029public abstract class AbstractCommand implements Command
030{
031    private static final Command[] ALL_COMMANDS = new Command[] {
032        PageCommand.ATTACH,
033        PageCommand.COMMENT,
034        PageCommand.CONFLICT,
035        PageCommand.DELETE,
036        PageCommand.DIFF,
037        PageCommand.EDIT,
038        PageCommand.INFO,
039        PageCommand.NONE,
040        PageCommand.OTHER,
041        PageCommand.PREVIEW,
042        PageCommand.RENAME,
043        PageCommand.RSS,
044        PageCommand.UPLOAD,
045        PageCommand.VIEW,
046        GroupCommand.DELETE_GROUP,
047        GroupCommand.EDIT_GROUP,
048        GroupCommand.VIEW_GROUP,
049        WikiCommand.CREATE_GROUP,
050        WikiCommand.ERROR,
051        WikiCommand.FIND,
052        WikiCommand.INSTALL,
053        WikiCommand.LOGIN,
054        WikiCommand.LOGOUT,
055        WikiCommand.MESSAGE,
056        WikiCommand.PREFS,
057        WikiCommand.WORKFLOW,
058        WikiCommand.ADMIN,
059        RedirectCommand.REDIRECT
060    };
061
062    private static final String    HTTPS = "HTTPS://";
063
064    private static final String    HTTP = "HTTP://";
065
066    private final String           m_jsp;
067
068    private final String           m_jspFriendlyName;
069
070    private final String           m_urlPattern;
071
072    private final String           m_requestContext;
073
074    private final String           m_contentTemplate;
075
076    private final Object           m_target;
077
078    /**
079     * Constructs a new Command with a specified wiki context, URL pattern,
080     * content template and target. The URL pattern is used to derive
081     * the JSP; if it is a "local" JSP (that is, it does not contain
082     * the <code>http://</code> or <code>https://</code> prefixes),
083     * then the JSP will be a cleansed version of the URL pattern;
084     * symbols (such as <code>%u</code>) will removed. If it the supplied
085     * URL pattern points to a non-local destination, the JSP will be set
086     * to the value supplied, unmodified.
087     * @param requestContext the request context
088     * @param urlPattern the URL pattern
089     * @param contentTemplate the content template; may be <code>null</code>
090     * @param target the target of the command, such as a WikiPage; may be
091     *            <code>null</code>
092     * @throws IllegalArgumentException if the request content or URL pattern is
093     *             <code>null</code>
094     */
095    protected AbstractCommand( String requestContext, String urlPattern, String contentTemplate, Object target )
096    {
097        if ( requestContext == null || urlPattern == null )
098        {
099            throw new IllegalArgumentException( "Request context, URL pattern and type must not be null." );
100        }
101
102        m_requestContext = requestContext;
103
104        if ( urlPattern.toUpperCase().startsWith( HTTP ) ||
105             urlPattern.toUpperCase().endsWith( HTTPS ) )
106        {
107            // For an HTTP/HTTPS url, pass it through without modification
108            m_jsp = urlPattern;
109            m_jspFriendlyName = "Special Page";
110        }
111        else
112        {
113            // For local JSPs, take everything to the left of ?, then
114            // delete all variable substitutions
115            String jsp = urlPattern;
116            int qPosition = urlPattern.indexOf( '?' );
117            if ( qPosition != -1 )
118            {
119                jsp = jsp.substring( 0, qPosition );
120            }
121            m_jsp = removeSubstitutions(jsp);
122
123            // Calculate the "friendly name" for the JSP
124            if ( m_jsp.toUpperCase().endsWith( ".JSP" ) )
125            {
126                m_jspFriendlyName = TextUtil.beautifyString( m_jsp.substring( 0, m_jsp.length() - 4 ) );
127            }
128            else
129            {
130                m_jspFriendlyName = m_jsp;
131            }
132        }
133
134        m_urlPattern = urlPattern;
135
136        m_contentTemplate = contentTemplate;
137
138        m_target = target;
139    }
140
141    //
142    //  This is just *so* much faster than doing String.replaceAll().  It would, in fact,
143    //  be worth to cache this value.
144    //
145    private String removeSubstitutions(String jsp)
146    {
147        //return jsp.replaceAll( "\u0025[a-z|A-Z]", "" );
148        StringBuilder newjsp = new StringBuilder( jsp.length() );
149        for( int i = 0; i < jsp.length(); i++ )
150        {
151            char c = jsp.charAt(i);
152            if( c == '%' && i < jsp.length()-1 && Character.isLetterOrDigit( jsp.charAt(i+1) ) )
153            {
154                i++;
155                continue;
156            }
157            newjsp.append( c );
158        }
159        return newjsp.toString();
160    }
161
162    /**
163     * Returns a defensively-created array of all
164     * static Commands.
165     * @return the array of commands
166     */
167    public static final Command[] allCommands()
168    {
169        return ALL_COMMANDS.clone();
170    }
171
172    /**
173     * @see org.apache.wiki.ui.Command#targetedCommand(Object)
174     */
175    public abstract Command targetedCommand( Object target );
176
177    /**
178     * @see org.apache.wiki.ui.Command#getContentTemplate()
179     */
180    public final String getContentTemplate()
181    {
182        return m_contentTemplate;
183    }
184
185    /**
186     * @see org.apache.wiki.ui.Command#getJSP()
187     */
188    public final String getJSP()
189    {
190        return m_jsp;
191    }
192
193    /**
194     * @see org.apache.wiki.ui.Command#getName()
195     */
196    public abstract String getName();
197
198    /**
199     * @see org.apache.wiki.ui.Command#getRequestContext()
200     */
201    public final String getRequestContext()
202    {
203        return m_requestContext;
204    }
205
206    /**
207     * @see org.apache.wiki.ui.Command#getTarget()
208     */
209    public final Object getTarget()
210    {
211        return m_target;
212    }
213
214    /**
215     * @see org.apache.wiki.ui.Command#getURLPattern()
216     */
217    public final String getURLPattern()
218    {
219        return m_urlPattern;
220    }
221
222    /**
223     * Returns the "friendly name" for this command's JSP, namely
224     * a beatified version of the JSP's name without the .jsp suffix.
225     * @return the friendly name
226     */
227    protected final String getJSPFriendlyName()
228    {
229        return m_jspFriendlyName;
230    }
231
232    /**
233     * Returns a String representation of the Command.
234     * @see java.lang.Object#toString()
235     */
236    public final String toString()
237    {
238        return "Command" +
239               "[context=" + m_requestContext + "," +
240               "urlPattern=" + m_urlPattern + "," +
241               "jsp=" +  m_jsp +
242               ( m_target == null ? "" : ",target=" + m_target + m_target.toString() ) +
243               "]";
244    }
245
246}