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.tags;
020
021import java.io.IOException;
022
023import javax.servlet.jsp.jstl.fmt.LocaleSupport;
024
025import org.apache.wiki.util.TextUtil;
026
027
028/**
029 *  Calculate pagination string. Used for page-info and search results
030 *
031 *  <P><B>Attributes</B></P>
032 *  <UL>
033 *    <LI> start - start item of the page to be highlighted
034 *    <LI> total - total number of items
035 *    <LI> pagesize - total number of items per page
036 *    <LI> maxlinks - number of page links to be generated
037 *    <LI> fmtkey - pagination prefix of the i18n resource keys. Following keys are used:
038 *    <br>fmtkey="info.pagination"
039 *      <UL>
040 *        <LI> info.pagination.first=<span class="first">First</span>
041 *        <LI> info.pagination.last=<span class="last">Last</span>
042 *        <LI> info.pagination.previous=<span class="prev">Previous</span>
043 *        <LI> info.pagination.next=<span class="next">Next</span>
044 *        <LI> info.pagination.all=<span class="all">all</span>
045 *        <LI> info.pagination.total=&nbsp;(Total items: {0} ) 
046 *        <LI> info.pagination.show.title=Show items from {0} to {1}
047 *        <LI> info.pagination.showall.title=Show all items
048 *      </UL>
049 *  </UL>
050 *  <P>Following optional attributes can be parameterized with '%s' (item count)</P>
051 *  <UL>
052 *    <LI> href - href of each page link. (optional)
053 *    <LI> onclick - onclick of each page link. (optional)
054 *  </UL>
055 *
056 *  @since 2.5.109
057 */
058public class SetPaginationTag
059    extends WikiTagBase
060{
061    private static final long serialVersionUID = 0L;
062    private static final int ALLITEMS = -1;
063
064    private int m_start;
065    private int m_total;
066    private int m_pagesize;
067    private int m_maxlinks;
068    private String m_fmtkey;
069    private String m_href;
070    private String m_onclick;
071
072    public void initTag()
073    {
074        super.initTag();
075        m_start = 0;
076        m_total = 0;
077        m_pagesize = 20;
078        m_maxlinks = 9;
079        m_fmtkey = null;
080        m_href = null;
081        m_onclick = null;
082    }
083
084    public void setStart(int arg)
085    {
086        m_start = arg;
087    }
088
089    public void setTotal(int arg)
090    {
091        m_total = arg;
092    }
093
094    public void setPagesize(int arg)
095    {
096        m_pagesize = arg;
097    }
098
099    public void setMaxlinks(int arg)
100    {
101        m_maxlinks = arg;
102        if( m_maxlinks % 2 == 0 ) m_maxlinks--; /* must be odd */
103    }
104
105    public void setFmtkey(String arg)
106    {
107        m_fmtkey = arg;
108    }
109
110    public void setHref(String arg)
111    {
112        m_href = arg;
113    }
114
115    public void setOnclick(String arg)
116    {
117        m_onclick = arg;
118    }
119
120
121    // 0 20 40 60
122    // 0 20 40 60 80 next last
123    // first previous 20 40 *60* 80 100 next last
124    // fist previous 40 60 80 100 120
125    public int doWikiStartTag()
126        throws IOException
127    {
128        if( m_total <= m_pagesize ) return SKIP_BODY;
129
130        StringBuilder pagination = new StringBuilder();
131
132        if( m_start > m_total ) m_start = m_total;
133        if( m_start < ALLITEMS ) m_start = 0;
134
135        int maxs = m_pagesize * m_maxlinks;
136        int mids = m_pagesize * ( m_maxlinks / 2 );
137
138        pagination.append( "<div class='pagination'>");
139
140        pagination.append( LocaleSupport.getLocalizedMessage(pageContext, m_fmtkey )+ " " );
141
142        int cursor = 0;
143        int cursormax = m_total;
144
145        if( m_total > maxs )   //need to calculate real window ends
146        {
147          if( m_start > mids ) cursor = m_start - mids;
148          if( (cursor + maxs) > m_total )
149            cursor = ( ( 1 + m_total/m_pagesize ) * m_pagesize ) - maxs ;
150
151          cursormax = cursor + maxs;
152        }
153
154
155        if( ( m_start == ALLITEMS ) || (cursor > 0) )
156        {
157            appendLink ( pagination, 0, m_fmtkey + ".first" );
158        }
159
160
161        if( (m_start != ALLITEMS ) && (m_start-m_pagesize >= 0) )
162        {
163            appendLink( pagination, m_start-m_pagesize, m_fmtkey + ".previous" );
164        }
165
166        if( m_start != ALLITEMS )
167        {
168          while( cursor < cursormax )
169          {
170            if( cursor == m_start )
171            {
172              pagination.append( "<span class='cursor'>" );
173              pagination.append( 1 + cursor/m_pagesize );
174              pagination.append( "</span>" );
175            }
176            else
177            {
178              appendLink( pagination, cursor, 1+cursor/m_pagesize );
179            }
180            cursor += m_pagesize;
181          }
182        }
183
184
185        if( (m_start != ALLITEMS ) && (m_start + m_pagesize < m_total) )
186        {
187            appendLink( pagination, m_start+m_pagesize, m_fmtkey + ".next" );
188
189        if( (m_start == ALLITEMS ) || (cursormax < m_total) )
190          appendLink ( pagination, ( (m_total/m_pagesize) * m_pagesize ), m_fmtkey + ".last" );
191        }
192
193        if( m_start == ALLITEMS )
194        {
195          pagination.append( "<span class='cursor'>" );
196          pagination.append( LocaleSupport.getLocalizedMessage(pageContext, m_fmtkey + ".all" ) );
197          pagination.append( "</span>&nbsp;&nbsp;" );
198        }
199        else
200        {
201          appendLink ( pagination, ALLITEMS, m_fmtkey + ".all" );
202        }
203
204        //(Total items: " + m_total + ")" );
205        pagination.append( LocaleSupport.getLocalizedMessage(pageContext, m_fmtkey + ".total",
206                           new Object[]{ m_total } ) );
207
208        pagination.append( "</div>" );
209
210
211        /* +++ processing done +++ */
212
213        String p = pagination.toString();
214
215        pageContext.getOut().println( p );
216
217        pageContext.setAttribute( "pagination", p ); /* and cache for later use in page context */
218
219        return SKIP_BODY;
220    }
221
222
223    /**
224     * Generate pagination links <a href='' title='' onclick=''>text</a>
225     * for pagination blocks starting a page.
226     * Uses m_href and m_onclick as attribute patterns
227     * '%s' in the patterns are replaced with page offset
228     *
229     * @param sb  : stringbuilder to write output to
230     * @param page : start of page block
231     * @param onclick : link text
232     *
233     **/
234    private void appendLink( StringBuilder sb, int page, String fmttextkey )
235    {
236        appendLink2( sb, page, LocaleSupport.getLocalizedMessage( pageContext, fmttextkey ) );
237    }
238    private void appendLink( StringBuilder sb, int page, int paginationblock )
239    {
240        appendLink2( sb, page, Integer.toString( paginationblock ) );
241    }
242    private void appendLink2( StringBuilder sb, int page, String text )
243    {
244        sb.append( "<a title=\"" );
245        if( page == ALLITEMS )
246        {
247            sb.append( LocaleSupport.getLocalizedMessage( pageContext, m_fmtkey + ".showall.title" ) );
248        }
249        else
250        {
251            sb.append( LocaleSupport.getLocalizedMessage( pageContext, m_fmtkey + ".show.title",
252                       new Object[]{ page + 1, page + m_pagesize } ) );
253        }
254        sb.append( "\" " );
255
256        if( m_href != null )
257        {
258            sb.append( "href=\"" );
259            sb.append( TextUtil.replaceString( m_href, "%s", Integer.toString( page ) ) );
260            sb.append( "\" " );
261        }
262
263        if( m_onclick != null )
264        {
265            sb.append( "onclick=\"" );
266            sb.append( TextUtil.replaceString( m_onclick, "%s", Integer.toString( page ) ) );
267            sb.append( "\" " );
268        }
269
270        sb.append( ">" );
271        sb.append( text );
272        sb.append( "</a>" );
273    }
274
275}