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        /**
205         * Returns the internal representation of the set of attributes associated with the
206         * Action.  The attributes are represented as name/value pairs.
207         *
208         * @return internal representation of the set of ActionAttribute objects.
209         */
210        @Override
211            public Map<String, String> getAttributes() {
212                    return this.attributes;
213            }
214    
215        @Override
216        public Long getVersionNumber() {
217            return versionNumber;
218        }
219            
220            /**
221         * This builder is used to construct instances of KRMS Repository Rule.  It enforces the constraints of the {@link RuleDefinitionContract}.
222         */
223        public static class Builder implements RuleDefinitionContract, ModelBuilder, Serializable {         
224            private static final long serialVersionUID = -7850514191699945347L;
225            
226                    private String id;
227            private String name;
228            private String description;
229            private String namespace;
230            private String typeId;
231            private String propId;
232            private boolean active;
233            private PropositionDefinition.Builder proposition;
234            private List<ActionDefinition.Builder> actions;
235            private Map<String, String> attributes;
236            private Long versionNumber;
237    
238            /**
239             * Private constructor for creating a builder with all of it's required attributes.
240             * 
241             * @param ruleId the id value to set, must not be null or blank
242             * @param name the name value to set, must not be null or blank
243             * @param namespace the namespace value to set, must not be null or blank
244             * @param typeId the typeId value to set
245             * @param propId the propId value to set, must not be null or blank
246             */
247            private Builder(String ruleId, String name, String namespace, String typeId, String propId) {
248                setId(ruleId);
249                setName(name);
250                setNamespace(namespace);
251                setTypeId(typeId);
252                setPropId(propId);
253                setActive(true);
254                setAttributes(new HashMap<String, String>());
255            }
256    
257            /**
258             * Create a builder with the given parameters.
259             *
260             * @param ruleId the id value to set, must not be null or blank
261             * @param name the name value to set, must not be null or blank
262             * @param namespace the namespace value to set, must not be null or blank
263             * @param typeId the typeId value to set
264             * @param propId the propId value to set, must not be null or blank
265             * @return Builder with the given values set
266             */
267            public static Builder create(String ruleId, String name, String namespace, String typeId, String propId){
268                    return new Builder(ruleId, name, namespace, typeId, propId);
269            }
270            
271            /**
272             * Creates a builder by populating it with data from the given {@link RuleDefinitionContract}.
273             * 
274             * @param contract the contract from which to populate this builder
275             * @return an instance of the builder populated with data from the contract
276             */
277            public static Builder create(RuleDefinitionContract contract) {
278                    if (contract == null) {
279                    throw new IllegalArgumentException("contract is null");
280                }
281    
282                    List <ActionDefinition.Builder> actionList = new ArrayList<ActionDefinition.Builder>();
283                    if (contract.getActions() != null){
284                            for (ActionDefinitionContract actionContract : contract.getActions()){
285                                    ActionDefinition.Builder actBuilder = ActionDefinition.Builder.create(actionContract);
286                                    actionList.add(actBuilder);
287                            }
288                    }
289                    
290                Builder builder =  new Builder(contract.getId(), contract.getName(),
291                            contract.getNamespace(), contract.getTypeId(), contract.getPropId());
292                if (contract.getProposition() != null) {
293                    builder.setProposition(PropositionDefinition.Builder.create(contract.getProposition()));
294                }
295                    if (contract.getAttributes() != null){
296                    builder.setAttributes(new HashMap<String, String>(contract.getAttributes()));
297                    }
298                builder.setActions(actionList);
299                builder.setVersionNumber(contract.getVersionNumber());
300                builder.setDescription(contract.getDescription());
301                builder.setActive(contract.isActive());
302                return builder;
303            }
304    
305                    /**
306                     * Sets the value of the id on this builder to the given value.
307                     * 
308                     * @param ruleId the id value to set, must not be null or blank
309                     * @throws IllegalArgumentException if the id is null or blank
310                     */
311    
312            public void setId(String ruleId) {
313                if (ruleId != null && StringUtils.isBlank(ruleId)) {
314                    throw new IllegalArgumentException("rule ID must be null or else non-blank");
315                }
316                            this.id = ruleId;
317                    }
318    
319            /**
320             * Sets the value of the name on this builder to the given value
321             * @param name the name value to set, must not be null or blank
322             * @throws IllegalArgumentException if the name is null or blank
323             */
324            public void setName(String name) {
325                if (StringUtils.isBlank(name)) {
326                    throw new IllegalArgumentException("name is blank");
327                }
328                this.name = name;
329            }
330    
331            /**
332             * Sets the value of the description on this builder to the given value
333             * @param description
334             */
335            public void setDescription(String description) {
336                this.description = description;
337            }
338    
339            /**
340             * Sets the value of the namespace on this builder to the given value
341             * @param namespace the namespace value to set, must not be null or blank
342             * @throws IllegalArgumentException if the namespace is null or blank
343             */
344            public void setNamespace(String namespace) {
345                if (StringUtils.isBlank(namespace)) {
346                    throw new IllegalArgumentException("namespace is blank");
347                }
348                            this.namespace = namespace;
349                    }
350    
351            /**
352             * Sets the value of the typeId on this builder to the given value
353             * @param typeId the typeId value to set
354             */
355                    public void setTypeId(String typeId) {
356                            this.typeId = typeId;
357                    }
358    
359            /**
360             * Sets the value of the active on this builder to the given value
361             * @param active the active value to set
362             */
363            public void setActive(boolean active) {
364                this.active = active;
365            }
366    
367            /**
368             * Sets the value of the propId on this builder to the given value
369             * @param propId the propId value to set, must not be null or blank
370             * @throws IllegalArgumentException if the propId is null or blank
371             */
372                    public void setPropId(String propId) {
373                        if (propId != null && StringUtils.isBlank(propId)) {
374                            throw new IllegalArgumentException("propId must be null or non-blank");
375                        }
376                            this.propId = propId;
377                    }
378    
379            /**
380             * Sets the value of the proposition on this builder to the given value
381             * @param prop the proposition value to set, must not be null
382             */
383                    public void setProposition(PropositionDefinition.Builder prop) {
384                            this.proposition = prop;
385                            this.setPropId(prop.getId());
386                    }
387    
388            /**
389             * Sets the value of the actions on this builder to the given value
390             * @param actions the actions value to set, can be null
391             */
392                    public void setActions(List<ActionDefinition.Builder> actions) {
393                            if (actions == null){
394                                    this.actions = Collections.unmodifiableList(new ArrayList<ActionDefinition.Builder>());
395                                    return;
396                            }
397                            this.actions = Collections.unmodifiableList(actions);
398                    }
399    
400            /**
401             * Sets the value of the attributes on this builder to the given value
402             * @param attributes the attributes values to set, can be null
403             */
404                    public void setAttributes(Map<String, String> attributes){
405                            if (attributes == null){
406                                    this.attributes = Collections.emptyMap();
407                            }
408                            this.attributes = Collections.unmodifiableMap(attributes);
409                    }
410    
411            /**
412             * Sets the value of the versionNumber on this builder to the given value
413             * @param versionNumber the versionNumber value to set
414             */
415            public void setVersionNumber(Long versionNumber){
416                this.versionNumber = versionNumber;
417            }
418            
419                    @Override
420                    public String getId() {
421                            return id;
422                    }
423    
424                    @Override
425                    public String getName() {
426                            return name;
427                    }
428                    
429                    @Override
430                    public String getDescription() {
431                        return description;
432                    }
433    
434                    @Override
435                    public String getNamespace() {
436                            return namespace;
437                    }
438    
439                    @Override
440                    public String getTypeId() {
441                            return typeId;
442                    }
443    
444                    @Override
445                    public String getPropId() {
446                            return propId;
447                    }
448    
449            @Override
450            public boolean isActive() {
451                return active;
452            }
453    
454                    @Override
455                    public PropositionDefinition.Builder getProposition() {
456                            return proposition;
457                    }
458    
459                    @Override
460                    public List<ActionDefinition.Builder> getActions(){
461                            return actions;
462                    }
463                    @Override
464                    public Map<String, String> getAttributes() {
465                            return attributes;
466                    }
467    
468            @Override
469            public Long getVersionNumber() {
470                return versionNumber;
471            }
472    
473                    /**
474                     * Builds an instance of a Rule based on the current state of the builder.
475                     * 
476                     * @return the fully-constructed Rule
477                     */
478            @Override
479            public RuleDefinition build() {
480                return new RuleDefinition(this);
481            }
482                    
483        }
484            
485            /**
486             * Defines some internal constants used on this class.
487             */
488            public static class Constants {
489                    final static String ROOT_ELEMENT_NAME = "rule";
490                    final static String TYPE_NAME = "RuleType";
491            }
492            
493            /**
494             * A private class which exposes constants which define the XML element names to use
495             * when this object is marshalled to XML.
496             */
497            public static class Elements {
498                    final static String ID = "id";
499            final static String NAME = "name";
500            final static String DESCRIPTION = "description";
501                    final static String NAMESPACE = "namespace";
502                    final static String TYPE_ID = "typeId";
503                    final static String PROPOSITION = "proposition";
504                    final static String ACTIONS = "actions";
505                    final static String ACTION = "action";
506            final static String ACTIVE = "active";
507                    final static String ATTRIBUTES = "attributes";
508            }
509    
510    }