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    package org.apache.wiki.plugin;
020    
021    import java.util.*;
022    
023    import org.apache.log4j.Logger;
024    import org.apache.oro.text.regex.*;
025    import org.apache.wiki.ReferenceManager;
026    import org.apache.wiki.WikiContext;
027    import org.apache.wiki.WikiEngine;
028    import org.apache.wiki.WikiPage;
029    import org.apache.wiki.api.exceptions.PluginException;
030    import org.apache.wiki.api.plugin.WikiPlugin;
031    import org.apache.wiki.util.TextUtil;
032    
033    
034    /**
035     *  Displays the pages referring to the current page.
036     *
037     *  <p>Parameters</p>
038     *  <ul>
039     *    <li><b>name</b> - Name of the root page. Default name of calling page
040     *    <li><b>type</b> - local|externalattachment
041     *    <li><b>depth</b> - How many levels of pages to be parsed.
042     *    <li><b>include</b> - Include only these pages. (eg. include='UC.*|BP.*' )
043     *    <li><b>exclude</b> - Exclude with this pattern. (eg. exclude='LeftMenu' )
044     *    <li><b>format</b> -  full|compact, FULL now expands all levels correctly
045     *  </ul>
046     *
047     */
048    public class ReferredPagesPlugin implements WikiPlugin
049    {
050        private static Logger log = Logger.getLogger( ReferredPagesPlugin.class );
051        private WikiEngine     m_engine;
052        private int            m_depth;
053        private HashSet<String> m_exists  = new HashSet<String>();
054        private StringBuffer   m_result  = new StringBuffer(1024);
055        private PatternMatcher m_matcher = new Perl5Matcher();
056        private Pattern        m_includePattern;
057        private Pattern        m_excludePattern;
058        private boolean m_formatCompact  = true;
059        private boolean m_formatSort     = false;
060    
061        /** The parameter name for the root page to start from.  Value is <tt>{@value}</tt>. */
062        public static final String PARAM_ROOT    = "page";
063    
064        /** The parameter name for the depth.  Value is <tt>{@value}</tt>. */
065        public static final String PARAM_DEPTH   = "depth";
066    
067        /** The parameter name for the type of the references.  Value is <tt>{@value}</tt>. */
068        public static final String PARAM_TYPE    = "type";
069        
070        /** The parameter name for the included pages.  Value is <tt>{@value}</tt>. */
071        public static final String PARAM_INCLUDE = "include";
072        
073        /** The parameter name for the excluded pages.  Value is <tt>{@value}</tt>. */
074        public static final String PARAM_EXCLUDE = "exclude";
075        
076        /** The parameter name for the format.  Value is <tt>{@value}</tt>. */
077        public static final String PARAM_FORMAT  = "format";
078        
079        /** The minimum depth. Value is <tt>{@value}</tt>. */
080        public static final int    MIN_DEPTH = 1;
081        
082        /** The maximum depth. Value is <tt>{@value}</tt>. */
083        public static final int    MAX_DEPTH = 8;
084    
085        /**
086         *  {@inheritDoc}
087         */
088        public String execute( WikiContext context, Map<String, String> params )
089            throws PluginException
090        {
091            m_engine = context.getEngine();
092    
093            WikiPage         page   = context.getPage();
094            if( page == null ) return "";
095    
096            // parse parameters
097            String rootname = params.get( PARAM_ROOT );
098            if( rootname == null ) rootname = page.getName() ;
099    
100            String format = params.get( PARAM_FORMAT );
101            if( format == null) format = "";
102            if( format.indexOf( "full" ) >=0 ) m_formatCompact = false ;
103            if( format.indexOf( "sort" ) >=0 ) m_formatSort    = true  ;
104    
105            m_depth = TextUtil.parseIntParameter( params.get( PARAM_DEPTH ), MIN_DEPTH );
106            if( m_depth > MAX_DEPTH )  m_depth = MAX_DEPTH;
107    
108            String includePattern = params.get(PARAM_INCLUDE);
109            if( includePattern == null ) includePattern = ".*";
110    
111            String excludePattern = params.get(PARAM_EXCLUDE);
112            if( excludePattern == null ) excludePattern = "^$";
113    
114            log.debug( "Fetching referred pages for "+ rootname +
115                       " with a depth of "+ m_depth +
116                       " with include pattern of "+ includePattern +
117                       " with exclude pattern of "+ excludePattern );
118    
119            //
120            // do the actual work
121            //
122            String href  = context.getViewURL(rootname);
123            String title = "ReferredPagesPlugin: depth["+m_depth+
124                           "] include["+includePattern+"] exclude["+excludePattern+
125                           "] format["+(m_formatCompact ? "compact" : "full") +
126                           (m_formatSort ? " sort" : "") + "]";
127    
128            m_result.append("<div class=\"ReferredPagesPlugin\">\n");
129            m_result.append("<a class=\"wikipage\" href=\""+ href +
130                            "\" title=\"" + title +
131                            "\">" + rootname + "</a>\n");
132            m_exists.add(rootname);
133    
134            // pre compile all needed patterns
135            // glob compiler :  * is 0..n instance of any char  -- more convenient as input
136            // perl5 compiler : .* is 0..n instances of any char -- more powerful
137            //PatternCompiler g_compiler = new GlobCompiler();
138            PatternCompiler compiler = new Perl5Compiler();
139    
140            try
141            {
142                m_includePattern = compiler.compile(includePattern);
143    
144                m_excludePattern = compiler.compile(excludePattern);
145            }
146            catch( MalformedPatternException e )
147            {
148                if (m_includePattern == null )
149                {
150                    throw new PluginException("Illegal include pattern detected.");
151                }
152                else if (m_excludePattern == null )
153                {
154                    throw new PluginException("Illegal exclude pattern detected.");
155                }
156                else
157                {
158                    throw new PluginException("Illegal internal pattern detected.");
159                }
160            }
161    
162            // go get all referred links
163            getReferredPages(context,rootname, 0);
164    
165            // close and finish
166            m_result.append ("</div>\n" ) ;
167    
168            return m_result.toString() ;
169        }
170    
171    
172        /**
173         * Retrieves a list of all referred pages. Is called recursively
174         * depending on the depth parameter
175         */
176        @SuppressWarnings("unchecked")
177        private void getReferredPages( WikiContext context, String pagename, int depth )
178        {
179            if( depth >= m_depth ) return;  // end of recursion
180            if( pagename == null ) return;
181            if( !m_engine.pageExists(pagename) ) return;
182    
183            ReferenceManager mgr = m_engine.getReferenceManager();
184    
185            Collection<String> allPages = mgr.findRefersTo( pagename );
186    
187            handleLinks( context, allPages, ++depth, pagename );
188        }
189    
190        private void handleLinks(WikiContext context,Collection<String> links, int depth, String pagename)
191        {
192            boolean isUL = false;
193            HashSet<String> localLinkSet = new HashSet<String>();  // needed to skip multiple
194            // links to the same page
195            localLinkSet.add(pagename);
196    
197            ArrayList<String> allLinks = new ArrayList<String>();
198    
199            if( links != null )
200                allLinks.addAll( links );
201    
202            if( m_formatSort ) context.getEngine().getPageSorter().sort( allLinks );
203    
204            for( Iterator i = allLinks.iterator(); i.hasNext(); )
205            {
206                String link = (String) i.next() ;
207    
208                if( localLinkSet.contains( link ) ) continue; // skip multiple
209                                                              // links to the same
210                                                              // page
211                localLinkSet.add( link );
212    
213                if( !m_engine.pageExists( link ) ) continue; // hide links to non
214                                                             // existing pages
215    
216                if(  m_matcher.matches( link , m_excludePattern ) ) continue;
217                if( !m_matcher.matches( link , m_includePattern ) ) continue;
218    
219                if( m_exists.contains( link ) )
220                {
221                    if( !m_formatCompact )
222                    {
223                        if( !isUL )
224                        {
225                            isUL = true; m_result.append("<ul>\n");
226                        }
227    
228                        m_result.append("<li> " + link + " </li>\n");
229    
230                        getReferredPages( context, link, depth );  // added recursive
231                                                          // call - on general
232                                                          // request
233                    }
234                }
235                else
236                {
237                    if( !isUL )
238                    {
239                        isUL = true; m_result.append("<ul>\n");
240                    }
241    
242                    String href = context.getURL(WikiContext.VIEW,link);
243                    m_result.append("<li><a class=\"wikipage\" href=\""+ href +"\">"+link+"</a></li>\n" );
244    
245                    m_exists.add( link );
246    
247                    getReferredPages( context, link, depth );
248                }
249            }
250    
251            if( isUL ) m_result.append("</ul>\n");
252        }
253    }
254