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     */
019    package org.apache.wiki.tags;
020    
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.Map;
024    
025    import javax.servlet.jsp.JspException;
026    import javax.servlet.jsp.JspWriter;
027    import javax.servlet.jsp.tagext.BodyContent;
028    import javax.servlet.jsp.tagext.BodyTag;
029    
030    import org.apache.log4j.Logger;
031    import org.apache.wiki.WikiContext;
032    import org.apache.wiki.WikiEngine;
033    import org.apache.wiki.WikiPage;
034    import org.apache.wiki.WikiProvider;
035    import org.apache.wiki.api.exceptions.ProviderException;
036    import org.apache.wiki.attachment.Attachment;
037    import org.apache.wiki.parser.JSPWikiMarkupParser;
038    import org.apache.wiki.parser.MarkupParser;
039    import org.apache.wiki.util.TextUtil;
040    
041    /**
042     *  Provides a generic link tag for all kinds of linking
043     *  purposes.
044     *  <p>
045     *  If parameter <i>jsp</i> is defined, constructs a URL pointing
046     *  to the specified JSP page, under the baseURL known by the WikiEngine.
047     *  Any ParamTag name-value pairs contained in the body are added to this
048     *  URL to provide support for arbitrary JSP calls.
049     *  <p>
050     *  @since 2.3.50
051     */
052    public class LinkTag extends WikiLinkTag implements ParamHandler, BodyTag {
053        
054        static final long serialVersionUID = 0L;
055        private static final Logger log = Logger.getLogger( LinkTag.class );
056    
057        private String m_version = null;
058        private String m_class   = null;
059        private String m_style   = null;
060        private String m_title   = null;
061        private String m_target  = null;
062        private String m_compareToVersion = null;
063        private String m_rel       = null;
064        private String m_jsp     = null;
065        private String m_ref     = null;
066        private String m_context = WikiContext.VIEW;
067        private String m_accesskey = null;
068        private String m_templatefile = null;
069    
070        private boolean m_absolute = false;
071        private boolean m_overrideAbsolute = false;
072    
073        private Map<String, String> m_containedParams;
074    
075        private BodyContent m_bodyContent;
076    
077        public void initTag()
078        {
079            super.initTag();
080            m_version = m_class = m_style = m_title = m_target = m_compareToVersion = m_rel = m_jsp = m_ref = m_accesskey = m_templatefile = null;
081            m_context = WikiContext.VIEW;
082            m_absolute = false;
083        }
084    
085        public void setTemplatefile( String key )
086        {
087            m_templatefile = key;
088        }
089    
090        public void setAccessKey( String key )
091        {
092            m_accesskey = key;
093        }
094    
095        public void setAbsolute( String arg )
096        {
097            m_overrideAbsolute = true;
098            m_absolute = TextUtil.isPositive( arg );
099        }
100    
101        public String getVersion()
102        {
103            return m_version;
104        }
105    
106        public void setVersion( String arg )
107        {
108            m_version = arg;
109        }
110    
111        public void setClass( String arg )
112        {
113            m_class = arg;
114        }
115    
116        public void setStyle( String style )
117        {
118            m_style = style;
119        }
120    
121        public void setTitle( String title )
122        {
123            m_title = title;
124        }
125    
126        public void setTarget( String target )
127        {
128            m_target = target;
129        }
130    
131        public void setCompareToVersion( String ver )
132        {
133            m_compareToVersion = ver;
134        }
135    
136        public void setRel( String rel )
137        {
138            m_rel = rel;
139        }
140    
141        public void setRef( String ref )
142        {
143            m_ref = ref;
144        }
145    
146        public void setJsp( String jsp )
147        {
148            m_jsp = jsp;
149        }
150    
151        public void setContext( String context )
152        {
153            m_context = context;
154        }
155    
156        /**
157         * Support for ParamTag supplied parameters in body.
158         */
159        public void setContainedParameter( String name, String value )
160        {
161            if( name != null )
162            {
163                if( m_containedParams == null )
164                {
165                    m_containedParams = new HashMap<String, String>();
166                }
167                m_containedParams.put( name, value );
168            }
169        }
170    
171    
172        /**
173         *  This method figures out what kind of an URL should be output.  It mirrors heavily
174         *  on JSPWikiMarkupParser.handleHyperlinks();
175         *
176         * @return the URL
177         * @throws ProviderException
178         */
179        private String figureOutURL()
180            throws ProviderException
181        {
182            String url = null;
183            WikiEngine engine = m_wikiContext.getEngine();
184    
185            if( m_pageName == null )
186            {
187                WikiPage page = m_wikiContext.getPage();
188    
189                if( page != null )
190                {
191                    m_pageName = page.getName();
192                }
193            }
194    
195            if( m_templatefile != null )
196            {
197                String params = addParamsForRecipient( null, m_containedParams );
198                String template = engine.getTemplateDir();
199                url = engine.getURL( WikiContext.NONE, "templates/"+template+"/"+m_templatefile, params, false );
200            }
201            else if( m_jsp != null )
202            {
203                String params = addParamsForRecipient( null, m_containedParams );
204                //url = m_wikiContext.getURL( WikiContext.NONE, m_jsp, params );
205                url = engine.getURL( WikiContext.NONE, m_jsp, params, m_absolute );
206            }
207            else if( m_ref != null )
208            {
209                int interwikipoint;
210    
211                if( JSPWikiMarkupParser.isExternalLink(m_ref) )
212                {
213                    url = m_ref;
214                }
215                else if( (interwikipoint = m_ref.indexOf(":")) != -1 )
216                {
217                    String extWiki = m_ref.substring( 0, interwikipoint );
218                    String wikiPage = m_ref.substring( interwikipoint+1 );
219    
220                    url = engine.getInterWikiURL( extWiki );
221    
222                    if( url != null )
223                    {
224                        url = TextUtil.replaceString( url, "%s", wikiPage );
225                    }
226                }
227                else if( m_ref.startsWith("#") )
228                {
229                    // Local link
230                }
231                else if( TextUtil.isNumber(m_ref) )
232                {
233                    // Reference
234                }
235                else
236                {
237                    int hashMark = -1;
238    
239                    String parms = (m_version != null) ? "version="+getVersion() : null;
240    
241                    //
242                    //  Internal wiki link, but is it an attachment link?
243                    //
244                    WikiPage p = engine.getPage( m_pageName );
245    
246                    if( p instanceof Attachment )
247                    {
248                        url = m_wikiContext.getURL( WikiContext.ATTACH, m_pageName );
249                    }
250                    else if( (hashMark = m_ref.indexOf('#')) != -1 )
251                    {
252                        // It's an internal Wiki link, but to a named section
253    
254                        String namedSection = m_ref.substring( hashMark+1 );
255                        String reallink     = m_ref.substring( 0, hashMark );
256    
257                        reallink = MarkupParser.cleanLink( reallink );
258    
259                        String matchedLink;
260                        String sectref = "";
261                        if( (matchedLink = engine.getFinalPageName( reallink )) != null )
262                        {
263                            sectref = "section-"+engine.encodeName(matchedLink)+"-"+namedSection;
264                            sectref = "#"+sectref.replace('%', '_');
265                        }
266                        else
267                        {
268                            matchedLink = reallink;
269                        }
270    
271                        url = makeBasicURL( m_context, matchedLink, parms, m_absolute ) + sectref;
272                    }
273                    else
274                    {
275                        String reallink = MarkupParser.cleanLink( m_ref );
276    
277                        url = makeBasicURL( m_context, reallink, parms, m_absolute );
278                    }
279                }
280            }
281            else if( m_pageName != null && m_pageName.length() > 0 )
282            {
283                WikiPage p = engine.getPage( m_pageName );
284    
285                String parms = (m_version != null) ? "version="+getVersion() : null;
286    
287                parms = addParamsForRecipient( parms, m_containedParams );
288    
289                if( p instanceof Attachment )
290                {
291                    String ctx = m_context;
292                    // Switch context appropriately when attempting to view an
293                    // attachment, but don't override the context setting otherwise
294                    if( m_context == null || m_context.equals( WikiContext.VIEW ) )
295                    {
296                        ctx = WikiContext.ATTACH;
297                    }
298                    url = engine.getURL( ctx, m_pageName, parms, m_absolute );
299                    //url = m_wikiContext.getURL( ctx, m_pageName, parms );
300                }
301                else
302                {
303                    url = makeBasicURL( m_context, m_pageName, parms, m_absolute );
304                }
305            }
306            else
307            {
308                String page = engine.getFrontPage();
309                url = makeBasicURL( m_context, page, null, m_absolute );
310            }
311    
312            return url;
313        }
314    
315        private String addParamsForRecipient( String addTo, Map< String, String > params )
316        {
317            if( params == null || params.size() == 0 )
318            {
319                return addTo;
320            }
321            StringBuilder buf = new StringBuilder();
322            Iterator< Map.Entry< String, String > > it = params.entrySet().iterator();
323            while( it.hasNext() )
324            {
325                Map.Entry< String, String > e = it.next();
326                String n = e.getKey();
327                String v = e.getValue();
328                buf.append( n );
329                buf.append( "=" );
330                buf.append( v );
331                if( it.hasNext() )
332                {
333                    buf.append( "&amp;" );
334                }
335            }
336            if( addTo == null )
337            {
338                return buf.toString();
339            }
340            if( !addTo.endsWith( "&amp;" ) )
341            {
342                return addTo + "&amp;" + buf.toString();
343            }
344            return addTo + buf.toString();
345        }
346    
347        private String makeBasicURL( String context, String page, String parms, boolean absolute )
348        {
349            String url;
350            WikiEngine engine = m_wikiContext.getEngine();
351    
352            if( context.equals( WikiContext.DIFF ) )
353            {
354                int r1 = 0;
355                int r2 = 0;
356    
357                if( DiffLinkTag.VER_LATEST.equals(getVersion()) )
358                {
359                    WikiPage latest = engine.getPage( page, WikiProvider.LATEST_VERSION );
360    
361                    r1 = latest.getVersion();
362                }
363                else if( DiffLinkTag.VER_PREVIOUS.equals(getVersion()) )
364                {
365                    r1 = m_wikiContext.getPage().getVersion() - 1;
366                    r1 = (r1 < 1 ) ? 1 : r1;
367                }
368                else if( DiffLinkTag.VER_CURRENT.equals(getVersion()) )
369                {
370                    r1 = m_wikiContext.getPage().getVersion();
371                }
372                else
373                {
374                    r1 = Integer.parseInt( getVersion() );
375                }
376    
377                if( DiffLinkTag.VER_LATEST.equals(m_compareToVersion) )
378                {
379                    WikiPage latest = engine.getPage( page, WikiProvider.LATEST_VERSION );
380    
381                    r2 = latest.getVersion();
382                }
383                else if( DiffLinkTag.VER_PREVIOUS.equals(m_compareToVersion) )
384                {
385                    r2 = m_wikiContext.getPage().getVersion() - 1;
386                    r2 = (r2 < 1 ) ? 1 : r2;
387                }
388                else if( DiffLinkTag.VER_CURRENT.equals(m_compareToVersion) )
389                {
390                    r2 = m_wikiContext.getPage().getVersion();
391                }
392                else
393                {
394                    r2 = Integer.parseInt( m_compareToVersion );
395                }
396    
397                parms = "r1="+r1+"&amp;r2="+r2;
398            }
399    
400            //url = m_wikiContext.getURL( m_context, m_pageName, parms );
401            url = engine.getURL( m_context, m_pageName, parms, m_absolute );
402    
403            return url;
404        }
405    
406        public int doWikiStartTag() throws Exception
407        {
408            return EVAL_BODY_BUFFERED;
409        }
410    
411        public int doEndTag()
412        {
413            try
414            {
415                if( !m_overrideAbsolute )
416                {
417                    // TODO: see WikiContext.getURL(); this check needs to be specified somewhere.
418                    WikiEngine engine = m_wikiContext.getEngine();
419                    m_absolute = "absolute".equals( engine.getWikiProperties().getProperty( WikiEngine.PROP_REFSTYLE ) );
420                }
421    
422                JspWriter out = pageContext.getOut();
423                String url = figureOutURL();
424    
425                StringBuffer sb = new StringBuffer( 20 );
426    
427                sb.append( (m_class != null)   ? "class=\""+m_class+"\" " : "" );
428                sb.append( (m_style != null)   ? "style=\""+m_style+"\" " : "" );
429                sb.append( (m_target != null ) ? "target=\""+m_target+"\" " : "" );
430                sb.append( (m_title != null )  ? "title=\""+m_title+"\" " : "" );
431                sb.append( (m_rel != null )    ? "rel=\""+m_rel+"\" " : "" );
432                sb.append( (m_accesskey != null) ? "accesskey=\""+m_accesskey+"\" " : "" );
433    
434                switch( m_format )
435                {
436                  case URL:
437                    out.print( url );
438                    break;
439                  default:
440                  case ANCHOR:
441                    out.print("<a "+sb.toString()+" href=\""+url+"\">");
442                    break;
443                }
444    
445                // Add any explicit body content. This is not the intended use
446                // of LinkTag, but happens to be the way it has worked previously.
447                if( m_bodyContent != null )
448                {
449                    String linktext = m_bodyContent.getString().trim();
450                    out.write( linktext );
451                }
452    
453                //  Finish off by closing opened anchor
454                if( m_format == ANCHOR ) out.print("</a>");
455            }
456            catch( Exception e )
457            {
458                // Yes, we want to catch all exceptions here, including RuntimeExceptions
459                log.error( "Tag failed", e );
460            }
461    
462            return EVAL_PAGE;
463        }
464    
465        public void setBodyContent( BodyContent bc )
466        {
467            m_bodyContent = bc;
468        }
469    
470        public void doInitBody() throws JspException
471        {
472        }
473    }