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
020package org.apache.wiki.plugin;
021
022import org.apache.commons.lang3.StringUtils;
023import org.apache.log4j.Logger;
024import org.apache.wiki.api.core.Context;
025import org.apache.wiki.api.core.ContextEnum;
026import org.apache.wiki.api.exceptions.PluginException;
027import org.apache.wiki.api.exceptions.ProviderException;
028import org.apache.wiki.api.plugin.Plugin;
029import org.apache.wiki.pages.PageManager;
030import org.apache.wiki.references.ReferenceManager;
031import org.jdom2.Element;
032import org.jdom2.Namespace;
033import org.jdom2.output.Format;
034import org.jdom2.output.XMLOutputter;
035
036import java.util.ArrayList;
037import java.util.List;
038import java.util.Map;
039import java.util.Set;
040import java.util.regex.Pattern;
041
042/**
043 *  A Plugin that creates an index of pages according to a certain pattern.
044 *  <br />
045 *  The default is to include all pages.
046 *  <p>
047 *  This is a rewrite of the earlier JSPWiki IndexPlugin using JDOM2.
048 8  </p>
049 *  <p>
050 *  Parameters (from AbstractReferralPlugin):
051 *  </p>
052 *  <ul>
053 *    <li><b>include</b> - A regexp pattern for marking which pages should be included.</li>
054 *    <li><b>exclude</b> - A regexp pattern for marking which pages should be excluded.</li>
055 *  </ul>
056 *  
057 * @author Ichiro Furusato
058 */
059public class IndexPlugin extends AbstractReferralPlugin implements Plugin {
060
061    private static final Logger log = Logger.getLogger(IndexPlugin.class);
062
063    private Namespace xmlns_XHTML = Namespace.getNamespace("http://www.w3.org/1999/xhtml");
064    
065    /**
066     * {@inheritDoc}
067     */
068    @Override
069    public String execute( final Context context, final Map<String,String> params ) throws PluginException {
070        final String include = params.get(PARAM_INCLUDE);
071        final String exclude = params.get(PARAM_EXCLUDE);
072        
073        final Element masterDiv = getElement("div","index");
074        final Element indexDiv = getElement("div","header");
075        masterDiv.addContent(indexDiv);
076        try {
077            final List<String> pages = listPages(context,include,exclude);
078            context.getEngine().getManager( PageManager.class ).getPageSorter().sort(pages);
079            char initialChar = ' ';
080            Element currentDiv = new Element("div",xmlns_XHTML);            
081            for( final String name : pages ) {
082                if( StringUtils.isNotBlank( name ) &&  name.charAt(0) != initialChar ) {
083                    if ( initialChar != ' ' ) {
084                        indexDiv.addContent(" - ");
085                    }                    
086                    initialChar = name.charAt(0);
087                    masterDiv.addContent(makeHeader(String.valueOf(initialChar)));
088                    currentDiv = getElement("div","body");
089                    masterDiv.addContent(currentDiv);
090                    indexDiv.addContent(getLink("#"+initialChar,String.valueOf(initialChar)));
091                } else {
092                    currentDiv.addContent(", ");
093                }
094                currentDiv.addContent( getLink( context.getURL( ContextEnum.PAGE_VIEW.getRequestContext(), name ), name ) );
095            }
096            
097        } catch( final ProviderException e ) {
098            log.warn("could not load page index",e);
099            throw new PluginException( e.getMessage() );
100        }
101        // serialize to raw format string (no changes to whitespace)
102        final XMLOutputter out = new XMLOutputter(Format.getRawFormat());
103        return out.outputString(masterDiv);
104    }
105
106    private Element getLink( final String href, final String content ) {
107        final Element a = new Element( "a", xmlns_XHTML );
108        a.setAttribute( "href", href );
109        a.addContent( content );
110        return a;
111    }
112
113    private Element makeHeader( final String initialChar ) {
114        final Element span = getElement( "span", "section" );
115        final Element a = new Element( "a", xmlns_XHTML );
116        a.setAttribute( "id", initialChar );
117        a.addContent( initialChar );
118        span.addContent( a );
119        return span;
120    }
121
122    private Element getElement( final String gi, final String classValue ) {
123        final Element elt = new Element( gi, xmlns_XHTML );
124        elt.setAttribute( "class", classValue );
125        return elt;
126    }
127
128    /**
129     *  Grabs a list of all pages and filters them according to the include/exclude patterns.
130     *  
131     * @param context
132     * @param include
133     * @param exclude
134     * @return A list containing page names which matched the filters.
135     * @throws ProviderException
136     */
137    private List<String> listPages( final Context context, final String include, final String exclude ) throws ProviderException {
138        final Pattern includePtrn = include != null ? Pattern.compile( include ) : Pattern.compile(".*");
139        final Pattern excludePtrn = exclude != null ? Pattern.compile( exclude ) : Pattern.compile("\\p{Cntrl}"); // there are no control characters in page names
140        final List< String > result = new ArrayList<>();
141        final Set< String > pages = context.getEngine().getManager( ReferenceManager.class ).findCreated();
142        for( final String pageName : pages ) {
143            if( excludePtrn.matcher( pageName ).matches() ) {
144                continue;
145            }
146            if( includePtrn.matcher( pageName ).matches() ) {
147                result.add( pageName );
148            }
149        }
150        return result;
151    }
152
153}