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