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;
025import java.util.regex.Matcher;
026import java.util.regex.Pattern;
027
028public final class JSonSchemaHelper {
029
030    // 0 = text, 1 = enum, 2 = boolean, 3 = integer or number
031    private static final Pattern PATTERN = Pattern.compile("\"(.+?)\"|\\[(.+)\\]|(true|false)|(-?\\d+\\.?\\d*)");
032    private static final String QUOT = """;
033
034    private JSonSchemaHelper() {
035    }
036
037    /**
038     * Parses the json schema to split it into a list or rows, where each row contains key value pairs with the metadata
039     *
040     * @param group the group to parse from such as <tt>component</tt>, <tt>componentProperties</tt>, or <tt>properties</tt>.
041     * @param json the json
042     * @return a list of all the rows, where each row is a set of key value pairs with metadata
043     */
044    public static List<Map<String, String>> parseJsonSchema(String group, String json, boolean parseProperties) {
045        List<Map<String, String>> answer = new ArrayList<Map<String, String>>();
046        if (json == null) {
047            return answer;
048        }
049
050        boolean found = false;
051
052        // parse line by line
053        String[] lines = json.split("\n");
054        for (String line : lines) {
055            // we need to find the group first
056            if (!found) {
057                String s = line.trim();
058                found = s.startsWith("\"" + group + "\":") && s.endsWith("{");
059                continue;
060            }
061
062            // we should stop when we end the group
063            if (line.equals("  },") || line.equals("  }")) {
064                break;
065            }
066
067            // need to safe encode \" so we can parse the line
068            line = line.replaceAll("\"\\\\\"\"", '"' + QUOT + '"');
069
070            Map<String, String> row = new LinkedHashMap<String, String>();
071            Matcher matcher = PATTERN.matcher(line);
072
073            String key;
074            if (parseProperties) {
075                // when parsing properties the first key is given as name, so the first parsed token is the value of the name
076                key = "name";
077            } else {
078                key = null;
079            }
080            while (matcher.find()) {
081                if (key == null) {
082                    key = matcher.group(1);
083                } else {
084                    String value = matcher.group(1);
085                    if (value != null) {
086                        // its text based
087                        value = value.trim();
088                        // decode
089                        value = value.replaceAll(QUOT, "\"");
090                        value = decodeJson(value);
091                    }
092                    if (value == null) {
093                        // not text then its maybe an enum?
094                        value = matcher.group(2);
095                        if (value != null) {
096                            // its an enum so strip out " and trim spaces after comma
097                            value = value.replaceAll("\"", "");
098                            value = value.replaceAll(", ", ",");
099                            value = value.trim();
100                        }
101                    }
102                    if (value == null) {
103                        // not text then its maybe a boolean?
104                        value = matcher.group(3);
105                    }
106                    if (value == null) {
107                        // not text then its maybe a integer?
108                        value = matcher.group(4);
109                    }
110                    if (value != null) {
111                        row.put(key, value);
112                    }
113                    // reset
114                    key = null;
115                }
116            }
117            if (!row.isEmpty()) {
118                answer.add(row);
119            }
120        }
121
122        return answer;
123    }
124
125    private static String decodeJson(String value) {
126        // json encodes a \ as \\ so we need to decode from \\ back to \
127        if ("\\\\".equals(value)) {
128            value = "\\";
129        }
130        return value;
131    }
132
133    public static boolean isComponentLenientProperties(List<Map<String, String>> rows) {
134        for (Map<String, String> row : rows) {
135            if (row.containsKey("lenientProperties")) {
136                return "true".equals(row.get("lenientProperties"));
137            }
138        }
139        return false;
140    }
141
142    public static boolean isComponentConsumerOnly(List<Map<String, String>> rows) {
143        for (Map<String, String> row : rows) {
144            if (row.containsKey("consumerOnly")) {
145                return "true".equals(row.get("consumerOnly"));
146            }
147        }
148        return false;
149    }
150
151    public static boolean isComponentProducerOnly(List<Map<String, String>> rows) {
152        for (Map<String, String> row : rows) {
153            if (row.containsKey("producerOnly")) {
154                return "true".equals(row.get("producerOnly"));
155            }
156        }
157        return false;
158    }
159
160    public static boolean isPropertyConsumerOnly(List<Map<String, String>> rows, String name) {
161        for (Map<String, String> row : rows) {
162            String labels = null;
163            boolean found = false;
164            if (row.containsKey("name")) {
165                found = name.equals(row.get("name"));
166            }
167            if (row.containsKey("label")) {
168                labels = row.get("label");
169            }
170            if (found) {
171                return labels != null && labels.contains("consumer");
172            }
173        }
174        return false;
175    }
176
177    public static boolean isPropertyProducerOnly(List<Map<String, String>> rows, String name) {
178        for (Map<String, String> row : rows) {
179            String labels = null;
180            boolean found = false;
181            if (row.containsKey("name")) {
182                found = name.equals(row.get("name"));
183            }
184            if (row.containsKey("label")) {
185                labels = row.get("label");
186            }
187            if (found) {
188                return labels != null && labels.contains("producer");
189            }
190        }
191        return false;
192    }
193
194    public static boolean isPropertyRequired(List<Map<String, String>> rows, String name) {
195        for (Map<String, String> row : rows) {
196            boolean required = false;
197            boolean found = false;
198            if (row.containsKey("name")) {
199                found = name.equals(row.get("name"));
200            }
201            if (row.containsKey("required")) {
202                required = "true".equals(row.get("required"));
203            }
204            if (found) {
205                return required;
206            }
207        }
208        return false;
209    }
210
211    public static String getPropertyKind(List<Map<String, String>> rows, String name) {
212        for (Map<String, String> row : rows) {
213            String kind = null;
214            boolean found = false;
215            if (row.containsKey("name")) {
216                found = name.equals(row.get("name"));
217            }
218            if (row.containsKey("kind")) {
219                kind = row.get("kind");
220            }
221            if (found) {
222                return kind;
223            }
224        }
225        return null;
226    }
227
228    public static boolean isPropertyBoolean(List<Map<String, String>> rows, String name) {
229        for (Map<String, String> row : rows) {
230            String type = null;
231            boolean found = false;
232            if (row.containsKey("name")) {
233                found = name.equals(row.get("name"));
234            }
235            if (row.containsKey("type")) {
236                type = row.get("type");
237            }
238            if (found) {
239                return "boolean".equals(type);
240            }
241        }
242        return false;
243    }
244
245    public static boolean isPropertyInteger(List<Map<String, String>> rows, String name) {
246        for (Map<String, String> row : rows) {
247            String type = null;
248            boolean found = false;
249            if (row.containsKey("name")) {
250                found = name.equals(row.get("name"));
251            }
252            if (row.containsKey("type")) {
253                type = row.get("type");
254            }
255            if (found) {
256                return "integer".equals(type);
257            }
258        }
259        return false;
260    }
261
262    public static boolean isPropertyNumber(List<Map<String, String>> rows, String name) {
263        for (Map<String, String> row : rows) {
264            String type = null;
265            boolean found = false;
266            if (row.containsKey("name")) {
267                found = name.equals(row.get("name"));
268            }
269            if (row.containsKey("type")) {
270                type = row.get("type");
271            }
272            if (found) {
273                return "number".equals(type);
274            }
275        }
276        return false;
277    }
278
279    public static boolean isPropertyObject(List<Map<String, String>> rows, String name) {
280        for (Map<String, String> row : rows) {
281            String type = null;
282            boolean found = false;
283            if (row.containsKey("name")) {
284                found = name.equals(row.get("name"));
285            }
286            if (row.containsKey("type")) {
287                type = row.get("type");
288            }
289            if (found) {
290                return "object".equals(type);
291            }
292        }
293        return false;
294    }
295
296    public static String getPropertyDefaultValue(List<Map<String, String>> rows, String name) {
297        for (Map<String, String> row : rows) {
298            String defaultValue = null;
299            boolean found = false;
300            if (row.containsKey("name")) {
301                found = name.equals(row.get("name"));
302            }
303            if (row.containsKey("defaultValue")) {
304                defaultValue = row.get("defaultValue");
305            }
306            if (found) {
307                return defaultValue;
308            }
309        }
310        return null;
311    }
312
313    public static String stripOptionalPrefixFromName(List<Map<String, String>> rows, String name) {
314        for (Map<String, String> row : rows) {
315            String optionalPrefix = null;
316            boolean found = false;
317            if (row.containsKey("optionalPrefix")) {
318                optionalPrefix = row.get("optionalPrefix");
319            }
320            if (row.containsKey("name")) {
321                if (optionalPrefix != null && name.startsWith(optionalPrefix)) {
322                    name = name.substring(optionalPrefix.length());
323                    // try again
324                    return stripOptionalPrefixFromName(rows, name);
325                } else {
326                    found = name.equals(row.get("name"));
327                }
328            }
329            if (found) {
330                return name;
331            }
332        }
333        return name;
334    }
335
336    public static String getPropertyEnum(List<Map<String, String>> rows, String name) {
337        for (Map<String, String> row : rows) {
338            String enums = null;
339            boolean found = false;
340            if (row.containsKey("name")) {
341                found = name.equals(row.get("name"));
342            }
343            if (row.containsKey("enum")) {
344                enums = row.get("enum");
345            }
346            if (found) {
347                return enums;
348            }
349        }
350        return null;
351    }
352
353    public static String getPropertyPrefix(List<Map<String, String>> rows, String name) {
354        for (Map<String, String> row : rows) {
355            String prefix = null;
356            boolean found = false;
357            if (row.containsKey("name")) {
358                found = name.equals(row.get("name"));
359            }
360            if (row.containsKey("prefix")) {
361                prefix = row.get("prefix");
362            }
363            if (found) {
364                return prefix;
365            }
366        }
367        return null;
368    }
369
370    public static boolean isPropertyMultiValue(List<Map<String, String>> rows, String name) {
371        for (Map<String, String> row : rows) {
372            boolean multiValue = false;
373            boolean found = false;
374            if (row.containsKey("name")) {
375                found = name.equals(row.get("name"));
376            }
377            if (row.containsKey("multiValue")) {
378                multiValue = "true".equals(row.get("multiValue"));
379            }
380            if (found) {
381                return multiValue;
382            }
383        }
384        return false;
385    }
386
387    public static String getPropertyNameFromNameWithPrefix(List<Map<String, String>> rows, String name) {
388        for (Map<String, String> row : rows) {
389            String propertyName = null;
390            boolean found = false;
391            if (row.containsKey("name")) {
392                propertyName = row.get("name");
393            }
394            if (row.containsKey("prefix")) {
395                String preifx = row.get("prefix");
396                found = name.startsWith(preifx);
397            }
398            if (found) {
399                return propertyName;
400            }
401        }
402        return null;
403    }
404
405    public static Map<String, String> getRow(List<Map<String, String>> rows, String key) {
406        for (Map<String, String> row : rows) {
407            if (key.equals(row.get("name"))) {
408                return row;
409            }
410        }
411        return null;
412    }
413
414    public static Set<String> getNames(List<Map<String, String>> rows) {
415        Set<String> answer = new LinkedHashSet<String>();
416        for (Map<String, String> row : rows) {
417            if (row.containsKey("name")) {
418                answer.add(row.get("name"));
419            }
420        }
421        return answer;
422    }
423
424}