/*
* `gnu.iou'
* Copyright (C) 2006 John Pritchard.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package gnu.iou;
import java.util.Enumeration;
import java.util.NoSuchElementException;
/**
* A linked- list queue (or stack).
*
* @author John Pritchard (john@syntelos.org)
*/
public class queue extends Object implements Enumeration, Cloneable {
/**
* List node can be subclassed, eg, for a soft queue. Such a
* `queue' subclass needs to set the queue type in its
* constructors using the equivalent of
* <pre>
* q_t = new Enqueued().getClass();
* </pre>
*
* <p> The `Enqueued' methods `get', `reinit', `cloneEnqueued',
* `copy' and `contains' need to be defined in an `Enqueued'
* subclass that employs an object intermediary like a
* SoftReference. Refer to the java sourcecode for this class for
* more information.
*
* @author John Pritchard */
public static class Enqueued extends Object implements Cloneable {
protected Object o = null;
protected Enqueued n = null;
public Enqueued(){
super();
}
public Object get(){
return o;
}
public final Object get(int idx){
if (0 > idx)
return null;
else if (0 == idx)
return this.get();
else if (null != this.n)
return this.n.get(idx-1);
else
return null;
}
public void free(){
o = null;
n = null;
}
public void clear(){
if ( null != n) n.clear();
o = null;
n = null;
}
public boolean contains( Object obj){
if ( o == obj)
return true;
else if ( null != n)
return n.contains(obj);
else
return false;
}
public void reinit( Object obj){
o = obj;
n = null;
}
public String toString(){
return super.toString()+"["+o+"]";
}
public Enqueued cloneEnqueued(){
try {
Enqueued enq = (Enqueued)super.clone();
if ( this.o instanceof copy)
enq.o = ((copy)this.o).copy();
if ( null != n)
enq.n = this.n.cloneEnqueued();
return enq;
}
catch ( CloneNotSupportedException cnx){
return null;
}
}
/**
* Clone, dropping without remainer of list
*/
public Enqueued copy(){
try {
Enqueued enq = (Enqueued)super.clone();
if ( this.o instanceof copy)
enq.o = ((copy)this.o).copy();
enq.n = null;
return enq;
}
catch ( CloneNotSupportedException cnx){
return null;
}
}
}
public final static Class DefaultEnqueuedClass = Enqueued.class;
///////queue////
private volatile Enqueued list = null;
/**
* Queue type set by <tt>`this()'</tt> constructor and replaceable
* by subclasses' constructors. */
protected Class q_t ;
private lck pushpoplock = new lck();
/**
* By default it's a queue (FIFO), but with this on, it's a stack
* (LIFO). This is used exclusively to determined this behavior
* in the "push" method. */
private boolean stack = false;
/**
* Recycle bin
*/
private Enqueued free_q = null;
/**
* Create a new FIFO queue (not LIFO stack).
*/
public queue(){
super();
q_t = DefaultEnqueuedClass;
}
/**
* @param use_stack If true, this is a stack (LIFO), otherwise
* it's a queue (FIFO). */
public queue( boolean use_stack){
this();
stack = use_stack;
}
public lck locker(){ return pushpoplock;}
public void clear(){
if ( null != list){
list.clear();
list = null;
}
if ( null != free_q){
free_q.clear();
free_q = null;
}
}
public boolean contains ( Object obj){
if ( null == list)
return false;
else
return list.contains(obj);
}
/*
* Synchronizing push and pop prevents popping an object twice
* concurrently, and tends to improve the execution patterns of
* user code. */
/*
* User interface for stack or queue "push", depending on constructor.
*/
// public void push( Object o){
// push(this,o,stack);
// }
/**
* User interface for stack or queue "push", depending on constructor.
*
* @param usr The queue user is the object with a field for the
* queue. For <tt>`lck.LckDesc'</tt>. For example, locks used by
* the <tt>`Context'</tt> or its fields use the <tt>`Context'</tt>
* (instance) object for their <tt>`usr'</tt>.
*
* @param o Object for queue/stack
*
* @see lck#LckDesc */
public void push( Object usr, Object o){
push(usr,o,stack);
}
/**
* User interface for stack or queue, depending on constructor,
* with override option for FIFO or LIFO behavior in stack or
* queue.
*
* <p> This interface should be used only when needing to change
* the behavior of the stack or queue, for exampe using an
* enumeration or queue with a "pushback" of the last popped
* element. Otherwise use the regular <code>`push(Object)'</code>
* method.
*
* @param usr The queue user is the object with a field for the
* queue. For <tt>`lck.LckDesc'</tt>. For example, locks used by
* the <tt>`Context'</tt> or its fields use the <tt>`Context'</tt>
* (instance) object for their <tt>`usr'</tt>.
*
* @param o Object to push
*
* @param use_stack_fifo If true uses stack behavior (FIFO),
* otherwise false uses queue behavior (LIFO).
*
* @see lck#LckDesc */
public void push( Object usr, Object o, boolean use_stack_fifo){
if ( null == o) return;
try {
pushpoplock.serialize(usr);
if ( null == list){
list = free_pop(o);
return;
}
else {
if ((stack && use_stack_fifo)||((!stack) && use_stack_fifo)){
// FIFO
Enqueued q = list;
list = free_pop(o);
list.n = q;
return;
}
else {
// LIFO
for ( Enqueued q = list; q != null; q = q.n){
if ( null == q.n){
q.n = free_pop(o);
return;
}
}
}
}
}
finally {
pushpoplock.unlock(usr);
}
}
/**
* Look at the next "pop" element without popping it (removing it)
* from the queue/stack.
*
* @param usr The queue user is the object with a field for the
* queue. For <tt>`lck.LckDesc'</tt>. For example, locks used by
* the <tt>`Context'</tt> or its fields use the <tt>`Context'</tt>
* (instance) object for their <tt>`usr'</tt>.
*
* @see lck#LckDesc
*/
public Object peek( Object usr){
if ( null != list){
while ( null == list.get()) // support for softq
pop(usr);
return list.get();
}
else
return null;
}
/**
* Look at the next "pop" element without popping it (removing it)
* from the queue/stack.
*
* @param usr The queue user is the object with a field for the
* queue. For <tt>`lck.LckDesc'</tt>. For example, locks used by
* the <tt>`Context'</tt> or its fields use the <tt>`Context'</tt>
* (instance) object for their <tt>`usr'</tt>.
*
* @param idx Index from top to bottom counting from zero
*
* @see lck#LckDesc
*/
public Object peek( Object usr, int idx){
if ( null != list){
while ( null == list.get()) // support for softq
pop(usr);
return list.get(idx);
}
else
return null;
}
/**
* Return and remove the next element from the queue or stack.
*
* @param usr The queue user is the object with a field for the
* queue. For <tt>`lck.LckDesc'</tt>. For example, locks used by
* the <tt>`Context'</tt> or its fields use the <tt>`Context'</tt>
* (instance) object for their <tt>`usr'</tt>.
*
* @see lck#LckDesc
*/
public Object pop( Object usr){
if ( null == list)
return null;
try {
pushpoplock.serialize(usr);
Enqueued q; Object o;
while (true){
q = list;
if ( null == q)
return null;
else {
list = q.n;
o = q.get(); // support for softq
free_push(q);
if ( null != o)
return o;
}
}
}
finally {
pushpoplock.unlock(usr);
}
}
public boolean isEmpty( Object usr){ return (null == peek(usr));}
public boolean isNotEmpty( Object usr){ return (null != peek(usr));}
/**
* @param usr The queue user is the object with a field for the
* queue. For <tt>`lck.LckDesc'</tt>. For example, locks used by
* the <tt>`Context'</tt> or its fields use the <tt>`Context'</tt>
* (instance) object for their <tt>`usr'</tt>.
*
* @see lck#LckDesc */
public int size( Object usr){
if ( null == peek(usr))
return 0;
else {
int many = 0;
for ( Enqueued q = list; null != q; q = q.n){
if ( null != q.get())
many++;
}
return many;
}
}
/**
* @param usr The queue user is the object with a field for the
* queue. For <tt>`lck.LckDesc'</tt>. For example, locks used by
* the <tt>`Context'</tt> or its fields use the <tt>`Context'</tt>
* (instance) object for their <tt>`usr'</tt>.
*
* @param obj Target to remove
*
* @return Target when found and removed, otherwise null.
*
* @see lck#LckDesc */
public Object remove ( Object usr, Object obj){
if ( null == peek(usr))
return null;
else {
for ( Enqueued q = list, p = null; null != q; p = q, q = q.n){
if ( obj == q.get()){
if ( null == p)
list = q.n;
else
p.n = q.n;
free_push(q);
return obj;
}
}
return null;
}
}
/**
* Size greater than one.
*
* <p> This can be used for maintaining a default element at the
* bottom of the stack without counting the whole stack.
*
* <p> If a peek reveals the default element, and the stack size is
* not greater than one, then the stack position is at this bottom
* default element which would not be popped.
*
* <p> A <tt>`queue'</tt> subclass should redefine this
* <tt>`Enqueued'</tt> optimized method for its requirements. */
public boolean size_gt_1(){
return (null != list && null != list.n);
}
/**
* Pop all elements into a string. Concatenate all enqueued
* elements into a string, each separated by a space (0x20). */
public String popall_String(Object usr){
return popall_String(usr,' ');
}
public String toString(){
chbuf strbuf = new chbuf();
if (stack)
strbuf.append("stack");
else
strbuf.append("queue");
int many = 0;
Object obj;
for ( Enqueued q = list; null != q; q = q.n){
obj = q.get();
if ( null != obj){
if ( 0 == many)
strbuf.append('[');
else
strbuf.append(',');
many += 1;
strbuf.append(obj);
}
}
if ( 0 < many)
strbuf.append(']');
return strbuf.toString();
}
public boolean equals ( Object ano){
if ( ano == this)
return true;
else if ( ano instanceof queue)
return toString().equals(ano.toString());
else
return false;
}
public queue cloneQueue(){
try {
queue clone = (queue)super.clone();
clone.pushpoplock = new lck();
lck.LckDesc( clone, chbuf.cat("QUEUE #",Integer.toString(System.identityHashCode(clone))));
if ( null != list)
clone.list = list.cloneEnqueued();
clone.free_q = null;
return clone;
}
catch ( CloneNotSupportedException cnx){
return null;
}
}
/**
* Clone without depth of queue -- with only the top element of
* the queue/ stack.
*/
public queue copy(){
try {
queue clone = (queue)super.clone();
clone.pushpoplock = new lck();
lck.LckDesc( clone, chbuf.cat("QUEUE #",Integer.toString(System.identityHashCode(clone))));
if ( null != list)
clone.list = list.copy();
clone.free_q = null;
return clone;
}
catch ( CloneNotSupportedException cnx){
return null;
}
}
/**
* Pop all elements into a string. Concatenate all enqueued
* elements into a string, each separated by the argument
* character. Uses the java Object 'toString' method. */
public String popall_String( Object usr, char sep){
if (null == list) return null;
chbuf strbuf = new chbuf();
Object o ;
while ( null != (o = pop(usr))){
strbuf.append(o);
if ( null != list)
strbuf.append(sep);
}
return strbuf.toString();
}
private void free_push ( Enqueued eo){
eo.free();
if ( null == free_q)
free_q = eo;
else {
eo.n = free_q;
free_q = eo;
}
}
private Enqueued free_pop ( Object o){
Enqueued f = free_q;
if ( null == f){
try {
f = (Enqueued)q_t.newInstance();
}
catch ( InstantiationException insx){insx.printStackTrace();}
catch ( IllegalAccessException ilacx){ilacx.printStackTrace();}
f.reinit(o); // null ptr exc, if q_t can't instantiate
return f;
}
else {
free_q = f.n;
f.reinit(o);
return f;
}
}
/**
* Returns true if the enumeration contains more elements; false
* if its empty.
*
* @see java.util.Enumeration
*/
public boolean hasMoreElements(){
return isEmpty(this);
}
/**
* Returns the next element of the enumeration. Calls to this
* method will enumerate successive elements.
*
* @exception NoSuchElementException If no more elements exist.
*
* @see java.util.Enumeration
*/
public Object nextElement(){
if (isEmpty(this))
throw new NoSuchElementException();
else
return pop(this);
}
public Enumeration elements(){
return this.cloneQueue();
}
}