This is an automated email from the ASF dual-hosted git repository.

juanpablo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jspwiki.git

commit 66d89ff8881dd2bdd2c127673d00adbddd58971b
Author: juanpablo <[email protected]>
AuthorDate: Fri Mar 13 20:02:22 2020 +0100

    JSPWIKI-303: PageFilter backwards compatibility while having a clean, 
public API
---
 jspwiki-210-adapters/pom.xml                       |  8 ++
 .../apache/wiki/api/filters/BasicPageFilter.java   | 91 ++++++++++++++++++++++
 .../wiki/api/filters/FilterSupportOperations.java  | 54 +++++++++++++
 .../org/apache/wiki/api/filters/PageFilter.java    | 73 ++++++++++-------
 4 files changed, 200 insertions(+), 26 deletions(-)

diff --git a/jspwiki-210-adapters/pom.xml b/jspwiki-210-adapters/pom.xml
index 8b4126b..cde8e1c 100644
--- a/jspwiki-210-adapters/pom.xml
+++ b/jspwiki-210-adapters/pom.xml
@@ -34,6 +34,14 @@
       <groupId>${project.groupId}</groupId>
       <artifactId>jspwiki-main</artifactId>
       <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>jspwiki-api</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
     </dependency>
 
     <dependency>
diff --git 
a/jspwiki-210-adapters/src/main/java/org/apache/wiki/api/filters/BasicPageFilter.java
 
b/jspwiki-210-adapters/src/main/java/org/apache/wiki/api/filters/BasicPageFilter.java
new file mode 100644
index 0000000..1be7789
--- /dev/null
+++ 
b/jspwiki-210-adapters/src/main/java/org/apache/wiki/api/filters/BasicPageFilter.java
@@ -0,0 +1,91 @@
+/* 
+    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.api.filters;
+
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.WikiEngine;
+import org.apache.wiki.api.core.Context;
+import org.apache.wiki.api.core.Engine;
+import org.apache.wiki.api.exceptions.FilterException;
+
+import java.lang.reflect.Method;
+import java.util.Properties;
+
+import static 
org.apache.wiki.api.filters.FilterSupportOperations.executePageFilterPhase;
+import static 
org.apache.wiki.api.filters.FilterSupportOperations.methodOfNonPublicAPI;
+
+
+/**
+ * Hooks all filters not using the public api into it.
+ */
+public class BasicPageFilter extends BasePageFilter {
+
+    public void initialize( final WikiEngine engine, final Properties 
properties ) throws FilterException {
+        this.m_engine = engine;
+    }
+
+    public String preTranslate( final WikiContext wikiContext, final String 
content ) throws FilterException {
+        return content;
+    }
+
+    public String preTranslate( final Context wikiContext, final String 
content ) throws FilterException {
+        final Method m = methodOfNonPublicAPI( this, "preTranslate", 
"org.apache.wiki.WikiContext", "java.lang.String" );
+        return executePageFilterPhase( () -> content, m, this, wikiContext, 
content );
+        // return content;
+    }
+
+    public String postTranslate( final WikiContext wikiContext, final String 
htmlContent ) throws FilterException {
+        return htmlContent;
+    }
+
+    public String postTranslate( final Context wikiContext, final String 
htmlContent ) throws FilterException {
+        final Method m = methodOfNonPublicAPI( this, "postTranslate", 
"org.apache.wiki.WikiContext", "java.lang.String" );
+        return executePageFilterPhase( () -> htmlContent, m, this, 
wikiContext, htmlContent );
+        // return htmlContent;
+    }
+
+    public String preSave( final WikiContext wikiContext, final String content 
) throws FilterException {
+        return content;
+    }
+
+    public String preSave( final Context wikiContext, final String content ) 
throws FilterException {
+        final Method m = methodOfNonPublicAPI( this, "preSave", 
"org.apache.wiki.WikiContext", "java.lang.String" );
+        return executePageFilterPhase( () -> content, m, this, wikiContext, 
content );
+        // return content;
+    }
+
+    public void postSave( final WikiContext wikiContext, final String content 
) throws FilterException {
+    }
+
+    public void postSave( final Context wikiContext, final String content ) 
throws FilterException {
+        final Method m = methodOfNonPublicAPI( this, "postSave", 
"org.apache.wiki.WikiContext", "java.lang.String" );
+        executePageFilterPhase( () -> null, m, this, content );
+        // empty method
+    }
+
+    public void destroy( final WikiEngine engine ) {
+    }
+
+    public void destroy( final Engine engine ) {
+        final Method m = methodOfNonPublicAPI( this, "destroy", 
"org.apache.wiki.WikiEngine" );
+        executePageFilterPhase( () -> null, m, this, engine );
+        // empty method
+    }
+
+}
diff --git 
a/jspwiki-api/src/main/java/org/apache/wiki/api/filters/FilterSupportOperations.java
 
