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