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    }