Files
gzz-mirror/Modules/nile/Nile1Text.java

357 lines
11 KiB
Java
Raw Permalink Normal View History

2026-09-14 20:19:29 -04:00
/*
Nile1Text.java
*
* Copyright (c) 2000, 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
*/
package org.gzigzag.module;
import org.gzigzag.*;
import java.util.*;
/** A library of routines for operating on streams with Nile1 text model.
* @see Nile1
* @deprecated Use Nile2Unit
*/
public abstract class Nile1Text {
public static final String rcsid = "$Id: Nile1Text.java,v 1.9 2000/12/07 00:37:19 tjl Exp $";
public static boolean dbg = true;
static void p(String s) { if(dbg) System.out.println(s); }
static void pa(String s) { System.out.println(s); }
// Stream.Iterator iter = Stream.getIterator(start, "d.nile");
static public int START = 1;
static public int END = -1;
static public int ANY = 0;
static boolean isWordBreak(Stream.Iterator iter, int edge) {
char cur = iter.getChar();
if(cur == -1) return true;
if(!iter.move(-1)) return true;
char prev = iter.getChar();
iter.move(1);
return isWordBreak(prev, cur, edge);
}
static boolean isWordBreak(char prev, char cur, int edge) {
boolean ret = isWordBreak0(prev, cur, edge);
p("IsWordBreak: '"+prev+"' '"+cur+"' "+edge+ " RETURNING: "+ret);
return ret;
}
static boolean isWordBreak0(char prev, char cur, int edge) {
boolean curlet = Character.isLetterOrDigit(cur);
boolean curws = Character.isWhitespace(cur);
boolean prevlet = Character.isLetterOrDigit(prev);
boolean prevws = Character.isWhitespace(prev);
if(!curlet && !curws) {
if(edge != END) return true; // punctuation -> break start
return prevlet || !prevws; // punct -> br end if prev ltr or punct
}
if(prevlet && curlet) return false;
if(prevlet && curws) return edge != START;
// --> prevlet == false
if(!prevws) { // previous was punctuation ->
if(edge != START) return true;
return curlet; // Starting edge only if this is a letter.
}
if(prevws && curws) return false;
if(prevws && curlet) return edge != END;
// shouldn't get here
throw new ZZError("isWordBreak logic error!");
}
/** Find the nsteps:th next wordbreak.
* Always alters iter.
* <p>
* Characters are split into three categories:
* letter-or-digit, whitespace and other.
* whitespace is always a word boundary (but there is no
* word boundary between two whitespace characters).
* The characters in the other category are words by themselves.
* So basically there is a word break at character c, if
* <ul>
* <li> c is the first character in the stream.
* <li> c is one beyond the last character in the stream
* <li> c is punctuation
* <li> c is of a different category (see above) than
* the character just before it.
* </ul>
* @param nsteps The number of steps, a positive or negative number.
* @param edge Which edge of the word do we want: START, END or ANY.
*/
static public boolean wordEdge(Stream.Iterator iter,
boolean includeThis, int nsteps,
int edge) {
if(nsteps == 0) throw new ZZError("Can't step 0 words");
int dir = (nsteps > 0 ? 1 : -1);
for(; nsteps != 0; nsteps -= dir) {
if(!wordEdge0(iter, includeThis, dir, edge))
return false;
includeThis = false;
}
return true;
}
static private boolean wordEdge0(Stream.Iterator iter,
boolean includeThis, int dir,
int edge) {
char prev = 0;
char cur = iter.getChar();
if(dir > 0) {
if(includeThis) {
if(!iter.move(-dir)) return true;
prev = iter.getChar();
iter.move(dir);
if(isWordBreak(prev, cur, edge)) return true;
}
while(iter.move(dir)) {
prev = cur;
cur = iter.getChar();
if(cur == -1) return true; // Ran into end of text.
if(isWordBreak(prev, cur, edge)) return true;
}
return (edge != START);
} else {
if(!includeThis) {
if(!iter.move(dir)) return false;
cur = iter.getChar();
}
while(iter.move(dir)) {
prev = cur;
cur = iter.getChar();
if(isWordBreak(cur, prev, edge)) {
iter.move(-dir);
return true;
}
}
return (edge != END);
}
}
static public boolean sentenceEdge(Stream.Iterator iter,
boolean includeThis, int nsteps, int edge) {
if(nsteps == 0) throw new ZZError("Can't step 0 words");
int dir = (nsteps > 0 ? 1 : -1);
for(; nsteps != 0; nsteps -= dir) {
if(!sentenceEdge0(iter, includeThis, dir, edge))
return false;
includeThis = false;
}
return true;
}
static public boolean sentenceEdge0(Stream.Iterator iter,
boolean includeThis, int dir, int edge) {
if(edge == 0)
throw new ZZError("Looking for either sentence edge not implemented");
if(edge == START) {
// If we cannot move, we're at the beginning.
if(!iter.move(-1))
if(dir < 0 || includeThis) return includeThis;
// Back out
iter.move(1);
// Back up two, one by one, and then search non-inclusive.
iter.move(-1);
iter.move(-1);
if(iter.find(dir, includeThis, " ")) {
iter.move(2);
return true;
} else {
// There is a start in the beginning, but not at the end.
iter.end(dir);
return (dir < 0);
}
} else {
if(!iter.move(1)) return includeThis;
iter.move(-1);
// Here we just find because end is at the found text
if(iter.find(dir, includeThis, " "))
return true;
else {
iter.end(dir);
// There is an end in the end, but not at the beginning.
return (dir > 0);
}
}
}
static boolean ws(char c) { return Character.isWhitespace(c); }
static boolean punct(char c) {
return (!Character.isWhitespace(c)) && (!Character.isLetterOrDigit(c));
}
static boolean lett(char c) { return Character.isLetterOrDigit(c); }
/** Adjust the locations to cut text from.
* Basically, cut one or two spaces after the latter location,
* and if none, and there is a single space preceding,
* cut it along.
* <p>
* @param leaveout An output parameter: the values in the two-member
* array are set to the number of characters
* to leave out of the cut text.
* An important meaning is given to negative
* values: a negative value means "add this many
* spaces".
*/
static public void adjustCut(Stream.Iterator iter1,
Stream.Iterator iter2,
int[] leaveout) {
leaveout[0] = leaveout[1] = 0;
// First off, do not adjust if one of them is not word end.
if(!isWordBreak(iter1, START) || !(isWordBreak(iter2, END))) return;
char c1 = iter2.getChar(0);
char c2 = iter2.getChar(1);
// If there are one or two spaces afterwards, cut them along.
if(ws(c1)) {
iter2.move(1);
if(ws(c2))
iter2.move(1);
} else {
// If there is one character before...
char b1 = iter1.getChar(-1);
char b2 = iter1.getChar(-2);
if(ws(b1) && !ws(b2)) {
// This is a tricky situation. What we have here is
// cutting off in
// word WORD, word
// without the comma.
// The space before WORD needs to be removed.
// This is what the leaveout means.
//
// Additionally, for pasting at the correct boundary
// to work right, we need to add a space *after* this
// word when pasting. See adjustPaste.
iter1.move(-1);
leaveout[0] = 1;
}
}
}
/** The adjustment to a paste operation.
* @param adjust Analogour to the leaveout parameter of
* adjustCut. Positive values mean
* leaving this many characters out
* and negative values mean
* adding that many spaces.
*/
static public void adjustPaste(Stream.Iterator to,
Stream.Iterator start,
Stream.Iterator end,
int[] adjust) {
adjust[0] = adjust[1] = 0;
if(!isWordBreak(to, START)) return;
// If to is at a letter-punct edge, make sure
// pasted text starts with space and does not end with one.
// Otherwise, make sure it ends with space but doesn't start
// with it.
char c1 = to.getChar(-1);
char c2 = to.getChar(0);
if(lett(c1) && punct(c2)) {
// Now, start but don't end with space.
if(!ws(start.getChar(0))) adjust[0] = -1;
if(ws(end.getChar(-1))) adjust[1] = 1;
} else {
if(ws(start.getChar(0))) adjust[0] = 1;
if(!ws(end.getChar(-1))) adjust[1] = -1;
}
}
static public boolean paragraphEdge(Stream.Iterator iter,
boolean includeThis, int nsteps, int edge) {
if(nsteps == 0) throw new ZZError("Can't step 0 paragraphs");
int dir = (nsteps > 0 ? 1 : -1);
for(; nsteps != 0; nsteps -= dir) {
if(!paragraphEdge0(iter, includeThis, dir, edge))
return false;
includeThis = false;
}
return true;
}
static public boolean paragraphEdge0(Stream.Iterator iter,
boolean includeThis, int dir, int edge) {
ZZCell c = iter.get().get();
int offs = iter.get().getOffs();
boolean beg = (offs == 0);
boolean end = (offs == c.getText().length());
if(edge == 0)
throw new ZZError("Can't look for paragraph end / begin");
// XXX Should we set the end edge to point to negative
// direction?
// if(edge == START) {
if(dir > 0) {
if(!beg || !includeThis) c = c.s("d.nile");
} else {
if(beg && !includeThis) c = c.s("d.nile", -1);
}
// Now we can go on, using includeThis always.
if(c == null) return false;
c = Nile1Ops.findStruct(c, dir, true);
if(c != null) {
iter.set(new ZZCursorVirtual(c, 0));
if(edge == START)
iter.move(0, 1);
else
iter.move(0, -1);
return true;
} else {
if(edge == END && dir > 0) {
iter.end(); return true;
}
return false;
}
// }
}
public interface Edger {
boolean edge(Stream.Iterator iter, boolean includeThis, int nsteps,
int whichEdge);
}
static public class WordEdger implements Edger {
public boolean edge(Stream.Iterator iter, boolean includeThis,
int nsteps, int whichEdge) {
return wordEdge(iter, includeThis, nsteps, whichEdge);
}
}
static public class SentenceEdger implements Edger {
public boolean edge(Stream.Iterator iter, boolean includeThis,
int nsteps, int whichEdge) {
return sentenceEdge(iter, includeThis, nsteps, whichEdge);
}
}
static public class ParagraphEdger implements Edger {
public boolean edge(Stream.Iterator iter, boolean includeThis,
int nsteps, int whichEdge) {
return paragraphEdge(iter, includeThis, nsteps, whichEdge);
}
}
}