001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krms.api.repository.rule;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.core.api.CoreConstants;
020    import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
021    import org.kuali.rice.core.api.mo.ModelBuilder;
022    import org.kuali.rice.core.api.util.jaxb.MapStringStringAdapter;
023    import org.kuali.rice.krms.api.repository.action.ActionDefinition;
024    import org.kuali.rice.krms.api.repository.action.ActionDefinitionContract;
025    import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition;
026    
027    import javax.xml.bind.annotation.XmlAccessType;
028    import javax.xml.bind.annotation.XmlAccessorType;
029    import javax.xml.bind.annotation.XmlAnyElement;
030    import javax.xml.bind.annotation.XmlElement;
031    import javax.xml.bind.annotation.XmlElementWrapper;
032    import javax.xml.bind.annotation.XmlRootElement;
033    import javax.xml.bind.annotation.XmlTransient;
034    import javax.xml.bind.annotation.XmlType;
035    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
036    import java.io.Serializable;
037    import java.util.ArrayList;
038    import java.util.Collection;
039    import java.util.Collections;
040    import java.util.HashMap;
041    import java.util.List;
042    import java.util.Map;
043    
044    /**
045     * Concrete model object implementation of KRMS Repository Rule 
046     * immutable. 
047     * Instances of Rule can be (un)marshalled to and from XML.
048     *
049     * @see RuleDefinitionContract
050     * @see org.kuali.rice.krms.framework.engine.Rule
051     */
052    @XmlRootElement(name = RuleDefinition.Constants.ROOT_ELEMENT_NAME)
053    @XmlAccessorType(XmlAccessType.NONE)
054    @XmlType(name = RuleDefinition.Constants.TYPE_NAME, propOrder = {
055                    RuleDefinition.Elements.ID,
056                    RuleDefinition.Elements.NAME,
057            RuleDefinition.Elements.NAMESPACE,
058            RuleDefinition.Elements.DESCRIPTION,
059                    RuleDefinition.Elements.TYPE_ID,
060            RuleDefinition.Elements.ACTIVE,
061                    RuleDefinition.Elements.PROPOSITION,
062                    RuleDefinition.Elements.ACTIONS,
063                    RuleDefinition.Elements.ATTRIBUTES,
064            CoreConstants.CommonElements.VERSION_NUMBER,
065                    CoreConstants.CommonElements.FUTURE_ELEMENTS
066    })
067    public final class RuleDefinition extends AbstractDataTransferObject implements RuleDefinitionContract {
068            private static final long serialVersionUID = 2783959459503209577L;
069    
070            @XmlElement(name = Elements.ID, required=true)
071            private final String id;
072        @XmlElement(name = Elements.NAME, required=true)
073            private final String name;
074            @XmlElement(name = Elements.NAMESPACE, required=true)
075            private final String namespace;
076        @XmlElement(name = Elements.DESCRIPTION, required=false)
077        private final String description;
078            @XmlElement(name = Elements.TYPE_ID, required=true)
079            private final String typeId;
080            @XmlElement(name = Elements.PROPOSITION, required=true)
081            private final PropositionDefinition proposition;
082        @XmlElement(name = Elements.ACTIVE, required = false)
083        private final boolean active;
084    
085            @XmlElementWrapper(name = Elements.ACTIONS)
086            @XmlElement(name = Elements.ACTION, required=false)
087            private final List<ActionDefinition> actions;
088            
089            @XmlElement(name = Elements.ATTRIBUTES, required = false)
090            @XmlJavaTypeAdapter(value = MapStringStringAdapter.class)
091            private final Map<String, String> attributes;
092            
093        @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false)
094        private final Long versionNumber;
095            
096            @SuppressWarnings("unused")
097        @XmlAnyElement
098        private final Collection<org.w3c.dom.Element> _futureElements = null;
099            
100            @XmlTransient
101            private String propId;
102    
103            /** 
104         * This constructor should never be called.  
105         * It is only present for use during JAXB unmarshalling. 
106         */
107        private RuleDefinition() {
108            this.id = null;
109            this.name = null;
110            this.namespace = null;
111            this.description = null;
112            this.typeId = null;
113            this.propId = null;
114            this.active = true;
115            this.proposition = null;
116            this.actions = null;
117            this.attributes = null;
118            this.versionNumber = null;
119        }
120        
121        /**
122             * Constructs a KRMS Repository Rule object from the given builder.  
123             * This constructor is private and should only ever be invoked from the builder.
124             * 
125             * @param builder the Builder from which to construct the Rule
126             */
127        private RuleDefinition(Builder builder) {
128            this.id = builder.getId();
129            this.name = builder.getName();
130            this.namespace = builder.getNamespace();
131            this.typeId = builder.getTypeId();
132            this.propId = builder.getPropId();
133            this.description = builder.getDescription();
134            this.active = builder.isActive();
135    
136            if (builder.getProposition() != null) {
137                this.proposition = builder.getProposition().build();
138            } else {
139                this.proposition = null;
140            }
141            
142            List<ActionDefinition> actionList = new ArrayList<ActionDefinition> ();
143            if (builder.getActions() != null){
144                    for (ActionDefinition.Builder b : builder.actions){
145                            actionList.add(b.build());
146                    }
147                this.actions = Collections.unmodifiableList(actionList);
148            } else {
149                this.actions = Collections.emptyList();
150            }
151            if (builder.attributes != null){
152                    this.attributes = Collections.unmodifiableMap(builder.getAttributes());
153            } else {
154                    this.attributes = null;
155            }
156            this.versionNumber = builder.getVersionNumber();
157        }
158        
159            @Override
160            public String getId() {
161                    return this.id;
162            }
163    
164            @Override
165            public String getName() {
166                    return this.name;
167            }
168    
169        @Override
170        public String getDescription() {
171            return this.description;
172        }
173    
174        @Override
175            public String getNamespace() {
176                    return this.namespace;
177            }
178    
179            @Override
180            public String getTypeId() {
181                    return this.typeId;
182            }
183    
184            @Override
185            public String getPropId(){
186                    return this.propId;
187            }
188    
189        @Override
190        public boolean isActive() {
191            return this.active;
192        }
193    
194            @Override
195            public PropositionDefinition getProposition(){
196                    return this.proposition;
197            }
198            
199            @Override
200            public List<ActionDefinition> getActions(){
201                    return this.actions;
202            }
203                    
204            @Override
205            public Map<String, String> getAttributes() {
206                    return this.attributes;
207            }
208    
209        @Override
210        public Long getVersionNumber() {
211            return versionNumber;
212        }
213            
214            /**
215         * This builder is used to construct instances of KRMS Repository Rule.  It enforces the constraints of the {@link RuleDefinitionContract}.
216         */
217        public static class Builder implements RuleDefinitionContract, ModelBuilder, Serializable {         
218            private static final long serialVersionUID = -7850514191699945347L;
219            
220                    private String id;
221            private String name;
222            private String description;
223            private String namespace;
224            private String typeId;
225            private String propId;
226            private boolean active;
227            private PropositionDefinition.Builder proposition;
228            private List<ActionDefinition.Builder> actions;
229            private Map<String, String> attributes;
230            private Long versionNumber;
231    
232                    /**
233                     * Private constructor for creating a builder with all of it's required attributes.
234                     */
235            private Builder(String ruleId, String name, String namespace, String typeId, String propId) {
236                setId(ruleId);
237                setName(name);
238                setNamespace(namespace);
239                setTypeId(typeId);
240                setPropId(propId);
241                setActive(true);
242                setAttributes(new HashMap<String, String>());
243            }
244    
245            /**
246             * Create a builder with the given parameters.
247             *
248             * @param ruleId
249             * @param name
250             * @param namespace
251             * @param typeId
252             * @param propId
253             * @return Builder
254             */
255            public static Builder create(String ruleId, String name, String namespace, String typeId, String propId){
256                    return new Builder(ruleId, name, namespace, typeId, propId);
257            }
258            
259            /**
260             * Creates a builder by populating it with data from the given {@link RuleDefinitionContract}.
261             * 
262             * @param contract the contract from which to populate this builder
263             * @return an instance of the builder populated with data from the contract
264             */
265            public static Builder create(RuleDefinitionContract contract) {
266                    if (contract == null) {
267                    throw new IllegalArgumentException("contract is null");
268                }
269    
270                    List <ActionDefinition.Builder> actionList = new ArrayList<ActionDefinition.Builder>();
271                    if (contract.getActions() != null){
272                            for (ActionDefinitionContract actionContract : contract.getActions()){
273                                    ActionDefinition.Builder actBuilder = ActionDefinition.Builder.create(actionContract);
274                                    actionList.add(actBuilder);
275                            }
276                    }
277                    
278                Builder builder =  new Builder(contract.getId(), contract.getName(),
279                            contract.getNamespace(), contract.getTypeId(), contract.getPropId());
280                if (contract.getProposition() != null) {
281                    builder.setProposition(PropositionDefinition.Builder.create(contract.getProposition()));
282                }
283                    if (contract.getAttributes() != null){
284                    builder.setAttributes(new HashMap<String, String>(contract.getAttributes()));
285                    }
286                builder.setActions(actionList);
287                builder.setVersionNumber(contract.getVersionNumber());
288                builder.setDescription(contract.getDescription());
289                builder.setActive(contract.isActive());
290                return builder;
291            }
292    
293                    /**
294                     * Sets the value of the id on this builder to the given value.
295                     * 
296                     * @param ruleId the id value to set, must not be null or blank
297                     * @throws IllegalArgumentException if the id is null or blank
298                     */
299    
300            public void setId(String ruleId) {
301                if (ruleId != null && StringUtils.isBlank(ruleId)) {
302                    throw new IllegalArgumentException("rule ID must be null or else non-blank");
303                }
304                            this.id = ruleId;
305                    }
306    
307            /**
308             * Sets the value of the name on this builder to the given value
309             * @param name
310             * @throws IllegalArgumentException if the name is null or blank
311             */
312            public void setName(String name) {
313                if (StringUtils.isBlank(name)) {
314                    throw new IllegalArgumentException("name is blank");
315                }
316                this.name = name;
317            }
318    
319            /**
320             * Sets the value of the description on this builder to the given value
321             * @param description
322             */
323            public void setDescription(String description) {
324                this.description = description;
325            }
326    
327            /**
328             * Sets the value of the namespace on this builder to the given value
329             * @param namespace
330             * @throws IllegalArgumentException if the namespace is null or blank
331             */
332            public void setNamespace(String namespace) {
333                if (StringUtils.isBlank(namespace)) {
334                    throw new IllegalArgumentException("namespace is blank");
335                }
336                            this.namespace = namespace;
337                    }
338    
339            /**
340             * Sets the value of the typeId on this builder to the given value
341             * @param typeId
342             */
343                    public void setTypeId(String typeId) {
344                            this.typeId = typeId;
345                    }
346    
347            /**
348             * Sets the value of the active on this builder to the given value
349             * @param active
350             */
351            public void setActive(boolean active) {
352                this.active = active;
353            }
354    
355            /**
356             * Sets the value of the propId on this builder to the given value
357             * @param propId
358             * @throws IllegalArgumentException if the propId is null or blank
359             */
360                    public void setPropId(String propId) {
361                        if (propId != null && StringUtils.isBlank(propId)) {
362                            throw new IllegalArgumentException("propId must be null or non-blank");
363                        }
364                            this.propId = propId;
365                    }
366    
367            /**
368             * Sets the value of the proposition on this builder to the given value
369             * @param prop
370             */
371                    public void setProposition(PropositionDefinition.Builder prop) {
372                            this.proposition = prop;
373                            this.setPropId(prop.getId());
374                    }
375    
376            /**
377             * Sets the value of the actions on this builder to the given value
378             * @param actions
379             */
380                    public void setActions(List<ActionDefinition.Builder> actions) {
381                            if (actions == null){
382                                    this.actions = Collections.unmodifiableList(new ArrayList<ActionDefinition.Builder>());
383                                    return;
384                            }
385                            this.actions = Collections.unmodifiableList(actions);
386                    }
387    
388            /**
389             * Sets the value of the attributes on this builder to the given value
390             * @param attributes
391             */
392                    public void setAttributes(Map<String, String> attributes){
393                            if (attributes == null){
394                                    this.attributes = Collections.emptyMap();
395                            }
396                            this.attributes = Collections.unmodifiableMap(attributes);
397                    }
398    
399            /**
400             * Sets the value of the versionNumber on this builder to the given value
401             * @param versionNumber
402             */
403            public void setVersionNumber(Long versionNumber){
404                this.versionNumber = versionNumber;
405            }
406            
407                    @Override
408                    public String getId() {
409                            return id;
410                    }
411    
412                    @Override
413                    public String getName() {
414                            return name;
415                    }
416                    
417                    @Override
418                    public String getDescription() {
419                        return description;
420                    }
421    
422                    @Override
423                    public String getNamespace() {
424                            return namespace;
425                    }
426    
427                    @Override
428                    public String getTypeId() {
429                            return typeId;
430                    }
431    
432                    @Override
433                    public String getPropId() {
434                            return propId;
435                    }
436    
437            @Override
438            public boolean isActive() {
439                return active;
440            }
441    
442                    @Override
443                    public PropositionDefinition.Builder getProposition() {
444                            return proposition;
445                    }
446    
447                    @Override
448                    public List<ActionDefinition.Builder> getActions(){
449                            return actions;
450                    }
451                    @Override
452                    public Map<String, String> getAttributes() {
453                            return attributes;
454                    }
455    
456            @Override
457            public Long getVersionNumber() {
458                return versionNumber;
459            }
460    
461                    /**
462                     * Builds an instance of a Rule based on the current state of the builder.
463                     * 
464                     * @return the fully-constructed Rule
465                     */
466            @Override
467            public RuleDefinition build() {
468                return new RuleDefinition(this);
469            }
470                    
471        }
472            
473            /**
474             * Defines some internal constants used on this class.
475             */
476            public static class Constants {
477                    final static String ROOT_ELEMENT_NAME = "rule";
478                    final static String TYPE_NAME = "RuleType";
479            }
480            
481            /**
482             * A private class which exposes constants which define the XML element names to use
483             * when this object is marshalled to XML.
484             */
485            public static class Elements {
486                    final static String ID = "id";
487            final static String NAME = "name";
488            final static String DESCRIPTION = "description";
489                    final static String NAMESPACE = "namespace";
490                    final static String TYPE_ID = "typeId";
491                    final static String PROPOSITION = "proposition";
492                    final static String ACTIONS = "actions";
493                    final static String ACTION = "action";
494            final static String ACTIVE = "active";
495                    final static String ATTRIBUTES = "attributes";
496            }
497    
498    }