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