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.kew.engine.node;
017    
018    import java.io.Serializable;
019    import java.util.ArrayList;
020    import java.util.Iterator;
021    import java.util.List;
022    
023    import javax.persistence.CascadeType;
024    import javax.persistence.Column;
025    import javax.persistence.Entity;
026    import javax.persistence.FetchType;
027    import javax.persistence.GeneratedValue;
028    import javax.persistence.Id;
029    import javax.persistence.JoinColumn;
030    import javax.persistence.JoinTable;
031    import javax.persistence.ManyToMany;
032    import javax.persistence.ManyToOne;
033    import javax.persistence.NamedQueries;
034    import javax.persistence.NamedQuery;
035    import javax.persistence.OneToMany;
036    import javax.persistence.OneToOne;
037    import javax.persistence.Table;
038    import javax.persistence.Version;
039    
040    import org.apache.commons.lang.builder.ToStringBuilder;
041    import org.hibernate.annotations.Fetch;
042    import org.hibernate.annotations.FetchMode;
043    import org.hibernate.annotations.GenericGenerator;
044    import org.hibernate.annotations.Parameter;
045    import org.kuali.rice.core.framework.persistence.jpa.OrmUtils;
046    import org.kuali.rice.kew.api.document.node.RouteNodeInstanceState;
047    import org.kuali.rice.kew.doctype.bo.DocumentType;
048    import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
049    import org.kuali.rice.kew.service.KEWServiceLocator;
050    
051    
052    /**
053     * Represents a materialized instance of a {@link RouteNode} definition on a {@link DocumentRouteHeaderValue}.  Node instances
054     * are generated by the engine using the {@link RouteNode} as a prototype and connected as a 
055     * Directed Acyclic Graph.
056     *
057     * @author Kuali Rice Team (rice.collab@kuali.org)
058     */
059    @Entity
060    @Table(name="KREW_RTE_NODE_INSTN_T")
061    //@Sequence(name="KREW_RTE_NODE_S",property="routeNodeInstanceId")
062    @NamedQueries({
063            @NamedQuery(name="RouteNodeInstance.FindByRouteNodeInstanceId",query="select r from RouteNodeInstance r where r.routeNodeInstanceId = :routeNodeInstanceId"),
064            @NamedQuery(name="RouteNodeInstance.FindActiveNodeInstances",query="select r from RouteNodeInstance r where r.documentId = :documentId and r.active = true"),
065            @NamedQuery(name="RouteNodeInstance.FindTerminalNodeInstances",query="select r from RouteNodeInstance r where r.documentId = :documentId and r.active = false and r.complete = true"),
066            @NamedQuery(name="RouteNodeInstance.FindInitialNodeInstances",query="select d.initialRouteNodeInstances from DocumentRouteHeaderValue d where d.documentId = :documentId"),
067            @NamedQuery(name="RouteNodeInstance.FindProcessNodeInstances", query="select r from RouteNodeInstance r where r.process.routeNodeInstanceId = :processId"),
068            @NamedQuery(name="RouteNodeInstance.FindRouteNodeInstances", query="select r from RouteNodeInstance r where r.documentId = :documentId")
069    })
070    public class RouteNodeInstance implements Serializable {
071        
072            private static final long serialVersionUID = 7183670062805580420L;
073            
074            @Id
075            @GeneratedValue(generator="KREW_RTE_NODE_S")
076            @GenericGenerator(name="KREW_RTE_NODE_S",strategy="org.hibernate.id.enhanced.SequenceStyleGenerator",parameters={
077                            @Parameter(name="sequence_name",value="KREW_RTE_NODE_S"),
078                            @Parameter(name="value_column",value="id")
079            })
080            @Column(name="RTE_NODE_INSTN_ID")
081            private String routeNodeInstanceId;
082        @Column(name="DOC_HDR_ID")
083            private String documentId;
084        @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE})
085            @JoinColumn(name="BRCH_ID")
086            private Branch branch;
087        @OneToOne(fetch=FetchType.EAGER)
088            @JoinColumn(name="RTE_NODE_ID")
089        private RouteNode routeNode;
090        @Column(name="ACTV_IND")
091        private boolean active = false;
092        @Column(name="CMPLT_IND")
093        private boolean complete = false;
094        @Column(name="INIT_IND")
095        private boolean initial = true;
096        @OneToOne(fetch=FetchType.EAGER,cascade={CascadeType.PERSIST, CascadeType.MERGE})
097            @JoinColumn(name="PROC_RTE_NODE_INSTN_ID")
098            private RouteNodeInstance process;
099        
100        @ManyToMany(fetch=FetchType.EAGER,cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
101        @JoinTable(name = "KREW_RTE_NODE_INSTN_LNK_T", joinColumns = @JoinColumn(name = "FROM_RTE_NODE_INSTN_ID"), inverseJoinColumns = @JoinColumn(name = "TO_RTE_NODE_INSTN_ID"))
102        @Fetch(value = FetchMode.SELECT)
103        private List<RouteNodeInstance> nextNodeInstances = new ArrayList<RouteNodeInstance>();
104        
105        @ManyToMany(fetch=FetchType.EAGER, mappedBy="nextNodeInstances")
106        @Fetch(value = FetchMode.SELECT)
107        //@JoinTable(name = "KREW_RTE_NODE_INSTN_LNK_T", joinColumns = @JoinColumn(name = "TO_RTE_NODE_INSTN_ID"), inverseJoinColumns = @JoinColumn(name = "FROM_RTE_NODE_INSTN_ID"))
108        private List<RouteNodeInstance> previousNodeInstances = new ArrayList<RouteNodeInstance>();
109    
110        @OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE}, mappedBy="nodeInstance", orphanRemoval=true)    
111        @Fetch(value = FetchMode.SELECT)
112        private List<NodeState> state = new ArrayList<NodeState>();
113            
114        @Version
115            @Column(name="VER_NBR")
116            private Integer lockVerNbr;
117        
118        private List<DocumentRouteHeaderValue> initialDocumentRouteHeaderValues = new ArrayList<DocumentRouteHeaderValue>();
119    
120        public boolean isActive() {
121            return active;
122        }
123        public void setActive(boolean active) {
124            this.active = active;
125        }
126        
127        public boolean isComplete() {
128            return complete;
129        }
130        public void setComplete(boolean complete) {
131            this.complete = complete;
132        }
133        public Branch getBranch() {
134            return branch;
135        }
136        public void setBranch(Branch branch) {
137            this.branch = branch;
138        }
139        public RouteNode getRouteNode() {
140            return routeNode;
141        }
142        public void setRouteNode(RouteNode node) {
143            this.routeNode = node;
144        }
145        public String getRouteNodeInstanceId() {
146            return routeNodeInstanceId;
147        }
148        public void setRouteNodeInstanceId(String routeNodeInstanceId) {
149            this.routeNodeInstanceId = routeNodeInstanceId;
150        }
151        public String getDocumentId() {
152            return documentId;
153        }
154        public void setDocumentId(String documentId) {
155            this.documentId = documentId;
156        }
157        public List<RouteNodeInstance> getNextNodeInstances() {
158            return nextNodeInstances;
159        }
160        public RouteNodeInstance getNextNodeInstance(int index) {
161            while (getNextNodeInstances().size() <= index) {
162                    nextNodeInstances.add(new RouteNodeInstance());
163            }
164            return (RouteNodeInstance) getNextNodeInstances().get(index);
165        }
166        public void setNextNodeInstances(List<RouteNodeInstance> nextNodeInstances) {
167            this.nextNodeInstances = nextNodeInstances;
168        }
169        public List<RouteNodeInstance> getPreviousNodeInstances() {
170            return previousNodeInstances;
171        }
172        public RouteNodeInstance getPreviousNodeInstance(int index) {
173            while (previousNodeInstances.size() <= index) {
174                    previousNodeInstances.add(new RouteNodeInstance());
175            }
176            return (RouteNodeInstance) getPreviousNodeInstances().get(index);
177        }
178        public void setPreviousNodeInstances(List<RouteNodeInstance> previousNodeInstances) {
179            this.previousNodeInstances = previousNodeInstances;
180        }
181        public boolean isInitial() {
182            return initial;
183        }
184        public void setInitial(boolean initial) {
185            this.initial = initial;
186        }
187        public List<NodeState> getState() {
188            return state;
189        }
190        public void setState(List<NodeState> state) {
191            this.state.clear();
192            this.state.addAll(state);
193            //this.state = state;
194        }
195        public RouteNodeInstance getProcess() {
196                    return process;
197            }
198            public void setProcess(RouteNodeInstance process) {
199                    this.process = process;
200            }
201            public Integer getLockVerNbr() {
202            return lockVerNbr;
203        }
204        public void setLockVerNbr(Integer lockVerNbr) {
205            this.lockVerNbr = lockVerNbr;
206        }
207        
208        public NodeState getNodeState(String key) {
209            for (Iterator iter = getState().iterator(); iter.hasNext();) {
210                NodeState nodeState = (NodeState) iter.next();
211                if (nodeState.getKey().equals(key)) {
212                    return nodeState;
213                }
214            }
215            return null;
216        }
217        
218        public void addNodeState(NodeState state) {
219            this.state.add(state);
220            state.setNodeInstance(this);
221        }
222        
223        public void removeNodeState(String key) {
224            for (Iterator iter = getState().iterator(); iter.hasNext();) {
225                NodeState nodeState = (NodeState) iter.next();
226                if (nodeState.getKey().equals(key)) {
227                    iter.remove();
228                    break;
229                }
230            }
231        }
232        
233        public void addNextNodeInstance(RouteNodeInstance nextNodeInstance) {
234            nextNodeInstances.add(nextNodeInstance);
235            nextNodeInstance.getPreviousNodeInstances().add(this);
236        }
237        
238        public void removeNextNodeInstance(RouteNodeInstance nextNodeInstance) {
239            nextNodeInstances.remove(nextNodeInstance);
240            nextNodeInstance.getPreviousNodeInstances().remove(this);
241        }
242        
243        public void clearNextNodeInstances() {
244            for (Iterator iterator = nextNodeInstances.iterator(); iterator.hasNext();) {
245                RouteNodeInstance nextNodeInstance = (RouteNodeInstance) iterator.next();
246                iterator.remove();
247                nextNodeInstance.getPreviousNodeInstances().remove(this);
248            }
249        }
250        
251        public String getName() {
252            return (getRouteNode() == null ? null : getRouteNode().getRouteNodeName());
253        }
254        
255        public boolean isInProcess() {
256            return getProcess() != null;
257        }
258        
259        public DocumentType getDocumentType() {
260            return KEWServiceLocator.getDocumentTypeService().findByDocumentId(getDocumentId());
261        }
262        
263        /*
264         * methods used to display route node instances' data on documentoperation.jsp
265         */
266        
267        public NodeState getNodeStateByIndex(int index){
268            while (state.size() <= index) {
269                state.add(new NodeState());
270            }
271            return (NodeState) getState().get(index);
272        }   
273    
274        public void populateState(List<NodeState> state) {
275            this.state.addAll(state);
276         }
277    
278        public List<DocumentRouteHeaderValue> getInitialDocumentRouteHeaderValues() {
279            return initialDocumentRouteHeaderValues;
280        }
281    
282        public void setInitialDocumentRouteHeaderValues(List<DocumentRouteHeaderValue> initialDocumentRouteHeaderValues) {
283            this.initialDocumentRouteHeaderValues = initialDocumentRouteHeaderValues;
284        }
285        
286        public String toString() {
287            return new ToStringBuilder(this)
288                .append("routeNodeInstanceId", routeNodeInstanceId)
289                .append("documentId", documentId)
290                .append("branch", branch == null ? null : branch.getBranchId())
291                .append("routeNode", routeNode == null ? null : routeNode.getRouteNodeName())
292                .append("active", active)
293                .append("complete", complete)
294                .append("initial", initial)
295                .append("process", process)
296                .append("state", state == null ? null : state.size())
297                .toString();
298        }
299        
300            //@PrePersist
301            public void beforeInsert(){
302                    OrmUtils.populateAutoIncValue(this, KEWServiceLocator.getEntityManagerFactory().createEntityManager());
303            }
304    
305    
306            public static org.kuali.rice.kew.api.document.node.RouteNodeInstance to(RouteNodeInstance routeNodeInstance) {
307                    if (routeNodeInstance == null) {
308                            return null;
309                    }
310                    org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder builder = org.kuali.rice.kew.api.document.node
311                    .RouteNodeInstance.Builder.create();
312                    builder.setActive(routeNodeInstance.isActive());
313                    builder.setBranchId(routeNodeInstance.getBranch().getBranchId());
314                    builder.setComplete(routeNodeInstance.isComplete());
315                    builder.setDocumentId(routeNodeInstance.getDocumentId());
316                    builder.setId(routeNodeInstance.getRouteNodeInstanceId());
317                    builder.setInitial(routeNodeInstance.isInitial());
318                    builder.setName(routeNodeInstance.getName());
319                    if (routeNodeInstance.getProcess() != null) {
320                            builder.setProcessId(routeNodeInstance.getProcess().getRouteNodeInstanceId());
321                    }
322                    builder.setRouteNodeId(routeNodeInstance.getRouteNode().getRouteNodeId());
323                    List<RouteNodeInstanceState.Builder> states = new ArrayList<RouteNodeInstanceState.Builder>();
324                    for (NodeState stateBo : routeNodeInstance.getState()) {
325                            RouteNodeInstanceState.Builder stateBuilder = RouteNodeInstanceState.Builder.create();
326                            stateBuilder.setId(stateBo.getStateId());
327                            stateBuilder.setKey(stateBo.getKey());
328                            stateBuilder.setValue(stateBo.getValue());
329                            states.add(stateBuilder);
330                    }
331                    builder.setState(states);
332    
333            List<org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder> nextNodes = new ArrayList<org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder>();
334            if (routeNodeInstance.getNextNodeInstances() != null) {
335                for (RouteNodeInstance next : routeNodeInstance.getNextNodeInstances()) {
336                    // will this make things blow up?
337                    nextNodes.add(org.kuali.rice.kew.api.document.node.RouteNodeInstance.Builder.create(RouteNodeInstance.to(next)));
338                }
339            }
340            builder.setNextNodeInstances(nextNodes);
341    
342                    return builder.build();
343    
344    
345    
346            }
347        
348    }
349