001package ezvcard; 002 003import java.util.IdentityHashMap; 004import java.util.Iterator; 005import java.util.List; 006import java.util.Map; 007import java.util.Map.Entry; 008import java.util.stream.Collectors; 009import java.util.stream.Stream; 010 011import ezvcard.property.VCardProperty; 012import ezvcard.util.ListMultimap; 013import ezvcard.util.StringUtils; 014 015/* 016 Copyright (c) 2012-2026, Michael Angstadt 017 All rights reserved. 018 019 Redistribution and use in source and binary forms, with or without 020 modification, are permitted provided that the following conditions are met: 021 022 1. Redistributions of source code must retain the above copyright notice, this 023 list of conditions and the following disclaimer. 024 2. Redistributions in binary form must reproduce the above copyright notice, 025 this list of conditions and the following disclaimer in the documentation 026 and/or other materials provided with the distribution. 027 028 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 029 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 030 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 031 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 032 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 033 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 034 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 035 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 036 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 037 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 038 */ 039 040/** 041 * <p> 042 * Holds the validation warnings of a vCard. 043 * </p> 044 * <p> 045 * <b>Examples:</b> 046 * </p> 047 * 048 * <pre class="brush:java"> 049 * //validate a vCard object according to the rules of a specific version 050 * ValidationWarnings warnings = vcard.validate(VCardVersion.V3_0); 051 * 052 * //print all warnings to a string: 053 * System.out.println(warnings.toString()); 054 * //sample output: 055 * //W01: A FormattedName property is required for vCard versions 3.0 and 4.0. 056 * //[Gender] | W02: Property is not supported in this vCard version. Supported versions are: [4.0] 057 * 058 * //iterate over the warnings 059 * for (Map.Entry<VCardProperty, List<Warning>> entry : warnings) { 060 * //the property that caused the warning(s) 061 * VCardProperty property = entry.getKey(); 062 * 063 * //the list of warnings that belong to this property 064 * List<Warning> propWarnings = entry.getValue(); 065 * 066 * if (property == null) { 067 * //it's a warning about the vCard as a whole 068 * } 069 * 070 * //each warning message has a numeric code 071 * //this allows you to programmatically respond to specific warning messages 072 * List<Warning> propWarnings = entry.getValue(); 073 * for (Warning w : propWarnings) { 074 * System.out.println("Code: " + w.getCode()); 075 * System.out.printkn("Message: " + w.getMessage()); 076 * } 077 * } 078 * 079 * //you can also get the warnings of specific property classes 080 * List<Warnings> telWarnings = warnings.getByProperty(Telephone.class); 081 * </pre> 082 * 083 * @author Michael Angstadt 084 * @see VCard#validate 085 */ 086public class ValidationWarnings implements Iterable<Map.Entry<VCardProperty, List<ValidationWarning>>> { 087 private final ListMultimap<VCardProperty, ValidationWarning> warnings = new ListMultimap<>(new IdentityHashMap<>()); 088 089 /** 090 * Adds a validation warning. 091 * @param property the property that caused the warning 092 * @param warning the warning 093 */ 094 public void add(VCardProperty property, ValidationWarning warning) { 095 warnings.put(property, warning); 096 } 097 098 /** 099 * Adds a property's validation warnings. 100 * @param property the property that caused the warnings 101 * @param warnings the warnings 102 */ 103 public void add(VCardProperty property, List<ValidationWarning> warnings) { 104 this.warnings.putAll(property, warnings); 105 } 106 107 /** 108 * Gets all of the validation warnings. 109 * @return the validation warnings 110 */ 111 public ListMultimap<VCardProperty, ValidationWarning> getWarnings() { 112 return warnings; 113 } 114 115 /** 116 * Determines whether or not the warnings list is empty. 117 * @return true if there are no warnings, false if there is at least one 118 * warning 119 */ 120 public boolean isEmpty() { 121 return warnings.isEmpty(); 122 } 123 124 /** 125 * Gets all validation warnings that belong to a property of a specific 126 * class. 127 * @param propertyClass the property class (e.g. {@code Telephone.class}) or 128 * null to get the warnings that apply to the vCard as a whole 129 * @return the validation warnings 130 */ 131 public List<ValidationWarning> getByProperty(Class<? extends VCardProperty> propertyClass) { 132 //@formatter:off 133 return warnings.stream() 134 .filter(entry -> isPropertyInstanceOf(entry.getKey(), propertyClass)) 135 .flatMap(entry -> entry.getValue().stream()) 136 .collect(Collectors.toList()); 137 //@formatter:on 138 } 139 140 private boolean isPropertyInstanceOf(VCardProperty property, Class<? extends VCardProperty> clazz) { 141 return (property == null && clazz == null) || (property != null && clazz == property.getClass()); 142 } 143 144 /** 145 * <p> 146 * Outputs all validation warnings as a newline-delimited string. For 147 * example: 148 * </p> 149 * 150 * <pre> 151 * W01: A FormattedName property is required for vCard versions 3.0 and 4.0. 152 * [Gender] | W02: Property is not supported in this vCard version. Supported versions are: [4.0] 153 * </pre> 154 */ 155 @Override 156 public String toString() { 157 return warnings.stream().flatMap(entry -> { 158 VCardProperty property = entry.getKey(); 159 List<ValidationWarning> propViolations = entry.getValue(); 160 return propViolations.stream().map(propViolation -> toString(property, propViolation)); 161 }).collect(Collectors.joining(StringUtils.NEWLINE)); 162 } 163 164 private String toString(VCardProperty property, ValidationWarning warning) { 165 StringBuilder sb = new StringBuilder(); 166 String codeString = warning.getCodeString(); 167 168 if (property != null) { 169 sb.append('[').append(property.getClass().getSimpleName()).append(']'); 170 sb.append(codeString.isEmpty() ? ": " : " | "); 171 } 172 173 if (!codeString.isEmpty()) { 174 sb.append(codeString).append(": "); 175 } 176 177 sb.append(warning.getMessage()); 178 179 return sb.toString(); 180 } 181 182 public Iterator<Entry<VCardProperty, List<ValidationWarning>>> iterator() { 183 return warnings.iterator(); 184 } 185 186 /** 187 * Streams the underlying map. 188 * @return the stream 189 */ 190 public Stream<Map.Entry<VCardProperty, List<ValidationWarning>>> stream() { 191 return warnings.stream(); 192 } 193}