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 }