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