View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.renderkit.html;
21  
22  import org.apache.myfaces.tobago.internal.context.DateTimeI18n;
23  import org.apache.myfaces.tobago.layout.AutoLayoutToken;
24  import org.apache.myfaces.tobago.layout.LayoutToken;
25  import org.apache.myfaces.tobago.layout.LayoutTokens;
26  import org.apache.myfaces.tobago.layout.MeasureLayoutToken;
27  import org.apache.myfaces.tobago.layout.RelativeLayoutToken;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  import java.util.ArrayList;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.StringTokenizer;
35  
36  public class JsonUtils {
37  
38    private static final Logger LOG = LoggerFactory.getLogger(JsonUtils.class);
39  
40    private JsonUtils() {
41    }
42  
43    private static void encode(final StringBuilder builder, final String name, final String[] value) {
44      builder.append("\"");
45      builder.append(name);
46      builder.append("\":");
47      encode(builder, value);
48      builder.append(",");
49    }
50  
51    public static void encode(StringBuilder builder, String[] value) {
52      builder.append("[");
53      boolean colon = false;
54      for (final String item : value) {
55        if (colon) {
56          builder.append(",");
57        }
58        builder.append("\"");
59        builder.append(item);
60        builder.append("\"");
61        colon = true;
62      }
63      builder.append("]");
64    }
65  
66    public static void encode(StringBuilder builder, List<Integer> value) {
67      builder.append("[");
68      boolean colon = false;
69      for (final Integer item : value) {
70        if (colon) {
71          builder.append(",");
72        }
73        builder.append(item);
74        colon = true;
75      }
76      builder.append("]");
77    }
78  
79    static void encode(final StringBuilder builder, final String name, final Boolean value) {
80      builder.append("\"");
81      builder.append(name);
82      builder.append("\":");
83      builder.append(Boolean.toString(value));
84      builder.append(",");
85    }
86  
87    static void encode(final StringBuilder builder, final String name, final Integer value) {
88      builder.append("\"");
89      builder.append(name);
90      builder.append("\":");
91      builder.append(Integer.toString(value));
92      builder.append(",");
93    }
94  
95    static void encode(final StringBuilder builder, final String name, String value) {
96      value = value.replaceAll("\\\"", "\\\\\\\""); // todo: optimize
97      builder.append("\"");
98      builder.append(name);
99      builder.append("\":\"");
100     builder.append(value);
101     builder.append("\",");
102   }
103 
104   public static String encode(final CommandMap commandMap) {
105     final StringBuilder builder = new StringBuilder();
106     builder.append("{");
107     final int initialLength = builder.length();
108 
109     final Command click = commandMap.getClick();
110     if (click != null) {
111       encode(builder, "click", click);
112     }
113 
114     final Map<String, Command> other = commandMap.getOther();
115     if (other != null) {
116       for(final Map.Entry<String, Command> entry : other.entrySet()) {
117         encode(builder, entry.getKey(), entry.getValue());
118       }
119     }
120 
121     if (builder.length() - initialLength > 0) {
122       assert builder.charAt(builder.length() - 1) == ',';
123       builder.deleteCharAt(builder.length() - 1);
124     }
125 
126     builder.append("}");
127     return builder.toString();
128   }
129 
130   static void encode(final StringBuilder builder, final String name, final Command command) {
131     builder.append("\"");
132     builder.append(name);
133     builder.append("\":{");
134     final int initialLength = builder.length();
135 
136     final String action = command.getAction();
137     if (action != null) {
138       encode(builder, "action", action);
139     }
140     final Boolean transition = command.getTransition();
141     if (transition != null && !transition) { // true is the default, so encoding is needed.
142       encode(builder, "transition", transition);
143     }
144     final String target = command.getTarget();
145     if (target != null) {
146       encode(builder, "target", target);
147     }
148     final String url = command.getUrl();
149     if (url != null) {
150       encode(builder, "url", url);
151     }
152     final String partially = command.getPartially();
153     if (partially != null) {
154       encode(builder, "partially", partially);
155     }
156     final String focus = command.getFocus();
157     if (focus != null) {
158       encode(builder, "focus", focus);
159     }
160     final String confirmation = command.getConfirmation();
161     if (confirmation != null) {
162       encode(builder, "confirmation", confirmation);
163     }
164     final Integer delay  = command.getDelay();
165     if (delay != null) {
166       encode(builder, "delay", delay);
167     }
168     final Popup popup = command.getPopup();
169     if (popup != null) {
170       encode(builder, "popup", popup);
171     }
172     final Boolean omit = command.getOmit();
173     if (omit != null && omit) { // false is the default, so encoding is needed.
174       encode(builder, "omit", omit);
175     }
176 
177     if (builder.length() - initialLength > 0) {
178       assert builder.charAt(builder.length() - 1) == ',';
179       builder.deleteCharAt(builder.length() - 1);
180     }
181 
182     builder.append("},");
183   }
184 
185   static void encode(final StringBuilder builder, final String name, final Popup popup) {
186     builder.append("\"");
187     builder.append(name);
188     builder.append("\":{");
189     final int initialLength = builder.length();
190 
191     final String command = popup.getCommand();
192     if (command != null) {
193       encode(builder, "command", command);
194     }
195     final Boolean immediate = popup.isImmediate();
196     if (immediate != null) {
197       encode(builder, "immediate", immediate);
198     }
199     if (builder.length() - initialLength > 0) {
200       assert builder.charAt(builder.length() - 1) == ',';
201       builder.deleteCharAt(builder.length() - 1);
202     }
203 
204     builder.append("},");
205   }
206 
207   public static String encode(final DateTimeI18n dateTimeI18n) {
208     final StringBuilder builder = new StringBuilder();
209     builder.append("{");
210     final int initialLength = builder.length();
211 
212     encode(builder, "monthNames", dateTimeI18n.getMonthNames());
213     encode(builder, "monthNamesShort", dateTimeI18n.getMonthNamesShort());
214     encode(builder, "dayNames", dateTimeI18n.getDayNames());
215     encode(builder, "dayNamesShort", dateTimeI18n.getDayNamesShort());
216     encode(builder, "dayNamesMin", dateTimeI18n.getDayNamesMin());
217     encode(builder, "firstDay", dateTimeI18n.getFirstDay());
218 
219     if (builder.length() - initialLength > 0) {
220       assert builder.charAt(builder.length() - 1) == ',';
221       builder.deleteCharAt(builder.length() - 1);
222     }
223     builder.append("}");
224     return builder.toString();
225   }
226 
227   public static String encode(final String[] strings) {
228     if (strings == null) {
229       return null;
230     }
231     final StringBuilder builder = new StringBuilder();
232     encode(builder, strings);
233     return builder.toString();
234   }
235 
236   public static String encode(final List<Integer> integers) {
237     if (integers == null) {
238       return null;
239     }
240     final StringBuilder builder = new StringBuilder();
241     encode(builder, integers);
242     return builder.toString();
243   }
244 
245   public static void encode(final LayoutTokens bankHeads, final StringBuilder builder) {
246     builder.append("[");
247     for (final LayoutToken token : bankHeads.getTokens()) {
248       if (token instanceof RelativeLayoutToken) {
249         final int factor = ((RelativeLayoutToken) token).getFactor();
250         builder.append(factor);
251       } else if (token instanceof AutoLayoutToken) {
252         builder.append("\"auto\"");
253       } else if (token instanceof MeasureLayoutToken) {
254         builder.append("{\"measure\":\"");
255         builder.append(((MeasureLayoutToken) token).getMeasure());
256         builder.append("\"}");
257       } else {
258         LOG.warn("Not supported: " + token);
259       }
260       builder.append(',');
261     }
262     if (builder.charAt(builder.length() - 1) == ',') {
263       builder.deleteCharAt(builder.length() - 1);
264     }
265     builder.append("]");
266   }
267 
268   public static List<Integer> decodeIntegerArray(String json) {
269     json = json.trim();
270     if (json.length() < 2 || json.charAt(0) != '[' || json.charAt(json.length() - 1) != ']') {
271       LOG.error("Can't parse JSON array: no surrounding square brackets []: '{}'", json);
272       return null;
273     }
274     json = json.substring(1, json.length() - 1);
275     final List<Integer> result = new ArrayList<Integer>();
276     final StringTokenizer tokenizer = new StringTokenizer(json, ",");
277     while (tokenizer.hasMoreTokens()) {
278       final String token = tokenizer.nextToken().trim();
279       try {
280         result.add(Integer.parseInt(token));
281       } catch (NumberFormatException e) {
282         LOG.error("Can't parse JSON array: not an integer token: '{}'", token);
283         // ignoring so far
284       }
285     }
286     return result;
287   }
288 }