b/jspwiki-api/src/main/java/org/apache/wiki/api/filters/FilterSupportOperations.java
new file mode 100644
index 0000000..85bdecd
--- /dev/null
+++ 
b/jspwiki-api/src/main/java/org/apache/wiki/api/filters/FilterSupportOperations.java
@@ -0,0 +1,54 @@
+package org.apache.wiki.api.filters;
+
+import org.apache.log4j.Logger;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.function.Supplier;
+
+
+class FilterSupportOperations {
+
+    private static final Logger LOG = Logger.getLogger( 
FilterSupportOperations.class );
+
+    /**
+     * Checks if a given object is using old, non public API for a given 
Filter. This check is two-fold:
+     * <ul>
+     *     <li>if the page filter class name starts with JSPWiki base package 
we assume we're using latest JSPWiki, and the public API</li>
+     *     <li>we try to execute the old, non public API equivalent method 
passed as parameters. If it exists, well, it is using the
+     *     old, non public API, and we return the method execution. In any 
other case we return null</li>
+     * </ul>
+     *
+     * @param pf given object to check
+     * @param method requested method
+     * @param params class names denoting method parameters
+     * @return old, non public api method if it exists, {@code null} otherwise
+     */
+    static Method methodOfNonPublicAPI( final PageFilter pf, final String 
method, final String... params ) {
+        if( !pf.getClass().getName().startsWith( "org.apache.wiki" ) ) {
+            try {
+                final Class< ? >[] classes = new Class< ? >[ params.length ];
+                for( int i = 0; i < params.length; i++ ) {
+                    classes[ i ] = Class.forName( params[ i ] );
+                }
+                return pf.getClass().getMethod( method, classes );
+            } catch( final ClassNotFoundException | NoSuchMethodException e ) {
+                return null;
+            }
+        }
+
+        return null;
+    }
+
+    static < R > R executePageFilterPhase( final Supplier< R > s, final Method 
m, final PageFilter pf, final Object... params ) {
+        if( m != null ) {
+            try {
+                return ( R )m.invoke( pf, params );
+            } catch( final IllegalAccessException | InvocationTargetException 
e ) {
+                LOG.warn( "Problems using filter adapter: " + e.getMessage(), 
e );
+            }
+        }
+        return s.get();
+    }
+
+}
diff --git 
a/jspwiki-api/src/main/java/org/apache/wiki/api/filters/PageFilter.java 
b/jspwiki-api/src/main/java/org/apache/wiki/api/filters/PageFilter.java
index 5ef3da8..19cd401 100644
--- a/jspwiki-api/src/main/java/org/apache/wiki/api/filters/PageFilter.java
+++ b/jspwiki-api/src/main/java/org/apache/wiki/api/filters/PageFilter.java
@@ -22,21 +22,28 @@ import org.apache.wiki.api.core.Context;
 import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.api.exceptions.FilterException;
 
+import java.lang.reflect.Method;
 import java.util.Properties;
 
