156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/*
256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Copyright (C) 2010 Google Inc.
356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Licensed under the Apache License, Version 2.0 (the "License");
556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * you may not use this file except in compliance with the License.
656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * You may obtain a copy of the License at
756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * http://www.apache.org/licenses/LICENSE-2.0
956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
1056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Unless required by applicable law or agreed to in writing, software
1156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * distributed under the License is distributed on an "AS IS" BASIS,
1256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * See the License for the specific language governing permissions and
1456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * limitations under the License.
1556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */
1656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
1756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpackage com.google.clearsilver.jsilver.functions;
1856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
1956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.autoescape.EscapeMode;
2056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
2156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.exceptions.JSilverInterpreterException;
2256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport com.google.clearsilver.jsilver.values.Value;
2356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
2456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.io.IOException;
2556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.HashMap;
2656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonimport java.util.Map;
2756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
2856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson/**
2956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * Simple implementation of FunctionFinder that you can register your own functions with.
3056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson *
3156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson * @see FunctionExecutor
3256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson */
3356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodsonpublic class FunctionRegistry implements FunctionExecutor {
3456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
3556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  protected Map<String, Function> functions = new HashMap<String, Function>();
3656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  protected Map<String, TextFilter> escapers = new HashMap<String, TextFilter>();
3756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
3856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public FunctionRegistry() {
3956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    setupDefaultFunctions();
4056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
4156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
4256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
4356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public Value executeFunction(String name, Value... args) {
4456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Function function = functions.get(name);
4556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (function == null) {
4656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException("Function not found " + name);
4756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
4856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Value result = function.execute(args);
4956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (result == null) {
5056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException("Function " + name + " did not return value");
5156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
5256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return result;
5356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
5456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
5556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
5656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void escape(String name, String input, Appendable output) throws IOException {
5756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (name == null || name.isEmpty() || name.equals("none")) {
5856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      output.append(input);
5956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    } else {
6056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      TextFilter escaper = escapers.get(name);
6156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      if (escaper == null) {
6256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        throw new JSilverInterpreterException("Unknown escaper: " + name);
6356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
6456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      escaper.filter(input, output);
6556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
6656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
6756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
6856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  @Override
6956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public boolean isEscapingFunction(String name) {
7056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    Function function = functions.get(name);
7156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    if (function == null) {
7256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      throw new JSilverInterpreterException("Function not found " + name);
7356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    }
7456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    return function.isEscapingFunction();
7556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
7656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
7756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
7856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Subclasses can override this to register their own functions.
7956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
8056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  protected void setupDefaultFunctions() {}
8156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
8256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
8356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Register a Function with a given name.
8456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
8556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void registerFunction(String name, Function function) {
8656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    functions.put(name, function);
8756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
8856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
8956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
9056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Register a TextFilter as a Function that takes a single String argument and returns the
9156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * filtered value.
9256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
9356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void registerFunction(String name, final TextFilter textFilter) {
9456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    registerFunction(name, textFilter, false);
9556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
9656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
9756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void registerFunction(String name, final TextFilter textFilter, final boolean isEscaper) {
9856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
9956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    // Adapt a TextFilter to the Function interface.
10056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    registerFunction(name, new Function() {
10156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      @Override
10256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      public Value execute(Value... args) {
10356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        if (args.length != 1) {
10456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          throw new IllegalArgumentException("Expected 1 argument");
10556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        }
10656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        String in = args[0].asString();
10756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        StringBuilder out = new StringBuilder(in.length());
10856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        try {
10956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          textFilter.filter(in, out);
11056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        } catch (IOException e) {
11156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          throw new JSilverInterpreterException(e.getMessage());
11256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        }
11356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
11456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        EscapeMode mode;
11556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        boolean isPartiallyEscaped;
11656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        if (isEscaper) {
11756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          // This function escapes its input. Hence the output is
11856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          // partiallyEscaped.
11956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          mode = EscapeMode.ESCAPE_IS_CONSTANT;
12056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          isPartiallyEscaped = true;
12156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        } else {
12256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          mode = EscapeMode.ESCAPE_NONE;
12356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          isPartiallyEscaped = false;
12456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          for (Value arg : args) {
12556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson            if (arg.isPartiallyEscaped()) {
12656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson              isPartiallyEscaped = true;
12756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson              break;
12856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson            }
12956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson          }
13056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        }
13156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        return Value.literalValue(out.toString(), mode, isPartiallyEscaped);
13256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
13356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
13456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      public boolean isEscapingFunction() {
13556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson        return isEscaper;
13656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson      }
13756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    });
13856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
13956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
14056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  /**
14156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * Registers an escaper, that is called when executing a &lt;?cs escape ?&gt; command.
14256ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   *
14356ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * @param name The name with which &lt;?cs escape ?&gt; will invoke this escaper.
14456ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   * @param escaper A TextFilter that implements the escaping functionality.
14556ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson   */
14656ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  public void registerEscapeMode(String name, TextFilter escaper) {
14756ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
14856ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson    escapers.put(name, escaper);
14956ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson  }
15056ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson
15156ed4167b942ec265f9cee70ac4d71d10b3835ceBen Dodson}
152