Browse Source

Scripting: Created a parameter parse to standardised script options

Colin Goodheart-Smithe 11 years ago
parent
commit
c047fbda9b

+ 11 - 8
src/main/java/org/elasticsearch/action/update/UpdateRequest.java

@@ -39,6 +39,8 @@ import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.index.VersionType;
+import org.elasticsearch.script.ScriptParameterParser;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
 import org.elasticsearch.script.ScriptService;
 
 import java.io.IOException;
@@ -578,6 +580,7 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
     }
 
     public UpdateRequest source(BytesReference source) throws Exception {
+        ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
         XContentType xContentType = XContentFactory.xContentType(source);
         try (XContentParser parser = XContentFactory.xContent(xContentType).createParser(source)) {
             XContentParser.Token token = parser.nextToken();
@@ -588,16 +591,8 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
             while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
                 if (token == XContentParser.Token.FIELD_NAME) {
                     currentFieldName = parser.currentName();
-                } else if ("script".equals(currentFieldName)) {
-                    script = parser.textOrNull();
-                    scriptType = ScriptService.ScriptType.INLINE;
-                } else if ("script_id".equals(currentFieldName)) {
-                    script = parser.textOrNull();
-                    scriptType = ScriptService.ScriptType.INDEXED;
                 } else if ("params".equals(currentFieldName)) {
                     scriptParams = parser.map();
-                } else if ("lang".equals(currentFieldName)) {
-                    scriptLang = parser.text();
                 } else if ("scripted_upsert".equals(currentFieldName)) {
                     scriptedUpsert = parser.booleanValue();
                 } else if ("upsert".equals(currentFieldName)) {
@@ -612,8 +607,16 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
                     docAsUpsert(parser.booleanValue());
                 } else if ("detect_noop".equals(currentFieldName)) {
                     detectNoop(parser.booleanValue());
+                } else {
+                    scriptParameterParser.token(currentFieldName, token, parser);
                 }
             }
+            ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
+            if (scriptValue != null) {
+                script = scriptValue.script();
+                scriptType = scriptValue.scriptType();
+            }
+            scriptLang = scriptParameterParser.lang();
         }
         return this;
     }

+ 13 - 10
src/main/java/org/elasticsearch/index/mapper/DocumentMapperParser.java

@@ -46,6 +46,8 @@ import org.elasticsearch.index.mapper.object.ObjectMapper;
 import org.elasticsearch.index.mapper.object.RootObjectMapper;
 import org.elasticsearch.index.settings.IndexSettings;
 import org.elasticsearch.index.similarity.SimilarityLookupService;
+import org.elasticsearch.script.ScriptParameterParser;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.script.ScriptService.ScriptType;
 
