1/*
2 * Copyright (C) 2017 The Android Open Source Project
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.googlecode.android_scripting.language;
18
19import com.googlecode.android_scripting.rpc.MethodDescriptor;
20import com.googlecode.android_scripting.rpc.ParameterDescriptor;
21
22import java.util.HashMap;
23import java.util.Map;
24
25/**
26 * Represents the programming language supported by the SL4A.
27 *
28 */
29public class Language {
30
31  private final static Map<Character, String> AUTO_CLOSE_MAP = buildAutoCloseMap('[', "[]", '{',
32      "{}", '(', "()", '\'', "''", '"', "\"\"");
33
34  /** Returns the initial template for newly created script. */
35  public String getContentTemplate() {
36    StringBuilder content = new StringBuilder(getImportStatement());
37    if (content.length() != 0) {
38      content.append('\n');
39    }
40    content.append(getRpcReceiverDeclaration(getDefaultRpcReceiver()));
41    return content.toString();
42  }
43
44  /** Returns the Android package import statement. */
45  protected String getImportStatement() {
46    return "";
47  }
48
49  /** Returns the RPC receiver declaration. */
50  protected String getRpcReceiverDeclaration(String rpcReceiver) {
51    return "";
52  }
53
54  /** Returns the default RPC receiver name. */
55  protected String getDefaultRpcReceiver() {
56    return "droid";
57  }
58
59  /**
60   * Returns the string containing opening and closing tokens if the input is an opening token.
61   * Returns {@code null} otherwise.
62   */
63  public String autoClose(char token) {
64    return AUTO_CLOSE_MAP.get(token);
65  }
66
67  /** Returns the RPC call text with given parameter values. */
68  public final String getRpcText(String content, MethodDescriptor rpc, String[] values) {
69    return getMethodCallText(getRpcReceiverName(content), rpc.getName(),
70        rpc.getParameterValues(values));
71  }
72
73  /** Returns the RPC receiver found in the given script. */
74  protected String getRpcReceiverName(String content) {
75    return getDefaultRpcReceiver();
76  }
77
78  /** Returns the method call text in the language. */
79  protected String getMethodCallText(String receiver, String method,
80      ParameterDescriptor[] parameters) {
81    StringBuilder result =
82        new StringBuilder().append(getApplyReceiverText(receiver)).append(getApplyOperatorText())
83            .append(method).append(getLeftParametersText());
84    String separator = "";
85    for (ParameterDescriptor parameter : parameters) {
86      result.append(separator).append(getValueText(parameter));
87      separator = getParameterSeparator();
88    }
89    result.append(getRightParametersText());
90
91    return result.toString();
92  }
93
94  /** Returns the apply receiver text. */
95  protected String getApplyReceiverText(String receiver) {
96    return receiver;
97  }
98
99  /** Returns the apply operator text. */
100  protected String getApplyOperatorText() {
101    return ".";
102  }
103
104  /** Returns the text to the left of the parameters. */
105  protected String getLeftParametersText() {
106    return "(";
107  }
108
109  /** Returns the text to the right of the parameters. */
110  protected String getRightParametersText() {
111    return ")";
112  }
113
114  /** Returns the parameter separator text. */
115  protected String getParameterSeparator() {
116    return ", ";
117  }
118
119  /** Returns the text of the quotation. */
120  protected String getQuote() {
121    return "\"";
122  }
123
124  /** Returns the text of the {@code null} value. */
125  protected String getNull() {
126    return "null";
127  }
128
129  /** Returns the text of the {{@code true} value. */
130  protected String getTrue() {
131    return "true";
132  }
133
134  /** Returns the text of the false value. */
135  protected String getFalse() {
136    return "false";
137  }
138
139  /** Returns the parameter value suitable for code generation. */
140  protected String getValueText(ParameterDescriptor parameter) {
141    if (parameter.getValue() == null) {
142      return getNullValueText();
143    } else if (parameter.getType().equals(String.class)) {
144      return getStringValueText(parameter.getValue());
145    } else if (parameter.getType().equals(Boolean.class)) {
146      return getBooleanValueText(parameter.getValue());
147    } else {
148      return parameter.getValue();
149    }
150  }
151
152  /** Returns the null value suitable for code generation. */
153  private String getNullValueText() {
154    return getNull();
155  }
156
157  /** Returns the string parameter value suitable for code generation. */
158  protected String getStringValueText(String value) {
159    // TODO(igorkarp): do not quote expressions once they could be detected.
160    return getQuote() + value + getQuote();
161  }
162
163  /** Returns the boolean parameter value suitable for code generation. */
164  protected String getBooleanValueText(String value) {
165    if (value.equals(Boolean.TRUE.toString())) {
166      return getTrue();
167    } else if (value.equals(Boolean.FALSE.toString())) {
168      return getFalse();
169    } else {
170      // If it is neither true nor false it is must be an expression.
171      return value;
172    }
173  }
174
175  private static Map<Character, String> buildAutoCloseMap(char c1, String s1, char c2, String s2,
176      char c3, String s3, char c4, String s4, char c5, String s5) {
177    Map<Character, String> map = new HashMap<Character, String>(5);
178    map.put(c1, s1);
179    map.put(c2, s2);
180    map.put(c3, s3);
181    map.put(c4, s4);
182    map.put(c5, s5);
183    return map;
184  }
185}
186