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 }