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