001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.krms.api.repository.rule;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.CoreConstants;
020import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
021import org.kuali.rice.core.api.mo.ModelBuilder;
022import org.kuali.rice.core.api.util.jaxb.MapStringStringAdapter;
023import org.kuali.rice.krms.api.KrmsConstants;
024import org.kuali.rice.krms.api.repository.action.ActionDefinition;
025import org.kuali.rice.krms.api.repository.action.ActionDefinitionContract;
026import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition;
027
028import javax.xml.bind.annotation.XmlAccessType;
029import javax.xml.bind.annotation.XmlAccessorType;
030import javax.xml.bind.annotation.XmlAnyElement;
031import javax.xml.bind.annotation.XmlElement;
032import javax.xml.bind.annotation.XmlElementWrapper;
033import javax.xml.bind.annotation.XmlRootElement;
034import javax.xml.bind.annotation.XmlTransient;
035import javax.xml.bind.annotation.XmlType;
036import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
037import java.io.Serializable;
038import java.util.ArrayList;
039import java.util.Collection;
040import java.util.Collections;
041import java.util.HashMap;
042import java.util.List;
043import 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})
068public 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}