001: /*
002: * Copyright (c) 2003-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.validation.util;
032:
033: import java.beans.PropertyChangeEvent;
034: import java.beans.PropertyChangeListener;
035: import java.util.LinkedList;
036: import java.util.List;
037:
038: import com.jgoodies.validation.ValidationMessage;
039: import com.jgoodies.validation.ValidationResult;
040: import com.jgoodies.validation.ValidationResultModel;
041:
042: /**
043: * A validation result model that holds multiple validation results.
044: * These can be concatenated or collapsed, see {@link #setExpanded(boolean)},
045: * {@link #getCollapsedValidationResult()}.<p>
046: *
047: * TODO: The collapsed validation result shall honor the severity as
048: * reported by the expanded validation result.<p>
049: *
050: * TODO: The expanded validation result shall be cached.
051: *
052: * @author Karsten Lentzsch
053: * @version $Revision: 1.10 $
054: *
055: * @see com.jgoodies.validation.util.DefaultValidationResultModel
056: */
057: public class ValidationResultModelContainer extends
058: DefaultValidationResultModel {
059:
060: // Names of Bound Bean Properties *****************************************
061:
062: /**
063: * The name of the bound read-write property for the expansion state.
064: *
065: * @see #isExpanded()
066: * @see #setExpanded(boolean)
067: */
068: public static final String PROPERTYNAME_EXPANDED = "expanded";
069:
070: // Instance Fields ********************************************************
071:
072: /**
073: * Refers to the {@link ValidationMessage} that is used in
074: * the collapsed state.
075: *
076: * @see #getCollapsedValidationResult()
077: */
078: private final ValidationMessage collapsedMessage;
079:
080: /**
081: * Indicates whether the contained validation results shall be
082: * reported as expanded or collapsed. In expanded state the individual
083: * validation results are concatenated; in collapsed state a
084: * {@link ValidationResult} will be created that consists only
085: * of the <em>collapsedMessage</em>.
086: *
087: * @see #isExpanded()
088: * @see #setExpanded(boolean)
089: */
090: private boolean expanded;
091:
092: /**
093: * A list that holds the child {@link ValidationResultModel}s.
094: */
095: private final List<ValidationResultModel> childResultModels;
096:
097: /**
098: * Listens to changes in the validation result of a child validation result
099: * model and updates the container result.
100: */
101: private final PropertyChangeListener childResultUpdateHandler;
102:
103: // Instance Creation ******************************************************
104:
105: /**
106: * Constructs a <code>ValidationResultModelContainer</code> using the
107: * given validation message for the collapsed state. The initial state
108: * is <em>expanded</em>.
109: *
110: * @param collapsedMessage the <code>ValidationMessage</code> used
111: * if the container is in collapsed state
112: * @throws NullPointerException if the collapsed message is <code>null</code>
113: */
114: public ValidationResultModelContainer(
115: ValidationMessage collapsedMessage) {
116: this .collapsedMessage = collapsedMessage;
117: childResultModels = new LinkedList<ValidationResultModel>();
118: expanded = true;
119: childResultUpdateHandler = new ChildResultUpdateHandler();
120: }
121:
122: // Accessors **************************************************************
123:
124: /**
125: * Returns if this container is in expanded or collapsed state.
126: *
127: * @return true if expanded
128: *
129: * @see #setExpanded(boolean)
130: */
131: public boolean isExpanded() {
132: return expanded;
133: }
134:
135: /**
136: * Sets the container state to expanded or collapsed.
137: *
138: * @param newExpanded true to expand, false to collapse
139: *
140: * @see #isExpanded()
141: */
142: public void setExpanded(boolean newExpanded) {
143: boolean oldExpanded = isExpanded();
144: expanded = newExpanded;
145: updateContainerResult();
146: firePropertyChange(PROPERTYNAME_EXPANDED, oldExpanded,
147: newExpanded);
148: }
149:
150: /**
151: * Returns the collapsed validation result, i. e. a {@link ValidationResult}
152: * that just consists of a single {@link ValidationMessage}; in this case
153: * the collapsed validation message.<p>
154: *
155: * TODO: Consider changing the implementation to <pre>
156: * ValidationResult wrapper = new ValidationResult();
157: * if (getExpandedValidationResult().hasMessages()) {
158: * wrapper.add(collapsedMessage);
159: * }
160: * return wrapper;
161: * </pre>
162: *
163: * @return the collapsed validation result
164: *
165: * @see #getExpandedValidationResult()
166: */
167: public ValidationResult getCollapsedValidationResult() {
168: ValidationResult wrapper = new ValidationResult();
169: wrapper.add(collapsedMessage);
170: return wrapper;
171: }
172:
173: /**
174: * Returns the concatenation of all validation results that are held
175: * by this container.
176: *
177: * @return the concatenation of all validation results that are held
178: * by this container
179: *
180: * @see #getCollapsedValidationResult()
181: */
182: public ValidationResult getExpandedValidationResult() {
183: ValidationResult concatenation = new ValidationResult();
184: for (ValidationResultModel resultModel : childResultModels) {
185: concatenation.addAllFrom(resultModel.getResult());
186: }
187: return concatenation;
188: }
189:
190: // Managing Child ValidationResultModels **********************************
191:
192: /**
193: * Adds the given {@link ValidationResultModel} to this container's list
194: * of children and registers the container to listen for changes in the child.
195: *
196: * @param resultModel the <code>ValidationResultModel</code> to be added
197: *
198: * @see #remove(ValidationResultModel)
199: */
200: public void add(ValidationResultModel resultModel) {
201: childResultModels.add(resultModel);
202: resultModel.addPropertyChangeListener(
203: ValidationResultModel.PROPERTYNAME_RESULT,
204: childResultUpdateHandler);
205: }
206:
207: /**
208: * Removes the given {@link ValidationResultModel} from this container's
209: * list of children. Also unregisters this container to no longer listen
210: * for changes in the child.
211: *
212: * @param resultModel the <code>ValidationResultModel</code> to be removed
213: *
214: * @see #add(ValidationResultModel)
215: */
216: public void remove(ValidationResultModel resultModel) {
217: childResultModels.remove(resultModel);
218: resultModel.removePropertyChangeListener(
219: ValidationResultModel.PROPERTYNAME_RESULT,
220: childResultUpdateHandler);
221: }
222:
223: // Accessors **************************************************************
224:
225: /**
226: * Rejects to set a new validation result. The validation result of this
227: * container will be computed. Therefore API users are prevented from
228: * executing this method; throws an {@link UnsupportedOperationException}.
229: *
230: * @param newResult the validation result to be set (ignored)
231: * @throws UnsupportedOperationException always
232: *
233: * @see #getResult()
234: */
235: @Override
236: public final void setResult(ValidationResult newResult) {
237: throw new UnsupportedOperationException(
238: "Cannot set a validation result for ValidationResultModelContainer. The result will be computed from the contained results instead.");
239: }
240:
241: // Child Result Updates ***************************************************
242:
243: /**
244: * Updates the container's compound result according to the current
245: * expansion state.
246: *
247: * @see #getExpandedValidationResult()
248: * @see #getCollapsedValidationResult()
249: */
250: private void updateContainerResult() {
251: ValidationResult newResult = isExpanded() ? getExpandedValidationResult()
252: : getCollapsedValidationResult();
253: super .setResult(newResult);
254: }
255:
256: /**
257: * Listens to changes in the property update.
258: */
259: private final class ChildResultUpdateHandler implements
260: PropertyChangeListener {
261:
262: /**
263: * The <em>validation result</em> property of a child has changed.
264: * updates the container result which in turn will notify registered
265: * users that a new version is coming.
266: *
267: * @param evt describes the property change
268: */
269: public void propertyChange(PropertyChangeEvent evt) {
270: updateContainerResult();
271: }
272: }
273:
274: }
|