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    @Override
073    public void initTag()
074    {
075        super.initTag();
076        m_start = 0;
077        m_total = 0;
078        m_pagesize = 20;
079        m_maxlinks = 9;
080        m_fmtkey = null;
081        m_href = null;
082        m_onclick = null;
083    }
084
085    public void setStart(final int arg)
086    {
087        m_start = arg;
088    }
089
090    public void setTotal(final int arg)
091    {
092        m_total = arg;
093    }
094
095    public void setPagesize(final int arg)
096    {
097        m_pagesize = arg;
098    }
099
100    public void setMaxlinks(final int arg)
101    {
102        m_maxlinks = arg;
103        if( m_maxlinks % 2 == 0 ) m_maxlinks--; /* must be odd */
104    }
105
106    public void setFmtkey(final String arg)
107    {
108        m_fmtkey = arg;
109    }
110
111    public void setHref(final String arg)
112    {
113        m_href = arg;
114    }
115
116    public void setOnclick(final String arg)
117    {
118        m_onclick = arg;
119    }
120
121
122    // 0 20 40 60
123    // 0 20 40 60 80 next last
124    // first previous 20 40 *60* 80 100 next last
125    // fist previous 40 60 80 100 120
126    @Override
127    public int doWikiStartTag()
128        throws IOException
129    {
130        if( m_total <= m_pagesize ) return SKIP_BODY;
131
132        final StringBuilder pagination = new StringBuilder();
133
134        if( m_start > m_total ) m_start = m_total;
135        if( m_start < ALLITEMS ) m_start = 0;
136
137        final int maxs = m_pagesize * m_maxlinks;
138        final int mids = m_pagesize * ( m_maxlinks / 2 );
139
140        pagination.append( "<div class='pagination'>");
141
142        pagination.append( LocaleSupport.getLocalizedMessage( pageContext, m_fmtkey ) ).append( " " );
143
144        int cursor = 0;
145        int cursormax = m_total;
146
147        if( m_total > maxs )   //need to calculate real window ends
148        {
149          if( m_start > mids ) cursor = m_start - mids;
150          if( (cursor + maxs) > m_total )
151            cursor = ( ( 1 + m_total/m_pagesize ) * m_pagesize ) - maxs ;
152
153          cursormax = cursor + maxs;
154        }
155
156
157        if( ( m_start == ALLITEMS ) || (cursor > 0) )
158        {
159            appendLink ( pagination, 0, m_fmtkey + ".first" );
160        }
161
162
163        if( (m_start != ALLITEMS ) && (m_start-m_pagesize >= 0) )
164        {
165            appendLink( pagination, m_start-m_pagesize, m_fmtkey + ".previous" );
166        }
167
168        if( m_start != ALLITEMS )
169        {
170          while( cursor < cursormax )
171          {
172            if( cursor == m_start )
173            {
174              pagination.append( "<span class='cursor'>" );
175              pagination.append( 1 + cursor/m_pagesize );
176              pagination.append( "</span>" );
177            }
178            else
179            {
180              appendLink( pagination, cursor, 1+cursor/m_pagesize );
181            }
182            cursor += m_pagesize;
183          }
184        }
185
186
187        if( (m_start != ALLITEMS ) && (m_start + m_pagesize < m_total) )
188        {
189            appendLink( pagination, m_start+m_pagesize, m_fmtkey + ".next" );
190
191        if( (m_start == ALLITEMS ) || (cursormax < m_total) )
192          appendLink ( pagination, ( (m_total/m_pagesize) * m_pagesize ), m_fmtkey + ".last" );
193        }
194
195        if( m_start == ALLITEMS )
196        {
197          pagination.append( "<span class='cursor'>" );
198          pagination.append( LocaleSupport.getLocalizedMessage(pageContext, m_fmtkey + ".all" ) );
199          pagination.append( "</span>&nbsp;&nbsp;" );
200        }
201        else
202        {
203          appendLink ( pagination, ALLITEMS, m_fmtkey + ".all" );
204        }
205
206        //(Total items: " + m_total + ")" );
207        pagination.append( LocaleSupport.getLocalizedMessage(pageContext, m_fmtkey + ".total",
208                           new Object[]{ m_total } ) );
209
210        pagination.append( "</div>" );
211
212
213        /* +++ processing done +++ */
214
215        final String p = pagination.toString();
216
217        pageContext.getOut().println( p );
218
219        pageContext.setAttribute( "pagination", p ); /* and cache for later use in page context */
220
221        return SKIP_BODY;
222    }
223
224
225    /**
226     * Generate pagination links <a href='' title='' onclick=''>text</a>
227     * for pagination blocks starting a page.
228     * Uses m_href and m_onclick as attribute patterns
229     * '%s' in the patterns are replaced with page offset
230     *
231     * @param sb  : stringbuilder to write output to
232     * @param page : start of page block
233     *
234     **/
235    private void appendLink(final StringBuilder sb, final int page, final String fmttextkey )
236    {
237        appendLink2( sb, page, LocaleSupport.getLocalizedMessage( pageContext, fmttextkey ) );
238    }
239    private void appendLink(final StringBuilder sb, final int page, final int paginationblock )
240    {
241        appendLink2( sb, page, Integer.toString( paginationblock ) );
242    }
243    private void appendLink2(final StringBuilder sb, final int page, final String text )
244    {
245        sb.append( "<a title=\"" );
246        if( page == ALLITEMS )
247        {
248            sb.append( LocaleSupport.getLocalizedMessage( pageContext, m_fmtkey + ".showall.title" ) );
249        }
250        else
251        {
252            sb.append( LocaleSupport.getLocalizedMessage( pageContext, m_fmtkey + ".show.title",
253                       new Object[]{ page + 1, page + m_pagesize } ) );
254        }
255        sb.append( "\" " );
256
257        if( m_href != null )
258        {
259            sb.append( "href=\"" );
260            sb.append( TextUtil.replaceString( m_href, "%s", Integer.toString( page ) ) );
261            sb.append( "\" " );
262        }
263
264        if( m_onclick != null )
265        {
266            sb.append( "onclick=\"" );
267            sb.append( TextUtil.replaceString( m_onclick, "%s", Integer.toString( page ) ) );
268            sb.append( "\" " );
269        }
270
271        sb.append( ">" );
272        sb.append( text );
273        sb.append( "</a>" );
274    }
275
276}