001    package org.springframework.rules.constraint.property;
002    
003    import org.springframework.binding.PropertyAccessStrategy;
004    import org.springframework.rules.constraint.IfTrue;
005    import org.springframework.rules.reporting.TypeResolvable;
006    import org.springframework.util.Assert;
007    
008    /**
009     * <p>
010     * Provides a way to trigger rules for propertyB when propertyA satisfies a
011     * certain condition:
012     * </p>
013     * 
014     * <pre>
015     *      if (propertyA satisfies the conditional constraint)
016     *      {
017     *              check the rules for propertyB
018     *      }
019     * </pre>
020     * 
021     * with an optional part:
022     * 
023     * <pre>
024     *  else
025     *  {
026     *      check the rules for propertyC
027     *  }
028     * </pre>
029     * 
030     * <p>
031     * More complex situations are possible by using compound constraints which
032     * leverages the previous to:
033     * </p>
034     * 
035     * <pre>
036     * if (constraint(propertyA, propertyB,...) == true)
037     * {
038     *     checkConstraint(property1, property2,...);
039     * }
040     * \\ optional part
041     * else
042     * {
043     *     checkConstraint(propertyX, propertyY,...);
044     * }
045     * </pre>
046     * 
047     * <p>
048     * This class can be compared to the {@link IfTrue} class: it applies the same
049     * pattern but on different <b>properties</b> instead of on a <b>property value</b>.
050     * </p>
051     * 
052     * @author jh
053     * 
054     */
055    public class ConditionalPropertyConstraint extends AbstractPropertyConstraint implements TypeResolvable {
056    
057            /** The condition which triggers further rules to be checked. */
058            private final PropertyConstraint ifConstraint;
059    
060            /** The constraint to be checked when the condition is satisfied. */
061            private final PropertyConstraint thenConstraint;
062    
063            /** The constraint to be checked when the condition is <b>NOT</b> satisfied. */
064            private final PropertyConstraint elseConstraint;
065    
066            /** Type used to fetch message. */
067            private String type;
068    
069            /**
070             * @see #ConditionalPropertyConstraint(PropertyConstraint, PropertyConstraint, String)
071             */
072            public ConditionalPropertyConstraint(PropertyConstraint ifConstraint, PropertyConstraint thenConstraint) {
073                    this(ifConstraint, thenConstraint, null, null);
074            }       
075            
076            /**
077             * Create a constraint which simulates the if...then pattern applied
078             * on separate properties.
079             * 
080             * @param ifConstraint the PropertyConstraint that triggers the test
081             * (satisfying a certain condition).
082             * @param thenConstraint the PropertyConstraint to test in the specified
083             * condition.
084             */
085            public ConditionalPropertyConstraint(PropertyConstraint ifConstraint, PropertyConstraint thenConstraint, String type) {
086                    this(ifConstraint, thenConstraint, null, type);
087            }
088            
089            /**
090             * @see #ConditionalPropertyConstraint(PropertyConstraint, PropertyConstraint, PropertyConstraint, String)
091             */
092            public ConditionalPropertyConstraint(PropertyConstraint ifConstraint, PropertyConstraint thenConstraint,
093                            PropertyConstraint elseConstraint) {
094                    this(ifConstraint, thenConstraint, elseConstraint, null);
095            }
096            /**
097             * Create a constraint which simulates the if...then...else pattern applied
098             * on separate properties.
099             * 
100             * @param ifConstraint the PropertyConstraint that triggers the test
101             * (satisfying a certain condition).
102             * @param thenConstraint the PropertyConstraint to test in the specified
103             * condition.
104             * @param elseConstraint the PropertyConstraint to test if the condition is
105             * <b>NOT</b> satisfied. May be <code>null</code>.
106             * @param type the messageCode used to fetch the message.
107             */
108            public ConditionalPropertyConstraint(PropertyConstraint ifConstraint, PropertyConstraint thenConstraint,
109                            PropertyConstraint elseConstraint, String type) {
110                    super(ifConstraint.getPropertyName());
111                    Assert.notNull(ifConstraint);
112                    Assert.notNull(thenConstraint);
113                    this.ifConstraint = ifConstraint;
114                    this.thenConstraint = thenConstraint;
115                    this.elseConstraint = elseConstraint;
116                    this.type = type;
117            }
118    
119            public boolean isCompoundRule() {
120                    return true;
121            }
122    
123            public boolean isDependentOn(String propertyName) {
124                    if (elseConstraint == null)
125                            return ifConstraint.isDependentOn(propertyName) || thenConstraint.isDependentOn(propertyName);
126    
127                    return ifConstraint.isDependentOn(propertyName) || thenConstraint.isDependentOn(propertyName)
128                                    || elseConstraint.isDependentOn(propertyName);
129            }
130    
131            protected boolean test(PropertyAccessStrategy domainObjectAccessStrategy) {
132                    if (ifConstraint.test(domainObjectAccessStrategy))
133                            return thenConstraint.test(domainObjectAccessStrategy);
134                    if (elseConstraint != null)
135                            return elseConstraint.test(domainObjectAccessStrategy);
136    
137                    return true;
138            }
139    
140            public String getType() {
141                    return type;
142            }
143    
144            public void setType(String type) {
145                    this.type = type;
146            }
147    }