001 /* 002 * Jpkg - Java library and tools for operating system package creation. 003 * 004 * Copyright (c) 2007 Three Rings Design, Inc. 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 1. Redistributions of source code must retain the above copyright 011 * notice, this list of conditions and the following disclaimer. 012 * 2. Redistributions in binary form must reproduce the above copyright 013 * notice, this list of conditions and the following disclaimer in the 014 * documentation and/or other materials provided with the distribution. 015 * 3. Neither the name of the copyright owner nor the names of contributors 016 * may be used to endorse or promote products derived from this software 017 * without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 023 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 024 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 025 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 026 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 027 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 029 * POSSIBILITY OF SUCH DAMAGE. 030 */ 031 package com.threerings.antidote.field; 032 033 import java.util.ArrayList; 034 import java.util.Arrays; 035 import java.util.List; 036 037 import org.apache.tools.ant.Project; 038 import org.apache.tools.ant.ProjectComponent; 039 import org.apache.tools.ant.Task; 040 import org.apache.tools.ant.types.Reference; 041 042 import com.threerings.antidote.ValidStatus; 043 import com.threerings.antidote.Violation; 044 import com.threerings.antidote.property.Property; 045 046 import static com.threerings.antidote.MutabilityHelper.objectIsNotSet; 047 import static com.threerings.antidote.MutabilityHelper.objectIsSet; 048 049 /** 050 * A base class for all Ant fields providing useful functionality, such as {@link Violation} management. 051 * Ideally this class would extend from {@link ProjectComponent}. However, {@link Task} objects 052 * act as fields and end up benefiting greatly from this classes functionality. Use the {@link BaseTask} 053 * functionality if the implementing class requires {@link Task} functionality, otherwise, use 054 * {@link BaseField}. 055 * Package private. Use one of the subclasses. 056 * @see BaseField 057 * @see BaseTask 058 */ 059 abstract class BaseComponent extends Task 060 implements ReferenceField 061 { 062 /** 063 * Implement validate() in the base abstract class ensuring correct behavior. Provide accessors 064 * to the list of violations in protected methods. 065 * @see #appendViolation(Violation) 066 */ 067 // from RequiresValidation 068 public final List<Violation> validate () 069 { 070 validateField(); 071 return _violations; 072 } 073 074 // from ReferenceField 075 public Object getReferencedField () 076 { 077 if (objectIsNotSet(_reference)) { 078 throw new UnsetReferenceException(); 079 } 080 081 return _reference.getReferencedObject(); 082 } 083 084 // from ReferenceField 085 public boolean isReference () 086 { 087 return objectIsSet(_reference); 088 } 089 090 /** 091 * A protected setter meant for concrete classes to optionally support Ant reference setting 092 * by using a public setter method which calls this, e.g. setRefid. 093 */ 094 protected void setReference (Reference reference) 095 { 096 _reference = reference; 097 } 098 099 /** 100 * Give each subclass a chance to do field specific validation. 101 */ 102 protected abstract void validateField (); 103 104 /** 105 * Add a violation to the list of violations returned when this {@link Field} is validated. 106 */ 107 protected void appendViolation (Violation violation) 108 { 109 _violations.add(violation); 110 } 111 112 /** 113 * Add a list of violations to the list of violations returned when this {@link Field} is validated. 114 */ 115 protected void appendViolationList (List<Violation> violations) 116 { 117 _violations.addAll(violations); 118 } 119 120 /** 121 * Validate a varargs list of {@link FieldWrapper} objects that are children of this {@link Field}. 122 * @see #validateChildFields(List) 123 */ 124 protected ValidStatus validateChildFields (FieldWrapper<?>...childFields) 125 { 126 return validateChildFields(Arrays.asList(childFields)); 127 } 128 129 /** 130 * Validate a {@link List} of {@link FieldWrapper} objects that are children of this {@link Field}. 131 * Return a {@link ValidStatus} enum describing the valid state of the list. 132 * @throws IllegalArgumentException If the supplied {@link FieldWrapper} is null. 133 */ 134 protected ValidStatus validateChildFields (List<? extends FieldWrapper<?>> childFields) 135 { 136 int invalid = 0; 137 for (final FieldWrapper<?> childField : childFields) { 138 if (objectIsNotSet(childField)) { 139 throw new IllegalArgumentException("Programmer error. Fields being validated cannot" + 140 "be null."); 141 } 142 143 final int before = _violations.size(); 144 appendViolationList(childField.validate()); 145 146 if (_violations.size() > before) { 147 invalid++; 148 } 149 } 150 151 if (invalid == childFields.size()) { 152 return ValidStatus.ALL_INVALID; 153 154 } else if (invalid == 0) { 155 return ValidStatus.ALL_VALID; 156 157 } else { 158 return ValidStatus.SOME_INVALID; 159 } 160 } 161 162 /** 163 * Validate a varargs list of {@link Property} objects. 164 * @see #validateProperties(List) 165 */ 166 protected ValidStatus validateProperties (Property<?>...properties) 167 { 168 return validateProperties(Arrays.asList(properties)); 169 } 170 171 /** 172 * Validate a list of {@link Property} objects. 173 * Return a {@link ValidStatus} enum describing the valid state of the list. 174 */ 175 protected ValidStatus validateProperties (List<Property<?>> properties) 176 { 177 int invalid = 0; 178 for (final Property<?> property : properties) { 179 final int before = _violations.size(); 180 appendViolationList(property.validate()); 181 182 if (_violations.size() > before) { 183 invalid++; 184 } 185 } 186 187 if (invalid == properties.size()) { 188 return ValidStatus.ALL_INVALID; 189 190 } else if (invalid == 0) { 191 return ValidStatus.ALL_VALID; 192 193 } else { 194 return ValidStatus.SOME_INVALID; 195 } 196 } 197 198 /** 199 * Validate a list of {@link Property} objects that are optional. If they are unset, nothing 200 * will happen. Returns the {@link ValidStatus} of the set properties. If no properties are set, 201 * ALL_VALID is returned. 202 */ 203 // TODO: can this be factored into the properties themselves, like FieldWrapper? 204 protected ValidStatus validateOptionalProperties (Property<?>...properties) 205 { 206 final List<Property<?>> setProperties = new ArrayList<Property<?>>(); 207 for (final Property<?> property : properties) { 208 if (property.isNotSet()) continue; 209 setProperties.add(property); 210 } 211 212 if (setProperties.size() == 0) { 213 return ValidStatus.ALL_VALID; 214 215 } else { 216 return validateProperties(setProperties); 217 } 218 } 219 220 /** 221 * If the supplied property is unset, then report a violation that the property cannot be 222 * unset if any of the supplied list of {@link Property} objects are set. 223 */ 224 protected void reportUnsetDependentProperties (Property<?> property, Property<?>...dependents) 225 { 226 if (property.isNotSet()) { 227 appendViolation(new UnsetDependentPropertyViolation(property, propertyNames(dependents))); 228 } 229 } 230 231 /** 232 * If the supplied property is set, e.g. not null, then report a violation that the property cannot 233 * be set if any of the supplied list of {@link Property} objects are set. 234 */ 235 protected void reportConflictingProperties (Property<?> property, Property<?>...conflicts) 236 { 237 if (property.isSet()) { 238 appendViolation(new ConflictingPropertiesViolation(property, propertyNames(conflicts))); 239 } 240 } 241 242 /** 243 * If the supplied {@link FieldWrapper} is unset then report a violation that the field cannot be 244 * unset if any of the supplied list of {@link FieldWrapper} objects are set. 245 */ 246 protected void reportUnsetDependentFields (FieldWrapper<?> field, FieldWrapper<?>...depends) 247 { 248 if (field.isNotSet()) { 249 appendViolation(new UnsetDependentFieldViolation(field, getFieldNames(depends))); 250 } 251 } 252 253 /** 254 * Report as a violation if the supplied {@link FieldWrapper} is unset. 255 */ 256 protected void reportUnsetField (FieldWrapper<?> field) 257 { 258 if (field.isNotSet()) { 259 appendViolation(new UnsetFieldViolation(field)); 260 } 261 } 262 263 /** 264 * Register a given {@link Field} class as a data type for this {@link Project}. 265 */ 266 protected void registerField (Class<? extends Field> clazz) 267 { 268 final Project project = getProject(); 269 if (objectIsNotSet(project)) { 270 throw new RuntimeException("Cannot register a field before setProject() has been called."); 271 } 272 project.addDataTypeDefinition(FieldHelper.getFieldInstance(clazz).getFieldName(), clazz); 273 } 274 275 /** 276 * Given a list of {@link Field} Class objects, return a string representation of the field names. 277 */ 278 private String getFieldNames (FieldWrapper<?>...fields) 279 { 280 final StringBuilder builder = new StringBuilder(); 281 for (final FieldWrapper<?> field : fields) { 282 if (builder.length() > 0) { 283 builder.append(", "); 284 } 285 builder.append('<').append(field.getFieldName()).append('>'); 286 } 287 return builder.toString(); 288 } 289 290 /** 291 * Given a list of {@link Property} objects, return a comma separated list of the property names. 292 */ 293 private String propertyNames (Property<?>...properties) 294 { 295 final StringBuilder builder = new StringBuilder(); 296 for (final Property<?> prop : properties) { 297 if (builder.length() > 0) { 298 builder.append(", "); 299 } 300 builder.append(prop.getPropertyName()); 301 } 302 return builder.toString(); 303 } 304 305 /** The optional reference meant to replace this field. */ 306 private Reference _reference; 307 308 /** The list of any validation violations for this {@link Field}. */ 309 private final List<Violation> _violations = new ArrayList<Violation>(); 310 }