Index: lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AnalysisSPILoader.java
===================================================================
--- lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AnalysisSPILoader.java	(revision 1460490)
+++ lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AnalysisSPILoader.java	(working copy)
@@ -104,10 +104,10 @@
     this.services = Collections.unmodifiableMap(services);
   }
   
-  public S newInstance(String name) {
+  public S newInstance(String name, Map<String,String> args) {
     final Class<? extends S> service = lookupClass(name);
     try {
-      return service.newInstance();
+      return service.getConstructor(Map.class).newInstance(args);
     } catch (Exception e) {
       throw new IllegalArgumentException("SPI class of type "+clazz.getName()+" with name '"+name+"' cannot be instantiated. " +
             "This is likely due to a misconfiguration of the java class '" + service.getName() + "': ", e);
Index: lucene/analysis/common/src/java/org/apache/lucene/analysis/util/TokenizerFactory.java
===================================================================
--- lucene/analysis/common/src/java/org/apache/lucene/analysis/util/TokenizerFactory.java	(revision 1460490)
+++ lucene/analysis/common/src/java/org/apache/lucene/analysis/util/TokenizerFactory.java	(working copy)
@@ -21,6 +21,7 @@
 import org.apache.lucene.util.AttributeSource.AttributeFactory;
 
 import java.io.Reader;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -33,8 +34,8 @@
       new AnalysisSPILoader<TokenizerFactory>(TokenizerFactory.class);
   
   /** looks up a tokenizer by name from context classpath */
-  public static TokenizerFactory forName(String name) {
-    return loader.newInstance(name);
+  public static TokenizerFactory forName(String name, Map<String,String> args) {
+    return loader.newInstance(name, args);
   }
   
   /** looks up a tokenizer class by name from context classpath */
@@ -61,6 +62,13 @@
   public static void reloadTokenizers(ClassLoader classloader) {
     loader.reload(classloader);
   }
+  
+  /**
+   * Initialize this factory via a set of key-value pairs.
+   */
+  protected TokenizerFactory(Map<String,String> args) {
+    super(args);
+  }
 
   /** Creates a TokenStream of the specified input using the default attribute factory. */
   public final Tokenizer create(Reader input) {
Index: lucene/analysis/common/src/java/org/apache/lucene/analysis/util/CharFilterFactory.java
===================================================================
--- lucene/analysis/common/src/java/org/apache/lucene/analysis/util/CharFilterFactory.java	(revision 1460490)
+++ lucene/analysis/common/src/java/org/apache/lucene/analysis/util/CharFilterFactory.java	(working copy)
@@ -18,6 +18,7 @@
  */
 
 import java.io.Reader;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.lucene.analysis.CharFilter;
@@ -32,8 +33,8 @@
       new AnalysisSPILoader<CharFilterFactory>(CharFilterFactory.class);
   
   /** looks up a charfilter by name from context classpath */
-  public static CharFilterFactory forName(String name) {
-    return loader.newInstance(name);
+  public static CharFilterFactory forName(String name, Map<String,String> args) {
+    return loader.newInstance(name, args);
   }
   
   /** looks up a charfilter class by name from context classpath */
@@ -61,6 +62,13 @@
     loader.reload(classloader);
   }
 
+  /**
+   * Initialize this factory via a set of key-value pairs.
+   */
+  protected CharFilterFactory(Map<String,String> args) {
+    super(args);
+  }
+
   /** Wraps the given Reader with a CharFilter. */
   public abstract Reader create(Reader input);
 }
Index: lucene/analysis/common/src/java/org/apache/lucene/analysis/util/TokenFilterFactory.java
===================================================================
--- lucene/analysis/common/src/java/org/apache/lucene/analysis/util/TokenFilterFactory.java	(revision 1460490)
+++ lucene/analysis/common/src/java/org/apache/lucene/analysis/util/TokenFilterFactory.java	(working copy)
@@ -17,6 +17,7 @@
  * limitations under the License.
  */
 
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.lucene.analysis.TokenStream;
@@ -32,8 +33,8 @@
           new String[] { "TokenFilterFactory", "FilterFactory" });
   
   /** looks up a tokenfilter by name from context classpath */
-  public static TokenFilterFactory forName(String name) {
-    return loader.newInstance(name);
+  public static TokenFilterFactory forName(String name, Map<String,String> args) {
+    return loader.newInstance(name, args);
   }
   
   /** looks up a tokenfilter class by name from context classpath */
@@ -60,6 +61,13 @@
   public static void reloadTokenFilters(ClassLoader classloader) {
     loader.reload(classloader);
   }
+  
+  /**
+   * Initialize this factory via a set of key-value pairs.
+   */
+  protected TokenFilterFactory(Map<String,String> args) {
+    super(args);
+  }
 
   /** Transform the specified input TokenStream */
   public abstract TokenStream create(TokenStream input);
Index: lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AbstractAnalysisFactory.java
===================================================================
--- lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AbstractAnalysisFactory.java	(revision 1460491)
+++ lucene/analysis/common/src/java/org/apache/lucene/analysis/util/AbstractAnalysisFactory.java	(working copy)
@@ -29,7 +29,6 @@
 import java.nio.charset.CodingErrorAction;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
@@ -41,9 +40,7 @@
  * <p>
  * The typical lifecycle for a factory consumer is:
  * <ol>
- *   <li>Create factory via its a no-arg constructor
- *   <li>Set version emulation by calling {@link #setLuceneMatchVersion(Version)}
- *   <li>Calls {@link #init(Map)} passing arguments as key-value mappings.
+ *   <li>Create factory via its constructor (or via XXXFactory.forName)
  *   <li>(Optional) If the factory uses resources such as files, {@link ResourceLoaderAware#inform(ResourceLoader)} is called to initialize those resources.
  *   <li>Consumer calls create() to obtain instances.
  * </ol>
@@ -51,27 +48,21 @@
 public abstract class AbstractAnalysisFactory {
 
   /** The original args, before init() processes them */
-  private Map<String,String> originalArgs;
-  
-  /** The init args */
-  protected Map<String,String> args;
+  private final Map<String,String> originalArgs;
 
   /** the luceneVersion arg */
-  protected Version luceneMatchVersion = null;
+  protected final Version luceneMatchVersion;
 
   /**
    * Initialize this factory via a set of key-value pairs.
    */
-  public void init(Map<String,String> args) {
+  protected AbstractAnalysisFactory(Map<String,String> args) {
     originalArgs = Collections.unmodifiableMap(args);
-    this.args = new HashMap<String,String>(args);
+    String version = args.remove("luceneMatchVersion");
+    luceneMatchVersion = version == null ? null : Version.parseLeniently(version);
   }
-
-  public Map<String,String> getArgs() {
-    return args;
-  }
   
-  public Map<String,String> getOriginalArgs() {
+  public final Map<String,String> getOriginalArgs() {
     return originalArgs;
   }
 
@@ -85,24 +76,20 @@
     }
   }
 
-  public void setLuceneMatchVersion(Version luceneMatchVersion) {
-    this.luceneMatchVersion = luceneMatchVersion;
-  }
-
-  public Version getLuceneMatchVersion() {
+  public final Version getLuceneMatchVersion() {
     return this.luceneMatchVersion;
   }
 
-  protected int getInt(String name) {
-    return getInt(name, -1, false);
+  protected final int getInt(Map<String,String> args, String name) {
+    return getInt(args, name, -1, false);
   }
 
-  protected int getInt(String name, int defaultVal) {
-    return getInt(name, defaultVal, true);
+  protected final int getInt(Map<String,String> args, String name, int defaultVal) {
+    return getInt(args, name, defaultVal, true);
   }
 
-  protected int getInt(String name, int defaultVal, boolean useDefault) {
-    String s = args.get(name);
+  protected final int getInt(Map<String,String> args, String name, int defaultVal, boolean useDefault) {
+    String s = args.remove(name);
     if (s == null) {
       if (useDefault) {
         return defaultVal;
@@ -112,12 +99,12 @@
     return Integer.parseInt(s);
   }
 
-  protected boolean getBoolean(String name, boolean defaultVal) {
-    return getBoolean(name, defaultVal, true);
+  protected final boolean getBoolean(Map<String,String> args, String name, boolean defaultVal) {
+    return getBoolean(args, name, defaultVal, true);
   }
 
-  protected boolean getBoolean(String name, boolean defaultVal, boolean useDefault) {
-    String s = args.get(name);
+  protected final boolean getBoolean(Map<String,String> args, String name, boolean defaultVal, boolean useDefault) {
+    String s = args.remove(name);
     if (s==null) {
       if (useDefault) return defaultVal;
       throw new IllegalArgumentException("Configuration Error: missing parameter '" + name + "'");
@@ -128,13 +115,13 @@
   /**
    * Compiles a pattern for the value of the specified argument key <code>name</code> 
    */
-  protected Pattern getPattern(String name) {
+  protected final Pattern getPattern(Map<String,String> args, String name) {
     try {
-      String pat = args.get(name);
+      String pat = args.remove(name);
       if (null == pat) {
         throw new IllegalArgumentException("Configuration Error: missing parameter '" + name + "'");
       }
-      return Pattern.compile(args.get(name));
+      return Pattern.compile(pat);
     } catch (PatternSyntaxException e) {
       throw new IllegalArgumentException
         ("Configuration Error: '" + name + "' can not be parsed in " +
@@ -146,7 +133,7 @@
    * Returns as {@link CharArraySet} from wordFiles, which
    * can be a comma-separated list of filenames
    */
-  protected CharArraySet getWordSet(ResourceLoader loader,
+  protected final CharArraySet getWordSet(ResourceLoader loader,
       String wordFiles, boolean ignoreCase) throws IOException {
     assureMatchVersion();
     List<String> files = splitFileNames(wordFiles);
@@ -168,13 +155,13 @@
   /**
    * Returns the resource's lines (with content treated as UTF-8)
    */
-  protected List<String> getLines(ResourceLoader loader, String resource) throws IOException {
+  protected final List<String> getLines(ResourceLoader loader, String resource) throws IOException {
     return WordlistLoader.getLines(loader.openResource(resource), IOUtils.CHARSET_UTF_8);
   }
 
   /** same as {@link #getWordSet(ResourceLoader, String, boolean)},
    * except the input is in snowball format. */
-  protected CharArraySet getSnowballWordSet(ResourceLoader loader,
+  protected final CharArraySet getSnowballWordSet(ResourceLoader loader,
       String wordFiles, boolean ignoreCase) throws IOException {
     assureMatchVersion();
     List<String> files = splitFileNames(wordFiles);
@@ -209,7 +196,7 @@
    * @param fileNames the string containing file names
    * @return a list of file names with the escaping backslashed removed
    */
-  protected List<String> splitFileNames(String fileNames) {
+  protected final List<String> splitFileNames(String fileNames) {
     if (fileNames == null)
       return Collections.<String>emptyList();
 
Index: lucene/analysis/common/src/java/org/apache/lucene/analysis/util/ElisionFilterFactory.java
===================================================================
--- lucene/analysis/common/src/java/org/apache/lucene/analysis/util/ElisionFilterFactory.java	(revision 1460490)
+++ lucene/analysis/common/src/java/org/apache/lucene/analysis/util/ElisionFilterFactory.java	(working copy)
@@ -18,6 +18,8 @@
  */
 
 import java.io.IOException;
+import java.util.Map;
+
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.fr.FrenchAnalyzer;
 
@@ -36,19 +38,27 @@
  */
 public class ElisionFilterFactory extends TokenFilterFactory implements ResourceLoaderAware, MultiTermAwareComponent {
 
+  /** Creates a new ElisionFilterFactory */
+  public ElisionFilterFactory(Map<String,String> args) {
+    super(args);
+    articlesFile = args.remove("articles");
+    ignoreCase = getBoolean(args, "ignoreCase", false);
+    if (!args.isEmpty()) {
+      throw new IllegalArgumentException("Unknown parameters: " + args);
+    }
+  }
+
+  private final String articlesFile;
+  private final boolean ignoreCase;
   private CharArraySet articles;
 
   @Override
   public void inform(ResourceLoader loader) throws IOException {
-    String articlesFile = args.get("articles");
-    boolean ignoreCase = getBoolean("ignoreCase", false);
-
-    if (articlesFile != null) {
+    if (articlesFile == null) {
+      articles = FrenchAnalyzer.DEFAULT_ARTICLES;
+    } else {
       articles = getWordSet(loader, articlesFile, ignoreCase);
     }
-    if (articles == null) {
-      articles = FrenchAnalyzer.DEFAULT_ARTICLES;
-    }
   }
 
   @Override
