1 | package desmoj.core.simulator; |
2 | |
3 | import java.util.Iterator; |
4 | |
5 | /** |
6 | * ProcessQueue provides models with a ready-to-use element to enqueue |
7 | * <code>SimProcess</code>es in. The sort order of the ProcessQueue is |
8 | * determined first by the priorities of the enqueued SimProcesses and second by |
9 | * the given sort order. The default sort order is FIFO (first in, first out) |
10 | * but others like LIFO (last in, first out) can be chosen, too. See the |
11 | * constants in class <code>QueueBased</code> and the derived classes from |
12 | * <code>QueueList</code>. The capacity of the ProcessQueue, that is the |
13 | * maximum number of SimProcesses enqueued, can be chosen, too. Note that in |
14 | * contrast to the 'plain' queue, this ProcessQueue always expects and returns |
15 | * objects that are derived from class <code>SimProcess</code>. When |
16 | * modelling using the process-, activity-, or transaction-oriented paradigm |
17 | * where SimProcesses are used to represent the model's entities, this |
18 | * ProcessQueue can be used instead of the standard Queue to reduce the amount |
19 | * of casts needed otherwise. |
20 | * |
21 | * @see QueueBased |
22 | * @see QueueList |
23 | * @see QueueListFifo |
24 | * @see QueueListLifo |
25 | * |
26 | * @version DESMO-J, Ver. 2.3.3 copyright (c) 2011 |
27 | * @author Tim Lechler |
28 | * @author modified by Soenke Claassen |
29 | * |
30 | * Licensed under the Apache License, Version 2.0 (the "License"); |
31 | * you may not use this file except in compliance with the License. You |
32 | * may obtain a copy of the License at |
33 | * http://www.apache.org/licenses/LICENSE-2.0 |
34 | * |
35 | * Unless required by applicable law or agreed to in writing, software |
36 | * distributed under the License is distributed on an "AS IS" |
37 | * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
38 | * or implied. See the License for the specific language governing |
39 | * permissions and limitations under the License. |
40 | * |
41 | */ |
42 | public class ProcessQueue<P extends SimProcess> extends QueueBased implements Iterable<P> { |
43 | |
44 | /** |
45 | * The queue implementation that actually stores the entities |
46 | */ |
47 | private QueueList<P> _ql; |
48 | |
49 | /** |
50 | * Counter for the sim-processes which are refused to be enqueued, because |
51 | * the queue capacity is full. |
52 | */ |
53 | private long _refused; |
54 | |
55 | |
56 | /** |
57 | * Constructs a simple priority based waiting-queue for SimProcesses, the |
58 | * kind of queue implementation (FIFO or LIFO) and the capacity of the queue |
59 | * can be chosen. |
60 | * <p> |
61 | * The usage of the generic version <code>ProcessQueue<Type></code> where |
62 | * <code>Type</code> is derived from <code>SimProcess</code> is recommended |
63 | * for type safety. Using the raw type <code>ProcessQueue</code> yields a queue |
64 | * in which any <code>SimProcess</code> can be enqueued, potentially requiring |
65 | * type casting on accessing processes enqueued. |
66 | * |
67 | * @param owner |
68 | * Model : The model this ProcessQueue is associated to |
69 | * @param name |
70 | * java.lang.String : The process-queue's name |
71 | * @param sortOrder |
72 | * int : determines the sort order of the underlying queue |
73 | * implementation. Choose a constant from <code>QueueBased</code>: |
74 | * <code>QueueBased.FIFO</code>, <code>QueueBased.LIFO</code> or |
75 | * QueueBased.Random. |
76 | * @param qCapacity |
77 | * int : The capacity of the ProcessQueue, that is how many |
78 | * processes can be enqueued. Zero (0) means unlimited capacity. |
79 | * @param showInReport |
80 | * boolean : Flag if process-queue should produce a report |
81 | * @param showInTrace |
82 | * boolean : Flag for process-queue to produce trace messages |
83 | */ |
84 | public ProcessQueue(Model owner, String name, int sortOrder, int qCapacity, |
85 | boolean showInReport, boolean showInTrace) { |
86 | |
87 | super(owner, name, showInReport, showInTrace); // create the QBased |
88 | // object |
89 | reset(); |
90 | |
91 | // determine the queueing strategy |
92 | switch (sortOrder) { |
93 | case QueueBased.FIFO : |
94 | _ql = new QueueListFifo<P>(); break; |
95 | case QueueBased.LIFO : |
96 | _ql = new QueueListLifo<P>(); break; |
97 | case QueueBased.RANDOM : |
98 | _ql = new QueueListRandom<P>(); break; |
99 | default : |
100 | sendWarning( |
101 | "The given sortOrder parameter " + sortOrder + " is not valid! " |
102 | + "A queue with Fifo sort order will be created.", |
103 | "ProcessQueueQueue : " |
104 | + getName() |
105 | + " Constructor: ProcessQueue(Model owner, String name, " |
106 | + "int sortOrder, long qCapacity, boolean showInReport, " |
107 | + "boolean showInTrace)", |
108 | "A valid positive integer number must be provided to " |
109 | + "determine the sort order of the queue.", |
110 | "Make sure to provide a valid positive integer number " |
111 | + "by using the constants in the class QueueBased, like " |
112 | + "QueueBased.FIFO, QueueBased.LIFO or QueueBased.RANDOM."); |
113 | _ql = new QueueListFifo<P>(); |
114 | } |
115 | |
116 | // give the QueueList a reference to this QueueBased |
117 | _ql.setQueueBased(this); |
118 | |
119 | // set the capacity of the queue |
120 | queueLimit = qCapacity; |
121 | |
122 | // check if it the capacity does make sense |
123 | if (qCapacity < 0) { |
124 | sendWarning( |
125 | "The given capacity of the queue is negative! " |
126 | + "A queue with unlimited capacity will be created instead.", |
127 | "ProcessQueue : " |
128 | + getName() |
129 | + " Constructor: ProcessQueue(Model owner, String name, " |
130 | + "int sortOrder, long qCapacity, boolean showInReport, " |
131 | + "boolean showInTrace)", |
132 | "A negative capacity for a queue does not make sense.", |
133 | "Make sure to provide a valid positive capacity " |
134 | + "for the queue."); |
135 | // set the capacity to the maximum value |
136 | queueLimit = Integer.MAX_VALUE; |
137 | } |
138 | |
139 | // check if qCapacity is zero (that means unlimited capacity) |
140 | if (qCapacity == 0) { |
141 | // set the capacity to the maximum value |
142 | queueLimit = Integer.MAX_VALUE; |
143 | } |
144 | |
145 | } |
146 | |
147 | /** |
148 | * Constructs a simple priority and FIFO based waiting-queue for |
149 | * Sim-processes with unlimited capacity of the queue. |
150 | * <p> |
151 | * The usage of the generic version <code>ProcessQueue<Type></code> where |
152 | * <code>Type</code> is derived from <code>SimProcess</code> is recommended |
153 | * for type safety. Using the raw type <code>ProcessQueue</code> yields a queue |
154 | * in which any <code>SimProcess</code> can be enqueued, potentially requiring |
155 | * type casting on accessing processes enqueued. |
156 | * |
157 | * @param owner |
158 | * Model : The model this process-queue is associated to |
159 | * @param name |
160 | * java.lang.String : The process-queue's name |
161 | * @param showInReport |
162 | * boolean : Flag if process-queue should produce a report |
163 | * @param showInTrace |
164 | * boolean : Flag for process-queue to produce trace messages |
165 | */ |
166 | public ProcessQueue(Model owner, String name, boolean showInReport, |
167 | boolean showInTrace) |
168 | { |
169 | super(owner, name, showInReport, showInTrace); // create the QBased |
170 | // object |
171 | reset(); |
172 | |
173 | // make the queue with Fifo queueing discipline and unlimited capacity |
174 | _ql = new QueueListFifo<P>(); |
175 | _ql.setQueueBased(this); |
176 | |
177 | } |
178 | |
179 | /** |
180 | * Returns a process-queue-reporter to produce a report about this |
181 | * process-queue. |
182 | * |
183 | * @return desmoj.report.Reporter : The reporter for this process-queue |
184 | */ |
185 | public desmoj.core.report.Reporter createReporter() |
186 | { |
187 | |
188 | return new desmoj.core.report.ProcessQueueReporter(this); |
189 | |
190 | } |
191 | |
192 | /** |
193 | * Returns the first SimProcess queued in this process-queue or |
194 | * <code>null</code> in case the queue is empty. |
195 | * |
196 | * @return desmoj.SimProcess : The first SimProcess in the process-queue or |
197 | * <code>null</code> if the process-queue is empty |
198 | */ |
199 | public P first() { |
200 | |
201 | return _ql.first(); // straight design |
202 | |
203 | } |
204 | |
205 | /** |
206 | * Returns the first SimProcess queued in this process-queue that applies to |
207 | * the given condition. The process-queue is searched from front to end and |
208 | * the first SimProcess that returns <code>true</code> when the condition |
209 | * is applied to it is returned by this method. If no SimProcess applies to |
210 | * the given condition or the process-queue is empty, <code>null</code> |
211 | * will be returned. |
212 | * |
213 | * @return desmoj.core.SimProcess : The first process queued in this |
214 | * process-queue applying to the given condition or <code>null</code> |
215 | * @param c |
216 | * Condition : The condition that the sim-process returned must |
217 | * confirm |
218 | */ |
219 | public P first(Condition<P> c) { |
220 | |
221 | if (c == null) { |
222 | sendWarning( |
223 | "Can not return first SimProcess complying to condition!", |
224 | "ProcessQueue : " + getName() |
225 | + " Method: void first(Condition c)", |
226 | "The Condition 'c' given as parameter is a null reference!", |
227 | "Check to always have valid references when querying Queues."); |
228 | return null; // no proper parameter |
229 | } |
230 | if (_ql.isEmpty()) |
231 | return null; // nobody home to be checked |
232 | for (P tmp = _ql.first(); tmp != null; tmp = _ql.succ(tmp)) { |
233 | if (c.check(tmp)) |
234 | return tmp; |
235 | } |
236 | |
237 | // if no SimProcess complies to the condition just return null |
238 | return null; |
239 | |
240 | } |
241 | |
242 | /** |
243 | * Returns the <code>SimProcess</code> queued at the named position. |
244 | * The first position is 0, the last one size()-1. |
245 | * |
246 | * @return int :The position of the process as an <code>int</code>. |
247 | * Returns -1 if no such position exists. |
248 | */ |
249 | public int get(P p) |
250 | { |
251 | |
252 | return _ql.get(p); |
253 | |
254 | } |
255 | |
256 | /** |
257 | * Returns the <code>SimProcess</code> queued at the named position. |
258 | * The first position is 0, the last one size()-1. |
259 | * |
260 | * @return desmoj.core.SimProcess : The <code>SimProcess</code> at the position of |
261 | * <code>int</code> or <code>null</code> if no such position exists. |
262 | */ |
263 | public P get(int index) { |
264 | return _ql.get(index); |
265 | } |
266 | |
267 | /** |
268 | * Returns the underlying queue implementation, providing access to the |
269 | * QueueList implementation, e.g. to add PropertyChangeListeners. |
270 | * |
271 | * @return desmoj.core.simulator.QueueList : The underlying queue implementation of this |
272 | * ProcessQueue. |
273 | */ |
274 | public QueueList<P> getQueueList() { |
275 | |
276 | return _ql; // that's all |
277 | } |
278 | |
279 | /** |
280 | * Returns the implemented queueing discipline of the underlying queue as a |
281 | * String, so it can be displayed in the report. |
282 | * |
283 | * @return String : The String indicating the queueing discipline. |
284 | */ |
285 | public String getQueueStrategy() { |
286 | |
287 | return _ql.getAbbreviation(); // that's it |
288 | |
289 | } |
290 | |
291 | /** |
292 | * Returns the number of entities refused to be enqueued in the queue, |
293 | * because the capacity limit is reached. |
294 | * |
295 | * @return long : The number of entities refused to be enqueued in the |
296 | * queue. |
297 | */ |
298 | public long getRefused() { |
299 | |
300 | return _refused; // that's it |
301 | } |
302 | |
303 | /** |
304 | * Enters a new SimProcess into the ProcessQueue. If the capacity of the |
305 | * ProcessQueue is full, the entity will not be enqueued and |
306 | * <code>false</code> will be returned. The sim-process will be stored in |
307 | * the ProcessQueue until method <code>remove(SimProcess e)</code> is |
308 | * called with this specific SimProcess. Simprocesses are ordered according |
309 | * to their priority. Higher priorities are sorted in front of lower |
310 | * priorities. Simprocesses with same priority are orderer according to the |
311 | * strategy specified in the constructor. The first SimProcess inside the |
312 | * process-queue will always be the one with the highest priority. |
313 | * |
314 | * @return boolean : Is <code>true</code> if insertion was successful, |
315 | * <code>false</code> otherwise (i.e. capacity limit is reached). |
316 | * @param e |
317 | * desmoj.SimProcess : The sim-process to be added to the |
318 | * ProcessQueue |
319 | */ |
320 | public boolean insert(P e) { |
321 | |
322 | if (e == null) { // null returns with warning |
323 | sendWarning("Can not insert SimProcess!", "ProcessQueue : " |
324 | + getName() + " Method: boolean insert" + "(SimProcess e)", |
325 | "The sim-process given as parameter is a null reference!", |
326 | "Check to always have valid references when enqueueing " |
327 | + "Entities"); |
328 | return false; // no proper parameter |
329 | } |
330 | |
331 | if (!isModelCompatible(e)) { |
332 | sendWarning("Can not insert SimProcess!", "ProcessQueue : " |
333 | + getName() + " Method: boolean insert" + "(SimProcess e)", |
334 | "The sim-process given as parameter is not compatible to " |
335 | + "the model this process-queue belongs to!", |
336 | "Check if your submodels are allowed to mingle with other " |
337 | + "model's components."); |
338 | return false; // not of my model type!!! |
339 | } |
340 | |
341 | if (queueLimit <= length()) { |
342 | |
343 | if (currentlySendDebugNotes()) { |
344 | sendDebugNote("refuses to insert " + e.getQuotedName() |
345 | + " because the " |
346 | + "capacity limit is reached. ProcessQueue:<br>" |
347 | + _ql.toString()); |
348 | } |
349 | |
350 | if (currentlySendTraceNotes()) { |
351 | sendTraceNote("is refused to be enqueued in " |
352 | + this.getQuotedName() + "because the capacity limit (" |
353 | + getQueueLimit() + ") of this " |
354 | + "ProcessQueue is reached"); |
355 | } |
356 | |
357 | _refused++; // count the refused ones |
358 | |
359 | return false; // capacity limit is reached |
360 | } |
361 | |
362 | _ql.insert(e); // that's it |
363 | |
364 | if (currentlySendDebugNotes()) { |
365 | sendDebugNote("inserts " + e.getQuotedName() |
366 | + " in the ProcessQueue:<br>" + _ql.toString()); |
367 | } |
368 | |
369 | // produce trace output |
370 | if (currentlySendTraceNotes()) { |
371 | if (e == currentEntity() && currentEntityAll().size() == 1) { |
372 | sendTraceNote("inserts itself into " + this.getQuotedName()); |
373 | } else { |
374 | sendTraceNote("inserts " + e.getName() + " into " |
375 | + this.getQuotedName()); |
376 | } |
377 | } |
378 | |
379 | return true; |
380 | } |
381 | |
382 | /** |
383 | * Enters a new SimProcess into the process-queue and places it after the |
384 | * given SimProcess. If the capacity of the ProcessQueue is full, the entity |
385 | * will not be enqueued and <code>false</code> will be returned. Make sure |
386 | * that the sim-process given as reference is already queued inside the |
387 | * process-queue, else the sim-process will not be enqueued and |
388 | * <code>false</code> will be returned. The sim-process will be stored in |
389 | * the ProcessQueue until method <code>remove(SimProcess e)</code> is |
390 | * called with this specific SimProcess. |
391 | * |
392 | * @return boolean : Is <code>true</code> if insertion was successful, |
393 | * <code>false</code> otherwise (i.e. capacity limit is reached). |
394 | * @param e |
395 | * SimProcess : The sim-process to be added to the process-queue |
396 | * @param after |
397 | * SimProcess : The sim-process after which SimProcess 'e' is to |
398 | * be inserted |
399 | */ |
400 | public boolean insertAfter(P e, P after) { |
401 | |
402 | if (e == null) { |
403 | sendWarning( |
404 | "Can not insert SimProcess!", |
405 | "ProcessQueue : " |
406 | + getName() |
407 | + " Method: boolean insertAfter(SimProcess e, SimProcess after)", |
408 | "The sim-process -e- given as parameter is a null reference!", |
409 | "Check to always have valid references when enqueueing Entities"); |
410 | return false; // no proper parameter |
411 | } |
412 | |
413 | if (after == null) { |
414 | sendWarning( |
415 | "Can not insert SimProcess!", |
416 | "ProcessQueue : " |
417 | + getName() |
418 | + " Method: boolean insertAfter(SimProcess e, SimProcess after)", |
419 | "The sim-process -after- given as parameter is a null reference!", |
420 | "Check to always have valid references when enqueueing Entities"); |
421 | return false; // no proper parameter |
422 | } |
423 | |
424 | if (!isModelCompatible(e)) { |
425 | sendWarning( |
426 | "Can not insert SimProcess!", |
427 | "ProcessQueue : " |
428 | + getName() |
429 | + " Method: boolean insertAfter(SimProcess e, SimProcess after)", |
430 | "The sim-process given as parameter is not compatible to " |
431 | + "the model this process-queue belongs to!", |
432 | "Check if your submodels are allowed to mingle with other " |
433 | + "model's components."); |
434 | return false; // not of my model type!!! |
435 | } |
436 | |
437 | if (queueLimit <= length()) { |
438 | |
439 | if (currentlySendDebugNotes()) { |
440 | sendDebugNote("refuses to insert " + e.getQuotedName() |
441 | + " because the " |
442 | + "capacity limit is reached. ProcessQueue:<br>" |
443 | + _ql.toString()); |
444 | } |
445 | |
446 | if (currentlySendTraceNotes()) { |
447 | sendTraceNote("is refused to be enqueued in " |
448 | + this.getQuotedName() + "because the capacity limit (" |
449 | + getQueueLimit() + ") of this " |
450 | + "ProcessQueue is reached"); |
451 | } |
452 | |
453 | _refused++; // count the refused ones |
454 | |
455 | return false; // capacity limit is reached |
456 | } |
457 | |
458 | boolean successful = _ql.insertAfter(e, after); // elegantly done... |
459 | |
460 | if (currentlySendDebugNotes()) { |
461 | sendDebugNote("inserts " + e.getQuotedName() + " after " |
462 | + after.getQuotedName() + " in the ProcessQueue:<br>" |
463 | + _ql.toString()); |
464 | } |
465 | |
466 | // produce trace output |
467 | if (currentlySendTraceNotes()) { |
468 | if (e == currentEntity() && currentEntityAll().size() == 1) { |
469 | sendTraceNote("inserts itself into " + this.getQuotedName() |
470 | + " after " + after.getName()); |
471 | } else { |
472 | sendTraceNote("inserts " + e.getName() + " into " |
473 | + this.getQuotedName() + " after " + after.getName()); |
474 | } |
475 | } |
476 | |
477 | return successful; |
478 | |
479 | } |
480 | |
481 | /** |
482 | * Enters a new SimProcess into the ProcessQueue and places it in front of |
483 | * the given SimProcess. If the capacity of the ProcessQueue is full, the |
484 | * Entity will not be enqueued and <code>false</code> will be returned. |
485 | * Make sure that the sim-process given as reference is already queued inside |
486 | * the ProcessQueue, else the sim-process will not be enqueued and |
487 | * <code>false</code> will be returned. The sim-process will be stored in |
488 | * the ProcessQueue until method <code>remove(SimProcess e)</code> is |
489 | * called with this specific SimProcess. |
490 | * |
491 | * @return boolean : Is <code>true</code> if insertion was successful, |
492 | * <code>false</code> otherwise (i.e. capacity limit is reached). |
493 | * @param e |
494 | * SimProcess : The sim-process to be added to the processqQueue |
495 | * @param before |
496 | * SimProcess : The sim-process before which the sim-process 'e' is |
497 | * to be inserted |
498 | */ |
499 | public boolean insertBefore(P e, P before) { |
500 | |
501 | if (e == null) { |
502 | sendWarning( |
503 | "Can not insert SimProcess!", |
504 | "ProcessQueue : " |
505 | + getName() |
506 | + " Method: boolean insertBefore(SimProcess e, SimProcess before)", |
507 | "The sim-process -e- given as parameter is a null reference!", |
508 | "Check to always have valid references when enqueueing Entities"); |
509 | return false; // no proper parameter |
510 | } |
511 | |
512 | if (before == null) { |
513 | sendWarning( |
514 | "Can not insert SimProcess!", |
515 | "ProcessQueue : " |
516 | + getName() |
517 | + " Method: boolean insertBefore(SimProcess e, SimProcess before)", |
518 | "The sim-process -before- given as parameter is a null reference!", |
519 | "Check to always have valid references when enqueueing Entities"); |
520 | return false; // no proper parameter |
521 | } |
522 | |
523 | if (!isModelCompatible(e)) { |
524 | sendWarning( |
525 | "Can not insert SimProcess!", |
526 | "ProcessQueue : " |
527 | + getName() |
528 | + " Method: boolean insertBefore(SimProcess e, SimProcess before)", |
529 | "The sim-process given as parameter is not compatible to " |
530 | + "the model this process-queue belongs to!", |
531 | "Check if your submodels are allowed to mingle with other " |
532 | + "model's components."); |
533 | return false; // not of my model type!!! |
534 | } |
535 | |
536 | if (queueLimit <= length()) { |
537 | |
538 | if (currentlySendDebugNotes()) { |
539 | sendDebugNote("refuses to insert " + e.getQuotedName() |
540 | + " because the " |
541 | + "capacity limit is reached. ProcessQueue:<br>" |
542 | + _ql.toString()); |
543 | } |
544 | |
545 | if (currentlySendTraceNotes()) { |
546 | sendTraceNote("is refused to be enqueued in " |
547 | + this.getQuotedName() + "because the capacity limit (" |
548 | + getQueueLimit() + ") of this " |
549 | + "ProcessQueue is reached"); |
550 | } |
551 | |
552 | _refused++; // count the refused ones |
553 | |
554 | return false; // capacity limit is reached |
555 | } |
556 | |
557 | boolean successful = _ql.insertBefore(e, before); // elegantly done... |
558 | |
559 | if (currentlySendDebugNotes()) { |
560 | sendDebugNote("inserts " + e.getQuotedName() + " before " |
561 | + before.getQuotedName() + " in the ProcessQueue:<br>" |
562 | + _ql.toString()); |
563 | } |
564 | |
565 | // produce trace output |
566 | if (currentlySendTraceNotes()) { |
567 | if (e == currentEntity() && currentEntityAll().size() == 1) { |
568 | sendTraceNote("inserts itself into " + this.getQuotedName() |
569 | + " before " + before.getName()); |
570 | } else { |
571 | sendTraceNote("inserts " + e.getName() + " into " |
572 | + this.getQuotedName() + " before " + before.getName()); |
573 | } |
574 | } |
575 | |
576 | return successful; |
577 | } |
578 | |
579 | /** |
580 | * Returns a boolean value indicating if the process-queue is empty or if any |
581 | * number of SimProcess is currently enqueued in it. |
582 | * |
583 | * @return boolean : Is <code>true</code> if the process-queue is empty, |
584 | * <code>false</code> otherwise |
585 | */ |
586 | public boolean isEmpty() { |
587 | |
588 | return _ql.isEmpty(); |
589 | |
590 | } |
591 | |
592 | /** |
593 | * Returns the last SimProcess queued in this process-queue or |
594 | * <code>null</code> in case the process-queue is empty. |
595 | * |
596 | * @return desmoj.SimProcess : The last SimProcess in the process-queue or |
597 | * <code>null</code> if the process-queue is empty |
598 | */ |
599 | public P last() { |
600 | |
601 | return _ql.last(); // straight design again |
602 | |
603 | } |
604 | |
605 | /** |
606 | * Returns the last SimProcess queued in this process-queue that applies to |
607 | * the given condition. The process-queue is searched from end to front and |
608 | * the first SimProcess that returns <code>true</code> when the condition |
609 | * is applied to it is returned by this method. If no SimProcess applies to |
610 | * the given condition or the process-queue is empty, <code>null</code> |
611 | * will be returned. |
612 | * |
613 | * @return desmoj.SimProcess : The last SimProcess queued in this |
614 | * process-queue applying to the given condition or <code>null</code> |
615 | * @param c |
616 | * Condition : The condition that the sim-process returned must |
617 | * comply to |
618 | */ |
619 | public P last(Condition<P> c) { |
620 | |
621 | if (c == null) { |
622 | sendWarning( |
623 | "Can not insert SimProcess!", |
624 | "ProcessQueue : " + getName() |
625 | + " Method: SimProcess last(Condition c)", |
626 | "The Condition -c- given as parameter is a null reference!", |
627 | "Check to always have valid references when querying Queues."); |
628 | return null; // no proper parameter |
629 | } |
630 | |
631 | if (_ql.isEmpty()) |
632 | return null; // nobody home to be checked |
633 | |
634 | for (P tmp = _ql.last(); tmp != null; tmp = _ql.pred(tmp)) { |
635 | if (c.check(tmp)) |
636 | return tmp; |
637 | } |
638 | |
639 | // if no SimProcess complies to the condition just return null |
640 | return null; |
641 | |
642 | } |
643 | |
644 | /** |
645 | * Returns the sim-process enqueued directly before the given SimProcess in |
646 | * the process-queue. If the given SimProcess is not contained in this |
647 | * process-queue or is at the first position thus having no possible |
648 | * predecessor, <code>null</code> is returned. |
649 | * |
650 | * @return desmoj.SimProcess : The sim-process directly before the given |
651 | * SimProcess in the process-queue or <code>null</code>. |
652 | * @param e |
653 | * desmoj.SimProcess : An SimProcess in the process-queue |
654 | */ |
655 | public P pred(P e) { |
656 | |
657 | if (e == null) { |
658 | sendWarning( |
659 | "Can not find predecessor of SimProcess in Queue!", |
660 | "ProcessQueue : " + getName() |
661 | + " Method: SimProcess pred(SimProcess e)", |
662 | "The sim-process 'e' given as parameter is a null reference!", |
663 | "Check to always have valid references when querying for Entities"); |
664 | return null; // no proper parameter |
665 | } |
666 | |
667 | return _ql.pred(e); |
668 | |
669 | } |
670 | |
671 | /** |
672 | * Returns the sim-process enqueued before the given SimProcess in the |
673 | * process-queue that also complies to the condition given. If the given |
674 | * Sim-process is not contained in this process-queue or is at the first |
675 | * position thus having no possible predecessor, <code>null</code> is |
676 | * returned. If no other SimProcess before the given one complies to the |
677 | * condition, <code>null</code> is returned, too. |
678 | * |
679 | * @return desmoj.SimProcess : The sim-process before the given SimProcess in |
680 | * the process-queue complying to the condition or <code>null</code>. |
681 | * @param e |
682 | * SimProcess : A sim-process in the process-queue |
683 | * @param c |
684 | * Condition : The condition that the preceeding SimProcess has |
685 | * to comply to |
686 | */ |
687 | public P pred(P e, Condition<P> c) { |
688 | |
689 | if (e == null) { |
690 | sendWarning( |
691 | "Can not find predecessor of SimProcess in Queue!", |
692 | "ProcessQueue : " |
693 | + getName() |
694 | + " Method: SimProcess pred(SimProcess e, Condition c)", |
695 | "The sim-process 'e' given as parameter is a null reference!", |
696 | "Check to always have valid references when querying for Entities"); |
697 | return null; // no proper parameter |
698 | } |
699 | |
700 | if (c == null) { |
701 | sendWarning( |
702 | "Can not return previous SimProcess complying to condition!", |
703 | "ProcessQueue : " |
704 | + getName() |
705 | + " Method: SimProcess pred(SimProcess e, Condition c)", |
706 | "The Condition 'c' given as parameter is a null reference!", |
707 | "Check to always have valid references when querying Queues."); |
708 | return null; // no proper parameter |
709 | } |
710 | |
711 | for (P tmp = pred(e); tmp != null; tmp = pred(tmp)) { |
712 | if (c.check(tmp)) |
713 | return tmp; |
714 | } |
715 | |
716 | return null; // obviously not found here, empty or doesn't comply |
717 | |
718 | } |
719 | |
720 | /** |
721 | * Removes the given SimProcess from the process-queue. If the given |
722 | * Sim-process is not in the process-queue, a warning will be issued but |
723 | * nothing else will be changed. |
724 | * |
725 | * @param e |
726 | * SimProcess : The sim-process to be removed from the |
727 | * process-queue |
728 | */ |
729 | public void remove(SimProcess e) { |
730 | |
731 | if (e == null) { |
732 | sendWarning( |
733 | "Can not remove SimProcess from Queue!", |
734 | "ProcessQueue : " + getName() |
735 | + " Method: void remove(SimProcess e)", |
736 | "The sim-process 'e' given as parameter is a null reference!", |
737 | "Check to always have valid references when removing " |
738 | + "Entities"); |
739 | return; // no proper parameter |
740 | } |
741 | |
742 | if (!_ql.remove((P)e)) |
743 | { // watch out, removes SimProcess as a side |
744 | // effect!!! |
745 | sendWarning("Can not remove SimProcess from Queue!", |
746 | "ProcessQueue : " + getName() |
747 | + " Method: void remove(SimProcess e)", |
748 | "The sim-process 'e' given as parameter is not enqueued in " |
749 | + "this queue!", |
750 | "Make sure the sim-process is inside the queue you want it " |
751 | + "to be removed."); |
752 | return; // not enqueued here |
753 | } |
754 | else // done |
755 | { |
756 | |
757 | } |
758 | |
759 | if (currentlySendDebugNotes()) { |
760 | sendDebugNote("remove " + e.getQuotedName() + "<br>" |
761 | + _ql.toString()); |
762 | } |
763 | |
764 | // produce trace output |
765 | if (currentlySendTraceNotes()) { |
766 | if (e == currentEntity() && currentEntityAll().size() == 1) { |
767 | sendTraceNote("removes itself from " + this.getQuotedName()); |
768 | } else { |
769 | sendTraceNote("removes " + e.getQuotedName() + " from " |
770 | + this.getQuotedName()); |
771 | } |
772 | } |
773 | |
774 | } |
775 | |
776 | /** |
777 | * Removes the process queued at the given position. |
778 | * The first position is 0, the last one length()-1. |
779 | * |
780 | * @return : The method returns <code>true</code> if a <code>SimProcess</code> |
781 | * exists at the given position or <code>false></code> if otherwise. |
782 | */ |
783 | public boolean remove(int index) |
784 | { |
785 | if (index < 0 || index >= this.length()) return false; |
786 | |
787 | P p = get(index); |
788 | if (p == null) { |
789 | return false; |
790 | } else { |
791 | remove(p); |
792 | return true; |
793 | } |
794 | } |
795 | |
796 | /** |
797 | * Resets all statistical counters to their default values. The mininum and |
798 | * maximum length of the queue are set to the current number of queued |
799 | * objects. The counter for the entities refused to be enqueued will be |
800 | * reset. |
801 | */ |
802 | public void reset() { |
803 | |
804 | super.reset(); // reset of QueueBased |
805 | |
806 | _refused = 0; |
807 | |
808 | } |
809 | |
810 | /** |
811 | * Sets the queue capacity to a new value. Only if the new capacity is equal |
812 | * or larger than the current length of the queue! |
813 | * |
814 | * @param newCapacity |
815 | * int : The new capacity of this ProcessQueue. |
816 | */ |
817 | public void setQueueCapacity(int newCapacity) { |
818 | |
819 | // check if the new capacity is negative or larger than the current |
820 | // length |
821 | // of the queue |
822 | if (newCapacity < length() || newCapacity < 0) { |
823 | sendWarning( |
824 | "The new capacity is negative or would be smaller than the " |
825 | + "number of entities already enqueued in this ProcessQueue. The capacity " |
826 | + "will remain unchanged!", |
827 | getClass().getName() + ": " + getQuotedName() |
828 | + ", Method: " |
829 | + "void setQueueCapacity(int newCapacity)", |
830 | "The ProcessQueue already contains more entities than the new capacity " |
831 | + "could hold. What should happen to the remaining entities?", |
832 | "Make sure to change the capacity only to a non negative value larger " |
833 | + "than the current length of this ProcessQueue."); |
834 | |
835 | return; // ignore that rubbish and just return |
836 | } |
837 | |
838 | // set the capacity of the queue to the new value |
839 | queueLimit = newCapacity; |
840 | |
841 | } |
842 | |
843 | /** |
844 | * Sets the sort order of this ProcessQueue to a new value and makes this |
845 | * ProcessQueue use another <code>QueueList</code> with the specified |
846 | * queueing discipline. Please choose a constant from |
847 | * <code>QueueBased</code> (<code>QueueBased.FIFO</code>, |
848 | * <code>QueueBased.FIFO</code> or <code>QueueBased.Random</code>) |
849 | * The sort order of a ProcessQueue can only be changed if the queue is empty. |
850 | * |
851 | * @param sortOrder |
852 | * int : determines the sort order of the underlying |
853 | * <code>QueueList</code> implementation (<code>QueueBased.FIFO</code>, |
854 | * <code>QueueBased.FIFO</code> or <code>QueueBased.Random</code>) |
855 | */ |
856 | public void setQueueStrategy(int sortOrder) { |
857 | |
858 | // check if the queue is empty |
859 | if (!isEmpty()) { |
860 | sendWarning( |
861 | "The ProcessQueue for which the queueing discipline should be " |
862 | + "changed is not empty. The queueing discipline will remain unchanged!", |
863 | getClass().getName() + ": " + getQuotedName() |
864 | + ", Method: " |
865 | + "void setQueueStrategy(int sortOrder)", |
866 | "The ProcessQueue already contains some processes ordered according a " |
867 | + "certain order.", |
868 | "Make sure to change the sort order only for an empty ProcessQueue."); |
869 | |
870 | return; // ignore that rubbish and just return |
871 | } |
872 | |
873 | // determine the queueing strategy |
874 | switch (sortOrder) { |
875 | case QueueBased.FIFO : |
876 | _ql = new QueueListFifo<P>(); break; |
877 | case QueueBased.LIFO : |
878 | _ql = new QueueListLifo<P>(); break; |
879 | case QueueBased.RANDOM : |
880 | _ql = new QueueListRandom<P>(); break; |
881 | default : |
882 | sendWarning( |
883 | "The given sortOrder parameter is negative or too big! " |
884 | + "The sort order of the ProcessQueue will remain unchanged!", |
885 | getClass().getName() + ": " + getQuotedName() |
886 | + ", Method: " |
887 | + "void setQueueStrategy(int sortOrder)", |
888 | "A valid positive integer number must be provided to " |
889 | + "determine the sort order of the queue.", |
890 | "Make sure to provide a valid positive integer number " |
891 | + "by using the constants in the class QueueBased, like " |
892 | + "QueueBased.FIFO, QueueBased.LIFO or QueueBased.RANDOM."); |
893 | return; |
894 | } |
895 | _ql.setQueueBased(this); |
896 | |
897 | } |
898 | |
899 | /** |
900 | * Sets the number of entities refused to be enqueued in the queue because |
901 | * the capacity limit is reached to a new value. |
902 | * |
903 | * @param n |
904 | * long : the new number of entities refused to be enqueued in |
905 | * the queue because the capacity limit is reached. |
906 | */ |
907 | public void setRefused(long n) { |
908 | // check if n is negative |
909 | if (n < 0) { |
910 | sendWarning( |
911 | "Attempt to set the number of entities refused to enqueue in " |
912 | + "the ProcessQueue to a negative value. The attempted action " |
913 | + "is ignored!", "ProcessQueue : " + getName() |
914 | + " Method: void setRefused(long n)", |
915 | "The number given as parameter n is negative! That makes no " |
916 | + "sense!", |
917 | "Make sure to provide only positive numbers as parameter n."); |
918 | return; |
919 | } |
920 | |
921 | this._refused = n; // save the new value |
922 | |
923 | } |
924 | |
925 | /** |
926 | * Returns the sim-process enqueued directly after the given SimProcess in |
927 | * the process-queue. If the given SimProcess is not contained in this |
928 | * process-queue or is at the last position thus having no possible |
929 | * successor, <code>null</code> is returned. |
930 | * |
931 | * @return desmoj.SimProcess : The sim-process directly after the given |
932 | * SimProcess in the ProcessQueue or <code>null</code> |
933 | * @param e |
934 | * desmoj.SimProcess : A sim-process in the process-queue |
935 | */ |
936 | public P succ(P e) { |
937 | |
938 | if (e == null) { |
939 | sendWarning( |
940 | "Can not find successor of SimProcess in Queue!", |
941 | "ProcessQueue : " + getName() |
942 | + " Method: SimProcess succ(SimProcess e)", |
943 | "The sim-process 'e' given as parameter is a null reference!", |
944 | "Check to always have valid references when querying for " |
945 | + "Entities"); |
946 | return null; // no proper parameter |
947 | } |
948 | |
949 | return _ql.succ(e); |
950 | |
951 | } |
952 | |
953 | /** |
954 | * Returns the sim-process enqueued after the given SimProcess in the |
955 | * process-queue that also complies to the condition given. If the given |
956 | * Sim-process is not contained in this process-queue or is at the last |
957 | * position thus having no possible successor, <code>null</code> is |
958 | * returned. If no other SimProcess after the given one complies to the |
959 | * condition, <code>null</code> is returned, too. |
960 | * |
961 | * @return desmoj.SimProcess : The sim-process after the given SimProcess in |
962 | * the process-queue complying to the condition or <code>null</code>. |
963 | * @param e |
964 | * SimProcess : A sim-process in the process-queue |
965 | * @param c |
966 | * Condition : The condition that the succeeding SimProcess has |
967 | * to comply to |
968 | */ |
969 | public P succ(P e, Condition<P> c) { |
970 | |
971 | if (e == null) { |
972 | sendWarning( |
973 | "Can not find predecessor of SimProcess in Queue!", |
974 | "ProcessQueue : " |
975 | + getName() |
976 | + " Method: SimProcess succ(SimProcess e, Condition c)", |
977 | "The sim-process 'e' given as parameter is a null reference!", |
978 | "Check to always have valid references when querying for Entities"); |
979 | return null; // no proper parameter |
980 | } |
981 | |
982 | if (c == null) { |
983 | sendWarning( |
984 | "Can not return previous SimProcess complying to condition!", |
985 | "ProcessQueue : " |
986 | + getName() |
987 | + " Method: SimProcess succ(SimProcess e, Condition c)", |
988 | "The Condition 'c' given as parameter is a null reference!", |
989 | "Check to always have valid references when querying Queues."); |
990 | return null; // no proper parameter |
991 | } |
992 | |
993 | for (P tmp = succ(e); tmp != null; tmp = succ(tmp)) { |
994 | if (c.check(tmp)) |
995 | return tmp; |
996 | } |
997 | |
998 | return null; // obviously not found here, empty or doesn't comply |
999 | |
1000 | } |
1001 | |
1002 | /** |
1003 | * Returns an iterator over the processes enqueued. |
1004 | * |
1005 | * @return java.lang.Iterator<P> : An iterator over the processes enqueued. |
1006 | */ |
1007 | public Iterator<P> iterator() { |
1008 | return new ProcessQueueIterator(this); |
1009 | } |
1010 | |
1011 | /** |
1012 | * Private queue iterator, e.g. required for processing all queue elements in a |
1013 | * for-each-loop. |
1014 | */ |
1015 | private class ProcessQueueIterator implements Iterator<P> { |
1016 | |
1017 | ProcessQueue<P> clientQ; |
1018 | P next, lastReturned; |
1019 | |
1020 | public ProcessQueueIterator(ProcessQueue<P> clientQ) { |
1021 | this.clientQ = clientQ; |
1022 | next = clientQ.first(); |
1023 | lastReturned = null; |
1024 | } |
1025 | public boolean hasNext() { |
1026 | return next != null; |
1027 | } |
1028 | public P next() { |
1029 | lastReturned = next; |
1030 | next = clientQ.succ(next); |
1031 | return lastReturned; |
1032 | } |
1033 | public void remove() { |
1034 | clientQ.remove(lastReturned); |
1035 | } |
1036 | } |
1037 | } |