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