Author: ajaquith
Date: Wed Jan  6 16:43:50 2010
New Revision: 896518

URL: http://svn.apache.org/viewvc?rev=896518&view=rev
Log:
Minor refactoring to content-inspection package to better support multi-field 
inspections.

Removed:
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/CaptchaInspector.java
Modified:
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/AkismetInspector.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BanListInspector.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BotTrapInspector.java
    incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Change.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ChangeRateInspector.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspection.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionInterruptedException.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionPlan.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspector.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/LinkCountInspector.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/PatternInspector.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ReputationManager.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/SpamInspectionFactory.java
    
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/UserInspector.java
    incubator/jspwiki/trunk/src/java/org/apache/wiki/filters/SpamFilter.java
    
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/InspectionTest.java
    
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/ReputationManagerTest.java
    
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/SpamInspectionFactoryTest.java

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/AkismetInspector.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/AkismetInspector.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/AkismetInspector.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/AkismetInspector.java
 Wed Jan  6 16:43:50 2010
@@ -32,17 +32,24 @@
     {
         m_akismetAPIKey = TextUtil.getStringProperty( config.getProperties(), 
PROP_AKISMET_API_KEY, m_akismetAPIKey );
     }
+    
+    /**
+     * Always returns {...@link Scope#REQUEST}.
+     */
+    public Scope getScope()
+    {
+        return Scope.REQUEST;
+    }
 
     /**
      * Returns {...@link Finding.Result#FAILED} if Akismet determines the 
change is
      * spam; {...@code null} otherwise.
      * @param inspection the current Inspection
-     * @param content the content that is being inspected
-     * @param change the subset of the content that represents the added or
+     * @param change the current contents, plus content that represents the 
added or
      *            deleted text since the last change
      * @return {...@link Finding.Result#FAILED} if the test fails; {...@code 
null} otherwise
      */
