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