001 package biweekly;
002
003 import java.util.ArrayList;
004 import java.util.Iterator;
005 import java.util.List;
006
007 import biweekly.ValidationWarnings.WarningsGroup;
008 import biweekly.component.ICalComponent;
009 import biweekly.property.ICalProperty;
010 import biweekly.util.StringUtils;
011 import biweekly.util.StringUtils.JoinCallback;
012
013 /*
014 Copyright (c) 2013, Michael Angstadt
015 All rights reserved.
016
017 Redistribution and use in source and binary forms, with or without
018 modification, are permitted provided that the following conditions are met:
019
020 1. Redistributions of source code must retain the above copyright notice, this
021 list of conditions and the following disclaimer.
022 2. Redistributions in binary form must reproduce the above copyright notice,
023 this list of conditions and the following disclaimer in the documentation
024 and/or other materials provided with the distribution.
025
026 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
027 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
028 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
029 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
030 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
031 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
032 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
033 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
034 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036 */
037
038 /**
039 * <p>
040 * Holds the validation warnings of an iCalendar object.
041 * </p>
042 * <p>
043 * <b>Examples:</b>
044 *
045 * <pre class="brush:java">
046 * //validate an iCalendar object
047 * ValidationWarnings warnings = ical.validate();
048 *
049 * //print all warnings to a string:
050 * System.out.println(warnings.toString());
051 * //sample output:
052 * //[ICalendar]: ProductId is not set (it is a required property).
053 * //[ICalendar > VEvent > DateStart]: DateStart must come before DateEnd.
054 * //[ICalendar > VEvent > VAlarm]: The trigger must specify which date field its duration is relative to.
055 *
056 * //iterate over each warnings group
057 * //this gives you access to the property/component object and its parent components
058 * for (WarningsGroup group : warnings) {
059 * ICalProperty prop = group.getProperty();
060 * if (prop == null) {
061 * //then it was a component that caused the warnings
062 * ICalComponent comp = group.getComponent();
063 * }
064 *
065 * //get parent components
066 * List<ICalComponent> hierarchy = group.getComponentHierarchy();
067 *
068 * //get warning messages
069 * List<String> messages = group.getMessages();
070 * }
071 *
072 * //you can also get the warnings of specific properties/components
073 * List<WarningsGroup> dtstartWarnings = warnings.getByProperty(DateStart.class);
074 * List<WarningsGroup> veventWarnings = warnings.getByComponent(VEvent.class);
075 * </pre>
076 *
077 * </p>
078 * @author Michael Angstadt
079 * @see ICalendar#validate()
080 */
081 public class ValidationWarnings implements Iterable<WarningsGroup> {
082 private final List<WarningsGroup> warnings;
083
084 /**
085 * Creates a new validation warnings list.
086 * @param warnings the validation warnings
087 */
088 public ValidationWarnings(List<WarningsGroup> warnings) {
089 this.warnings = warnings;
090 }
091
092 /**
093 * Gets all validation warnings of a given property.
094 * @param propertyClass the property (e.g. {@code DateStart.class})
095 * @return the validation warnings
096 */
097 public List<WarningsGroup> getByProperty(Class<? extends ICalProperty> propertyClass) {
098 List<WarningsGroup> warnings = new ArrayList<WarningsGroup>();
099 for (WarningsGroup group : this.warnings) {
100 ICalProperty property = group.getProperty();
101 if (property == null) {
102 continue;
103 }
104
105 if (propertyClass == property.getClass()) {
106 warnings.add(group);
107 }
108 }
109 return warnings;
110 }
111
112 /**
113 * Gets all validation warnings of a given component.
114 * @param componentClass the component (e.g. {@code VEvent.class})
115 * @return the validation warnings
116 */
117 public List<WarningsGroup> getByComponent(Class<? extends ICalComponent> componentClass) {
118 List<WarningsGroup> warnings = new ArrayList<WarningsGroup>();
119 for (WarningsGroup group : this.warnings) {
120 ICalComponent component = group.getComponent();
121 if (component == null) {
122 continue;
123 }
124
125 if (componentClass == component.getClass()) {
126 warnings.add(group);
127 }
128 }
129 return warnings;
130 }
131
132 /**
133 * Gets all the validation warnings.
134 * @return the validation warnings
135 */
136 public List<WarningsGroup> getWarnings() {
137 return warnings;
138 }
139
140 /**
141 * Determines whether there are any validation warnings.
142 * @return true if there are none, false if there are one or more
143 */
144 public boolean isEmpty() {
145 return warnings.isEmpty();
146 }
147
148 /**
149 * <p>
150 * Outputs all validation warnings as a newline-delimited string. For
151 * example:
152 * </p>
153 *
154 * <pre>
155 * [ICalendar]: ProductId is not set (it is a required property).
156 * [ICalendar > VEvent > DateStart]: DateStart must come before DateEnd.
157 * [ICalendar > VEvent > VAlarm]: The trigger must specify which date field its duration is relative to.
158 * </pre>
159 */
160 @Override
161 public String toString() {
162 return StringUtils.join(warnings, StringUtils.NEWLINE);
163 }
164
165 /**
166 * Iterates over each warning group (same as calling
167 * {@code getWarnings().iterator()}).
168 * @return the iterator
169 */
170 public Iterator<WarningsGroup> iterator() {
171 return warnings.iterator();
172 }
173
174 /**
175 * Holds the validation warnings of a property or component.
176 * @author Michael Angstadt
177 */
178 public static class WarningsGroup {
179 private final ICalProperty property;
180 private final ICalComponent component;
181 private final List<ICalComponent> componentHierarchy;
182 private final List<String> messages;
183
184 /**
185 * Creates a new set of validation warnings for a property.
186 * @param property the property that caused the warnings
187 * @param componentHierarchy the hierarchy of components that the
188 * property belongs to
189 * @param messages the warning messages
190 */
191 public WarningsGroup(ICalProperty property, List<ICalComponent> componentHierarchy, List<String> messages) {
192 this(null, property, componentHierarchy, messages);
193 }
194
195 /**
196 * Creates a new set of validation warnings for a component.
197 * @param component the component that caused the warnings
198 * @param componentHierarchy the hierarchy of components that the
199 * component belongs to
200 * @param messages the warning messages
201 */
202 public WarningsGroup(ICalComponent component, List<ICalComponent> componentHierarchy, List<String> messages) {
203 this(component, null, componentHierarchy, messages);
204 }
205
206 private WarningsGroup(ICalComponent component, ICalProperty property, List<ICalComponent> componentHierarchy, List<String> messages) {
207 this.component = component;
208 this.property = property;
209 this.componentHierarchy = componentHierarchy;
210 this.messages = messages;
211 }
212
213 /**
214 * Gets the property object that caused the validation warnings.
215 * @return the property object or null if a component caused the
216 * warnings.
217 */
218 public ICalProperty getProperty() {
219 return property;
220 }
221
222 /**
223 * Gets the component object that caused the validation warnings.
224 * @return the component object or null if a property caused the
225 * warnings.
226 */
227 public ICalComponent getComponent() {
228 return component;
229 }
230
231 /**
232 * Gets the hierarchy of components that the property or component
233 * belongs to.
234 * @return the component hierarchy
235 */
236 public List<ICalComponent> getComponentHierarchy() {
237 return componentHierarchy;
238 }
239
240 /**
241 * Gets the warning messages.
242 * @return the warning messages
243 */
244 public List<String> getMessages() {
245 return messages;
246 }
247
248 /**
249 * <p>
250 * Outputs each message in this warnings group as a newline-delimited
251 * string. Each line includes the component hierarchy and the name of
252 * the property/component. For example:
253 * </p>
254 *
255 * <pre>
256 * [ICalendar > VEvent > VAlarm]: Email alarms must have at least one attendee.
257 * [ICalendar > VEvent > VAlarm]: The trigger must specify which date field its duration is relative to.
258 * </pre>
259 */
260 @Override
261 public String toString() {
262 final String prefix = "[" + buildPath() + "]: ";
263 return StringUtils.join(messages, StringUtils.NEWLINE, new JoinCallback<String>() {
264 public void handle(StringBuilder sb, String message) {
265 sb.append(prefix).append(message);
266 }
267 });
268 }
269
270 private String buildPath() {
271 StringBuilder sb = new StringBuilder();
272
273 if (!componentHierarchy.isEmpty()) {
274 String delimitor = " > ";
275
276 StringUtils.join(componentHierarchy, delimitor, sb, new JoinCallback<ICalComponent>() {
277 public void handle(StringBuilder sb, ICalComponent component) {
278 sb.append(component.getClass().getSimpleName());
279 }
280 });
281 sb.append(delimitor);
282 }
283
284 if (property != null) {
285 sb.append(property.getClass().getSimpleName());
286 } else {
287 sb.append(component.getClass().getSimpleName());
288 }
289
290 return sb.toString();
291 }
292 }
293 }