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.io.Serializable; 020import java.util.Arrays; 021import java.util.Collections; 022import java.util.LinkedHashMap; 023import java.util.LinkedHashSet; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027 028/** 029 * Details result of validating endpoint uri. 030 */ 031public class EndpointValidationResult implements Serializable { 032 033 private final String uri; 034 private int errors; 035 036 // general 037 private String syntaxError; 038 private String unknownComponent; 039 private String incapable; 040 041 // options 042 private Set<String> unknown; 043 private Map<String, String[]> unknownSuggestions; 044 private Set<String> lenient; 045 private Set<String> notConsumerOnly; 046 private Set<String> notProducerOnly; 047 private Set<String> required; 048 private Set<String> deprecated; 049 private Map<String, String> invalidEnum; 050 private Map<String, String[]> invalidEnumChoices; 051 private Map<String, String[]> invalidEnumSuggestions; 052 private Map<String, String> invalidReference; 053 private Map<String, String> invalidBoolean; 054 private Map<String, String> invalidInteger; 055 private Map<String, String> invalidNumber; 056 private Map<String, String> defaultValues; 057 058 public EndpointValidationResult() { 059 this(null); 060 } 061 062 public EndpointValidationResult(String uri) { 063 this.uri = uri; 064 } 065 066 public String getUri() { 067 return uri; 068 } 069 070 public boolean hasErrors() { 071 return errors > 0; 072 } 073 074 public int getNumberOfErrors() { 075 return errors; 076 } 077 078 public boolean isSuccess() { 079 boolean ok = syntaxError == null && unknownComponent == null && incapable == null 080 && unknown == null && required == null; 081 if (ok) { 082 ok = notConsumerOnly == null && notProducerOnly == null; 083 } 084 if (ok) { 085 ok = invalidEnum == null && invalidEnumChoices == null && invalidReference == null 086 && invalidBoolean == null && invalidInteger == null && invalidNumber == null; 087 } 088 return ok; 089 } 090 091 public void addSyntaxError(String syntaxError) { 092 this.syntaxError = syntaxError; 093 errors++; 094 } 095 096 public void addIncapable(String uri) { 097 this.incapable = uri; 098 errors++; 099 } 100 101 public void addUnknownComponent(String name) { 102 this.unknownComponent = name; 103 errors++; 104 } 105 106 public void addUnknown(String name) { 107 if (unknown == null) { 108 unknown = new LinkedHashSet<>(); 109 } 110 if (!unknown.contains(name)) { 111 unknown.add(name); 112 errors++; 113 } 114 } 115 116 public void addUnknownSuggestions(String name, String[] suggestions) { 117 if (unknownSuggestions == null) { 118 unknownSuggestions = new LinkedHashMap<>(); 119 } 120 unknownSuggestions.put(name, suggestions); 121 } 122 123 public void addLenient(String name) { 124 if (lenient == null) { 125 lenient = new LinkedHashSet<>(); 126 } 127 if (!lenient.contains(name)) { 128 lenient.add(name); 129 } 130 } 131 132 public void addRequired(String name) { 133 if (required == null) { 134 required = new LinkedHashSet<>(); 135 } 136 if (!required.contains(name)) { 137 required.add(name); 138 errors++; 139 } 140 } 141 142 public void addDeprecated(String name) { 143 if (deprecated == null) { 144 deprecated = new LinkedHashSet<>(); 145 } 146 if (!deprecated.contains(name)) { 147 deprecated.add(name); 148 } 149 } 150 151 public void addInvalidEnum(String name, String value) { 152 if (invalidEnum == null) { 153 invalidEnum = new LinkedHashMap<>(); 154 } 155 if (!invalidEnum.containsKey(name)) { 156 invalidEnum.put(name, value); 157 errors++; 158 } 159 } 160 161 public void addInvalidEnumChoices(String name, String[] choices) { 162 if (invalidEnumChoices == null) { 163 invalidEnumChoices = new LinkedHashMap<>(); 164 } 165 invalidEnumChoices.put(name, choices); 166 } 167 168 public void addInvalidEnumSuggestions(String name, String[] suggestions) { 169 if (invalidEnumSuggestions == null) { 170 invalidEnumSuggestions = new LinkedHashMap<>(); 171 } 172 invalidEnumSuggestions.put(name, suggestions); 173 } 174 175 public void addInvalidReference(String name, String value) { 176 if (invalidReference == null) { 177 invalidReference = new LinkedHashMap<>(); 178 } 179 if (!invalidReference.containsKey(name)) { 180 invalidReference.put(name, value); 181 errors++; 182 } 183 } 184 185 public void addInvalidBoolean(String name, String value) { 186 if (invalidBoolean == null) { 187 invalidBoolean = new LinkedHashMap<>(); 188 } 189 if (!invalidBoolean.containsKey(name)) { 190 invalidBoolean.put(name, value); 191 errors++; 192 } 193 } 194 195 public void addInvalidInteger(String name, String value) { 196 if (invalidInteger == null) { 197 invalidInteger = new LinkedHashMap<>(); 198 } 199 if (!invalidInteger.containsKey(name)) { 200 invalidInteger.put(name, value); 201 errors++; 202 } 203 } 204 205 public void addInvalidNumber(String name, String value) { 206 if (invalidNumber == null) { 207 invalidNumber = new LinkedHashMap<>(); 208 } 209 if (!invalidNumber.containsKey(name)) { 210 invalidNumber.put(name, value); 211 errors++; 212 } 213 } 214 215 public void addDefaultValue(String name, String value) { 216 if (defaultValues == null) { 217 defaultValues = new LinkedHashMap<>(); 218 } 219 defaultValues.put(name, value); 220 } 221 222 public void addNotConsumerOnly(String name) { 223 if (notConsumerOnly == null) { 224 notConsumerOnly = new LinkedHashSet<>(); 225 } 226 if (!notConsumerOnly.contains(name)) { 227 notConsumerOnly.add(name); 228 errors++; 229 } 230 } 231 232 public void addNotProducerOnly(String name) { 233 if (notProducerOnly == null) { 234 notProducerOnly = new LinkedHashSet<>(); 235 } 236 if (!notProducerOnly.contains(name)) { 237 notProducerOnly.add(name); 238 errors++; 239 } 240 } 241 242 public String getSyntaxError() { 243 return syntaxError; 244 } 245 246 public String getIncapable() { 247 return incapable; 248 } 249 250 public Set<String> getUnknown() { 251 return unknown; 252 } 253 254 public Set<String> getLenient() { 255 return lenient; 256 } 257 258 public Map<String, String[]> getUnknownSuggestions() { 259 return unknownSuggestions; 260 } 261 262 public String getUnknownComponent() { 263 return unknownComponent; 264 } 265 266 public Set<String> getRequired() { 267 return required; 268 } 269 270 public Set<String> getDeprecated() { 271 return deprecated; 272 } 273 274 public Map<String, String> getInvalidEnum() { 275 return invalidEnum; 276 } 277 278 public Map<String, String[]> getInvalidEnumChoices() { 279 return invalidEnumChoices; 280 } 281 282 public List<String> getEnumChoices(String optionName) { 283 if (invalidEnumChoices != null) { 284 String[] enums = invalidEnumChoices.get(optionName); 285 if (enums != null) { 286 return Arrays.asList(enums); 287 } 288 } 289 290 return Collections.emptyList(); 291 } 292 293 public Map<String, String> getInvalidReference() { 294 return invalidReference; 295 } 296 297 public Map<String, String> getInvalidBoolean() { 298 return invalidBoolean; 299 } 300 301 public Map<String, String> getInvalidInteger() { 302 return invalidInteger; 303 } 304 305 public Map<String, String> getInvalidNumber() { 306 return invalidNumber; 307 } 308 309 public Map<String, String> getDefaultValues() { 310 return defaultValues; 311 } 312 313 public Set<String> getNotConsumerOnly() { 314 return notConsumerOnly; 315 } 316 317 public Set<String> getNotProducerOnly() { 318 return notProducerOnly; 319 } 320 321 /** 322 * A human readable summary of the validation errors. 323 * 324 * @param includeHeader whether to include a header 325 * @return the summary, or <tt>null</tt> if no validation errors 326 */ 327 public String summaryErrorMessage(boolean includeHeader) { 328 return summaryErrorMessage(includeHeader, true); 329 } 330 331 /** 332 * A human readable summary of the validation errors. 333 * 334 * @param includeHeader whether to include a header 335 * @param ignoreDeprecated whether to ignore deprecated options in use as an error or not 336 * @return the summary, or <tt>null</tt> if no validation errors 337 */ 338 public String summaryErrorMessage(boolean includeHeader, boolean ignoreDeprecated) { 339 boolean ok = isSuccess(); 340 341 // special check if we should ignore deprecated options being used 342 if (ok && !ignoreDeprecated) { 343 ok = deprecated == null; 344 } 345 346 if (ok) { 347 return null; 348 } 349 350 if (incapable != null) { 351 return "\tIncapable of parsing uri: " + incapable; 352 } else if (syntaxError != null) { 353 return "\tSyntax error: " + syntaxError; 354 } else if (unknownComponent != null) { 355 return "\tUnknown component: " + unknownComponent; 356 } 357 358 // for each invalid option build a reason message 359 Map<String, String> options = new LinkedHashMap<>(); 360 if (unknown != null) { 361 for (String name : unknown) { 362 if (unknownSuggestions != null && unknownSuggestions.containsKey(name)) { 363 String[] suggestions = unknownSuggestions.get(name); 364 if (suggestions != null && suggestions.length > 0) { 365 String str = Arrays.asList(suggestions).toString(); 366 options.put(name, "Unknown option. Did you mean: " + str); 367 } else { 368 options.put(name, "Unknown option"); 369 } 370 } else { 371 options.put(name, "Unknown option"); 372 } 373 } 374 } 375 if (notConsumerOnly != null) { 376 for (String name : notConsumerOnly) { 377 options.put(name, "Option not applicable in consumer only mode"); 378 } 379 } 380 if (notProducerOnly != null) { 381 for (String name : notProducerOnly) { 382 options.put(name, "Option not applicable in producer only mode"); 383 } 384 } 385 if (required != null) { 386 for (String name : required) { 387 options.put(name, "Missing required option"); 388 } 389 } 390 if (deprecated != null) { 391 for (String name : deprecated) { 392 options.put(name, "Deprecated option"); 393 } 394 } 395 if (invalidEnum != null) { 396 for (Map.Entry<String, String> entry : invalidEnum.entrySet()) { 397 String name = entry.getKey(); 398 String[] choices = invalidEnumChoices.get(name); 399 String defaultValue = defaultValues != null ? defaultValues.get(entry.getKey()) : null; 400 String str = Arrays.asList(choices).toString(); 401 String msg = "Invalid enum value: " + entry.getValue() + ". Possible values: " + str; 402 if (invalidEnumSuggestions != null) { 403 String[] suggestions = invalidEnumSuggestions.get(name); 404 if (suggestions != null && suggestions.length > 0) { 405 str = Arrays.asList(suggestions).toString(); 406 msg += ". Did you mean: " + str; 407 } 408 } 409 if (defaultValue != null) { 410 msg += ". Default value: " + defaultValue; 411 } 412 413 options.put(entry.getKey(), msg); 414 } 415 } 416 if (invalidReference != null) { 417 for (Map.Entry<String, String> entry : invalidReference.entrySet()) { 418 boolean empty = isEmpty(entry.getValue()); 419 if (empty) { 420 options.put(entry.getKey(), "Empty reference value"); 421 } else if (!entry.getValue().startsWith("#")) { 422 options.put(entry.getKey(), "Invalid reference value: " + entry.getValue() + " must start with #"); 423 } else { 424 options.put(entry.getKey(), "Invalid reference value: " + entry.getValue()); 425 } 426 } 427 } 428 if (invalidBoolean != null) { 429 for (Map.Entry<String, String> entry : invalidBoolean.entrySet()) { 430 boolean empty = isEmpty(entry.getValue()); 431 if (empty) { 432 options.put(entry.getKey(), "Empty boolean value"); 433 } else { 434 options.put(entry.getKey(), "Invalid boolean value: " + entry.getValue()); 435 } 436 } 437 } 438 if (invalidInteger != null) { 439 for (Map.Entry<String, String> entry : invalidInteger.entrySet()) { 440 boolean empty = isEmpty(entry.getValue()); 441 if (empty) { 442 options.put(entry.getKey(), "Empty integer value"); 443 } else { 444 options.put(entry.getKey(), "Invalid integer value: " + entry.getValue()); 445 } 446 } 447 } 448 if (invalidNumber != null) { 449 for (Map.Entry<String, String> entry : invalidNumber.entrySet()) { 450 boolean empty = isEmpty(entry.getValue()); 451 if (empty) { 452 options.put(entry.getKey(), "Empty number value"); 453 } else { 454 options.put(entry.getKey(), "Invalid number value: " + entry.getValue()); 455 } 456 } 457 } 458 459 // build a table with the error summary nicely formatted 460 // lets use 24 as min length 461 int maxLen = 24; 462 for (String key : options.keySet()) { 463 maxLen = Math.max(maxLen, key.length()); 464 } 465 String format = "%" + maxLen + "s %s"; 466 467 // build the human error summary 468 StringBuilder sb = new StringBuilder(); 469 if (includeHeader) { 470 sb.append("Endpoint validator error\n"); 471 sb.append("---------------------------------------------------------------------------------------------------------------------------------------\n"); 472 sb.append("\n"); 473 } 474 if (uri != null) { 475 sb.append("\t").append(uri).append("\n"); 476 } else { 477 sb.append("\n"); 478 } 479 for (Map.Entry<String, String> option : options.entrySet()) { 480 String out = String.format(format, option.getKey(), option.getValue()); 481 sb.append("\n\t").append(out); 482 } 483 484 return sb.toString(); 485 } 486 487 private static boolean isEmpty(String value) { 488 return value == null || value.isEmpty() || value.trim().isEmpty(); 489 } 490}