-    public Finding[] inspect( Inspection inspection, String content, Change 
change )
+    public Finding[] inspect( Inspection inspection, Change change )
     {
         WikiContext context = inspection.getContext();
         HttpServletRequest req = context.getHttpRequest();

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BanListInspector.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BanListInspector.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BanListInspector.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BanListInspector.java
 Wed Jan  6 16:43:50 2010
@@ -15,6 +15,14 @@
  */
 public class BanListInspector implements Inspector
 {
+    /**
+     * Always returns {...@link Scope#REQUEST}.
+     */
+    public Scope getScope()
+    {
+        return Scope.REQUEST;
+    }
+
     public void initialize( InspectionPlan config )
     {
     }
@@ -23,12 +31,11 @@
      * Returns {...@link Finding.Result#FAILED} if the IP address is banned;
      * {...@code null} otherwise.
      * @param inspection the current Inspection
-     * @param content the content that is being inspected
-     * @param change the subset of the content that represents the added or
+     * @param change the current contents, plus content that represents the 
added or
      *            deleted text since the last change
      * @return {...@link Finding.Result#FAILED} if the test fails; {...@code 
null} otherwise
      */
-    public Finding[] inspect( Inspection inspection, String content, Change 
change )
+    public Finding[] inspect( Inspection inspection, Change change )
     {
         ReputationManager banList = 
inspection.getPlan().getReputationManager();
         WikiContext context = inspection.getContext();

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BotTrapInspector.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BotTrapInspector.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BotTrapInspector.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/BotTrapInspector.java
 Wed Jan  6 16:43:50 2010
@@ -24,6 +24,14 @@
     /** Request parameter containing the encoded payload. */
     public static final String REQ_SPAM_PARAM = "__wikiCheck";
 
+    /**
+     * Always returns {...@link Scope#REQUEST}.
+     */
+    public Scope getScope()
+    {
+        return Scope.REQUEST;
+    }
+
     public void initialize( InspectionPlan config )
     {
     }
@@ -32,12 +40,11 @@
      * Returns {...@link Finding.Result#FAILED} if any of the spam parameters 
are invalid;
      * {...@code null} otherwise.
      * @param inspection the current Inspection
-     * @param content the content that is being inspected
-     * @param change the subset of the content that represents the added or
+     * @param change the current contents, plus content that represents the 
added or
      *            deleted text since the last change
      * @return {...@link Finding.Result#FAILED} if the test fails; {...@code 
null} otherwise
      */
-    public Finding[] inspect( Inspection inspection, String content, Change 
change )
+    public Finding[] inspect( Inspection inspection, Change change )
     {
         WikiContext context = inspection.getContext();
         HttpServletRequest request = context.getHttpRequest();

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Change.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Change.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Change.java 
(original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Change.java 
Wed Jan  6 16:43:50 2010
@@ -11,12 +11,16 @@
 import org.apache.wiki.api.WikiPage;
 
 /**
- * Embodies the differences between two Strings, or between a proposed page 
text
- * and the page's current value. This class is immutable and therefore
+ * Embodies the differences between two Strings, or between a proposed text
+ * and current value for a field. This class is immutable and therefore
  * thread-safe.
  */
 public class Change
 {
+    private final String m_name;
+
+    private final String m_value;
+
     private final String m_change;
 
     private final int m_adds;
@@ -27,23 +31,25 @@
      * Convenience method that returns a Change object representing a new text
      * string. String {...@code newText} will be represented as a single add.
      * 
+     * @param name the name of the field that is being changed
      * @param newText the new text
      * @return the Change object
      */
-    public static Change getChange( String newText )
+    public static Change getChange( String name, String newText )
     {
-        return new Change( 1, 0, newText );
+        return new Change( name, newText, 1, 0, newText );
     }
 
     /**
      * Compares a proposed (new) text string against an existing (old) String,
      * and returns a Change object representing any differences between the 
two.
      * 
+     * @param name the name of the field that is being changed
      * @param oldText the old text
      * @param newText the new text
      * @return the change, or the empty string if there is no difference
      */
-    public static Change getChange( String oldText, String newText ) throws 
DifferentiationFailedException
+    public static Change getChange( String name, String oldText, String 
newText ) throws DifferentiationFailedException
     {
         if( oldText == null || newText == null )
         {
@@ -57,7 +63,7 @@
 
         if( rev == null || rev.size() == 0 )
         {
-            return new Change( 0, 0, null );
+            return new Change( name, newText, 0, 0, null );
         }
 
         int adds = 0;
@@ -82,13 +88,18 @@
                 removals++;
             }
         }
-        return new Change( adds, removals, changes.toString() );
+        return new Change( name, newText, adds, removals, changes.toString() );
+    }
+
+    public String getName()
+    {
+        return m_name;
     }
 
     /**
      * Compares a proposed text String for a page against the page's current
      * content, and returns a Change object representing any differences 
between
-     * the two.
+     * the two. The field's name is {...@code page}.
      * 
      * @param context the wiki context containing the page. This must be a
      *            page-related WikiContext
@@ -122,12 +133,14 @@
             }
         }
 
-        return getChange( changes.toString(), newText );
+        return getChange( "page", changes.toString(), newText );
     }
 
-    private Change( int adds, int removals, String change )
+    private Change( String name, String newText, int adds, int removals, 
String change )
     {
         super();
+        m_name = name;
+        m_value = newText;
         m_adds = adds;
         m_removals = removals;
         m_change = change;
@@ -136,7 +149,7 @@
     public boolean equals( Object o )
     {
         if( o instanceof Change )
-            return m_change.equals( ((Change) o).m_change );
+            return m_name.equals( ((Change) o).m_name ) && m_change.equals( 
((Change) o).m_change );
 
         return false;
     }
@@ -156,6 +169,15 @@
         return m_removals;
     }
 
+    /**
+     * Returns the current (i.e., new) value of the Change. 
+     * @return the current value
+     */
+    public String getValue()
+    {
+        return m_value;
+    }
+
     public int hashCode()
     {
         return m_change.hashCode() + 17;

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ChangeRateInspector.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ChangeRateInspector.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ChangeRateInspector.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ChangeRateInspector.java
 Wed Jan  6 16:43:50 2010
@@ -38,6 +38,14 @@
      */
     private int m_limitSimilarChanges = 2;
 
+    /**
+     * Always returns {...@link Scope#FIELD}.
+     */
+    public Scope getScope()
+    {
+        return Scope.FIELD;
+    }
+
     public void initialize( InspectionPlan config )
     {
         Properties props = config.getProperties();
@@ -45,7 +53,7 @@
 
         m_limitSimilarChanges = TextUtil.getIntegerProperty( props, 
PROP_SIMILARCHANGES, m_limitSimilarChanges );
 
-        log.info( "# Spam filter initialized.  Temporary ban time " + 
config.getReputationManager().getBanTime()
+        log.info( "Spam filter initialized.  Temporary ban time " + 
config.getReputationManager().getBanTime()
                   + " mins, max page changes/minute: " + 
m_limitSinglePageChanges );
     }
 
@@ -53,12 +61,11 @@
      * Returns {...@link Finding.Result#FAILED} if the user has recently 
submitted too many
      * aggregate or identical changes; {...@code null} otherwise.
      * @param inspection the current Inspection
-     * @param content the content that is being inspected
-     * @param change the subset of the content that represents the added or
+     * @param change the current contents, plus content that represents the 
added or
      *            deleted text since the last change
      * @return {...@link Finding.Result#FAILED} if the test fails; {...@code 
null} otherwise
      */
-    public Finding[] inspect( Inspection inspection, String content, Change 
change )
+    public Finding[] inspect( Inspection inspection, Change change )
     {
         HttpServletRequest req = inspection.getContext().getHttpRequest();
         if( req == null )
@@ -81,7 +88,7 @@
             }
 
             // Has this change been seen before?
-            if( host.getChange() != null && host.getChange().equals( change ) )
+            if( host.getChange() != null && host.getChange().equals( 
change.getChange() ) )
             {
                 changeCounter++;
             }

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspection.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspection.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspection.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspection.java
 Wed Jan  6 16:43:50 2010
@@ -4,6 +4,7 @@
 
 import org.apache.wiki.WikiContext;
 import org.apache.wiki.api.WikiPage;
+import org.apache.wiki.content.inspect.Inspector.Scope;
 import org.apache.wiki.log.Logger;
 import org.apache.wiki.log.LoggerFactory;
 import org.apache.wiki.util.TextUtil;
@@ -165,69 +166,64 @@
     }
 
     /**
-     * Executes the chain of Inspector objects for a supplied content String
-     * and, optionally, the {...@link Change} object associated with it. All 
scores
-     * are initially reset to zero when this method executes.
-     * 
-     * @param content the content String. If {...@code null}, this method 
returns
-     *            silently.
-     * @param change the Change represented by {...@code content}, relative to 
a
-     *            previous version. If this parameter is {...@code null}, the 
text
-     *            is considered "all new," and a Change object will be created
-     *            by calling {...@link Change#getChange(String)}.
+     * Executes the chain of Inspector objects for a supplied number of
+     * {...@link Change} objects. All scores are initially reset to zero when 
this
+     * method executes. The Inspectors with {...@link Scope#REQUEST} execute 
first,
+     * in order. Then the Inspectors with {...@link Scope#FIELD} execute, in 
order,
+     * once per Target.
+     * 
+     * @param changes one or more field changes; that is, a field-name,
+     *            content and content-change tuple. If {...@code null}, this 
method will
+     *            execute only the Inspectors that have scope of
+     *            {...@link Scope#REQUEST}.
      */
-    public void inspect( String content, Change change )
+    public void inspect( Change... changes )
     {
-        if( content == null )
-        {
-            return;
-        }
-        if( change == null )
-        {
-            change = Change.getChange( content );
-        }
-
         m_scores.clear();
         m_findings.clear();
         Inspector[] inspectors = m_plan.getInspectors();
-        InspectionListener currentListener = null;
+
         try
         {
+            // Execute the request-scoped inspectors
             for( Inspector inspector : inspectors )
             {
-                float weight = m_plan.getWeight( inspector );
-                Finding[] findings = inspector.inspect( this, content, change 
);
-                if( findings != null )
+                if( inspector.getScope() == Scope.REQUEST )
+                {
+                    Finding[] findings = inspector.inspect( this, null );
+                    processFindings( inspector, findings );
+                }
+            }
+
+            if( changes == null )
+            {
+                return;
+            }
+
+            // Execute the field-scoped inspectors
+            for( Change change : changes )
+            {
+                for( Inspector inspector : inspectors )
                 {
-                    for( Finding finding : findings )
+                    if( inspector.getScope() == Scope.FIELD )
                     {
-                        // Increase/decrease score here
-                        Topic topic = finding.getTopic();
-                        updateScore( topic, finding, weight );
-
-                        // Notify any listeners that the score has changed
-                        List<InspectionListener> listeners = m_listeners.get( 
topic );
-                        if( listeners != null )
-                        {
-                            for( InspectionListener listener : listeners )
-                            {
-                                currentListener = listener;
-                                listener.changedScore( this, finding );
-                            }
-                        }
-                        log( inspector, finding, weight );
+                        Finding[] findings = inspector.inspect( this, change );
+                        processFindings( inspector, findings );
                     }
                 }
             }
         }
         catch( InspectionInterruptedException e )
         {
-            log.debug( "Inspection " + m_uid + " interrupted by " + 
currentListener.getClass().getName() );
+            log.debug( "Inspection " + m_uid + " interrupted by " + 
e.getSource().getClass().getName() );
         }
 
-        // Add the change to the ReputationManager's list of recent changes
+        // Add the changes to the ReputationManager's list of recent changes
         ReputationManager mgr = m_plan.getReputationManager();
-        mgr.addModifier( m_context.getHttpRequest(), change );
+        for( Change change : changes )
+        {
+            mgr.addModifier( m_context.getHttpRequest(), change );
+        }
     }
 
     /**
@@ -250,6 +246,37 @@
     }
 
     /**
+     * Processes any Findings that result from an Inspector's processing
+     * @param inspector the inspector that executed
+     * @param findings the findings it produced
+     * @throws InspectionInterruptedException if any listeners interrupt 
processing
+     */
+    private void processFindings( Inspector inspector, Finding[] findings ) 
throws InspectionInterruptedException
+    {
+        float weight = m_plan.getWeight( inspector );
+        if( findings != null )
+        {
+            for( Finding finding : findings )
+            {
+                // Increase/decrease score here
+                Topic topic = finding.getTopic();
+                updateScore( topic, finding, weight );
+
+                // Notify any listeners that the score has changed
+                List<InspectionListener> listeners = m_listeners.get( topic );
+                if( listeners != null )
+                {
+                    for( InspectionListener listener : listeners )
+                    {
+                        listener.changedScore( this, finding );
+                    }
+                }
+                log( inspector, finding, weight );
+            }
+        }
+    }
+
+    /**
      * Logs a Finding to the inspection log.
      * 
      * @param finding the Finding to log

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionInterruptedException.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionInterruptedException.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionInterruptedException.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionInterruptedException.java
 Wed Jan  6 16:43:50 2010
@@ -7,12 +7,21 @@
 {
     private static final long serialVersionUID = -2989289836320240924L;
 
+    private final InspectionListener m_source;
+
     /**
      * Constructs a new InspectionInterruptedException with a String message.
+     * @param source the source of the interruption
      * @param message the message
      */
-    public InspectionInterruptedException( String message )
+    public InspectionInterruptedException( InspectionListener source, String 
message )
     {
         super( message );
+        m_source = source;
+    }
+
+    public InspectionListener getSource()
+    {
+        return m_source;
     }
 }

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionPlan.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionPlan.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionPlan.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/InspectionPlan.java
 Wed Jan  6 16:43:50 2010
@@ -23,8 +23,11 @@
 
     private final ReputationManager m_reputationManager;
 
-    private Captcha m_captcha = null;
-
+    /**
+     * Returns the {...@link ReputationManager} associated with the 
InspectionPlan.
+     * 
+     * @return the reputation manager
+     */
     public ReputationManager getReputationManager()
     {
         return m_reputationManager;
@@ -38,6 +41,15 @@
 
     private final Map<Inspector, Float> m_inspectors;
 
+    /**
+     * Constructs a new InspectionPlan with a supplied set of properties for
+     * configuration. When the plan is instantiated, a new
+     * {...@link ReputationManager} will also be created. Its ban-time will be 
set
+     * to the value specified in the properties file by key
+     * {...@link #PROP_BANTIME}.
+     * 
+     * @param props the wiki properties
+     */
     public InspectionPlan( Properties props )
     {
         super();
@@ -61,34 +73,40 @@
     {
         m_inspectors.put( inspector, Float.valueOf( weight ) );
         inspector.initialize( this );
-        if ( inspector instanceof CaptchaInspector )
-        {
-            m_captcha = ((CaptchaInspector)inspector).getCaptcha();
-        }
     }
 
     /**
-     * Returns the {...@link Captcha} object, if one was initialized by
-     * a {...@link CaptchaInspector} passed to {...@link 
#addInspector(Inspector, float)}.
-     * @return the Captcha, or {...@code null} if not supplied or configured
+     * Returns the array of {...@link Inspector} objects that are part of the
+     * InspectionPlan. The order of the array reflects the order in which the
+     * Inspectors were added.
+     * 
+     * @return the array, which may be zero-length if no Inspectors were added
+     *         before calling this method
      */
-    public Captcha getCaptcha()
-    {
-        return m_captcha;
-    }
-
     public Inspector[] getInspectors()
     {
         Set<Inspector> inspectors = m_inspectors.keySet();
         return inspectors.toArray( new Inspector[inspectors.size()] );
     }
 
+    /**
+     * Returns the weight for a particular Inspector. If no weight was assigned
+     * or if the inspector does not exist, returns {0f}.
+     * 
+     * @param inspector the inspector whose weight is being sought
+     * @return the weight
+     */
     public float getWeight( Inspector inspector )
     {
         Float weight = m_inspectors.get( inspector );
         return weight == null ? 0f : weight.floatValue();
     }
 
+    /**
+     * The wiki properties used to initialize the InspectionPlan.
+     * 
+     * @return the properties
+     */
     public Properties getProperties()
     {
         return m_props;

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspector.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspector.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspector.java 
(original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/Inspector.java 
Wed Jan  6 16:43:50 2010
@@ -24,10 +24,30 @@
      * {...@code null}.
      * 
      * @param inspection the current Inspection
-     * @param content the content that
-     * @param change the subset of the content that represents the added or
+     * @param change the current contents, plus content that represents the 
added or
      *            deleted text since the last change
      * @return the Findings
      */
-    public Finding[] inspect( Inspection inspection, String content, Change 
change );
+    public Finding[] inspect( Inspection inspection, Change change );
+
+    /**
+     * Returns the {...@link Scope} of the inspector: per-field or per-request.
+     * Request-scoped inspectors are invoked just once for the entire request.
+     * Field-scoped inspectors are invoked on every field that is being
+     * protected.
+     */
+    public Scope getScope();
+
+    /**
+     * The scope of an inspector, which can operate on individual fields or for
+     * the entire request.
+     */
+    public enum Scope
+    {
+        /** Inspector that should be executed for every field. */
+        FIELD,
+
+        /** Inspector that should be executed just once per form. */
+        REQUEST
+    }
 }

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/LinkCountInspector.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/LinkCountInspector.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/LinkCountInspector.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/LinkCountInspector.java
 Wed Jan  6 16:43:50 2010
@@ -36,6 +36,14 @@
      */
     private int m_maxUrls = 10;
 
+    /**
+     * Always returns {...@link Scope#FIELD}.
+     */
+    public Scope getScope()
+    {
+        return Scope.FIELD;
+    }
+
     public void initialize( InspectionPlan config )
     {
         m_config = config;
@@ -56,12 +64,11 @@
      * Returns {...@link Finding.Result#FAILED} if the proposed change 
contains too many
      * links; {...@code null} otherwise.
      * @param inspection the current Inspection
-     * @param content the content that is being inspected
-     * @param change the subset of the content that represents the added or
+     * @param change the current contents, plus content that represents the 
added or
      *            deleted text since the last change
      * @return {...@link Finding.Result#FAILED} if the test fails; {...@code 
null} otherwise
      */
-    public Finding[] inspect( Inspection inspection, String content, Change 
change )
+    public Finding[] inspect( Inspection inspection, Change change )
     {
         // Calculate the number of links in the addition.
         String tstChange = change.toString();

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/PatternInspector.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/PatternInspector.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/PatternInspector.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/PatternInspector.java
 Wed Jan  6 16:43:50 2010
@@ -45,6 +45,14 @@
 
     private static final String LISTVAR = "spamwords";
 
+    /**
+     * Always returns {...@link Scope#FIELD}.
+     */
+    public Scope getScope()
+    {
+        return Scope.FIELD;
+    }
+
     public void initialize( InspectionPlan config )
     {
         Properties properties = config.getProperties();
@@ -56,12 +64,11 @@
      * Returns {...@link Finding.Result#FAILED} if any contents are contained 
on
      * the banned-word pattern blacklist; {...@code null} otherwise.
      * @param inspection the current Inspection
-     * @param content the content that is being inspected
-     * @param change the subset of the content that represents the added or
+     * @param change the current contents, plus content that represents the 
added or
      *            deleted text since the last change
      * @return {...@link Finding.Result#FAILED} if the test fails; {...@code 
null} otherwise
      */
-    public Finding[] inspect( Inspection inspection, String content, Change 
change )
+    public Finding[] inspect( Inspection inspection, Change change )
     {
         WikiContext context = inspection.getContext();
         refreshBlacklists( context );

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ReputationManager.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ReputationManager.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ReputationManager.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/ReputationManager.java
 Wed Jan  6 16:43:50 2010
@@ -25,7 +25,7 @@
 
         private final String m_address;
 
-        private final Change m_change;
+        private final String m_change;
 
         /**
          * @param ipaddress
@@ -35,7 +35,7 @@
         public Host( String ipaddress, Change change, int releaseTime )
         {
             m_address = ipaddress;
-            m_change = change;
+            m_change = change == null ? null : change.getChange();
             m_releaseTime = System.currentTimeMillis() + releaseTime * 60 * 
1000L;
         }
 
@@ -49,7 +49,7 @@
             return m_address;
         }
 
-        public Change getChange()
+        public String getChange()
         {
             return m_change;
         }

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/SpamInspectionFactory.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/SpamInspectionFactory.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/SpamInspectionFactory.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/SpamInspectionFactory.java
 Wed Jan  6 16:43:50 2010
@@ -5,6 +5,7 @@
 import java.util.Properties;
 
 import org.apache.wiki.WikiEngine;
+import org.apache.wiki.api.WikiException;
 import org.apache.wiki.log.Logger;
 import org.apache.wiki.log.LoggerFactory;
 
@@ -12,7 +13,7 @@
  * Factory for creating spam-related {...@link Inspection} and
  * {...@link InspectionPlan} objects.
  */
-public class SpamInspectionFactory
+public final class SpamInspectionFactory
 {
     private static final Logger log = LoggerFactory.getLogger( 
SpamInspectionFactory.class );
 
@@ -39,7 +40,7 @@
             float currentScore = inspection.getScore( Topic.SPAM );
             if( currentScore < m_limit )
             {
-                throw new InspectionInterruptedException( "Limit reached." );
+                throw new InspectionInterruptedException( this, "Limit 
reached." );
             }
         }
     }
@@ -65,14 +66,47 @@
     /** Default limit at which we consider something to be spam. */
     protected static final float DEFAULT_SCORE_LIMIT = -0.01f;
 
+    protected static final String DEFAULT_CAPTCHA_CLASS = 
AsirraCaptcha.class.getName();
+
     /**
      * Default weight for Inspectors.
      */
     protected static final float DEFAULT_WEIGHT = 0f;
 
+    protected static final String PROP_CAPTCHA_CLASS = 
"jspwiki.captcha.implementation";
+
     private static final Map<WikiEngine, InspectionPlan> c_plans = new 
HashMap<WikiEngine, InspectionPlan>();
 
+    private static final Map<WikiEngine, Challenge> c_captchas = new 
HashMap<WikiEngine,Challenge>();
+
     private static final Map<WikiEngine, Float> c_spamLimits = new 
HashMap<WikiEngine, Float>();
+    
+    public static float defaultSpamLimit( WikiEngine engine )
+    {
+        Float limit = c_spamLimits.get( engine );
+        return limit == null ? DEFAULT_SCORE_LIMIT : limit.floatValue();
+    }
+
+    /**
+     * Looks up and returns the CAPTCHA ({...@link Challenge}) for a given 
WikiEngine.
+     * If the CAPTCHA does not exist, it will be created. <em>Callers should
+     * check for {...@code null} values, which indicate a CAPTCHA was
+     * not configured.</em>
+     * @return the CAPTCHA, or {...@code null} if one was not specified
+     * in the wiki engine's properties (or not configured)
+     */
+    public static final Challenge getCaptcha( WikiEngine engine ) throws 
WikiException
+    {
+        if ( c_captchas.containsKey( engine ) )
+        {
+            return c_captchas.get( engine );
+        }
+
+        // Lazily create
+        Challenge captcha = initCaptcha( engine.getWikiProperties() );
+        c_captchas.put( engine, captcha );
+        return captcha;
+    }
 
     /**
      * <p>
@@ -88,7 +122,6 @@
      * <li>{...@link BotTrapInspector}</li>
      * <li>{...@link AkismetInspector}</li>
      * <li>{...@link PatternInspector}</li>
-     * <li>{...@link CaptchaInspector}</li>
      * </ul>
      * <p>
      * The weights for each Inspector will be determined by examining {...@code
@@ -118,10 +151,11 @@
         plan.addInspector( new BotTrapInspector(), getWeight( props, 
BotTrapInspector.class ) );
         plan.addInspector( new AkismetInspector(), getWeight( props, 
AkismetInspector.class ) );
         plan.addInspector( new PatternInspector(), getWeight( props, 
PatternInspector.class ) );
-        plan.addInspector( new CaptchaInspector(), getWeight( props, 
CaptchaInspector.class ) );
-
         c_plans.put( engine, plan );
 
+        // Figure out the Challenge to use for this WikiEngine
+
+
         // Get the default spam score limits
         float limit = DEFAULT_SCORE_LIMIT;
         String limitString = props.getProperty( PROP_SCORE_LIMIT, 
String.valueOf( DEFAULT_SCORE_LIMIT ) );
@@ -160,10 +194,47 @@
         return weight;
     }
 
-    public static float defaultSpamLimit( WikiEngine engine )
+    /**
+     * Returns a new instance of the configured CAPTCHA implementation class,
+     * if one was specified by the {...@link #PROP_CAPTCHA_CLASS} property. If 
one
+     * was not specified, this method will attempt to initialize the class
+     * named by {...@link #DEFAULT_CAPTCHA_CLASS}. This method is guaranteed to
+     * return a no...@code null} value.
+     * 
+     * @param props the properties to examine
+     * @return an initialized CAPTCHA implementing class
+     * @throws WikiException if the CAPTCHA implementation cannot be found or
+     * initialized for any reason; the original Exception will be wrapped
+     */
+    protected static Challenge initCaptcha( Properties props ) throws 
WikiException
     {
-        Float limit = c_spamLimits.get( engine );
-        return limit == null ? DEFAULT_SCORE_LIMIT : limit.floatValue();
-    }
+        String captchaClassName = props.getProperty( PROP_CAPTCHA_CLASS, 
DEFAULT_CAPTCHA_CLASS );
+        Class<?> captchaClass = null;
+        try
+        {
+            captchaClass = Class.forName( captchaClassName );
+        }
+        catch( ClassNotFoundException e )
+        {
+            String msg = "No CAPTCHA implementation found for class " + 
captchaClassName;
+            log.error( msg, e );
+            throw new WikiException( msg, e );
+        }
 
+        Challenge captcha = null;
+        if( captchaClass != null )
+        {
+            try
+            {
+                captcha = (Challenge) captchaClass.newInstance();
+            }
+            catch( Exception e )
+            {
+                String msg = "Could not create CAPTCHA instance for class " + 
captchaClassName;
+                log.error( msg, e );
+                throw new WikiException( msg, e );
+            }
+        }
+        return captcha;
+    }
 }

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/UserInspector.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/UserInspector.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/UserInspector.java
 (original)
+++ 
incubator/jspwiki/trunk/src/java/org/apache/wiki/content/inspect/UserInspector.java
 Wed Jan  6 16:43:50 2010
@@ -22,6 +22,14 @@
      */
     private boolean m_ignoreAuthenticated = false;
 
+    /**
+     * Always returns {...@link Scope#REQUEST}.
+     */
+    public Scope getScope()
+    {
+        return Scope.REQUEST;
+    }
+
     public void initialize( InspectionPlan config )
     {
         Properties props = config.getProperties();
@@ -34,12 +42,11 @@
      * {...@link Finding.Result#PASSED}. Otherwise, the method returns 
{...@code null}
      * but does not affect the score in any way.
      * @param inspection the current Inspection
-     * @param content the content that is being inspected
-     * @param change the subset of the content that represents the added or
+     * @param change the current contents, plus content that represents the 
added or
      *            deleted text since the last change
      * @return {...@link Finding.Result#FAILED} if the test fails; {...@code 
null} otherwise
      */
-    public Finding[] inspect( Inspection inspection, String content, Change 
change )
+    public Finding[] inspect( Inspection inspection, Change change )
     {
         WikiContext context = inspection.getContext();
         if( context.hasAdminPermissions() )

Modified: 
incubator/jspwiki/trunk/src/java/org/apache/wiki/filters/SpamFilter.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/src/java/org/apache/wiki/filters/SpamFilter.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- incubator/jspwiki/trunk/src/java/org/apache/wiki/filters/SpamFilter.java 
(original)
+++ incubator/jspwiki/trunk/src/java/org/apache/wiki/filters/SpamFilter.java 
Wed Jan  6 16:43:50 2010
@@ -150,7 +150,7 @@
         Inspection inspection = new Inspection( context, m_plan );
         float spamScoreLimit = SpamInspectionFactory.defaultSpamLimit( 
m_engine );
         SpamInspectionFactory.setSpamLimit( inspection, spamScoreLimit );
-        inspection.inspect( content, change );
+        inspection.inspect( change );
         float spamScore = inspection.getScore( Topic.SPAM );
         context.setVariable( ATTR_SPAMFILTER_SCORE, spamScore );
 
@@ -184,7 +184,7 @@
     private String getRedirectPage( WikiContext ctx )
     {
         if( m_useCaptcha )
-            return ctx.getURL( WikiContext.NONE, "Captcha.jsp", "page=" + 
ctx.getEngine().encodeName( ctx.getPage().getName() ) );
+            return ctx.getURL( WikiContext.NONE, "Challenge.jsp", "page=" + 
ctx.getEngine().encodeName( ctx.getPage().getName() ) );
 
         return ctx.getURL( WikiContext.VIEW, m_errorPage );
     }

Modified: 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/InspectionTest.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/InspectionTest.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/InspectionTest.java
 (original)
+++ 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/InspectionTest.java
 Wed Jan  6 16:43:50 2010
@@ -82,7 +82,7 @@
             m_executed = true;
             if( m_interrupt )
             {
-                throw new InspectionInterruptedException( "Interrupted by 
TestInspectionListener." );
+                throw new InspectionInterruptedException( this, "Interrupted 
by TestInspectionListener." );
             }
         }
 
@@ -111,10 +111,15 @@
         {
         }
 
-        public Finding[] inspect( Inspection inspection, String content, 
Change change )
+        public Finding[] inspect( Inspection inspection, Change change )
         {
             return new Finding[] { new Finding( m_topic, m_result, 
m_result.toString() ) };
         }
+
+        public Scope getScope()
+        {
+            return Scope.FIELD;
+        }
     }
 
     public void testInspectOneTopic() throws Exception
@@ -133,7 +138,7 @@
         WikiPage page = m_engine.getFrontPage( ContentManager.DEFAULT_SPACE );
         WikiContext context = m_engine.getWikiContextFactory().newViewContext( 
page );
         Inspection inspection = new Inspection( context, plan );
-        inspection.inspect( "Sample text", null );
+        inspection.inspect( Change.getChange( "page", "Sample text" ) );
 
         // Result should be 2: +4 -2 +0
         assertEquals( 2.0f, inspection.getScore( Topic.SPAM ) );
@@ -157,7 +162,7 @@
         WikiPage page = m_engine.getFrontPage( ContentManager.DEFAULT_SPACE );
         WikiContext context = m_engine.getWikiContextFactory().newViewContext( 
page );
         Inspection inspection = new Inspection( context, plan );
-        inspection.inspect( "Sample text", null );
+        inspection.inspect( Change.getChange( "page", "Sample text" ) );
 
         // Result should be 2: +4 -2 +0
         assertEquals( 2.0f, inspection.getScore( Topic.SPAM ) );
@@ -184,7 +189,7 @@
         WikiPage page = m_engine.getFrontPage( ContentManager.DEFAULT_SPACE );
         WikiContext context = m_engine.getWikiContextFactory().newViewContext( 
page );
         Inspection inspection = new Inspection( context, plan );
-        inspection.inspect( "Sample text", null );
+        inspection.inspect( Change.getChange( "page", "Sample text" ) );
 
         // Verify that the findings were added in order
         Finding[] findings = inspection.getFindings( Topic.SPAM );
@@ -220,7 +225,7 @@
         inspection.addListener( new Topic( "Topic2" ), listener2 );
 
         // Run the inspection
-        inspection.inspect( "Sample text", null );
+        inspection.inspect( Change.getChange( "page", "Sample text" ) );
 
         // Verify that Spam listener fired, but Topic2 listener did not
         assertTrue( listener.isExecuted() );
@@ -248,7 +253,7 @@
         inspection.addListener( new Topic( "Topic2" ), listener2 );
 
         // Run the inspection
-        inspection.inspect( "Sample text", null );
+        inspection.inspect( Change.getChange( "page", "Sample text" ) );
 
         // Verify that Spam listener fired, but Topic2 listener did not
         assertTrue( listener.isExecuted() );

Modified: 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/ReputationManagerTest.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/ReputationManagerTest.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/ReputationManagerTest.java
 (original)
+++ 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/ReputationManagerTest.java
 Wed Jan  6 16:43:50 2010
@@ -68,7 +68,7 @@
         HttpServletRequest req = new MockHttpServletRequest( "/JSPWiki", 
"/servlet" );
         InspectionPlan plan = new InspectionPlan( m_props );
         ReputationManager reputationManager = plan.getReputationManager();
-        Change change = Change.getChange( "changed text" );
+        Change change = Change.getChange( "page", "changed text" );
         reputationManager.addModifier( req, change );
         assertEquals( 1, reputationManager.getModifiers().length );
         Host host = reputationManager.getModifiers()[0];
@@ -76,10 +76,10 @@
         assertNotNull( host.getAddedTime() );
         assertNotNull( host.getReleaseTime() );
         assertNotSame( host.getAddedTime(), host.getReleaseTime() );
-        assertEquals( "changed text", host.getChange().getChange() );
+        assertEquals( "changed text", host.getChange() );
 
         req = new MockHttpServletRequest( "/JSPWiki", "/servlet" );
-        change = Change.getChange( "more changed text" );
+        change = Change.getChange( "page", "more changed text" );
         reputationManager.addModifier( req, change );
         assertEquals( 2, reputationManager.getModifiers().length );
     }

Modified: 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/SpamInspectionFactoryTest.java
URL: 
http://svn.apache.org/viewvc/incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/SpamInspectionFactoryTest.java?rev=896518&r1=896517&r2=896518&view=diff
==============================================================================
--- 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/SpamInspectionFactoryTest.java
 (original)
+++ 
incubator/jspwiki/trunk/tests/java/org/apache/wiki/content/inspect/SpamInspectionFactoryTest.java
 Wed Jan  6 16:43:50 2010
@@ -91,7 +91,7 @@
 
         // Running the inspection should cause the BanListInspector to fail
         String newText = "Sample text";
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( -2f, inspection.getScore( Topic.SPAM ) );
     }
 
@@ -113,7 +113,7 @@
         // Running inspection with simple text change should be fine
         Inspection inspection = createInspection( request );
         String newText = "Sample text";
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( PERFECT_SCORE, inspection.getScore( Topic.SPAM ) );
 
         // Now, make 100 more (slightly different) changes
@@ -121,7 +121,7 @@
         {
             inspection = createInspection( request );
             newText = "Sample text change " + i;
-            inspection.inspect( newText, null );
+            inspection.inspect( Change.getChange( "page", newText ) );
         }
         // Our change-rate check should fail
         assertEquals( -4f, inspection.getScore( Topic.SPAM ) );
@@ -145,12 +145,12 @@
         // Running inspection with simple text change should be fine
         Inspection inspection = createInspection( request );
         String newText = "Sample text";
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( PERFECT_SCORE, inspection.getScore( Topic.SPAM ) );
 
         // Now, make a change with 3 URLs in it (1 more than limit)
         newText = "http://www.jspwiki.org mailto:[email protected] 
https://www.freshcookies.org";;
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
 
         // Link-count check should fail
         assertEquals( -8f, inspection.getScore( Topic.SPAM ) );
@@ -174,7 +174,7 @@
         // Running inspection with simple text change should be fine
         Inspection inspection = createInspection( request );
         String newText = "Sample text";
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( PERFECT_SCORE, inspection.getScore( Topic.SPAM ) );
 
         // Now, make 50 more identical changes
@@ -182,9 +182,9 @@
         for( int i = 0; i < 50; i++ )
         {
             inspection = createInspection( request );
-            inspection.inspect( newText, null );
+            inspection.inspect( Change.getChange( "page", newText ) );
         }
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         // Our similarity check should fail
         assertEquals( -4f, inspection.getScore( Topic.SPAM ) );
     }
@@ -211,20 +211,20 @@
         // anything
         Inspection inspection = createInspection( request );
         String newText = "Sample text";
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( PERFECT_SCORE, inspection.getScore( Topic.SPAM ) );
 
         // Removing the UTF-8 token suggests we have a bot
         request.getParameterMap().remove( BotTrapInspector.REQ_ENCODING_CHECK 
);
         inspection = createInspection( request );
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( -16f, inspection.getScore( Topic.SPAM ) );
 
         // Removing the encrypted spam param suggests we have a bot
         setupSpamParams( request );
         request.getParameterMap().remove( BotTrapInspector.REQ_SPAM_PARAM );
         inspection = createInspection( request );
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( -16f, inspection.getScore( Topic.SPAM ) );
 
         // Supplying a non-null value for the first (trap) spam param should
@@ -232,19 +232,19 @@
         setupSpamParams( request );
         request.getParameterMap().put( BotTrapInspector.REQ_TRAP_PARAM, new 
String[] { "botSuppliedValue" } );
         inspection = createInspection( request );
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( -16f, inspection.getScore( Topic.SPAM ) );
 
         // Removing the second (token) spam param should trip it also
         setupSpamParams( request );
         request.getParameterMap().remove( "TOKENA" );
         inspection = createInspection( request );
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( -16f, inspection.getScore( Topic.SPAM ) );
 
         // / Re-run the inspection with all parameters intact
         setupSpamParams( request );
-        inspection.inspect( newText, null );
+        inspection.inspect( Change.getChange( "page", newText ) );
         assertEquals( PERFECT_SCORE, inspection.getScore( Topic.SPAM ) );
     }
 


Reply via email to