001    /**
002     * Copyright 2005-2011 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     */
051    @XmlRootElement(name = RuleDefinition.Constants.ROOT_ELEMENT_NAME)
052    @XmlAccessorType(XmlAccessType.NONE)
053    @XmlType(name = RuleDefinition.Constants.TYPE_NAME, propOrder = {
054                    RuleDefinition.Elements.ID,
055                    RuleDefinition.Elements.NAME,
056            RuleDefinition.Elements.NAMESPACE,
057            RuleDefinition.Elements.DESCRIPTION,
058                    RuleDefinition.Elements.TYPE_ID,
059                    RuleDefinition.Elements.PROPOSITION,
060                    RuleDefinition.Elements.ACTIONS,
061                    RuleDefinition.Elements.ATTRIBUTES,
062            CoreConstants.CommonElements.VERSION_NUMBER,
063                    CoreConstants.CommonElements.FUTURE_ELEMENTS
064    })
065    public final class RuleDefinition extends AbstractDataTransferObject implements RuleDefinitionContract {
066            private static final long serialVersionUID = 2783959459503209577L;
067    
068            @XmlElement(name = Elements.ID, required=true)
069            private final String id;
070        @XmlElement(name = Elements.NAME, required=true)
071            private final String name;
072            @XmlElement(name = Elements.NAMESPACE, required=true)
073            private final String namespace;
074        @XmlElement(name = Elements.DESCRIPTION, required=false)
075        private final String description;
076            @XmlElement(name = Elements.TYPE_ID, required=true)
077            private final String typeId;
078            @XmlElement(name = Elements.PROPOSITION, required=true)
079            private final PropositionDefinition proposition;
080            
081            @XmlElementWrapper(name = Elements.ACTIONS)
082            @XmlElement(name = Elements.ACTION, required=false)
083            private final List<ActionDefinition> actions;
084            
085            @XmlElement(name = Elements.ATTRIBUTES, required = false)
086            @XmlJavaTypeAdapter(value = MapStringStringAdapter.class)
087            private final Map<String, String> attributes;
088            
089        @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false)
090        private final Long versionNumber;
091            
092            @SuppressWarnings("unused")
093        @XmlAnyElement
094        private final Collection<org.w3c.dom.Element> _futureElements = null;
095            
096            @XmlTransient
097            private String propId;
098    
099            /** 
100         * This constructor should never be called.  
101         * It is only present for use during JAXB unmarshalling. 
102         */
103        private RuleDefinition() {
104            this.id = null;
105            this.name = null;
106            this.namespace = null;
107            this.description = null;
108            this.typeId = null;
109            this.propId = null;
110            this.proposition = null;
111            this.actions = null;
112            this.attributes = null;
113            this.versionNumber = null;
114        }
115        
116        /**
117             * Constructs a KRMS Repository Rule object from the given builder.  
118             * This constructor is private and should only ever be invoked from the builder.
119             * 
120             * @param builder the Builder from which to construct the Rule
121             */
122        private RuleDefinition(Builder builder) {
123            this.id = builder.getId();
124            this.name = builder.getName();
125            this.namespace = builder.getNamespace();
126            this.typeId = builder.getTypeId();
127            this.propId = builder.getPropId();
128            this.description = builder.getDescription();
129            
130            if (builder.getProposition() != null) {
131                this.proposition = builder.getProposition().build();
132            } else {
133                this.proposition = null;
134            }
135            
136            List<ActionDefinition> actionList = new ArrayList<ActionDefinition> ();
137            if (builder.getActions() != null){
138                    for (ActionDefinition.Builder b : builder.actions){
139                            actionList.add(b.build());
140                    }
141                this.actions = Collections.unmodifiableList(actionList);
142            } else {
143                this.actions = Collections.emptyList();
144            }
145            if (builder.attributes != null){
146                    this.attributes = Collections.unmodifiableMap(builder.getAttributes());
147            } else {
148                    this.attributes = null;
149            }
150            this.versionNumber = builder.getVersionNumber();
151        }
152        
153            @Override
154            public String getId() {
155                    return this.id;
156            }
157    
158            @Override
159            public String getName() {
160                    return this.name;
161            }
162    
163        public String getDescription() {
164            return this.description;
165        }
166    
167        @Override
168            public String getNamespace() {
169                    return this.namespace;
170            }
171    
172            @Override
173            public String getTypeId() {
174                    return this.typeId;
175            }
176    
177            @Override
178            public String getPropId(){
179                    return this.propId;
180            }
181    
182            @Override
183            public PropositionDefinition getProposition(){
184                    return this.proposition;
185            }
186            
187            @Override
188            public List<ActionDefinition> getActions(){
189                    return this.actions;
190            }
191                    
192            @Override
193            public Map<String, String> getAttributes() {
194                    return this.attributes;
195            }
196    
197        @Override
198        public Long getVersionNumber() {
199            return versionNumber;
200        }
201            
202            /**
203         * This builder is used to construct instances of KRMS Repository Rule.  It enforces the constraints of the {@link RuleDefinitionContract}.
204         */
205        public static class Builder implements RuleDefinitionContract, ModelBuilder, Serializable {         
206            private static final long serialVersionUID = -7850514191699945347L;
207            
208                    private String id;
209            private String name;
210            private String description;
211            private String namespace;
212            private String typeId;
213            private String propId;
214            private PropositionDefinition.Builder proposition;
215            private List<ActionDefinition.Builder> actions;
216            private Map<String, String> attributes;
217            private Long versionNumber;
218    
219                    /**
220                     * Private constructor for creating a builder with all of it's required attributes.
221                     */
222            private Builder(String ruleId, String name, String namespace, String typeId, String propId) {
223                setId(ruleId);
224                setName(name);
225                setNamespace(namespace);
226                setTypeId(typeId);
227                setPropId(propId);
228                setAttributes(new HashMap<String, String>());
229            }
230            
231            public static Builder create(String ruleId, String name, String namespace, String typeId, String propId){
232                    return new Builder(ruleId, name, namespace, typeId, propId);
233            }
234            
235            /**
236             * Creates a builder by populating it with data from the given {@link RuleDefinitionContract}.
237             * 
238             * @param contract the contract from which to populate this builder
239             * @return an instance of the builder populated with data from the contract
240             */
241            public static Builder create(RuleDefinitionContract contract) {
242                    if (contract == null) {
243                    throw new IllegalArgumentException("contract is null");
244                }
245    
246                    List <ActionDefinition.Builder> actionList = new ArrayList<ActionDefinition.Builder>();
247                    if (contract.getActions() != null){
248                            for (ActionDefinitionContract actionContract : contract.getActions()){
249                                    ActionDefinition.Builder actBuilder = ActionDefinition.Builder.create(actionContract);
250                                    actionList.add(actBuilder);
251                            }
252                    }
253                    
254                Builder builder =  new Builder(contract.getId(), contract.getName(),
255                            contract.getNamespace(), contract.getTypeId(), contract.getPropId());
256                if (contract.getProposition() != null) {
257                    builder.setProposition(PropositionDefinition.Builder.create(contract.getProposition()));
258                }
259                    if (contract.getAttributes() != null){
260                    builder.setAttributes(new HashMap<String, String>(contract.getAttributes()));
261                    }
262                builder.setActions(actionList);
263                builder.setVersionNumber(contract.getVersionNumber());
264                builder.setDescription(contract.getDescription());
265                return builder;
266            }
267    
268                    /**
269                     * Sets the value of the id on this builder to the given value.
270                     * 
271                     * @param ruleId the id value to set, must not be null or blank
272                     * @throws IllegalArgumentException if the id is null or blank
273                     */
274    
275            public void setId(String ruleId) {
276                if (ruleId != null && StringUtils.isBlank(ruleId)) {
277                    throw new IllegalArgumentException("rule ID must be null or else non-blank");
278                }
279                            this.id = ruleId;
280                    }
281         
282            public void setName(String name) {
283                if (StringUtils.isBlank(name)) {
284                    throw new IllegalArgumentException("name is blank");
285                }
286                this.name = name;
287            }
288         
289            public void setDescription(String description) {
290                if (description != null && StringUtils.isBlank(description)) {
291                    throw new IllegalArgumentException("description is non-null but is blank");
292                }
293                this.description = description;
294            }
295         
296            public void setNamespace(String namespace) {
297                if (StringUtils.isBlank(namespace)) {
298                    throw new IllegalArgumentException("namespace is blank");
299                }
300                            this.namespace = namespace;
301                    }
302         
303                    public void setTypeId(String typeId) {
304                            this.typeId = typeId;
305                    }
306                    
307                    public void setPropId(String propId) {
308                        if (propId != null && StringUtils.isBlank(propId)) {
309                            throw new IllegalArgumentException("propId must be null or non-blank");
310                        }
311                            this.propId = propId;
312                    }
313                    
314                    public void setProposition(PropositionDefinition.Builder prop) {
315                            this.proposition = prop;
316                            this.setPropId(prop.getId());
317                    }
318                    
319                    public void setActions(List<ActionDefinition.Builder> actions) {
320                            if (actions == null){
321                                    this.actions = Collections.unmodifiableList(new ArrayList<ActionDefinition.Builder>());
322                                    return;
323                            }
324                            this.actions = Collections.unmodifiableList(actions);
325                    }
326                    
327                    public void setAttributes(Map<String, String> attributes){
328                            if (attributes == null){
329                                    this.attributes = Collections.emptyMap();
330                            }
331                            this.attributes = Collections.unmodifiableMap(attributes);
332                    }
333                    
334            public void setVersionNumber(Long versionNumber){
335                this.versionNumber = versionNumber;
336            }
337            
338                    @Override
339                    public String getId() {
340                            return id;
341                    }
342    
343                    @Override
344                    public String getName() {
345                            return name;
346                    }
347                    
348                    @Override
349                    public String getDescription() {
350                        return description;
351                    }
352    
353                    @Override
354                    public String getNamespace() {
355                            return namespace;
356                    }
357    
358                    @Override
359                    public String getTypeId() {
360                            return typeId;
361                    }
362    
363                    @Override
364                    public String getPropId() {
365                            return propId;
366                    }
367    
368                    @Override
369                    public PropositionDefinition.Builder getProposition() {
370                            return proposition;
371                    }
372    
373                    @Override
374                    public List<ActionDefinition.Builder> getActions(){
375                            return actions;
376                    }
377                    @Override
378                    public Map<String, String> getAttributes() {
379                            return attributes;
380                    }
381    
382            @Override
383            public Long getVersionNumber() {
384                return versionNumber;
385            }
386    
387                    /**
388                     * Builds an instance of a Rule based on the current state of the builder.
389                     * 
390                     * @return the fully-constructed Rule
391                     */
392            @Override
393            public RuleDefinition build() {
394                return new RuleDefinition(this);
395            }
396                    
397        }
398            
399            /**
400             * Defines some internal constants used on this class.
401             */
402            public static class Constants {
403                    final static String ROOT_ELEMENT_NAME = "rule";
404                    final static String TYPE_NAME = "RuleType";
405            }
406            
407            /**
408             * A private class which exposes constants which define the XML element names to use
409             * when this object is marshalled to XML.
410             */
411            public static class Elements {
412                    final static String ID = "id";
413            final static String NAME = "name";
414            final static String DESCRIPTION = "description";
415                    final static String NAMESPACE = "namespace";
416                    final static String TYPE_ID = "typeId";
417                    final static String PROPOSITION = "proposition";
418                    final static String ACTIONS = "actions";
419                    final static String ACTION = "action";
420                    final static String ATTRIBUTES = "attributes";
421            }
422    
423    }