001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.runtimecatalog;
018
019import java.util.ArrayList;
020import java.util.LinkedHashMap;
021import java.util.LinkedHashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025
026import org.json.simple.JsonObject;
027import org.json.simple.Jsoner;
028
029public final class JSonSchemaHelper {
030
031    private JSonSchemaHelper() {
032    }
033
034    /**
035     * Parses the json schema to split it into a list or rows, where each row contains key value pairs with the metadata
036     *
037     * @param group the group to parse from such as <tt>component</tt>, <tt>componentProperties</tt>, or <tt>properties</tt>.
038     * @param json the json
039     * @return a list of all the rows, where each row is a set of key value pairs with metadata
040     * @throws RuntimeException is thrown if error parsing the json data
041     */
042    @SuppressWarnings("unchecked")
043    public static List<Map<String, String>> parseJsonSchema(String group, String json, boolean parseProperties) {
044        List<Map<String, String>> answer = new ArrayList<>();
045        if (json == null) {
046            return answer;
047        }
048
049        // convert into a List<Map<String, String>> structure which is expected as output from this parser
050        try {
051            JsonObject output = (JsonObject) Jsoner.deserialize(json);
052            for (String key : output.keySet()) {
053                Map row = output.getMap(key);
054                if (key.equals(group)) {
055                    if (parseProperties) {
056                        // flattern each entry in the row with name as they key, and its value as the content (its a map also)
057                        for (Object obj : row.entrySet()) {
058                            Map.Entry entry = (Map.Entry) obj;
059                            Map<String, String> newRow = new LinkedHashMap();
060                            newRow.put("name", entry.getKey().toString());
061
062                            Map newData = transformMap((Map) entry.getValue());
063                            newRow.putAll(newData);
064                            answer.add(newRow);
065                        }
066                    } else {
067                        // flattern each entry in the row as a list of single Map<key, value> elements
068                        Map newData = transformMap(row);
069                        for (Object obj : newData.entrySet()) {
070                            Map.Entry entry = (Map.Entry) obj;
071                            Map<String, String> newRow = new LinkedHashMap<>();
072                            newRow.put(entry.getKey().toString(), entry.getValue().toString());
073                            answer.add(newRow);
074                        }
075                    }
076                }
077            }
078        } catch (Exception e) {
079            // wrap parsing exceptions as runtime
080            throw new RuntimeException("Cannot parse json", e);
081        }
082
083        return answer;
084    }
085
086    private static Map<String, String> transformMap(Map jsonMap) {
087        Map<String, String> answer = new LinkedHashMap<>();
088
089        for (Object rowObj : jsonMap.entrySet()) {
090            Map.Entry rowEntry = (Map.Entry) rowObj;
091            // if its a list type then its an enum, and we need to parse it as a single line separated with comma
092            // to be backwards compatible
093            Object newValue = rowEntry.getValue();
094            if (newValue instanceof List) {
095                List list = (List) newValue;
096                CollectionStringBuffer csb = new CollectionStringBuffer(",");
097                for (Object line : list) {
098                    csb.append(line);
099                }
100                newValue = csb.toString();
101            }
102            // ensure value is escaped
103            String value = escapeJson(newValue.toString());
104            answer.put(rowEntry.getKey().toString(), value);
105        }
106
107        return answer;
108    }
109
110    private static String escapeJson(String value) {
111        // need to safe encode \r as \\r so its escaped
112        // need to safe encode \n as \\n so its escaped
113        // need to safe encode \t as \\t so its escaped
114        return value
115            .replaceAll("\\\\r", "\\\\\\r")
116            .replaceAll("\\\\n", "\\\\\\n")
117            .replaceAll("\\\\t", "\\\\\\t");
118    }
119
120    public static boolean isComponentLenientProperties(List<Map<String, String>> rows) {
121        for (Map<String, String> row : rows) {
122            if (row.containsKey("lenientProperties")) {
123                return "true".equals(row.get("lenientProperties"));
124            }
125        }
126        return false;
127    }
128
129    public static boolean isComponentConsumerOnly(List<Map<String, String>> rows) {
130        for (Map<String, String> row : rows) {
131            if (row.containsKey("consumerOnly")) {
132                return "true".equals(row.get("consumerOnly"));
133            }
134        }
135        return false;
136    }
137
138    public static boolean isComponentProducerOnly(List<Map<String, String>> rows) {
139        for (Map<String, String> row : rows) {
140            if (row.containsKey("producerOnly")) {
141                return "true".equals(row.get("producerOnly"));
142            }
143        }
144        return false;
145    }
146
147    public static boolean isPropertyConsumerOnly(List<Map<String, String>> rows, String name) {
148        for (Map<String, String> row : rows) {
149            String labels = null;
150            boolean found = false;
151            if (row.containsKey("name")) {
152                found = name.equals(row.get("name"));
153            }
154            if (row.containsKey("label")) {
155                labels = row.get("label");
156            }
157            if (found) {
158                return labels != null && labels.contains("consumer");
159            }
160        }
161        return false;
162    }
163
164    public static boolean isPropertyProducerOnly(List<Map<String, String>> rows, String name) {
165        for (Map<String, String> row : rows) {
166            String labels = null;
167            boolean found = false;
168            if (row.containsKey("name")) {
169                found = name.equals(row.get("name"));
170            }
171            if (row.containsKey("label")) {
172                labels = row.get("label");
173            }
174            if (found) {
175                return labels != null && labels.contains("producer");
176            }
177        }
178        return false;
179    }
180
181    public static boolean isPropertyRequired(List<Map<String, String>> rows, String name) {
182        for (Map<String, String> row : rows) {
183            boolean required = false;
184            boolean found = false;
185            if (row.containsKey("name")) {
186                found = name.equals(row.get("name"));
187            }
188            if (row.containsKey("required")) {
189                required = "true".equals(row.get("required"));
190            }
191            if (found) {
192                return required;
193            }
194        }
195        return false;
196    }
197
198    public static String getPropertyKind(List<Map<String, String>> rows, String name) {
199        for (Map<String, String> row : rows) {
200            String kind = null;
201            boolean found = false;
202            if (row.containsKey("name")) {
203                found = name.equals(row.get("name"));
204            }
205            if (row.containsKey("kind")) {
206                kind = row.get("kind");
207            }
208            if (found) {
209                return kind;
210            }
211        }
212        return null;
213    }
214
215    public static boolean isPropertyBoolean(List<Map<String, String>> rows, String name) {
216        for (Map<String, String> row : rows) {
217            String type = null;
218            boolean found = false;
219            if (row.containsKey("name")) {
220                found = name.equals(row.get("name"));
221            }
222            if (row.containsKey("type")) {
223                type = row.get("type");
224            }
225            if (found) {
226                return "boolean".equals(type);
227            }
228        }
229        return false;
230    }
231
232    public static boolean isPropertyInteger(List<Map<String, String>> rows, String name) {
233        for (Map<String, String> row : rows) {
234            String type = null;
235            boolean found = false;
236            if (row.containsKey("name")) {
237                found = name.equals(row.get("name"));
238            }
239            if (row.containsKey("type")) {
240                type = row.get("type");
241            }
242            if (found) {
243                return "integer".equals(type);
244            }
245        }
246        return false;
247    }
248
249    public static boolean isPropertyNumber(List<Map<String, String>> rows, String name) {
250        for (Map<String, String> row : rows) {
251            String type = null;
252            boolean found = false;
253            if (row.containsKey("name")) {
254                found = name.equals(row.get("name"));
255            }
256            if (row.containsKey("type")) {
257                type = row.get("type");
258            }
259            if (found) {
260                return "number".equals(type);
261            }
262        }
263        return false;
264    }
265
266    public static boolean isPropertyObject(List<Map<String, String>> rows, String name) {
267        for (Map<String, String> row : rows) {
268            String type = null;
269            boolean found = false;
270            if (row.containsKey("name")) {
271                found = name.equals(row.get("name"));
272            }
273            if (row.containsKey("type")) {
274                type = row.get("type");
275            }
276            if (found) {
277                return "object".equals(type);
278            }
279        }
280        return false;
281    }
282
283    public static String getPropertyDefaultValue(List<Map<String, String>> rows, String name) {
284        for (Map<String, String> row : rows) {
285            String defaultValue = null;
286            boolean found = false;
287            if (row.containsKey("name")) {
288                found = name.equals(row.get("name"));
289            }
290            if (row.containsKey("defaultValue")) {
291                defaultValue = row.get("defaultValue");
292            }
293            if (found) {
294                return defaultValue;
295            }
296        }
297        return null;
298    }
299
300    public static String stripOptionalPrefixFromName(List<Map<String, String>> rows, String name) {
301        for (Map<String, String> row : rows) {
302            String optionalPrefix = null;
303            boolean found = false;
304            if (row.containsKey("optionalPrefix")) {
305                optionalPrefix = row.get("optionalPrefix");
306            }
307            if (row.containsKey("name")) {
308                if (optionalPrefix != null && name.startsWith(optionalPrefix)) {
309                    name = name.substring(optionalPrefix.length());
310                    // try again
311                    return stripOptionalPrefixFromName(rows, name);
312                } else {
313                    found = name.equals(row.get("name"));
314                }
315            }
316            if (found) {
317                return name;
318            }
319        }
320        return name;
321    }
322
323    public static String getPropertyEnum(List<Map<String, String>> rows, String name) {
324        for (Map<String, String> row : rows) {
325            String enums = null;
326            boolean found = false;
327            if (row.containsKey("name")) {
328                found = name.equals(row.get("name"));
329            }
330            if (row.containsKey("enum")) {
331                enums = row.get("enum");
332            }
333            if (found) {
334                return enums;
335            }
336        }
337        return null;
338    }
339
340    public static String getPropertyPrefix(List<Map<String, String>> rows, String name) {
341        for (Map<String, String> row : rows) {
342            String prefix = null;
343            boolean found = false;
344            if (row.containsKey("name")) {
345                found = name.equals(row.get("name"));
346            }
347            if (row.containsKey("prefix")) {
348                prefix = row.get("prefix");
349            }
350            if (found) {
351                return prefix;
352            }
353        }
354        return null;
355    }
356
357    public static boolean isPropertyMultiValue(List<Map<String, String>> rows, String name) {
358        for (Map<String, String> row : rows) {
359            boolean multiValue = false;
360            boolean found = false;
361            if (row.containsKey("name")) {
362                found = name.equals(row.get("name"));
363            }
364            if (row.containsKey("multiValue")) {
365                multiValue = "true".equals(row.get("multiValue"));
366            }
367            if (found) {
368                return multiValue;
369            }
370        }
371        return false;
372    }
373
374    public static String getPropertyNameFromNameWithPrefix(List<Map<String, String>> rows, String name) {
375        for (Map<String, String> row : rows) {
376            String propertyName = null;
377            boolean found = false;
378            if (row.containsKey("name")) {
379                propertyName = row.get("name");
380            }
381            if (row.containsKey("prefix")) {
382                String preifx = row.get("prefix");
383                found = name.startsWith(preifx);
384            }
385            if (found) {
386                return propertyName;
387            }
388        }
389        return null;
390    }
391
392    public static Map<String, String> getRow(List<Map<String, String>> rows, String key) {
393        for (Map<String, String> row : rows) {
394            if (key.equals(row.get("name"))) {
395                return row;
396            }
397        }
398        return null;
399    }
400
401    public static Set<String> getNames(List<Map<String, String>> rows) {
402        Set<String> answer = new LinkedHashSet<>();
403        for (Map<String, String> row : rows) {
404            if (row.containsKey("name")) {
405                answer.add(row.get("name"));
406            }
407        }
408        return answer;
409    }
410
411}