@@ -301,18 +303,19 @@ public class DocumentMapperParser extends AbstractIndexComponent {
 
     @SuppressWarnings("unchecked")
     private void parseTransform(DocumentMapper.Builder docBuilder, Map<String, Object> transformConfig) {
-        String script = (String) transformConfig.remove("script_file");
-        ScriptType scriptType = ScriptType.FILE;
-        if (script == null) {
-            script = (String) transformConfig.remove("script_id");
-            scriptType = ScriptType.INDEXED;
-        }
-        if (script == null) {
-            script = (String) transformConfig.remove("script");
-            scriptType = ScriptType.INLINE;
+        ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
+        scriptParameterParser.parseConfig(transformConfig, true);
+        
+        String script = null;
+        ScriptType scriptType = null;
+        ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
+        if (scriptValue != null) {
+            script = scriptValue.script();
+            scriptType = scriptValue.scriptType();
         }
+        
         if (script != null) {
-            String scriptLang = (String) transformConfig.remove("lang");
+            String scriptLang = scriptParameterParser.lang();
             Map<String, Object> params = (Map<String, Object>)transformConfig.remove("params");
             docBuilder.transform(scriptService, script, scriptType, scriptLang, params);
         }

+ 12 - 13
src/main/java/org/elasticsearch/index/query/ScriptFilterParser.java

@@ -30,6 +30,8 @@ import org.elasticsearch.common.inject.Inject;
 import org.elasticsearch.common.lucene.docset.MatchDocIdSet;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.cache.filter.support.CacheKeyFilter;
+import org.elasticsearch.script.ScriptParameterParser;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.script.SearchScript;
 import org.elasticsearch.search.lookup.SearchLookup;
@@ -58,6 +60,7 @@ public class ScriptFilterParser implements FilterParser {
     @Override
     public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
         XContentParser parser = parseContext.parser();
+        ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
 
         XContentParser.Token token;
 
@@ -82,29 +85,25 @@ public class ScriptFilterParser implements FilterParser {
                     throw new QueryParsingException(parseContext.index(), "[script] filter does not support [" + currentFieldName + "]");
                 }
             } else if (token.isValue()) {
-                if ("script".equals(currentFieldName)) {
-                    script = parser.text();
-                    scriptType = ScriptService.ScriptType.INLINE;
-                } else if ("script_id".equals(currentFieldName)) {
-                    script = parser.text();
-                    scriptType = ScriptService.ScriptType.INDEXED;
-                } else if ("file".equals(currentFieldName)) {
-                    script = parser.text();
-                    scriptType = ScriptService.ScriptType.FILE;
-                } else if ("lang".equals(currentFieldName)) {
-                    scriptLang = parser.text();
-                } else if ("_name".equals(currentFieldName)) {
+                if ("_name".equals(currentFieldName)) {
                     filterName = parser.text();
                 } else if ("_cache".equals(currentFieldName)) {
                     cache = parser.booleanValue();
                 } else if ("_cache_key".equals(currentFieldName) || "_cacheKey".equals(currentFieldName)) {
                     cacheKey = new CacheKeyFilter.Key(parser.text());
-                } else {
+                } else if (!scriptParameterParser.token(currentFieldName, token, parser)){
                     throw new QueryParsingException(parseContext.index(), "[script] filter does not support [" + currentFieldName + "]");
                 }
             }
         }
 
+        ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
+        if (scriptValue != null) {
+            script = scriptValue.script();
+            scriptType = scriptValue.scriptType();
+        }
+        scriptLang = scriptParameterParser.lang();
+
         if (script == null) {
             throw new QueryParsingException(parseContext.index(), "script must be provided with a [script] filter");
         }

+ 11 - 12
src/main/java/org/elasticsearch/index/query/functionscore/script/ScriptScoreFunctionParser.java

@@ -28,6 +28,8 @@ import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.query.QueryParseContext;
 import org.elasticsearch.index.query.QueryParsingException;
 import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
+import org.elasticsearch.script.ScriptParameterParser;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.script.SearchScript;
 
@@ -52,7 +54,7 @@ public class ScriptScoreFunctionParser implements ScoreFunctionParser {
 
     @Override
     public ScoreFunction parse(QueryParseContext parseContext, XContentParser parser) throws IOException, QueryParsingException {
-
+        ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
         String script = null;
         String scriptLang = null;
         Map<String, Object> vars = null;
@@ -69,22 +71,19 @@ public class ScriptScoreFunctionParser implements ScoreFunctionParser {
                     throw new QueryParsingException(parseContext.index(), NAMES[0] + " query does not support [" + currentFieldName + "]");
                 }
             } else if (token.isValue()) {
-                if ("script".equals(currentFieldName)) {
-                    script = parser.text();
-                } else if ("script_id".equals(currentFieldName)) {
-                    script = parser.text();
-                    scriptType = ScriptService.ScriptType.INDEXED;
-                } else if ("file".equals(currentFieldName)) {
-                    script = parser.text();
-                    scriptType = ScriptService.ScriptType.FILE;
-                } else if ("lang".equals(currentFieldName)) {
-                    scriptLang = parser.text();
-                } else {
+                if (!scriptParameterParser.token(currentFieldName, token, parser)) {
                     throw new QueryParsingException(parseContext.index(), NAMES[0] + " query does not support [" + currentFieldName + "]");
                 }
             }
         }
 
+        ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
+        if (scriptValue != null) {
+            script = scriptValue.script();
+            scriptType = scriptValue.scriptType();
+        }
+        scriptLang = scriptParameterParser.lang();
+
         if (script == null) {
             throw new QueryParsingException(parseContext.index(), NAMES[0] + " requires 'script' field");
         }

+ 11 - 8
src/main/java/org/elasticsearch/rest/action/update/RestUpdateAction.java

@@ -34,7 +34,8 @@ import org.elasticsearch.index.VersionType;
 import org.elasticsearch.rest.*;
 import org.elasticsearch.rest.action.support.RestActions;
 import org.elasticsearch.rest.action.support.RestBuilderListener;
-import org.elasticsearch.script.ScriptService;
+import org.elasticsearch.script.ScriptParameterParser;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
 
 import java.util.Map;
 
@@ -69,14 +70,16 @@ public class RestUpdateAction extends BaseRestHandler {
             updateRequest.consistencyLevel(WriteConsistencyLevel.fromString(consistencyLevel));
         }
         updateRequest.docAsUpsert(request.paramAsBoolean("doc_as_upsert", updateRequest.docAsUpsert()));
-        if( request.hasParam("script") ) {
-            updateRequest.script(request.param("script"), ScriptService.ScriptType.INLINE);
-        } else if( request.hasParam("script_id") ) {
-            updateRequest.script(request.param("script_id"), ScriptService.ScriptType.INDEXED);
-        } else if( request.hasParam("script_file") ) {
-            updateRequest.script(request.param("script_file"), ScriptService.ScriptType.FILE);
+        ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
+        scriptParameterParser.parseParams(request);
+        ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
+        if (scriptValue != null) {
+            updateRequest.script(scriptValue.script(), scriptValue.scriptType());
+        }
+        String scriptLang = scriptParameterParser.lang();
+        if (scriptLang != null) {
+            updateRequest.scriptLang(scriptLang);
         }
-        updateRequest.scriptLang(request.param("lang"));
         for (Map.Entry<String, String> entry : request.params().entrySet()) {
             if (entry.getKey().startsWith("sp_")) {
                 updateRequest.addScriptParam(entry.getKey().substring(3), entry.getValue());

+ 236 - 0
src/main/java/org/elasticsearch/script/ScriptParameterParser.java

@@ -0,0 +1,236 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.elasticsearch.script;
+
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.xcontent.ToXContent.Params;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.script.ScriptService.ScriptType;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.Map.Entry;
+
+public class ScriptParameterParser {
+
+    public static final String FILE_SUFFIX = "_file";
+    public static final String INDEXED_SUFFIX = "_id";
+
+    private Map<String, ScriptParameterValue> parameterValues = new HashMap<>();
+    private Set<ParseField> inlineParameters;
+    private Set<ParseField> fileParameters;
+    private Set<ParseField> indexedParameters;
+    private String lang = null;
+
+    public ScriptParameterParser() {
+        this(null);
+    }
+
+    public ScriptParameterParser(Set<String> parameterNames) {
+        if (parameterNames == null || parameterNames.isEmpty()) {
+            inlineParameters = Collections.singleton(ScriptService.SCRIPT_INLINE);
+            fileParameters = Collections.singleton(ScriptService.SCRIPT_FILE);
+            indexedParameters = Collections.singleton(ScriptService.SCRIPT_ID);
+        } else {
+            inlineParameters = new HashSet<>();
+            fileParameters = new HashSet<>();
+            indexedParameters = new HashSet<>();
+            for (String parameterName : parameterNames) {
+                if (ScriptService.SCRIPT_LANG.match(parameterName)) {
+                    throw new IllegalArgumentException("lang is reserved and cannot be used as a parameter name");
+                }
+                inlineParameters.add(new ParseField(parameterName));
+                fileParameters.add(new ParseField(parameterName + FILE_SUFFIX));
+                indexedParameters.add(new ParseField(parameterName + INDEXED_SUFFIX));
+            }
+        }
+    }
+
+    public boolean token(String currentFieldName, XContentParser.Token token, XContentParser parser) throws IOException {
+        if (token == XContentParser.Token.VALUE_STRING) {
+            if (ScriptService.SCRIPT_LANG.match(currentFieldName)) {
+                lang  = parser.text();
+                return true;
+            } else {
+                for (ParseField parameter : inlineParameters) {
+                    if (parameter.match(currentFieldName)) {
+                        String coreParameterName = parameter.getPreferredName();
+                        putParameterValue(coreParameterName, parser.textOrNull(), ScriptType.INLINE);
+                        return true;
+                    }
+                }
+                for (ParseField parameter : fileParameters) {
+                    if (parameter.match(currentFieldName)) {
+                        String coreParameterName = parameter.getPreferredName().replace(FILE_SUFFIX, "");
+                        putParameterValue(coreParameterName, parser.textOrNull(), ScriptType.FILE);
+                        return true;
+                    }
+                }
+                for (ParseField parameter : indexedParameters) {
+                    if (parameter.match(currentFieldName)) {
+                        String coreParameterName = parameter.getPreferredName().replace(INDEXED_SUFFIX, "");
+                        putParameterValue(coreParameterName, parser.textOrNull(), ScriptType.INDEXED);
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    public void parseConfig(Map<String, Object> config, boolean removeMatchedEntries) {
+        for (Iterator<Entry<String, Object>> itr = config.entrySet().iterator(); itr.hasNext();) {
+            Entry<String, Object> entry = itr.next();
+            String parameterName = entry.getKey();
+            Object parameterValue = entry.getValue();
+            if (ScriptService.SCRIPT_LANG.match(parameterName)) {
+               if (parameterValue instanceof String) {
+                   lang = (String) parameterValue;
+                   if (removeMatchedEntries) {
+                       itr.remove();
+                   }
+               } else {
+                   throw new ScriptParameterParseException("Value must be of type String: [" + parameterName + "]");
+               }
+            } else {
+                for (ParseField parameter : inlineParameters) {
+                    if (parameter.match(parameterName)) {
+                        String coreParameterName = parameter.getPreferredName();
+                        String stringValue = null;
+                        if (parameterValue instanceof String) {
+                            stringValue = (String) parameterValue;
+                        } else {
+                            throw new ScriptParameterParseException("Value must be of type String: [" + parameterName + "]");
+                        }
+                        putParameterValue(coreParameterName, stringValue, ScriptType.INLINE);
+                        if (removeMatchedEntries) {
+                            itr.remove();
+                        }
+                    }
+                }
+                for (ParseField parameter : fileParameters) {
+                    if (parameter.match(parameterName)) {
+                        String coreParameterName = parameter.getPreferredName().replace(FILE_SUFFIX, "");;
+                        String stringValue = null;
+                        if (parameterValue instanceof String) {
+                            stringValue = (String) parameterValue;
+                        } else {
+                            throw new ScriptParameterParseException("Value must be of type String: [" + parameterName + "]");
+                        }
+                        putParameterValue(coreParameterName, stringValue, ScriptType.FILE);
+                        if (removeMatchedEntries) {
+                            itr.remove();
+                        }
+                    }
+                }
+                for (ParseField parameter : indexedParameters) {
+                    if (parameter.match(parameterName)) {
+                        String coreParameterName = parameter.getPreferredName().replace(INDEXED_SUFFIX, "");
+                        String stringValue = null;
+                        if (parameterValue instanceof String) {
+                            stringValue = (String) parameterValue;
+                        } else {
+                            throw new ScriptParameterParseException("Value must be of type String: [" + parameterName + "]");
+                        }
+                        putParameterValue(coreParameterName, stringValue, ScriptType.INDEXED);
+                        if (removeMatchedEntries) {
+                            itr.remove();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void putParameterValue(String coreParameterName, String script, ScriptType scriptType) {
+        if (parameterValues.get(coreParameterName) == null) {
+            parameterValues.put(coreParameterName, new ScriptParameterValue(script, scriptType));
+        } else {
+            throw new ScriptParameterParseException("Only one of [" + coreParameterName + ", " + coreParameterName
+                    + FILE_SUFFIX + ", " + coreParameterName + INDEXED_SUFFIX + "] is allowed.");
+        }
+    }
+
+    public void parseParams(Params params) {
+        lang = params.param(ScriptService.SCRIPT_LANG.getPreferredName());
+        for (ParseField parameter : inlineParameters) {
+            String value = params.param(parameter.getPreferredName());
+            if (value != null) {
+                String coreParameterName = parameter.getPreferredName();
+                putParameterValue(coreParameterName, value, ScriptType.INLINE);
+                
+            }
+        }
+        for (ParseField parameter : fileParameters) {
+            String value = params.param(parameter.getPreferredName());
+            if (value != null) {
+                String coreParameterName = parameter.getPreferredName().replace(FILE_SUFFIX, "");
+                putParameterValue(coreParameterName, value, ScriptType.FILE);
+                
+            }
+        }
+        for (ParseField parameter : indexedParameters) {
+            String value = params.param(parameter.getPreferredName());
+            if (value != null) {
+                String coreParameterName = parameter.getPreferredName().replace(INDEXED_SUFFIX, "");
+                putParameterValue(coreParameterName, value, ScriptType.INDEXED);
+                
+            }
+        }
+    }
+
+    public ScriptParameterValue getDefaultScriptParameterValue() {
+        return getScriptParameterValue(ScriptService.SCRIPT_INLINE.getPreferredName());
+    }
+
+    public ScriptParameterValue getScriptParameterValue(String parameterName) {
+        return parameterValues.get(parameterName);
+    }
+
+    public String lang() {
+        return lang;
+    }
+
+    public static class ScriptParameterValue {
+        private String script;
+        private ScriptType scriptType;
+
+        public ScriptParameterValue(String script, ScriptType scriptType) {
+            this.script = script;
+            this.scriptType = scriptType;
+        }
+
+        public String script() {
+            return script;
+        }
+
+        public ScriptType scriptType() {
+            return scriptType;
+        }
+    }
+
+    public static class ScriptParameterParseException extends ElasticsearchException {
+
+        public ScriptParameterParseException(String msg) {
+            super(msg);
+        }
+    }
+}

+ 13 - 12
src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricBuilder.java

@@ -20,6 +20,7 @@
 package org.elasticsearch.search.aggregations.metrics.scripted;
 
 import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.script.ScriptParameterParser;
 import org.elasticsearch.search.aggregations.metrics.MetricsAggregationBuilder;
 
 import java.io.IOException;
@@ -187,51 +188,51 @@ public class ScriptedMetricBuilder extends MetricsAggregationBuilder {
         }
         
         if (initScript != null) {
-            builder.field(ScriptedMetricParser.INIT_SCRIPT_FIELD.getPreferredName(), initScript);
+            builder.field(ScriptedMetricParser.INIT_SCRIPT, initScript);
         }
         
         if (mapScript != null) {
-            builder.field(ScriptedMetricParser.MAP_SCRIPT_FIELD.getPreferredName(), mapScript);
+            builder.field(ScriptedMetricParser.MAP_SCRIPT, mapScript);
         }
         
         if (combineScript != null) {
-            builder.field(ScriptedMetricParser.COMBINE_SCRIPT_FIELD.getPreferredName(), combineScript);
+            builder.field(ScriptedMetricParser.COMBINE_SCRIPT, combineScript);
         }
         
         if (reduceScript != null) {
-            builder.field(ScriptedMetricParser.REDUCE_SCRIPT_FIELD.getPreferredName(), reduceScript);
+            builder.field(ScriptedMetricParser.REDUCE_SCRIPT, reduceScript);
         }
         
         if (initScriptFile != null) {
-            builder.field(ScriptedMetricParser.INIT_SCRIPT_FILE_FIELD.getPreferredName(), initScriptFile);
+            builder.field(ScriptedMetricParser.INIT_SCRIPT + ScriptParameterParser.FILE_SUFFIX, initScriptFile);
         }
         
         if (mapScriptFile != null) {
-            builder.field(ScriptedMetricParser.MAP_SCRIPT_FILE_FIELD.getPreferredName(), mapScriptFile);
+            builder.field(ScriptedMetricParser.MAP_SCRIPT + ScriptParameterParser.FILE_SUFFIX, mapScriptFile);
         }
         
         if (combineScriptFile != null) {
-            builder.field(ScriptedMetricParser.COMBINE_SCRIPT_FILE_FIELD.getPreferredName(), combineScriptFile);
+            builder.field(ScriptedMetricParser.COMBINE_SCRIPT + ScriptParameterParser.FILE_SUFFIX, combineScriptFile);
         }
         
         if (reduceScriptFile != null) {
-            builder.field(ScriptedMetricParser.REDUCE_SCRIPT_FILE_FIELD.getPreferredName(), reduceScriptFile);
+            builder.field(ScriptedMetricParser.REDUCE_SCRIPT + ScriptParameterParser.FILE_SUFFIX, reduceScriptFile);
         }
         
         if (initScriptId != null) {
-            builder.field(ScriptedMetricParser.INIT_SCRIPT_ID_FIELD.getPreferredName(), initScriptId);
+            builder.field(ScriptedMetricParser.INIT_SCRIPT + ScriptParameterParser.INDEXED_SUFFIX, initScriptId);
         }
         
         if (mapScriptId != null) {
-            builder.field(ScriptedMetricParser.MAP_SCRIPT_ID_FIELD.getPreferredName(), mapScriptId);
+            builder.field(ScriptedMetricParser.MAP_SCRIPT + ScriptParameterParser.INDEXED_SUFFIX, mapScriptId);
         }
         
         if (combineScriptId != null) {
-            builder.field(ScriptedMetricParser.COMBINE_SCRIPT_ID_FIELD.getPreferredName(), combineScriptId);
+            builder.field(ScriptedMetricParser.COMBINE_SCRIPT + ScriptParameterParser.INDEXED_SUFFIX, combineScriptId);
         }
         
         if (reduceScriptId != null) {
-            builder.field(ScriptedMetricParser.REDUCE_SCRIPT_ID_FIELD.getPreferredName(), reduceScriptId);
+            builder.field(ScriptedMetricParser.REDUCE_SCRIPT + ScriptParameterParser.INDEXED_SUFFIX, reduceScriptId);
         }
         
         if (lang != null) {

+ 47 - 96
src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricParser.java

@@ -21,6 +21,8 @@ package org.elasticsearch.search.aggregations.metrics.scripted;
 
 import org.elasticsearch.common.ParseField;
 import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.script.ScriptParameterParser;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
 import org.elasticsearch.script.ScriptService.ScriptType;
 import org.elasticsearch.search.SearchParseException;
 import org.elasticsearch.search.aggregations.Aggregator;
@@ -28,24 +30,18 @@ import org.elasticsearch.search.aggregations.AggregatorFactory;
 import org.elasticsearch.search.internal.SearchContext;
 
 import java.io.IOException;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 public class ScriptedMetricParser implements Aggregator.Parser {
-
+    
+    public static final String INIT_SCRIPT = "init_script";
+    public static final String MAP_SCRIPT = "map_script";
+    public static final String COMBINE_SCRIPT = "combine_script";
+    public static final String REDUCE_SCRIPT = "reduce_script";
     public static final ParseField PARAMS_FIELD = new ParseField("params");
     public static final ParseField REDUCE_PARAMS_FIELD = new ParseField("reduce_params");
-    public static final ParseField INIT_SCRIPT_FIELD = new ParseField("init_script");
-    public static final ParseField MAP_SCRIPT_FIELD = new ParseField("map_script");
-    public static final ParseField COMBINE_SCRIPT_FIELD = new ParseField("combine_script");
-    public static final ParseField REDUCE_SCRIPT_FIELD = new ParseField("reduce_script");
-    public static final ParseField INIT_SCRIPT_FILE_FIELD = new ParseField("init_script_file");
-    public static final ParseField MAP_SCRIPT_FILE_FIELD = new ParseField("map_script_file");
-    public static final ParseField COMBINE_SCRIPT_FILE_FIELD = new ParseField("combine_script_file");
-    public static final ParseField REDUCE_SCRIPT_FILE_FIELD = new ParseField("reduce_script_file");
-    public static final ParseField INIT_SCRIPT_ID_FIELD = new ParseField("init_script_id");
-    public static final ParseField MAP_SCRIPT_ID_FIELD = new ParseField("map_script_id");
-    public static final ParseField COMBINE_SCRIPT_ID_FIELD = new ParseField("combine_script_id");
-    public static final ParseField REDUCE_SCRIPT_ID_FIELD = new ParseField("reduce_script_id");
     public static final ParseField LANG_FIELD = new ParseField("lang");
 
     @Override
@@ -55,19 +51,17 @@ public class ScriptedMetricParser implements Aggregator.Parser {
 
     @Override
     public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException {
-        String initScript = null;
-        String mapScript = null;
-        String combineScript = null;
-        String reduceScript = null;
         String scriptLang = null;
-        ScriptType initScriptType = ScriptType.INLINE;
-        ScriptType mapScriptType = ScriptType.INLINE;
-        ScriptType combineScriptType = ScriptType.INLINE;
-        ScriptType reduceScriptType = ScriptType.INLINE;
         Map<String, Object> params = null;
         Map<String, Object> reduceParams = null;
         XContentParser.Token token;
         String currentFieldName = null;
+        Set<String> scriptParameters = new HashSet<>();
+        scriptParameters.add(INIT_SCRIPT);
+        scriptParameters.add(MAP_SCRIPT);
+        scriptParameters.add(COMBINE_SCRIPT);
+        scriptParameters.add(REDUCE_SCRIPT);
+        ScriptParameterParser scriptParameterParser = new ScriptParameterParser(scriptParameters);
 
         while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
             if (token == XContentParser.Token.FIELD_NAME) {
@@ -81,87 +75,44 @@ public class ScriptedMetricParser implements Aggregator.Parser {
                     throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "].");
                 }
             } else if (token.isValue()) {
-                if (INIT_SCRIPT_FIELD.match(currentFieldName)) {
-                    if (initScript != null) {
-                        throw new SearchParseException(context, "Only one of [init_script, init_script_file, init_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    initScript = parser.text();
-                    initScriptType = ScriptType.INLINE;
-                } else if (MAP_SCRIPT_FIELD.match(currentFieldName)) {
-                    if (mapScript != null) {
-                        throw new SearchParseException(context, "Only one of [map_script, map_script_file, map_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    mapScript = parser.text();
-                    mapScriptType = ScriptType.INLINE;
-                } else if (COMBINE_SCRIPT_FIELD.match(currentFieldName)) {
-                    if (combineScript != null) {
-                        throw new SearchParseException(context, "Only one of [combine_script, combine_script_file, combine_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    combineScript = parser.text();
-                    combineScriptType = ScriptType.INLINE;
-                } else if (REDUCE_SCRIPT_FIELD.match(currentFieldName)) {
-                    if (reduceScript != null) {
-                        throw new SearchParseException(context, "Only one of [reduce_script, reduce_script_file, reduce_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    reduceScript = parser.text();
-                    reduceScriptType = ScriptType.INLINE;
-                } else if (INIT_SCRIPT_FILE_FIELD.match(currentFieldName)) {
-                    if (initScript != null) {
-                        throw new SearchParseException(context, "Only one of [init_script, init_script_file, init_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    initScript = parser.text();
-                    initScriptType = ScriptType.FILE;
-                } else if (MAP_SCRIPT_FILE_FIELD.match(currentFieldName)) {
-                    if (mapScript != null) {
-                        throw new SearchParseException(context, "Only one of [map_script, map_script_file, map_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    mapScript = parser.text();
-                    mapScriptType = ScriptType.FILE;
-                } else if (COMBINE_SCRIPT_FILE_FIELD.match(currentFieldName)) {
-                    if (combineScript != null) {
-                        throw new SearchParseException(context, "Only one of [combine_script, combine_script_file, combine_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    combineScript = parser.text();
-                    combineScriptType = ScriptType.FILE;
-                } else if (REDUCE_SCRIPT_FILE_FIELD.match(currentFieldName)) {
-                    if (reduceScript != null) {
-                        throw new SearchParseException(context, "Only one of [reduce_script, reduce_script_file, reduce_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    reduceScript = parser.text();
-                    reduceScriptType = ScriptType.FILE;
-                } else if (INIT_SCRIPT_ID_FIELD.match(currentFieldName)) {
-                    if (initScript != null) {
-                        throw new SearchParseException(context, "Only one of [init_script, init_script_file, init_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    initScript = parser.text();
-                    initScriptType = ScriptType.INDEXED;
-                } else if (MAP_SCRIPT_ID_FIELD.match(currentFieldName)) {
-                    if (mapScript != null) {
-                        throw new SearchParseException(context, "Only one of [map_script, map_script_file, map_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    mapScript = parser.text();
-                    mapScriptType = ScriptType.INDEXED;
-                } else if (COMBINE_SCRIPT_ID_FIELD.match(currentFieldName)) {
-                    if (combineScript != null) {
-                        throw new SearchParseException(context, "Only one of [combine_script, combine_script_file, combine_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    combineScript = parser.text();
-                    combineScriptType = ScriptType.INDEXED;
-                } else if (REDUCE_SCRIPT_ID_FIELD.match(currentFieldName)) {
-                    if (reduceScript != null) {
-                        throw new SearchParseException(context, "Only one of [reduce_script, reduce_script_file, reduce_script_id] is allowed in [" + aggregationName + "].");
-                    }
-                    reduceScript = parser.text();
-                    reduceScriptType = ScriptType.INDEXED;
-                } else if (LANG_FIELD.match(currentFieldName)) {
-                    scriptLang = parser.text();
-                } else {
+                if (!scriptParameterParser.token(currentFieldName, token, parser)) {
                     throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "].");
                 }
             } else {
                 throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "].");
             }
         }
+        
+        ScriptParameterValue initScriptValue = scriptParameterParser.getScriptParameterValue(INIT_SCRIPT);
+        String initScript = null;
+        ScriptType initScriptType = null;
+        if (initScriptValue != null) {
+            initScript = initScriptValue.script();
+            initScriptType = initScriptValue.scriptType();
+        }
+        ScriptParameterValue mapScriptValue = scriptParameterParser.getScriptParameterValue(MAP_SCRIPT);
+        String mapScript = null;
+        ScriptType mapScriptType = null;
+        if (mapScriptValue != null) {
+            mapScript = mapScriptValue.script();
+            mapScriptType = mapScriptValue.scriptType();
+        }
+        ScriptParameterValue combineScriptValue = scriptParameterParser.getScriptParameterValue(COMBINE_SCRIPT);
+        String combineScript = null;
+        ScriptType combineScriptType = null;
+        if (combineScriptValue != null) {
+            combineScript = combineScriptValue.script();
+            combineScriptType = combineScriptValue.scriptType();
+        }
+        ScriptParameterValue reduceScriptValue = scriptParameterParser.getScriptParameterValue(REDUCE_SCRIPT);
+        String reduceScript = null;
+        ScriptType reduceScriptType = null;
+        if (reduceScriptValue != null) {
+            reduceScript = reduceScriptValue.script();
+            reduceScriptType = reduceScriptValue.scriptType();
+        }
+        scriptLang = scriptParameterParser.lang();
+        
         if (mapScript == null) {
             throw new SearchParseException(context, "map_script field is required in [" + aggregationName + "].");
         }

+ 13 - 14
src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParser.java

@@ -28,6 +28,8 @@ import org.elasticsearch.index.mapper.FieldMapper;
 import org.elasticsearch.index.mapper.core.DateFieldMapper;
 import org.elasticsearch.index.mapper.core.NumberFieldMapper;
 import org.elasticsearch.index.mapper.ip.IpFieldMapper;
+import org.elasticsearch.script.ScriptParameterParser;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.script.SearchScript;
 import org.elasticsearch.search.SearchParseException;
@@ -79,6 +81,7 @@ public class ValuesSourceParser<VS extends ValuesSource> {
     private ValueType targetValueType = null;
     private boolean requiresSortedValues = false;
     private boolean requiresUniqueValues = false;
+    private ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
 
     private Input input = new Input();
 
@@ -96,25 +99,14 @@ public class ValuesSourceParser<VS extends ValuesSource> {
             } else if (formattable && "format".equals(currentFieldName)) {
                 input.format = parser.text();
             } else if (scriptable) {
-                if ("script".equals(currentFieldName)) {
-                    input.script = parser.text();
-                    input.scriptType = ScriptService.ScriptType.INLINE;
-                } else if ("script_id".equals(currentFieldName)) {
-                    input.script = parser.text();
-                    input.scriptType = ScriptService.ScriptType.INDEXED;
-                } else if ("script_file".equals(currentFieldName)) {
-                    input.script = parser.text();
-                    input.scriptType = ScriptService.ScriptType.FILE;
-                } else if ("lang".equals(currentFieldName)) {
-                    input.lang = parser.text();
-                } else if ("value_type".equals(currentFieldName) || "valueType".equals(currentFieldName)) {
+                if ("value_type".equals(currentFieldName) || "valueType".equals(currentFieldName)) {
                     input.valueType = ValueType.resolveForScript(parser.text());
                     if (targetValueType != null && input.valueType.isNotA(targetValueType)) {
                         throw new SearchParseException(context, aggType.name() + " aggregation [" + aggName +
                                 "] was configured with an incompatible value type [" + input.valueType + "]. [" + aggType +
                                 "] aggregation can only work on value of type [" + targetValueType + "]");
                     }
-                } else {
+                } else if (!scriptParameterParser.token(currentFieldName, token, parser)) {
                     return false;
                 }
                 return true;
@@ -135,7 +127,14 @@ public class ValuesSourceParser<VS extends ValuesSource> {
     }
 
     public ValuesSourceConfig<VS> config() {
-
+        
+        ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
+        if (scriptValue != null) {
+            input.script = scriptValue.script();
+            input.scriptType = scriptValue.scriptType();
+        }
+        input.lang = scriptParameterParser.lang();
+        
         ValueType valueType = input.valueType != null ? input.valueType : targetValueType;
 
         if (input.field == null) {

+ 14 - 12
src/main/java/org/elasticsearch/search/fetch/script/ScriptFieldsParseElement.java

@@ -20,6 +20,8 @@
 package org.elasticsearch.search.fetch.script;
 
 import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.script.ScriptParameterParser;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
 import org.elasticsearch.script.ScriptService;
 import org.elasticsearch.script.SearchScript;
 import org.elasticsearch.search.SearchParseElement;
@@ -52,6 +54,7 @@ public class ScriptFieldsParseElement implements SearchParseElement {
                 currentFieldName = parser.currentName();
             } else if (token == XContentParser.Token.START_OBJECT) {
                 String fieldName = currentFieldName;
+                ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
                 String script = null;
                 String scriptLang = null;
                 ScriptService.ScriptType scriptType = null;
@@ -63,22 +66,21 @@ public class ScriptFieldsParseElement implements SearchParseElement {
                     } else if (token == XContentParser.Token.START_OBJECT) {
                         params = parser.map();
                     } else if (token.isValue()) {
-                        if ("script".equals(currentFieldName)) {
-                            script = parser.text();
-                            scriptType = ScriptService.ScriptType.INLINE;
-                        } else if ("script_id".equals(currentFieldName)) {
-                            script = parser.text();
-                            scriptType = ScriptService.ScriptType.INDEXED;
-                        } else if ("file".equals(currentFieldName)) {
-                            script = parser.text();
-                            scriptType = ScriptService.ScriptType.FILE;
-                        } else if ("lang".equals(currentFieldName)) {
-                            scriptLang = parser.text();
-                        } else if ("ignore_failure".equals(currentFieldName)) {
+                        if ("ignore_failure".equals(currentFieldName)) {
                             ignoreException = parser.booleanValue();
+                        } else {
+                            scriptParameterParser.token(currentFieldName, token, parser);
                         }
                     }
                 }
+
+                ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
+                if (scriptValue != null) {
+                    script = scriptValue.script();
+                    scriptType = scriptValue.scriptType();
+                }
+                scriptLang = scriptParameterParser.lang();
+                
                 SearchScript searchScript = context.scriptService().search(context.lookup(), scriptLang, script, scriptType, params);
                 context.scriptFields().add(new ScriptFieldsContext.ScriptField(fieldName, searchScript, ignoreException));
             }

+ 1282 - 0
src/test/java/org/elasticsearch/script/ScriptParameterParserTest.java

@@ -0,0 +1,1282 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.elasticsearch.script;
+
+
+import org.elasticsearch.common.bytes.BytesArray;
+import org.elasticsearch.common.xcontent.ToXContent.MapParams;
+import org.elasticsearch.common.xcontent.XContentHelper;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.common.xcontent.XContentParser.Token;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterParseException;
+import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
+import org.elasticsearch.script.ScriptService.ScriptType;
+import org.elasticsearch.test.ElasticsearchTestCase;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.*;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.nullValue;
+
+public class ScriptParameterParserTest extends ElasticsearchTestCase {
+
+    @Test
+    public void testTokenDefaultInline() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"script\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        paramParser = new ScriptParameterParser(null);
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        paramParser = new ScriptParameterParser(new HashSet<String>());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenDefaultFile() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"script_file\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+
+        parser = XContentHelper.createParser(new BytesArray("{ \"scriptFile\" : \"scriptValue\" }"));
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser = new ScriptParameterParser();
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+
+        parser = XContentHelper.createParser(new BytesArray("{ \"file\" : \"scriptValue\" }"));
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser = new ScriptParameterParser();
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenDefaultIndexed() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"script_id\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+
+        parser = XContentHelper.createParser(new BytesArray("{ \"scriptId\" : \"scriptValue\" }"));
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser = new ScriptParameterParser();
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenDefaultNotFound() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo\" : \"bar\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(false));
+        assertThat(paramParser.getDefaultScriptParameterValue(), nullValue());
+        assertThat(paramParser.getScriptParameterValue("script"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenSingleParameter() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenSingleParameterFile() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo_file\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenSingleParameterIndexed() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo_id\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testTokenSingleParameterDelcaredTwiceInlineFile() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo\" : \"scriptValue\", \"foo_file\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser.token(parser.currentName(), parser.currentToken(), parser);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testTokenSingleParameterDelcaredTwiceInlineIndexed() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo\" : \"scriptValue\", \"foo_id\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser.token(parser.currentName(), parser.currentToken(), parser);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testTokenSingleParameterDelcaredTwiceFileInline() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo_file\" : \"scriptValue\", \"foo\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser.token(parser.currentName(), parser.currentToken(), parser);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testTokenSingleParameterDelcaredTwiceFileIndexed() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo_file\" : \"scriptValue\", \"foo_id\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser.token(parser.currentName(), parser.currentToken(), parser);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testTokenSingleParameterDelcaredTwiceIndexedInline() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo_id\" : \"scriptValue\", \"foo\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser.token(parser.currentName(), parser.currentToken(), parser);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testTokenSingleParameterDelcaredTwiceIndexedFile() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo_id\" : \"scriptValue\", \"foo_file\" : \"scriptValue\" }"));
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        paramParser.token(parser.currentName(), parser.currentToken(), parser);
+    }
+
+    @Test
+    public void testTokenMultipleParameters() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo\" : \"fooScriptValue\", \"bar_file\" : \"barScriptValue\", \"baz_id\" : \"bazScriptValue\" }"));
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenMultipleParametersWithLang() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo\" : \"fooScriptValue\", \"bar_file\" : \"barScriptValue\", \"lang\" : \"myLang\", \"baz_id\" : \"bazScriptValue\" }"));
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), equalTo("myLang"));
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), equalTo("myLang"));
+    }
+
+    @Test
+    public void testTokenMultipleParametersNotFound() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"other\" : \"scriptValue\" }"));
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(false));
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenMultipleParametersSomeNotFound() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo\" : \"fooScriptValue\", \"other_file\" : \"barScriptValue\", \"baz_id\" : \"bazScriptValue\" }"));
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other_file"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        Token token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other_file"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(false));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other_file"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        token = parser.nextToken();
+        while (token != Token.VALUE_STRING) {
+            token = parser.nextToken();
+        }
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(true));
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other_file"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testTokenMultipleParametersWrongType() throws IOException {
+        XContentParser parser = XContentHelper.createParser(new BytesArray("{ \"foo\" : \"fooScriptValue\", \"bar_file\" : \"barScriptValue\", \"baz_id\" : \"bazScriptValue\" }"));
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(paramParser.token(parser.currentName(), parser.currentToken(), parser), equalTo(false));
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testReservedParameters() {
+        Set<String> parameterNames = Collections.singleton("lang");
+        new ScriptParameterParser(parameterNames );
+    }
+
+    @Test
+    public void testConfigDefaultInline() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("script", "scriptValue");
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, true);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+        config = new HashMap<>();
+        config.put("script", "scriptValue");
+        paramParser = new ScriptParameterParser(null);
+        paramParser.parseConfig(config, true);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+        config = new HashMap<>();
+        config.put("script", "scriptValue");
+        paramParser = new ScriptParameterParser(new HashSet<String>());
+        paramParser.parseConfig(config, true);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+    }
+
+    @Test
+    public void testConfigDefaultFile() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("script_file", "scriptValue");
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, true);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+
+        config = new HashMap<>();
+        config.put("scriptFile", "scriptValue");
+        paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, true);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+
+        config = new HashMap<>();
+        config.put("file", "scriptValue");
+        paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, true);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+    }
+
+    @Test
+    public void testConfigDefaultIndexed() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("script_id", "scriptValue");
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, true);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+
+        config = new HashMap<>();
+        config.put("scriptId", "scriptValue");
+        paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, true);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+    }
+
+    @Test
+    public void testConfigDefaultIndexedNoRemove() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("script_id", "scriptValue");
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, false);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.size(), equalTo(1));
+        assertThat((String) config.get("script_id"), equalTo("scriptValue"));
+
+        config = new HashMap<>();
+        config.put("scriptId", "scriptValue");
+        paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, false);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.size(), equalTo(1));
+        assertThat((String) config.get("scriptId"), equalTo("scriptValue"));
+    }
+
+    @Test
+    public void testConfigDefaultNotFound() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "bar");
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseConfig(config, true);
+        assertThat(paramParser.getDefaultScriptParameterValue(), nullValue());
+        assertThat(paramParser.getScriptParameterValue("script"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.size(), equalTo(1));
+        assertThat((String) config.get("foo"), equalTo("bar"));
+    }
+
+    @Test
+    public void testConfigSingleParameter() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+    }
+
+    @Test
+    public void testConfigSingleParameterFile() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo_file", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+    }
+
+    @Test
+    public void testConfigSingleParameterIndexed() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo_id", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigSingleParameterDelcaredTwiceInlineFile() throws IOException {
+        Map<String, Object> config = new LinkedHashMap<>();
+        config.put("foo", "scriptValue");
+        config.put("foo_file", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigSingleParameterDelcaredTwiceInlineIndexed() throws IOException {
+        Map<String, Object> config = new LinkedHashMap<>();
+        config.put("foo", "scriptValue");
+        config.put("foo_id", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigSingleParameterDelcaredTwiceFileInline() throws IOException {
+        Map<String, Object> config = new LinkedHashMap<>();
+        config.put("foo_file", "scriptValue");
+        config.put("foo", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigSingleParameterDelcaredTwiceFileIndexed() throws IOException {
+        Map<String, Object> config = new LinkedHashMap<>();
+        config.put("foo_file", "scriptValue");
+        config.put("foo_id", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigSingleParameterDelcaredTwiceIndexedInline() throws IOException {
+        Map<String, Object> config = new LinkedHashMap<>();
+        config.put("foo_id", "scriptValue");
+        config.put("foo", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigSingleParameterDelcaredTwiceIndexedFile() throws IOException {
+        Map<String, Object> config = new LinkedHashMap<>();
+        config.put("foo_id", "scriptValue");
+        config.put("foo_file", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test
+    public void testConfigMultipleParameters() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", "barScriptValue");
+        config.put("baz_id", "bazScriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, true);
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.isEmpty(), equalTo(true));
+    }
+
+    @Test
+    public void testConfigMultipleParametersWithLang() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", "barScriptValue");
+        config.put("lang", "myLang");
+        config.put("baz_id", "bazScriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, true);
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), equalTo("myLang"));
+        assertThat(config.isEmpty(), equalTo(true));
+    }
+
+    @Test
+    public void testConfigMultipleParametersWithLangNoRemove() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", "barScriptValue");
+        config.put("lang", "myLang");
+        config.put("baz_id", "bazScriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, false);
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), equalTo("myLang"));
+        assertThat(config.size(), equalTo(4));
+        assertThat((String) config.get("foo"), equalTo("fooScriptValue"));
+        assertThat((String) config.get("bar_file"), equalTo("barScriptValue"));
+        assertThat((String) config.get("baz_id"), equalTo("bazScriptValue"));
+        assertThat((String) config.get("lang"), equalTo("myLang"));
+    }
+
+    @Test
+    public void testConfigMultipleParametersNotFound() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("other", "scriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, true);
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.size(), equalTo(1));
+        assertThat((String) config.get("other"), equalTo("scriptValue"));
+    }
+
+    @Test
+    public void testConfigMultipleParametersSomeNotFound() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("other_file", "barScriptValue");
+        config.put("baz_id", "bazScriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other_file"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, true);
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other_file"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        assertThat(config.size(), equalTo(1));
+        assertThat((String) config.get("other_file"), equalTo("barScriptValue"));
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigMultipleParametersInlineWrongType() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", 1l);
+        config.put("bar_file", "barScriptValue");
+        config.put("baz_id", "bazScriptValue");
+        config.put("lang", "myLang");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigMultipleParametersFileWrongType() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", 1l);
+        config.put("baz_id", "bazScriptValue");
+        config.put("lang", "myLang");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigMultipleParametersIndexedWrongType() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", "barScriptValue");
+        config.put("baz_id", 1l);
+        config.put("lang", "myLang");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testConfigMultipleParametersLangWrongType() throws IOException {
+        Map<String, Object> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", "barScriptValue");
+        config.put("baz_id", "bazScriptValue");
+        config.put("lang", 1l);
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        paramParser.parseConfig(config, true);
+    }
+
+    @Test
+    public void testParamsDefaultInline() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("script", "scriptValue");
+        MapParams params = new MapParams(config);
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseParams(params);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+        
+        paramParser = new ScriptParameterParser(null);
+        paramParser.parseParams(params);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+
+        paramParser = new ScriptParameterParser(new HashSet<String>());
+        paramParser.parseParams(params);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testParamsDefaultFile() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("script_file", "scriptValue");
+        MapParams params = new MapParams(config);
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseParams(params);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testParamsDefaultIndexed() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("script_id", "scriptValue");
+        MapParams params = new MapParams(config);
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseParams(params);
+        assertDefaultParameterValue(paramParser, "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testParamsDefaultNotFound() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("foo", "bar");
+        MapParams params = new MapParams(config);
+        ScriptParameterParser paramParser = new ScriptParameterParser();
+        paramParser.parseParams(params);
+        assertThat(paramParser.getDefaultScriptParameterValue(), nullValue());
+        assertThat(paramParser.getScriptParameterValue("script"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testParamsSingleParameter() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("foo", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INLINE);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testParamsSingleParameterFile() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("foo_file", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.FILE);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testParamsSingleParameterIndexed() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("foo_id", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+        assertParameterValue(paramParser, "foo", "scriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testParamsSingleParameterDelcaredTwiceInlineFile() throws IOException {
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("foo", "scriptValue");
+        config.put("foo_file", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testParamsSingleParameterDelcaredTwiceInlineIndexed() throws IOException {
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("foo", "scriptValue");
+        config.put("foo_id", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testParamsSingleParameterDelcaredTwiceFileInline() throws IOException {
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("foo_file", "scriptValue");
+        config.put("foo", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testParamsSingleParameterDelcaredTwiceFileIndexed() throws IOException {
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("foo_file", "scriptValue");
+        config.put("foo_id", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testParamsSingleParameterDelcaredTwiceIndexedInline() throws IOException {
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("foo_id", "scriptValue");
+        config.put("foo", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+    }
+
+    @Test(expected=ScriptParameterParseException.class)
+    public void testParamsSingleParameterDelcaredTwiceIndexedFile() throws IOException {
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("foo_id", "scriptValue");
+        config.put("foo_file", "scriptValue");
+        Set<String> parameters = Collections.singleton("foo");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+    }
+
+    @Test
+    public void testParamsMultipleParameters() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", "barScriptValue");
+        config.put("baz_id", "bazScriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testParamsMultipleParametersWithLang() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", "barScriptValue");
+        config.put("lang", "myLang");
+        config.put("baz_id", "bazScriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), equalTo("myLang"));
+    }
+
+    @Test
+    public void testParamsMultipleParametersWithLangNoRemove() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("bar_file", "barScriptValue");
+        config.put("lang", "myLang");
+        config.put("baz_id", "bazScriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertParameterValue(paramParser, "bar", "barScriptValue", ScriptType.FILE);
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), equalTo("myLang"));
+    }
+
+    @Test
+    public void testParamsMultipleParametersNotFound() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("other", "scriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    @Test
+    public void testParamsMultipleParametersSomeNotFound() throws IOException {
+        Map<String, String> config = new HashMap<>();
+        config.put("foo", "fooScriptValue");
+        config.put("other_file", "barScriptValue");
+        config.put("baz_id", "bazScriptValue");
+        Set<String> parameters = new HashSet<>();
+        parameters.add("foo");
+        parameters.add("bar");
+        parameters.add("baz");
+        ScriptParameterParser paramParser = new ScriptParameterParser(parameters);
+        assertThat(paramParser.getScriptParameterValue("foo"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other_file"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+        MapParams params = new MapParams(config);
+        paramParser.parseParams(params);
+        assertParameterValue(paramParser, "foo", "fooScriptValue", ScriptType.INLINE);
+        assertThat(paramParser.getScriptParameterValue("bar"), nullValue());
+        assertParameterValue(paramParser, "baz", "bazScriptValue", ScriptType.INDEXED);
+        assertThat(paramParser.getScriptParameterValue("bar_file"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("baz_id"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other"), nullValue());
+        assertThat(paramParser.getScriptParameterValue("other_file"), nullValue());
+        assertThat(paramParser.lang(), nullValue());
+    }
+
+    private void assertDefaultParameterValue(ScriptParameterParser paramParser, String expectedScript, ScriptType expectedScriptType) throws IOException {
+        ScriptParameterValue defaultValue = paramParser.getDefaultScriptParameterValue();
+        ScriptParameterValue defaultValueByName = paramParser.getScriptParameterValue("script");
+        assertThat(defaultValue.scriptType(), equalTo(expectedScriptType));
+        assertThat(defaultValue.script(), equalTo(expectedScript));
+        assertThat(defaultValueByName.scriptType(), equalTo(expectedScriptType));
+        assertThat(defaultValueByName.script(), equalTo(expectedScript));
+    }
+
+    private void assertParameterValue(ScriptParameterParser paramParser, String parameterName, String expectedScript, ScriptType expectedScriptType) throws IOException {
+        ScriptParameterValue value = paramParser.getScriptParameterValue(parameterName);
+        assertThat(value.scriptType(), equalTo(expectedScriptType));
+        assertThat(value.script(), equalTo(expectedScript));
+    }
+
+}