Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jclarion.clarion.ClarionQueue Maven / Gradle / Ivy
/**
* Copyright 2010, by Andrew Barnham
*
* The contents of this file are subject to
* GNU Lesser General Public License (LGPL), v.3
* http://www.gnu.org/licenses/lgpl.txt
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
*/
package org.jclarion.clarion;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Iterator;
import org.jclarion.clarion.memory.CMem;
import org.jclarion.clarion.runtime.CErrorImpl;
import org.jclarion.clarion.runtime.SimpleStringDecoder;
import org.jclarion.clarion.runtime.ref.RefVariable;
import org.jclarion.clarion.ClarionQueueEvent.EventType;
import org.jclarion.clarion.constants.*;
/**
* Model a queue
*
* A queue is like an Array collection in Java. Queues have following properties
*
* public fields define what is actually being stored on the queue. Works sortof
* like files in Clarion in that there current record which may not represent what
* is actually stored, and what is currently stored.
*
* Queues have what is known as sort orders. Records are sorted and you can
* quickly retrieve values on pre-gen sort orders.
*
* The active sort order defines the default ordering of records in a queue.
* i.e. record #1, record #2, record #3 retrievals may indicate a specific ordering
*
* One thing I am not clear on with respect to how clarion queues work is
* the fact that a queue is either list or sorted - how clarion intermixes
* commands of a list nature vs a sort nature. Will assume that lists are
* allowed to fall into disorder in event of any operation that changes
* ordering of elements in active sort order
*
*
* @author barney
*
*/
public class ClarionQueue extends ClarionGroup implements ClarionQueueReader
{
/**
* Helper to construct an order object
*
* @return
*/
public DefaultOrder ORDER() {
return new DefaultOrder(this);
}
protected static class QueueEntry implements Comparable
{
private Object value;
public QueueEntry() { }
public void copyFromGroup(GroupEntry base)
{
value=base.value;
if (value!=null) {
if (value instanceof ClarionCloneable) {
value=((ClarionCloneable)value).clarionClone();
return;
}
}
}
public void copyToGroup(GroupEntry base)
{
if (base.value!=null) {
if (base.value.getClass()!=value.getClass()) {
CMem sos = CMem.create();
((ClarionMemoryModel)value).serialize(sos);
((ClarionMemoryModel)base.value).deserialize(sos);
return;
}
if (base.value instanceof ClarionAny) {
((ClarionAny)base.value).setReferenceValue((ClarionAny)value);
return;
}
if (base.value instanceof ClarionCloneable) {
((ClarionCloneable)base.value).setValue(value);
return;
}
}
}
@SuppressWarnings("unchecked")
@Override
public int compareTo(QueueEntry qe) {
Object v=value;
Object v2=qe.value;
if (v!=null && v instanceof RefVariable>) {
v=((RefVariable>)v).get();
}
if (v2!=null && v2 instanceof RefVariable>) {
v2=((RefVariable>)v2).get();
}
if (v==null && v2==null) {
return 0;
}
if (v==null) return -1;
if (v2==null) return 1;
if (v instanceof ClarionObject) {
return ((ClarionObject) v).compareTo(v2);
}
if (v instanceof Comparable) {
return ((Comparable) v).compareTo(v2);
}
return v.toString().compareTo(v2.toString());
}
}
/**
* Model parameter ordering
*
* @author barney
*
*/
public static abstract class Order implements Comparator
{
public abstract boolean equalsPartial(Object o);
public abstract int getFieldCount();
public abstract void clear(Record r,int pos);
}
public static abstract class FunctionOrder extends Order
{
public FunctionOrder()
{
}
@Override
public boolean equalsPartial(Object o) {
return equals(o);
}
@Override
public int compare(Record o1, Record o2) {
return compare(toGroup(o1),toGroup(o2));
}
public ClarionGroup toGroup(Record o)
{
ClarionGroup g= new ClarionGroup();
int count=0;
for (QueueEntry e : o.fields ) {
count++;
g.addVariable("f"+count,e.value);
}
return g;
}
public abstract int compare(ClarionGroup cg1,ClarionGroup cg2);
@Override
public void clear(Record r, int pos) {
}
@Override
public int getFieldCount() {
return 0;
}
}
public static class DefaultOrder extends Order
{
private ArrayList order=new ArrayList();
private ClarionQueue base;
public DefaultOrder(ClarionQueue base)
{
this.base=base;
}
public DefaultOrder(ClarionQueue base,String s) {
this.base=base;
s=s.trim();
SimpleStringDecoder decoder=new SimpleStringDecoder(s);
while ( !decoder.end() ) {
int dir=1;
if (decoder.pop('+')) {
dir=1;
} else if (decoder.pop('-')) {
dir=-1;
}
String field = decoder.popString(',');
if (field.length()>0) {
int pos =0;
pos = field.indexOf(":");
if (pos==-1) {
pos = field.indexOf(".");
}
int p =base.getGroupParamPosition(field.substring(pos+1));
if (p==0) throw new IllegalStateException("Unknown sort field:"+field);
order.add(p*dir);
}
decoder.pop(',');
}
}
public DefaultOrder ascend(ClarionObject object)
{
if (object==null) throw new RuntimeException("Looking for null");
int r = base.flatWhere(object);
if (r==0) {
throw new RuntimeException("Sort field indeterminite");
}
order.add(r);
return this;
}
public DefaultOrder descend(ClarionObject object)
{
int r = base.flatWhere(object);
if (r==0) {
throw new RuntimeException("Sort field indeterminite");
}
order.add(-r);
return this;
}
private int cacheHash=-1;
public int hashCode() {
if (cacheHash==-1) {
cacheHash=order.hashCode();
}
return cacheHash;
}
public boolean equalsPartial(Object o) {
if (!(o instanceof DefaultOrder)) return false;
DefaultOrder oo = (DefaultOrder)o;
if (order.size()>oo.order.size()) return false;
for (int scan=0;scan0) {
diff=o1.fields[pos-1].compareTo(o2.fields[pos-1]);
} else {
diff=o2.fields[(-pos)-1].compareTo(o1.fields[(-pos)-1]);
}
if (diff!=0) return diff;
}
if (o1.id==-1 || o2.id==-1) return 0;
return o1.id-o2.id;
}
@Override
public void clear(Record r, int pos) {
if (pos>=order.size()) return;
int i = order.get(pos);
int cv=-1;
if (i<0) {
i=-i;
cv=1;
}
Object o = r.fields[i-1].value;
if (o!=null && o instanceof ClarionCloneable) {
((ClarionCloneable)o).clear(cv);
}
}
@Override
public int getFieldCount() {
return order.size();
}
}
public static class SpecialRecord extends Record
{
public Object anchor;
}
public static class Record
{
public int id;
public QueueEntry[] fields;
public int pos=-1;
public int posCalculated;
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object o) {
return ((Record)o).id==id;
}
}
private static class QueueData
{
private int lastMutate=0;
private List indexedSort=null;
private TreeSet activeSort=null;
private Map> sorts=null;
private Record current;
private int currentPos;
private int lastID;
private Object anchor;
}
private QueueData data;
public ClarionQueue()
{
data=new QueueData();
}
private List getList()
{
if (data.activeSort!=null && data.indexedSort==null) {
data.indexedSort=new ArrayList(data.activeSort);
}
if (data.indexedSort==null) {
data.indexedSort=new ArrayList();
}
return data.indexedSort;
}
private GroupEntry groupEntryCache[];
private void initCache()
{
if (groupEntryCache==null) {
groupEntryCache=new GroupEntry[map.size()];
map.values().toArray(groupEntryCache);
}
}
private Record createRecord()
{
return createRecord(++data.lastID);
}
public void setNextAnchor(Object anchor)
{
this.data.anchor=anchor;
}
private Record createRecord(int id)
{
initCache();
Record r;
if (data.anchor!=null) {
SpecialRecord sr = new SpecialRecord();
sr.anchor=this.data.anchor;
this.data.anchor=null;
r=sr;
} else {
r = new Record();
}
r.id=id;
r.fields=new QueueEntry[groupEntryCache.length];
for (int scan=0;scan s : data.sorts.values()) {
s.add(data.current);
}
}
data.lastMutate++;
}
CErrorImpl.getInstance().clearError();
notifyChange(new ClarionQueueEvent(this,
ClarionQueueEvent.EventType.ADD,data.currentPos));
}
/**
* Add record into queue - at specified position
* @param order
*/
public void add(ClarionNumber position)
{
add(position.intValue());
}
public void add(int position)
{
if (position<1) position=1;
synchronized(data) {
data.current = createRecord();
if (position-1<=getList().size()) {
getList().add(position-1,data.current);
data.currentPos=position;
} else {
getList().add(data.current);
data.currentPos=getList().size();
}
if (data.sorts!=null) {
for (TreeSet s : data.sorts.values()) {
s.add(data.current);
}
}
data.lastMutate++;
CErrorImpl.getInstance().clearError();
}
notifyChange(new ClarionQueueEvent(this,
ClarionQueueEvent.EventType.ADD,data.currentPos));
}
public void add(ClarionObject order)
{
if (order instanceof ClarionString) {
add(order.toString());
} else {
add(order.getNumber());
}
}
public void get(ClarionObject order)
{
if (order instanceof ClarionString) {
get(order.getString());
} else {
get(order.intValue());
}
}
/**
* Add new record into queue, at the specified position.
* If record already exists at a given position push it 'down'
*
* If position indicated is invalid, then insert at end
*
* @param order
*/
public void add(ClarionString order)
{
add(order.toString());
}
/**
* Add record into sorted list at specified list location.
*
* String is comma separated with leading '+' or '-' to indicate
* either ascending or descending order
*
* @param order
*/
public void add(String order)
{
add(new DefaultOrder(this,order));
}
private TreeSet getSortOrder(Order order)
{
TreeSet s;
if (this.data.sorts==null) {
this.data.sorts=new HashMap>();
}
s=data.sorts.get(order);
if (s==null) {
for (Map.Entry> entry : this.data.sorts.entrySet()) {
if (order.equalsPartial(entry.getKey())) {
s=entry.getValue();
break;
}
}
}
if (s==null) {
s=new TreeSet(order);
if (data.activeSort!=null) {
s.addAll(data.activeSort);
} else if (this.data.indexedSort!=null) {
s.addAll(data.indexedSort);
}
data.sorts.put(order,s);
}
return s;
}
private void changeActiveSortOrder(Order order)
{
TreeSet s=getSortOrder(order);
if (this.data.activeSort!=s) {
data.indexedSort=null;
this.data.activeSort=s;
data.lastMutate++;
} else {
data.indexedSort=null;
}
}
/**
* Add record into sorted list at specified list location.
*
*/
public void add(Order order)
{
synchronized(data) {
changeActiveSortOrder(order);
this.data.indexedSort=null;
data.current = createRecord();
data.currentPos=-1;
for (TreeSet s : data.sorts.values()) {
s.add(data.current);
}
}
CErrorImpl.getInstance().clearError();
notifyChange(new ClarionQueueEvent(this,
ClarionQueueEvent.EventType.ADD,data.currentPos));
}
/**
* Put record back into queue in last position as specified by
* prior get(int) or add().
*
* If last get or add operation was sort based - then put does not
* bother trying to insert in specific position
*
* If there are sort orders - reinsert record back into sort, taking
* into consideration possible change of sort location
*/
public void put()
{
put(true);
}
public void put(boolean keepIndexedSort)
{
if (data.current==null) {
return;
}
synchronized(data) {
if (data.sorts!=null) {
// check if record differs significantly enough from any
// sorting system to merit re-organisation
boolean disorder=false;
Record r = createRecord(data.current.id);
for ( TreeSet sort : data.sorts.values() ) {
if (sort.contains(r)) continue;
SortedSet ss;
ss = sort.headSet(data.current);
if (!ss.isEmpty()) {
Record prior = ss.last();
if (sort.comparator().compare(prior,r)>0) {
disorder=true;
break;
}
}
ss = sort.tailSet(data.current);
Iterator tscan=ss.iterator();
tscan.next();
if (tscan.hasNext()) {
Record next = tscan.next();
if (sort.comparator().compare(next,r)<0) {
disorder=true;
break;
}
}
}
if (disorder) {
data.lastMutate++;
for ( TreeSet sort : data.sorts.values() ) {
sort.remove(data.current);
}
storeRecord(data.current);
for ( TreeSet sort : data.sorts.values() ) {
sort.add(data.current);
}
if (!keepIndexedSort) {
data.indexedSort=null;
data.currentPos=-1;
}
} else {
storeRecord(data.current);
}
} else {
storeRecord(data.current);
}
}
notifyChange(new ClarionQueueEvent(this,
ClarionQueueEvent.EventType.PUT,data.currentPos));
}
/**
* Put record back into queue - using specified sort order
*/
public void put(Order order)
{
changeActiveSortOrder(order);
put(false);
}
/**
* Delete last retrieved record
*/
public void delete()
{
synchronized(data) {
if (data.current==null) {
CErrorImpl.getInstance().setError(Constants.NOENTRYERR, "Cannot Delete - no record");
return;
}
if (data.sorts!=null) {
for ( TreeSet sort : data.sorts.values() ) {
sort.remove(data.current);
}
}
if (data.indexedSort!=null) {
if (data.currentPos>0) {
data.indexedSort.remove(data.currentPos-1);
} else {
data.indexedSort.remove(data.current);
}
}
data.current=null;
data.lastMutate++;
}
notifyChange(new ClarionQueueEvent(this,
ClarionQueueEvent.EventType.DELETE,data.currentPos));
}
/**
* Remove all records
*/
public void free()
{
synchronized(data) {
this.data.activeSort=null;
this.data.current=null;
this.data.currentPos=-1;
this.data.indexedSort=null;
this.data.lastID=0;
this.data.sorts=null;
this.data.lastMutate++;
}
notifyChange(new ClarionQueueEvent(this,
ClarionQueueEvent.EventType.FREE,data.currentPos));
}
/**
* Set active sort order to the specified order
*
* @param order
*/
public void sort(Order order)
{
synchronized(data) {
changeActiveSortOrder(order);
}
notifyChange(new ClarionQueueEvent(this,
ClarionQueueEvent.EventType.SORT,data.currentPos));
}
public void sort(ClarionString order)
{
synchronized(data) {
changeActiveSortOrder(new DefaultOrder(this,order.toString()));
}
notifyChange(new ClarionQueueEvent(this,
ClarionQueueEvent.EventType.SORT,data.currentPos));
}
/**
* Return number of records in the queue
*
* @return
*/
public int records()
{
synchronized(data) {
if (this.data.indexedSort!=null) {
return this.data.indexedSort.size();
}
if (this.data.activeSort!=null) {
return this.data.activeSort.size();
}
}
return 0;
}
@Override
public ClarionObject getValueAt(int row, int column) {
synchronized(data) {
if (row<=0 || row>records()) return null;
Record r = getList().get(row-1);
if (r==null) return null;
return (ClarionObject)r.fields[column-1].value;
}
}
public ClarionObject[] getRecord(int pos)
{
synchronized(data) {
if (pos<=0 || pos>records()) return null;
Record r = getList().get(pos-1);
if (r==null) return null;
ClarionObject result[]=new ClarionObject[r.fields.length];
for (int scan=0;scanrecords()) {
CErrorImpl.getInstance().setError(Constants.NOENTRYERR,"Position out of range");
return;
}
Record r = getList().get(pos-1);
if (r==null) {
CErrorImpl.getInstance().setError(Constants.NOENTRYERR,"Record not found");
return;
}
restoreRecord(r);
data.current=r;
data.currentPos=pos;
}
CErrorImpl.getInstance().clearError();
}
/**
* Get specified record
*/
public void get(ClarionNumber pos)
{
get(pos.intValue());
}
/**
* Get first matching specified record based on order setting
* If no record is found - call to position() will yield pointer to
* where record should of been found had there been one
*/
public void get(ClarionString order)
{
get(new DefaultOrder(this,order.toString()));
}
public void get(Order order)
{
synchronized(data) {
TreeSet s=getSortOrder(order);
Order base = (Order)s.comparator();
Record scan = createRecord(0);
for (int c=order.getFieldCount();c tail = s.tailSet(scan);
if (s==data.activeSort) {
data.currentPos=records()+1-tail.size();
} else {
data.currentPos=-1;
}
data.current=null;
if (tail.isEmpty()) {
data.currentPos=records()+1;
CErrorImpl.getInstance().setError(30,"Record not found");
return;
}
Record candidate = tail.first();
scan.id=-1;
if (order.compare(candidate,scan)==0) {
CErrorImpl.getInstance().setError(0,"Record Found");
data.current=candidate;
restoreRecord(data.current);
} else {
CErrorImpl.getInstance().setError(30,"Record not found");
}
}
}
/**
* Not entirely sure what this does... Clarion documentation is very
* confusing
*
* Returns position in active sort order based on all fields. If no exact
* match then return next position.
*
* I am choosing to interprete this as thus:
* + if there is no active sort order (unsorted) then return getPointer()
* + if there is active sort order then return equivalent of get(order);getPointer()
* but do not explicitly call those -i.e. do not mess with buffer
*
* @return
*/
public int getPosition()
{
synchronized(data) {
if (this.data.activeSort==null) return getPointer();
Record scan = createRecord(0);
SortedSet tail = data.activeSort.tailSet(scan);
return records()+1-tail.size();
}
}
/**
* Return current position from 1 to n for last GET, ADD or PUT operation
*
* @return
*/
public int getPointer()
{
synchronized(data) {
if (data.currentPos==-1 && data.current!=null) {
data.currentPos=getPosition(data.current);
}
return data.currentPos;
}
}
public int getSortCount() {
if (data.sorts==null) return 0;
return data.sorts.size();
}
private List> listeners = new
ArrayList>();
/**
* Listen for changes to the objects representation. Note that listener
* is weakly referenced. You need to maintain a strong reference to the
* listener object in order for it not to be garbage collected
*
* @param cmcl
*/
public void addListener(ClarionQueueListener cmcl)
{
synchronized(listeners) {
listeners.add(new WeakReference(cmcl));
}
}
/**
* Stop Listening for changes to the objects representation
*
* @param cmcl
*/
public void removeListener(ClarionQueueListener cmcl)
{
synchronized(listeners) {
Iterator> scan;
scan = listeners.iterator();
while (scan.hasNext()) {
WeakReference val = scan.next();
ClarionQueueListener sval = val.get();
if (sval==cmcl || sval==null) {
scan.remove();
}
}
}
}
private void notifyChange(ClarionQueueEvent event) {
List notify = new ArrayList();
synchronized(listeners) {
Iterator> scan;
scan = listeners.iterator();
while (scan.hasNext()) {
WeakReference val = scan.next();
ClarionQueueListener sval = val.get();
if (sval==null) {
scan.remove();
} else {
notify.add(sval);
}
}
}
for ( ClarionQueueListener scan : notify ) {
scan.queueModified(event);
}
}
public String debugString()
{
StringBuilder out=new StringBuilder();
Iterable recs=null;
if (data.indexedSort!=null) {
recs =data.indexedSort;
} else {
recs =data.activeSort;
}
if (recs==null) return "";
for (Record r : recs) {
QueueEntry qe[] = r.fields;
for (int scan=0;scan0) out.append(',');
out.append(qe[scan].value);
if (qe[scan].value instanceof ClarionObject) {
ClarionObject o= (ClarionObject)qe[scan].value;
o=o.getValue();
out.append("(");
out.append("N:"+o.getName());
out.append(" G:"+o.getOwner());
out.append(")");
}
}
out.append('\n');
}
return out.toString();
}
private int getDepth(int row,int pos)
{
Record r = getList().get(row-1);
return Math.abs((((ClarionObject)r.fields[pos-1].value).intValue()));
}
@Override
public void toggle(int row, int column) {
synchronized(data) {
if (row<=0 || row>records()-1) return;
Record r = getList().get(row-1);
((ClarionObject)r.fields[column-1].value).setValue(
((ClarionObject)r.fields[column-1].value).negate());
}
notifyChange(new ClarionQueueEvent(this,EventType.PUT,row));
}
@Override
public int getQueueColumn(int col) {
return col;
}
@Override
public int getQueueRow(int row) {
return row;
}
public void doSetValueAt(int row, int column,ClarionObject value) {
synchronized(data) {
if (row<=0 || row>records()-1) return;
Record r = getList().get(row-1);
((ClarionObject)r.fields[column-1].value).setValue(value);
}
//notifyChange(new ClarionQueueEvent(this,EventType.PUT,row));
}
public boolean hasChildren(int row,int pos)
{
synchronized(data) {
if (row<=0 || row>records()-1) return false;
return getDepth(row,pos)records()) return null;
int depth=getDepth(row,pos);
boolean result[] = new boolean[depth];
while (true) {
row++;
if (row>records()) break;
int s_depth=getDepth(row,pos);
if (s_depth==depth) {
if (depth<=result.length) {
if (depth>0) result[depth-1]=true;
}
}
if (s_depth0) result[depth-1]=true;
}
}
return result;
}
}
public boolean hasSibling(int row,int pos)
{
synchronized(data) {
if (row<=0 || row>records()-1) return false;
int depth=getDepth(row,pos);
while (true) {
row++;
if (row>records()) return false;
int s_depth=getDepth(row,pos);
if (s_depth==depth) return true;
if (s_depth clazz) {
ClarionMemoryModel n =super.castTo(clazz);
if (n instanceof ClarionQueue) {
((ClarionQueue)n).data=this.data;
}
return n;
}
}