001 /* 002 * Jpkg - Java library and tools for operating system package creation. 003 * 004 * Copyright (c) 2007-2008 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.List; 035 036 import org.apache.tools.ant.Location; 037 038 import com.threerings.antidote.Violation; 039 040 import static com.threerings.antidote.MutabilityHelper.objectIsNotSet; 041 import static com.threerings.antidote.MutabilityHelper.objectIsSet; 042 043 /** 044 * A base class for common {@link FieldWrapper} functionality. 045 * Package private. Use one of the subclasses. 046 * @see RequiredField 047 * @see OptionalField 048 */ 049 abstract class BaseFieldWrapper<F extends ReferenceField> 050 implements FieldWrapper<F> 051 { 052 /** 053 * Construct a {@link FieldWrapper} by providing the already constructed {@link Field} to be 054 * wrapped and the parent {@link Field} which holds the wrapped {@link Field}. 055 * @param wrapped The {@link Field} to be wrapped. 056 * @param parent The wrapped {@link Field} parent. 057 * @throws IllegalArgumentException If either wrapped or parent is null. 058 */ 059 public BaseFieldWrapper (F wrapped, Field parent) 060 { 061 verifyWrappedNotNull(wrapped); 062 verifyParentNotNull(parent); 063 064 _wrapped = wrapped; 065 _name = wrapped.getFieldName(); 066 _parent = parent; 067 } 068 069 /** 070 * Construct a {@link FieldWrapper} which expects the wrapped {@link Field} to be set later 071 * using {@link #setField}. The {@link Class} of the wrapped {@link Field} is provided 072 * so that the name of the {@link Field} can be determined. The parent {@link Field} which 073 * holds the wrapped {@link Field} is also provided. 074 * @param parent The wrapped {@link Field} parent. 075 * @throws IllegalArgumentException If parent is null. 076 */ 077 public BaseFieldWrapper (Class<? extends Field> clazz, Field parent) 078 { 079 verifyParentNotNull(parent); 080 081 _name = FieldHelper.getFieldInstance(clazz).getFieldName(); 082 _parent = parent; 083 } 084 085 /** 086 * Construct a {@link FieldWrapper} which expects the wrapped {@link Field} to be set later 087 * using {@link #setField}. This constructor should be used for abstract {@link Field} 088 * classes where the name of the field cannot be known until a concrete class is constructed 089 * via the Ant setter or added method. The name of the abstract {@link Field} class is provided 090 * to be used for the field name until the wrapped field is set. The parent {@link Field} which 091 * holds the wrapped {@link Field} is also provided. 092 * @param abstractName The name of the abstract {@link Field} to be wrapped. 093 * @param parent The wrapped {@link Field} parent. 094 * @throws IllegalArgumentException If parent or abstractName is null. 095 */ 096 // TODO: revisit this. 097 public BaseFieldWrapper (String abstractName, Field parent) 098 { 099 if (objectIsNotSet(abstractName)) { 100 throw new IllegalArgumentException("Programmer error. The abstract Field name may not " + 101 "be null."); 102 } 103 verifyParentNotNull(parent); 104 105 _name = abstractName; 106 _parent = parent; 107 } 108 109 // from FieldWrapper 110 public void setField (F wrapped) 111 { 112 if (isSet()) { 113 _violations.add(new OnlyOneFieldViolation(this)); 114 return; 115 } 116 117 // verify the supplied field is not null 118 verifyWrappedNotNull(wrapped); 119 120 // update the name of the field in case it was initialized to the abstract class name. 121 _name = wrapped.getFieldName(); 122 123 _wrapped = wrapped; 124 } 125 126 // from FieldWrapper 127 public F getField () 128 { 129 if (isNotSet()) { 130 throw new UnsetWrappedFieldException(); 131 } 132 133 if (_wrapped.isReference()) { 134 @SuppressWarnings("unchecked") 135 final F field = (F)_wrapped.getReferencedField(); 136 return field; 137 138 } else { 139 return _wrapped; 140 } 141 } 142 143 // from FieldWrapper 144 public Field getParent () 145 { 146 return _parent; 147 } 148 149 // from Field 150 public String getFieldName () 151 { 152 return _name; 153 } 154 155 /** 156 * Provide the location of the wrapped {@link Field} if it is set, otherwise use the parent 157 * {@link Field} location, which will be a good estimate. 158 */ 159 // from Field 160 public Location getLocation () 161 { 162 if (isSet()) { 163 return _wrapped.getLocation(); 164 165 } else { 166 return _parent.getLocation(); 167 } 168 } 169 170 // from Mutable 171 public boolean isSet () 172 { 173 return objectIsSet(_wrapped); 174 } 175 176 // from Mutable 177 public boolean isNotSet () 178 { 179 return objectIsNotSet(_wrapped); 180 } 181 182 // from RequiresValidation 183 public final List<Violation> validate () 184 { 185 validateWrappedField(); 186 return _violations; 187 } 188 189 /** 190 * Add a violation to the list of violations returned when this {@link Field} is validated. 191 */ 192 protected void appendViolation (Violation violation) 193 { 194 _violations.add(violation); 195 } 196 197 /** 198 * Add a list of violations to the list of violations returned when this {@link Field} is validated. 199 */ 200 protected void appendViolationList (List<Violation> violations) 201 { 202 _violations.addAll(violations); 203 } 204 205 /** 206 * Provide subclasses a chance to perform additional validation, including validation of the 207 * wrapped field. 208 */ 209 protected abstract void validateWrappedField (); 210 211 /** 212 * Throws an {@link IllegalArgumentException} if the parent was not set. 213 */ 214 private void verifyParentNotNull (Field parent) 215 { 216 if (objectIsNotSet(parent)) { 217 throw new IllegalArgumentException("Programmer error. The wrapped Field parent may not " + 218 " be null. "); 219 } 220 } 221 222 /** 223 * Throws an {@link IllegalArgumentException} if the wrapped field was not set. 224 */ 225 private void verifyWrappedNotNull (Field wrapped) 226 { 227 if (objectIsNotSet(wrapped)) { 228 throw new IllegalArgumentException("Programmer error. The wrapped Field may not be null. " + 229 " Use setField() if the wrapped Field must be set after construction."); 230 } 231 } 232 233 /** The wrapped {@link Field}. May be null. */ 234 private F _wrapped; 235 236 /** The name of the wrapped {@link Field}. Will not be null. Non-final to support abstract fields. */ 237 private String _name; 238 239 /** The parent {@link Field} of this wrapped {@link Field}. */ 240 private final Field _parent; 241 242 /** The list of any validation violations for this {@link Field}. */ 243 private final List<Violation> _violations = new ArrayList<Violation>(); 244 }