001 /**
002 * Copyright 2005-2014 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 }