forgot to add WikiPageRenamer on last commit.. this should fix the build
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/content/WikiPageRenamer.java b/jspwiki-main/src/main/java/org/apache/wiki/content/WikiPageRenamer.java
new file mode 100644
index 0000000..f4da6b0
--- /dev/null
+++ b/jspwiki-main/src/main/java/org/apache/wiki/content/WikiPageRenamer.java
@@ -0,0 +1,350 @@
+/* 
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.  
+ */
+package org.apache.wiki.content;
+
+import org.apache.log4j.Logger;
+import org.apache.wiki.InternalWikiException;
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.WikiEngine;
+import org.apache.wiki.WikiPage;
+import org.apache.wiki.api.exceptions.ProviderException;
+import org.apache.wiki.api.exceptions.WikiException;
+import org.apache.wiki.attachment.Attachment;
+import org.apache.wiki.event.WikiEventManager;
+import org.apache.wiki.event.WikiPageRenameEvent;
+import org.apache.wiki.parser.JSPWikiMarkupParser;
+import org.apache.wiki.parser.MarkupParser;
+import org.apache.wiki.util.TextUtil;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * Provides page renaming functionality. Note that there used to be a similarly named class in 2.6, but due to unclear copyright, the
+ * class was completely rewritten from scratch for 2.8.
+ *
+ * @since 2.8
+ */
+public class WikiPageRenamer implements PageRenamer {
+
+    private static final Logger log = Logger.getLogger( WikiPageRenamer.class );
+    
+    private boolean m_camelCase = false;
+    
+    /**
+     *  Renames a page.
+     *  
+     *  @param context The current context.
+     *  @param renameFrom The name from which to rename.
+     *  @param renameTo The new name.
+     *  @param changeReferrers If true, also changes all the referrers.
+     *  @return The final new name (in case it had to be modified)
+     *  @throws WikiException If the page cannot be renamed.
+     */
+    public String renamePage( final WikiContext context, final String renameFrom, final String renameTo, final boolean changeReferrers ) throws WikiException {
+        //  Sanity checks first
+        if( renameFrom == null || renameFrom.length() == 0 ) {
+            throw new WikiException( "From name may not be null or empty" );
+        }
+        if( renameTo == null || renameTo.length() == 0 ) {
+            throw new WikiException( "To name may not be null or empty" );
+        }
+       
+        //  Clean up the "to" -name so that it does not contain anything illegal
+        final String renameToClean = MarkupParser.cleanLink( renameTo.trim() );
+        if( renameToClean.equals( renameFrom ) ) {
+            throw new WikiException( "You cannot rename the page to itself" );
+        }
+        
+        //  Preconditions: "from" page must exist, and "to" page must not yet exist.
+        final WikiEngine engine = context.getEngine();
+        final WikiPage fromPage = engine.getPage( renameFrom );
+        if( fromPage == null ) {
+            throw new WikiException("No such page "+renameFrom);
+        }
+        WikiPage toPage = engine.getPage( renameToClean );
+        if( toPage != null ) {
+            throw new WikiException( "Page already exists " + renameToClean );
+        }
+        
+        final Set< String > referrers = getReferencesToChange( fromPage, engine );
+
+        //  Do the actual rename by changing from the frompage to the topage, including all of the attachments
+        //  Remove references to attachments under old name
+        final List< Attachment > attachmentsOldName = engine.getAttachmentManager().listAttachments( fromPage );
+        for( final Attachment att: attachmentsOldName ) {
+            final WikiPage fromAttPage = engine.getPage( att.getName() );
+            engine.getReferenceManager().pageRemoved( fromAttPage );
+        }
+
+        engine.getPageManager().getProvider().movePage( renameFrom, renameToClean );
+        if( engine.getAttachmentManager().attachmentsEnabled() ) {
+            engine.getAttachmentManager().getCurrentProvider().moveAttachmentsForPage( renameFrom, renameToClean );
+        }
+        
+        //  Add a comment to the page notifying what changed.  This adds a new revision to the repo with no actual change.
+        toPage = engine.getPage( renameToClean );
+        if( toPage == null ) {
+            throw new InternalWikiException( "Rename seems to have failed for some strange reason - please check logs!" );
+        }
+        toPage.setAttribute( WikiPage.CHANGENOTE, fromPage.getName() + " ==> " + toPage.getName() );
+        toPage.setAuthor( context.getCurrentUser().getName() );
+        engine.getPageManager().putPageText( toPage, engine.getPureText( toPage ) );
+
+        //  Update the references
+        engine.getReferenceManager().pageRemoved( fromPage );
+        engine.updateReferences( toPage );
+
+        //  Update referrers
+        if( changeReferrers ) {
+            updateReferrers( context, fromPage, toPage, referrers );
+        }
+
+        //  re-index the page including its attachments
+        engine.getSearchManager().reindexPage( toPage );
+        
+        final Collection< Attachment > attachmentsNewName = engine.getAttachmentManager().listAttachments( toPage );
+        for( final Attachment att:attachmentsNewName ) {
+            final WikiPage toAttPage = engine.getPage( att.getName() );
+            // add reference to attachment under new page name
+            engine.updateReferences( toAttPage );
+            engine.getSearchManager().reindexPage( att );
+        }
+
+        firePageRenameEvent( renameFrom, renameToClean );
+
+        //  Done, return the new name.
+        return renameToClean;
+    }
+
+    /**
+     * Fires a WikiPageRenameEvent to all registered listeners. Currently not used internally by JSPWiki itself, but you can use it for
+     * something else.
+     *
+     * @param oldName the former page name
+     * @param newName the new page name
+     */
+    public void firePageRenameEvent( final String oldName, final String newName ) {
+        if( WikiEventManager.isListening(this) ) {
+            WikiEventManager.fireEvent(this, new WikiPageRenameEvent(this, oldName, newName ) );
+        }
+    }
+
+    /**
+     *  This method finds all the pages which have anything to do with the fromPage and
+     *  change any referrers it can figure out in that page.
+     *  
+     *  @param context WikiContext in which we operate
+     *  @param fromPage The old page
+     *  @param toPage The new page
+     */
+    private void updateReferrers( final WikiContext context, final WikiPage fromPage, final WikiPage toPage, final Set< String > referrers ) {
+        if( referrers.isEmpty() ) { // No referrers
+            return;
+        }
+
+        final WikiEngine engine = context.getEngine();
+        for( String pageName : referrers ) {
+            //  In case the page was just changed from under us, let's do this small kludge.
+            if( pageName.equals( fromPage.getName() ) ) {
+                pageName = toPage.getName();
+            }
+            
+            final WikiPage p = engine.getPage( pageName );
+
+            final String sourceText = engine.getPureText( p );
+            String newText = replaceReferrerString( context, sourceText, fromPage.getName(), toPage.getName() );
+
+            m_camelCase = TextUtil.getBooleanProperty( engine.getWikiProperties(), JSPWikiMarkupParser.PROP_CAMELCASELINKS, m_camelCase );
+            if( m_camelCase ) {
+                newText = replaceCCReferrerString( context, newText, fromPage.getName(), toPage.getName() );
+            }
+            
+            if( !sourceText.equals( newText ) ) {
+                p.setAttribute( WikiPage.CHANGENOTE, fromPage.getName()+" ==> "+toPage.getName() );
+                p.setAuthor( context.getCurrentUser().getName() );
+         
+                try {
+                    engine.getPageManager().putPageText( p, newText );
+                    engine.updateReferences( p );
+                } catch( final ProviderException e ) {
+                    //  We fail with an error, but we will try to continue to rename other referrers as well.
+                    log.error("Unable to perform rename.",e);
+                }
+            }
+        }
+    }
+
+    private Set<String> getReferencesToChange( final WikiPage fromPage, final WikiEngine engine ) {
+        final Set< String > referrers = new TreeSet<>();
+        final Collection< String > r = engine.getReferenceManager().findReferrers( fromPage.getName() );
+        if( r != null ) {
+            referrers.addAll( r );
+        }
+        
+        try {
+            final List< Attachment > attachments = engine.getAttachmentManager().listAttachments( fromPage );
+            for( final Attachment att : attachments  ) {
+                final Collection< String > c = engine.getReferenceManager().findReferrers( att.getName() );
+                if( c != null ) {
+                    referrers.addAll( c );
+                }
+            }
+        } catch( final ProviderException e ) {
+            // We will continue despite this error
+            log.error( "Provider error while fetching attachments for rename", e );
+        }
+        return referrers;
+    }
+
+    /**
+     *  Replaces camelcase links.
+     */
+    private String replaceCCReferrerString( final WikiContext context, final String sourceText, final String from, final String to ) {
+        final StringBuilder sb = new StringBuilder( sourceText.length()+32 );
+        final Pattern linkPattern = Pattern.compile( "\\p{Lu}+\\p{Ll}+\\p{Lu}+[\\p{L}\\p{Digit}]*" );
+        final Matcher matcher = linkPattern.matcher( sourceText );
+        int start = 0;
+        
+        while( matcher.find( start ) ) {
+            final String match = matcher.group();
+            sb.append( sourceText.substring( start, matcher.start() ) );
+            final int lastOpenBrace = sourceText.lastIndexOf( '[', matcher.start() );
+            final int lastCloseBrace = sourceText.lastIndexOf( ']', matcher.start() );
+            
+            if( match.equals( from ) && lastCloseBrace >= lastOpenBrace ) {
+                sb.append( to );
+            } else {
+                sb.append( match );
+            }
+            
+            start = matcher.end();
+        }
+        
+        sb.append( sourceText.substring( start ) );
+        
+        return sb.toString();
+    }
+
+    private String replaceReferrerString( final WikiContext context, final String sourceText, final String from, final String to ) {
+        final StringBuilder sb = new StringBuilder( sourceText.length()+32 );
+        
+        // This monstrosity just looks for a JSPWiki link pattern.  But it is pretty cool for a regexp, isn't it?  If you can
+        // understand this in a single reading, you have way too much time in your hands.
+        final Pattern linkPattern = Pattern.compile( "([\\[\\~]?)\\[([^\\|\\]]*)(\\|)?([^\\|\\]]*)(\\|)?([^\\|\\]]*)\\]" );
+        final Matcher matcher = linkPattern.matcher( sourceText );
+        int start = 0;
+        
+        while( matcher.find( start ) ) {
+            char charBefore = (char)-1;
+            
+            if( matcher.start() > 0 ) {
+                charBefore = sourceText.charAt( matcher.start() - 1 );
+            }
+            
+            if( matcher.group(1).length() > 0 || charBefore == '~' || charBefore == '[' ) {
+                //  Found an escape character, so I am escaping.
+                sb.append( sourceText.substring( start, matcher.end() ) );
+                start = matcher.end();
+                continue;
+            }
+
+            String text = matcher.group(2);
+            String link = matcher.group(4);
+            final String attr = matcher.group(6);
+             
+            if( link.length() == 0 ) {
+                text = replaceSingleLink( context, text, from, to );
+            } else {
+                link = replaceSingleLink( context, link, from, to );
+                
+                //  A very simple substitution, but should work for quite a few cases.
+                text = TextUtil.replaceString( text, from, to );
+            }
+        
+            //
+            //  Construct the new string
+            //
+            sb.append( sourceText.substring( start, matcher.start() ) );
+            sb.append( "[" ).append( text );
+            if( link.length() > 0 ) {
+                sb.append( "|" ).append( link );
+            }
+            if( attr.length() > 0 ) {
+                sb.append( "|" ).append( attr );
+            }
+            sb.append( "]" );
+            
+            start = matcher.end();
+        }
+        
+        sb.append( sourceText.substring( start ) );
+        
+        return sb.toString();
+    }
+
+    /**
+     *  This method does a correct replacement of a single link, taking into account anchors and attachments.
+     */
+    private String replaceSingleLink( final WikiContext context, final String original, final String from, final String newlink ) {
+        final int hash = original.indexOf( '#' );
+        final int slash = original.indexOf( '/' );
+        String realLink = original;
+
+        if( hash != -1 ) {
+            realLink = original.substring( 0, hash );
+        }
+        if( slash != -1 ) {
+            realLink = original.substring( 0,slash );
+        }
+
+        realLink = MarkupParser.cleanLink( realLink );
+        final String oldStyleRealLink = MarkupParser.wikifyLink( realLink );
+        
+        //WikiPage realPage  = context.getEngine().getPage( reallink );
+        // WikiPage p2 = context.getEngine().getPage( from );
+        
+        // System.out.println("   "+reallink+" :: "+ from);
+        // System.out.println("   "+p+" :: "+p2);
+        
+        //
+        //  Yes, these point to the same page.
+        //
+        if( realLink.equals( from ) || original.equals( from ) || oldStyleRealLink.equals( from ) ) {
+            //
+            //  if the original contains blanks, then we should introduce a link, for example:  [My Page]  =>  [My Page|My Renamed Page]
+            final int blank = realLink.indexOf( " ");
+            
+            if( blank != -1 ) {
+                return original + "|" + newlink;
+            }
+            
+            return newlink +
+                   ( ( hash > 0 ) ? original.substring( hash ) : "" ) +
+                   ( ( slash > 0 ) ? original.substring( slash ) : "" ) ;
+        }
+        
+        return original;
+    }
+}