/* ZZCell.java * * Copyright (c) 1999-2001, Ted Nelson and Tuomas Lukka * * You may use and distribute under the terms of either the GNU Lesser * General Public License, either version 2 of the license or, * at your choice, any later version. Alternatively, you may use and * distribute under the terms of the XPL. * * See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of * the licenses. * * This software 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 README * file for more details. * */ /* * Written by Tuomas Lukka */ /* * Well, he'd been in enough cells. There were ways to handle these * things. The important thing was to be direct. He got up and banged * on the bars until the warder sauntered along the corridor. * `Yes, mate?' * `I just want to get things sorted out,' said Rincewind. `It's * not as though I've got time to waste, okay?' * `Yep?' * `Is there any chance that you're going to fall asleep * in a chair opposite this cell with your keys fully exposed on a table * in front of you?' * - Terry Pratchett, The Last Continent, p.267 */ package org.gzigzag; import java.util.*; /** An abstract class to represent a ZigZag cell. *
* There is no unique cell to Java object mapping, so there may be * several equivalent Java objects representing the same cell. * Use the equals method for comparisons. */ public abstract class ZZCell { public static final String rcsid = "$Id: ZZCell.java,v 1.76 2001/02/08 11:18:39 ajk Exp $"; /** Obtain the unique ID for this cell. * Note that this method may throw an exception * if the space this cell is in does not support cell IDs. */ public String getID() { throw new NullPointerException("This cell type has no IDs!"); } /** Create a new cell connected to this cell. * @param dim Dimension to create the cell along * @param dir The direction, either positive or negative */ public final ZZCell N(String dim, int dir) { return N(dim, dir, null, 0); } public final ZZCell N(String dim) { return N(dim, 1, null, 0); } /** Get a new cell associated with this cell (ie.same slice) */ public abstract ZZCell N(); /** Get or create a new cell connected to this cell. * A convenience function which first tries to use getNeighbour * and if that returns null, creates a new neighbour. * @param dim Dimension to create the cell along * @param dir The direction, either positive or negative * @param obs The observer */ public ZZCell getOrNewCell(String dim, int dir, ZZObs o) { ZZCell r; if((r = s(dim, dir, o)) != null) return r; return N(dim, dir, o); } public ZZCell getOrNewCell(String dim, int dir) { return getOrNewCell(dim, dir, null); } public ZZCell getOrNewCell(String dim) { return getOrNewCell(dim, 1, null); } /** Create a new cell connected to this cell and attach an observer * to it * atomically. * @param dim Dimension to create the cell along * @param dir The direction, either positive or negative * @param obs The observer */ public final ZZCell N(String dim, int dir, ZZObs o) { return N(dim, dir, o, 0); } /** Create a new cell connected to this cell * to it * atomically. * @param dim Dimension to create the cell along * @param dir The direction, either positive or negative * @param flags The flags to guide the type of the new cell */ public final ZZCell N(String dim, int dir, long flags) { return N(dim, dir, null, flags); } /** Create a new cell connected to this cell and attach an observer * to the connection * atomically. *
* NOTE: this routine MUST insert the cell, not just blindly
* delete the former connection.
* @param dim Dimension to create the cell along
* @param dir The direction, either positive or negative
* @param obs The observer
* @param flags The flags to guide the type of the new cell
*/
public abstract ZZCell N(String dim, int dir, ZZObs o, long flags);
/** Delete this cell. */
public abstract void delete();
/** Delete this cell from a rank along a dimension. */
public void excise(String dim) {
synchronized(getSpace()) {
ZZCell cm = s(dim, -1);
ZZCell cp = s(dim, 1);
if(cm!=null && cp!=null) {
cm.disconnect(dim, 1);
cp.disconnect(dim, -1);
cm.connect(dim, cp);
}
else if(cm!=null)
cm.disconnect(dim, 1);
else if(cp!=null)
cp.disconnect(dim, -1);
}
}
/** Connect this cell to another along a dimension.
* If there is a former connection,
* a ZZConnectWouldBreakError is thrown.
* @param dim Dimension to connect along
* @param to Cell to connect to. This cell will end up poswards from us.
*/
public abstract void connect(String dim, ZZCell to);
/** Connect this cell to another along a dimension in a specified
* direction.
* If there is a former connection,
* a ZZConnectWouldBreakError is thrown.
* @param dim Dimension to connect along
* @param dir The direction, either positive or negative
* @param to Cell to connect to.
*/
public void connect(String dim, int dir, ZZCell to) {
if(dir > 0) {
connect(dim, to);
} else {
to.connect(dim, this);
}
}
/** Insert a cell next to this one in the rank.
* Both connections of the what cell along the specified dimension are
* deleted before inserting it.
* @param dim Dimension to connect along
* @param dir The direction, either positive or negative
* @param what Cell to insert
*/
public abstract void insert(String dim, int dir, ZZCell what);
/** Disconnect a connection from this cell.
* @param dim Dimension to disconnect along
* @param dir The direction, either positive or negative
*/
public abstract void disconnect(String dim, int dir);
/** Set the text contained in the cell.
* @param text The new text
*/
public abstract void setText(String text);
/** Set the text contained in the cell to a pointer.
* @param text The new text, as a span pointing to a stable scroll
*/
public abstract void setSpan(Span text);
/** Get a reference to the ZZSpace object describing the whole space */
public abstract ZZSpace getSpace();
/** Find the neighbour of this cell along a dimension
* attach an observer to this relation
* atomically.
* @param dim Dimension to go along
* @param dir The direction, either positive or negative
* @param obs The observer
* @return The neighbour, or null if none.
*/
public abstract ZZCell s(String dim, int dir, ZZObs o);
/** Find the neighbour of this cell along a dimension.
* atomically.
* @param dim Dimension to go along
* @param dir The direction, either positive or negative
* @return The neighbour, or null if none.
*/
public ZZCell s(String dim, int dir) {
return s(dim,dir,null);
}
/** Find the next cell along a dimension.
* Always moves poswards.
* @param dim Dimension to go along
* @return The neighbour, or null if none.
*/
public ZZCell s(String dim) {
return s(dim,1,null);
}
/** Find the headcell of a rank.
* XXX The default implementation does not handle circular ranks correctly!
* @param dim Dimension to go along
* @param dir The direction, either positive or negative
* @return The headcell
*/
public ZZCell h(String dim, int dir) {
return h(dim, dir, false, null);
}
public ZZCell h(String dim, int dir, ZZObs o) {
return h(dim, dir, false, o);
}
public ZZCell h(String dim, boolean ensuremove) {
return h(dim, -1, ensuremove, null);
}
public ZZCell h(String dim, int dir, boolean ensuremove) {
return h(dim, dir, ensuremove, null);
}
/** Find the headcell of a rank.
* The headcell is the most negative cell so this call is equivalent
* to h(dim, -1).
*/
public ZZCell h(String dim) {
return h(dim, -1, false, null);
}
/** Find the headcell of a rank.
* XXX The default implementation does not handle circular ranks correctly!
* HOWEVER, all code must be prepared for this function to return a cell
* that is just some cell in a looping rank. This means that a while loop
* looking for null going poswards from the headcell might be infinite
* and needs the user to explicitly detect loops.
* Just in case of synch problems, it is recommended that the user check
* both for equality to the headcell and use a
* LoopDetector, in case the loop changed.
* @param dim Dimension to go along
* @param dir The direction, either positive or negative
* @return The headcell
*/
public abstract ZZCell h(String dim, int dir, boolean ensuremove,
ZZObs o);
/** Count the length of the rank.
* @param dim Dimension to go along
* @return length of the rank.
*/
public int getRankLength(String dim) {
ZZCell cur = this;
ZZCell next;
int i = 0;
while((next=cur.s(dim,1))!=null) {
if(next == this)
throw new ZZInfiniteLoop("CIRCULAR HEAD");
cur = next;
i++;
}
cur = this;
while((next=cur.s(dim,-1))!=null) {
if(next == this)
throw new ZZInfiniteLoop("CIRCULAR HEAD");
cur = next;
i++;
}
return i+1;
}
/** Find the next intersection of two ranks.
* XXX The default implementation does not handle circular ranks correctly!
* @param dim Dimension to go along from this cell
* @param dir The direction, either positive or negative, from this cell
* @param cell2 The other cell
* @param dim2 Dimension to go along from the other cell
* @param dir2 The direction, either positive or negative, from the other cell
*/
public ZZCell intersect(String dim, int dir, ZZCell cell2, String dim2, int dir2) {
synchronized(getSpace()) {
// If we knew how to find the shorter one...
Hashtable h = new Hashtable();
ZZCell cur = this.s(dim,dir);
while(cur!=null) {
h.put(cur,cur);
cur = cur.s(dim,dir);
}
cur = cell2.s(dim2,dir2);
while(cur!=null) {
if(h.get(cur) != null) {
return cur;
}
cur = cur.s(dim2,dir2);
}
return null;
}
}
/** Find all intersections of two ranks.
* XXX The default implementation does not handle circular ranks correctly!
* @param dim Dimension to go along from this cell
* @param dir The direction, either positive or negative, from this cell
* @param cell2 The other cell
* @param dim2 Dimension to go along from the other cell
* @param dir2 The direction, either positive or negative, from the other cell
*/
public ZZCell[] intersectAll(String dim, int dir, ZZCell cell2, String dim2, int dir2) {
synchronized(getSpace()) {
// If we knew how to find the shorter one...
Hashtable h = new Hashtable();
ZZCell cur = this.s(dim,dir);
while(cur!=null) {
h.put(cur,cur);
cur = cur.s(dim,dir);
}
Vector v = new Vector();
cur = cell2.s(dim2,dir2);
while(cur!=null) {
if(h.get(cur) != null) {
v.addElement(cur);
}
cur = cur.s(dim2,dir2);
}
ZZCell[] r = new ZZCell[v.size()];
for(int i=0; i
* Note that not all spaces support transient cells so this is
* only a hint in that context. In getFlags, the meaning is real.
* @deprecated Usefulness being evaluated
*/
long FLAG_TRANSIENT = 0x00000001;
/** A flag meaning that the text stored in this cell is
* an address.
* The method getText() will automatically
* return the text stored in the cell, whether it is just text
* or a span of text from a permascroll.
* If you want the span, use
* getSpan().
* For users, a much better way to tell whether a cell is a span is
* simply '53' (Text in cell).
*/
public String toString() {
return "'"+getID() + "' (" + getText() + ")";
}
/** Enumerate a (semi)rank.
* @param dim The dimension along which the (semi)rank is to be enumerated.
* @return An enumeration of all cells starting from this cell
* along dim in positive direction.
*/
public Enumeration enumRank(String dim) {
return enumRank(dim, 1);
}
/** Enumerate a (semi)rank.
* @param dim The dimension along which the (semi)rank is to be enumerated.
* @param dir The direction along dim to enumerate.
* @return An enumeration of all cells starting from this cell
* along dim in direction dir.
*/
public Enumeration enumRank(String dim, int dir) {
class RankEnumeration implements Enumeration {
ZZCell c;
String dimension;
int direction;
public RankEnumeration(ZZCell c, String dimension, int direction) {
this.c = c;
this.dimension = dimension;
this.direction = direction;
}
public boolean hasMoreElements() {
return c != null;
}
public Object nextElement() {
if (!hasMoreElements()) throw new NoSuchElementException("");
ZZCell r = c;
c = c.s(dimension, direction);
return r;
}
};
return new RankEnumeration(this, dim, dir);
}
// Deprecated, old versions of the calls
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell newCell(String dim, int dir, long flags) {
return N(dim, dir, flags);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell newCell(String dim, int dir, ZZObs o) {
return N(dim, dir, o);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell newCell(String dim, int dir) {
return N(dim, dir);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell newCell() {
return N();
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getNeighbour(String dim, int dir, ZZObs o) {
return s(dim, dir, o);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getNeighbour(String dim, int dir) {
return s(dim, dir);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getSteps(String dim, int dir, ZZObs o) {
return s(dim, dir, o);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getSteps(String dim, int dir) {
return s(dim, dir);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getHeadcell(String dim, int dir, boolean ensuremove,
ZZObs o) {
return h(dim, dir, ensuremove, o);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getHeadcell(String dim, int dir) {
return h(dim, dir);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getHeadcell(String dim, int dir, ZZObs o) {
return h(dim, dir, o);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getHeadcell(String dim, int dir, boolean ensuremove) {
return h(dim, dir, ensuremove);
}
/** Deprecated.
* @deprecated Use the new one-letter functions instead.
*/
public final ZZCell getHeadcell(String dim) {
return h(dim);
}
// Deprecated stuff.
/** A flag passed to newCell meaning that the cell should not be stored
* persistently. This may enable some optimizations and
* save some persistent and/or temporary memory space.
* For example, cells that track which cells are shown in a view
* through a raster should probably be transient.
* c.getSpan() != null
.
* @deprecated Usefulness being evaluated
*/
long FLAG_ADDRESS = 0x00000002;
/** A flag meaning that the current cell is used only as a label.
* XXX Think about it a bit more - which operations shall it affect?
* @deprecated Usefulness being evaluated
*/
long FLAG_LABEL = 0x00000004;
/** Get the flags associated with this cell.
* @deprecated Usefulness being evaluated
*/
public long getFlags() { return 0; }
/** Set the flags associated with this cell.
* @deprecated Usefulness being evaluated
*/
public void setFlags( long s, long u ) { }
}