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 }