+import static 
org.apache.wiki.api.filters.FilterSupportOperations.executePageFilterPhase;
+import static 
org.apache.wiki.api.filters.FilterSupportOperations.methodOfNonPublicAPI;
+
 
 /**
- *  Provides a definition for a page filter.  A page filter is a class that 
can be used to transform the WikiPage content being saved or
- *  being loaded at any given time.
- *  <p>
- *  Note that the WikiContext.getPage() method always returns the context in 
which text is rendered, i.e. the original request.  Thus the
- *  content may actually be different content than what what the 
wikiContext.getPage() implies!  This happens often if you are for example
- *  including multiple pages on the same page.
- *  <p>
- *  PageFilters must be thread-safe!  There is only one instance of each 
PageFilter per each Engine invocation.  If you need to store data
- *  persistently, use VariableManager, or WikiContext.
- *  <p>
- *  As of 2.5.30, initialize() gains access to the Engine.
+ *  <p>Provides a definition for a page filter. A page filter is a class that 
can be used to transform the WikiPage content being saved or
+ *  being loaded at any given time.</p>
+ *  <p>Note that the Context#getPage() method always returns the context in 
which text is rendered, i.e. the original request. Thus the
+ *  content may actually be different content than what what the 
Context#getPage() implies! This happens often if you are for example
+ *  including multiple pages on the same page.</p>
+ *  <p>PageFilters must be thread-safe! There is only one instance of each 
PageFilter per each Engine invocation. If you need to store data
+ *  persistently, use VariableManager, or WikiContext.</p>
+ *  <p><strong>Design notes</strong></p>
+ *  <p>As of 2.5.30, initialize() gains access to the Engine.</p>
+ *  <p>As of 2.11.0.M7, almost all methods from BasicPageFilter end up here as 
default methods.</p>
+ *  <p>In order to preserve backwards compatibility with filters not using the 
public API, these default methods checks if a given filter
+ *  is using the old, non public API and, if that's the case attempt to 
execute the old, non public api corresponding method. If the filter
+ *  uses the public API, then the default callback is used. None of the 
default callbacks do anything, so it is a good idea for you to
+ *  implement only methods that you need.</p>
  */
 public interface PageFilter {
 
@@ -47,7 +54,7 @@ public interface PageFilter {
      *  @param properties The properties ripped from filters.xml.
      *  @throws FilterException If the filter could not be initialized. If 
this is thrown, the filter is not added to the internal queues.
      */
-    void initialize( Engine engine, Properties properties ) throws 
FilterException;
+    void initialize( final Engine engine, final Properties properties ) throws 
FilterException;
 
     /**
      *  This method is called whenever a page has been loaded from the 
provider, but not yet been sent through the markup-translation
@@ -55,36 +62,42 @@ public interface PageFilter {
      *
      *  @param context The current context.
      *  @param content WikiMarkup.
-     *  @return The modified wikimarkup content.
+     *  @return The modified wikimarkup content. Default implementation 
returns the markup as received.
      *  @throws FilterException If something goes wrong.  Throwing this causes 
the entire page processing to be abandoned.
      */
     default String preTranslate( final Context context, final String content ) 
throws FilterException {
-        return content;
+        final Method m = methodOfNonPublicAPI( this, "preTranslate", 
"org.apache.wiki.WikiContext", "java.lang.String" );
+        return executePageFilterPhase( () -> content, m, this, context, 
content );
+        // return content;
     }
 
     /**
      *  This method is called after a page has been fed through the 
translation process, so anything you are seeing here is translated
      *  content.  If you want to do any of your own WikiMarkup2HTML 
translation, do it here.
      *  
-     *  @param wikiContext The WikiContext.
-     *  @param htmlContent The translated HTML
-     *  @return The modified HTML
+     *  @param context The WikiContext.
+     *  @param htmlContent The translated HTML.
+     *  @return The modified HTML. Default implementation returns the 
translated html as received.
      *  @throws FilterException If something goes wrong.  Throwing this causes 
the entire page processing to be abandoned.
      */
-    default String postTranslate( final Context wikiContext, final String 
htmlContent ) throws FilterException {
-        return htmlContent;
+    default String postTranslate( final Context context, final String 
htmlContent ) throws FilterException {
+        final Method m = methodOfNonPublicAPI( this, "postTranslate", 
"org.apache.wiki.WikiContext", "java.lang.String" );
+        return executePageFilterPhase( () -> htmlContent, m, this, context, 
htmlContent );
+        // return htmlContent;
     }
 
     /**
      *  This method is called before the page has been saved to the 
PageProvider.
      *  
-     *  @param wikiContext The WikiContext
+     *  @param context The WikiContext
      *  @param content The wikimarkup that the user just wanted to save.
-     *  @return The modified wikimarkup
+     *  @return The modified wikimarkup. Default implementation returns the 
markup as received.
      *  @throws FilterException If something goes wrong.  Throwing this causes 
the entire page processing to be abandoned.
      */
-    default String preSave( final Context wikiContext, final String content ) 
throws FilterException {
-        return content;
+    default String preSave( final Context context, final String content ) 
throws FilterException {
+        final Method m = methodOfNonPublicAPI( this, "preSave", 
"org.apache.wiki.WikiContext", "java.lang.String" );
+        return executePageFilterPhase( () -> content, m, this, context, 
content );
+        // return content;
     }
 
     /**
@@ -93,11 +106,15 @@ public interface PageFilter {
      *  <p>
      *  Since the result is discarded from this method, this is only useful 
for things like counters, etc.
      *  
-     *  @param wikiContext The WikiContext
+     *  @param context The WikiContext
      *  @param content The content which was just stored.
      *  @throws FilterException If something goes wrong.  As the page is 
already saved, This is just logged.
      */
-    default void postSave( final Context wikiContext, final String content ) 
throws FilterException {}
+    default void postSave( final Context context, final String content ) 
throws FilterException {
+        final Method m = methodOfNonPublicAPI( this, "postSave", 
"org.apache.wiki.WikiContext", "java.lang.String" );
+        executePageFilterPhase( () -> null, m, this, content );
+        // empty method
+    }
 
     /**
      *  Called for every filter, e.g. on wiki engine shutdown. Use this if you 
have to 
@@ -106,6 +123,10 @@ public interface PageFilter {
      *  @param engine The Engine which owns this filter.
      *  @since 2.5.36
      */
-    default void destroy( final Engine engine ) {}
+    default void destroy( final Engine engine ) {
+        final Method m = methodOfNonPublicAPI( this, "destroy", 
"org.apache.wiki.WikiEngine" );
+        executePageFilterPhase( () -> null, m, this, engine );
+        // empty method
+    }
 
 }

Reply via email to