1/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.clearsilver.jsilver.functions;
18
19import com.google.clearsilver.jsilver.autoescape.EscapeMode;
20
21import com.google.clearsilver.jsilver.exceptions.JSilverInterpreterException;
22import com.google.clearsilver.jsilver.values.Value;
23
24import java.io.IOException;
25import java.util.HashMap;
26import java.util.Map;
27
28/**
29 * Simple implementation of FunctionFinder that you can register your own functions with.
30 *
31 * @see FunctionExecutor
32 */
33public class FunctionRegistry implements FunctionExecutor {
34
35  protected Map<String, Function> functions = new HashMap<String, Function>();
36  protected Map<String, TextFilter> escapers = new HashMap<String, TextFilter>();
37
38  public FunctionRegistry() {
39    setupDefaultFunctions();
40  }
41
42  @Override
43  public Value executeFunction(String name, Value... args) {
44    Function function = functions.get(name);
45    if (function == null) {
46      throw new JSilverInterpreterException("Function not found " + name);
47    }
48    Value result = function.execute(args);
49    if (result == null) {
50      throw new JSilverInterpreterException("Function " + name + " did not return value");
51    }
52    return result;
53  }
54
55  @Override
56  public void escape(String name, String input, Appendable output) throws IOException {
57    if (name == null || name.isEmpty() || name.equals("none")) {
58      output.append(input);
59    } else {
60      TextFilter escaper = escapers.get(name);
61      if (escaper == null) {
62        throw new JSilverInterpreterException("Unknown escaper: " + name);
63      }
64      escaper.filter(input, output);
65    }
66  }
67
68  @Override
69  public boolean isEscapingFunction(String name) {
70    Function function = functions.get(name);
71    if (function == null) {
72      throw new JSilverInterpreterException("Function not found " + name);
73    }
74    return function.isEscapingFunction();
75  }
76
77  /**
78   * Subclasses can override this to register their own functions.
79   */
80  protected void setupDefaultFunctions() {}
81
82  /**
83   * Register a Function with a given name.
84   */
85  public void registerFunction(String name, Function function) {
86    functions.put(name, function);
87  }
88
89  /**
90   * Register a TextFilter as a Function that takes a single String argument and returns the
91   * filtered value.
92   */
93  public void registerFunction(String name, final TextFilter textFilter) {
94    registerFunction(name, textFilter, false);
95  }
96
97  public void registerFunction(String name, final TextFilter textFilter, final boolean isEscaper) {
98
99    // Adapt a TextFilter to the Function interface.
100    registerFunction(name, new Function() {
101      @Override
102      public Value execute(Value... args) {
103        if (args.length != 1) {
104          throw new IllegalArgumentException("Expected 1 argument");
105        }
106        String in = args[0].asString();
107        StringBuilder out = new StringBuilder(in.length());
108        try {
109          textFilter.filter(in, out);
110        } catch (IOException e) {
111          throw new JSilverInterpreterException(e.getMessage());
112        }
113
114        EscapeMode mode;
115        boolean isPartiallyEscaped;
116        if (isEscaper) {
117          // This function escapes its input. Hence the output is
118          // partiallyEscaped.
119          mode = EscapeMode.ESCAPE_IS_CONSTANT;
120          isPartiallyEscaped = true;
121        } else {
122          mode = EscapeMode.ESCAPE_NONE;
123          isPartiallyEscaped = false;
124          for (Value arg : args) {
125            if (arg.isPartiallyEscaped()) {
126              isPartiallyEscaped = true;
127              break;
128            }
129          }
130        }
131        return Value.literalValue(out.toString(), mode, isPartiallyEscaped);
132      }
133
134      public boolean isEscapingFunction() {
135        return isEscaper;
136      }
137    });
138  }
139
140  /**
141   * Registers an escaper, that is called when executing a &lt;?cs escape ?&gt; command.
142   *
143   * @param name The name with which &lt;?cs escape ?&gt; will invoke this escaper.
144   * @param escaper A TextFilter that implements the escaping functionality.
145   */
146  public void registerEscapeMode(String name, TextFilter escaper) {
147
148    escapers.put(name, escaper);
149  }
150
151}
152