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 }