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&lt;VCardProperty, List&lt;Warning&gt;&gt; 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&lt;Warning&gt; 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&lt;Warning&gt; 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&lt;Warnings&gt; 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}