Files
gzz-mirror/Java/Main.java
2026-09-14 20:19:29 -04:00

536 lines
22 KiB
Java

/*
Main.java
*
* Copyright (c) 1999, 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, version control by Tuukka Hastrup,
* space locking by Antti-Juhani Kaijanaho.
*/
package org.gzigzag;
import org.gzigzag.*;
import java.awt.*;
import java.util.*;
import java.io.*;
import java.lang.reflect.*;
/** The class to run to start the ZZ client.
* This class parses the command line, prepares the space and starts the
* client. If needed, a new client space with default settings is created.
*
* The one obligatory argument to give this program is the name of the file
* to use for the cellscroll of the main space.
* Additionally, the following options can be given.
* <dl>
* <dt> <b>-new</b>
* <dd> Recreate the default client space starting from
* the main cell. This is important if the internal
* client structures used by GZigZag have changed since
* the space was created, or if this is a new space.
* If the cellscroll file given does not exist,
* this is the default.
* If the file already exists, the old system list
* and the list on d.2 down from the home cell
* are placed on the d.2 list, at a cell named
* OldSysList (see ZZDefaultSpace).
* <dt> <b>-convert</b>
* <dd> Convert an old, incompatible, space to compatible form.
* When conversion is needed, it will be told by the
* program. Taking backup before conversion is
* recommended. After conversion you might need to use
* <b>new</b> and <b>dcold</b> to catch up with
* configuration changes.
* <dt> <b>-import</b> XMLfile
* <dd> When creating a new space, you can import an XML
* file describing the space.
* <dt> <b>-export</b> XMLfile
* <dd> Export the whole space to the specified file
* <dt> <b>-recrmdim</b>
<dd> Recreate master dimension list from data on disk.
* <dt> <b>-dbg</b> classname
* <dd> Turn on debugging in the given class. For developers
* only.
* <dt> <b>-pda</b>
* <dd> Run in PDA-mode.
* <dt> <b>-span</b>
* <dd> Save information about cells whose spans overlap.
* This may be slow at the moment, but is necessary
* to show e.g. the overlaps of two nile text streams.
* <dt> <b>-ztpserv PORT</b>
* <dd> Invoke the ZTP server code instead of the ZZ client code.
* Listen on the TCP port PORT for incoming connections.
* <dt> <b>-dump</b>
* <dd> Dump the space to the file CELLSCROLLFILE.dmp (where
* CELLSCROLLFILE is the name of the space file).
*
* <dt> <b>-slurp FILENAME</b>
* <dd> Slurp the space from the file called FILENAME.
* <dt> <b>-versioneddims</b>
* <dd> Show connections from old versions along regular dimensions
* in addition to the special version: dimensions.
*
* </dl>
* <p>
* Some Filenames that start with ':' are special. For example,
* ':DS' starts with the ZZDimSpace instead of normal persistent space.
* You should probably only use these if you are hacking the core.
* <p>
* Example:
* <pre>
* java foo.Main -new /tmp/foo
* </pre>
* opens the cellscroll at /tmp/foo and creates the new client structure.
* XXX Make a good system for specifying the type.
* <p>
* Currently, if you give extra arguments after the file name, the system
* creates a ZZSlicedDimSpace instead.
* BE VERY CAREFUL WITH THIS! Once you put some slices there with a given
* root space, never leave them out or confusion will result.
* This feature should NOT be used if you're not prepared to hack the
* storage/*Slice* code.
*/
public class Main {
public static final String rcsid = "$Id: Main.java,v 1.70 2001/03/29 13:06:07 ajk Exp $";
public static final boolean dbg = true;
public static int zzclientversion = 2;
static final void p(String s) { if(dbg) System.out.println(s); }
static final void pa(String s) { System.out.println(s); }
/** For storing the classes whose debug prints are turned on.
* Otherwise they might get garbage collected and debugging not
* stay turned on.
*/
static Vector dbgclasses = new Vector();
/** Make this class print debug info.
* Any callers from outside Main must provide their own Vector
* to put the class objects in in order to avoid garbage collection.
*/
public static void debugClass(String name, Vector into) {
try {
p("Turning on debugging for class "+name);
Class clazz = Class.forName("org.gzigzag."+name);
Field f = clazz.getField("dbg");
f.setBoolean(null, true);
into.addElement(clazz);
into.addElement(f);
} catch(Exception e) {
e.printStackTrace();
pa(""+e);
throw new Error("Error while turning on debug info");
}
}
public static void main(String argv[]) {
pa("Starting gZigZag client, build: "+BuildInfo.info);
pa("Clientspace version used by program is "+zzclientversion+".");
try {
Class primact = Class.forName("org.gzigzag.ZZPrimitiveActions");
// *** Parse command line
if(argv.length < 1) {
System.err.println("No filename given");
SafeExit.exit(1);
}
CommandLineOptionsParser clop = new CommandLineOptionsParser();
String[] noargs = new String[0];
clop.put("pda", noargs, "run in PDA mode");
clop.put("new", noargs,
"Recreate the default client space "
+ "starting from the main cell. This "
+ "is important if the internal client "
+ "structures used by GZigZag have changed "
+ "since the space was created, or if this "
+ "is a new space. If the cellscroll file "
+ "given does not exist, this is the default. "
+ "If the file already exists, the old system "
+ "list and the list on d.2 down from the home "
+ "cell are placed on the d.2 list, at a cell "
+ "named OldSysList (see ZZDefaultSpace).");
clop.put("import", new String[] { "XMLFILE" },
"When creating a new space, you can import an XML "
+ "file describing the space.");
clop.put("export", new String[] { "XMLFILE" },
"Export the whole space to the specified file");
clop.put("convert", noargs,
"Convert an old, incompatible, space to compatible "
+ "form. When conversion is needed, it will be "
+ "told by the program. Taking backup before "
+ "conversion is recommended. After conversion you "
+ "might need to use -new and -dcold to catch up "
+ "with configuration changes.");
clop.put("xmlencoding", new String[] { "ENCODING" },
"Specify the character encoding used by the "
+ "XML export function.");
clop.put("ztpserv", new String[] { "PORT" },
"Invoke the ZTP server code instead of the ZZ "
+ "client code. Listen on the TCP port PORT for "
+ "incoming connections.");
clop.put("recrmdim", noargs,
"Recreate master dimension list from data on disk.");
clop.put("foolconvert", noargs, "???");
clop.put("textlog", noargs, "???");
clop.put("verboselog", noargs, "???");
clop.put("span", noargs, "Save information about cells whose "
+ "spans overlap. This may be slow at the moment, "
+ "but is necessary to show e.g. the overlaps of "
+ "two nile text streams.");
clop.put("dump", noargs, "Dump the space to the file "
+ "CELLSCROLLFILE.dmp (where CELLSCROLLFILE is the "
+ "name of the space file).");
clop.put("slurp", new String[] { "FILENAME" },
"Slurp the space from the file called FILENAME.");
clop.put("versioneddims", noargs,
"Show connections from old versions along regular "
+ "dimensions in addition to the special version: "
+ "dimensions.");
clop.put(new CommandLineOptionsParser.Option("dbg", new String[] { "CLASSNAME" },
"Turn on debugging "
+ "in the given "
+ "class. For "
+ "developers only.") {
public void action(CommandLineOptionsParser clops,
String[] args) {
debugClass(args[0], dbgclasses);
}
});
clop.putHelp("help", "shows this help screen", true,
"Usage:\nGZigZag takes one mandatory parameter: the name "
+ "of the directory where the space is (to be) stored. "
+ "Additionally, one or more of the following options "
+ "may be specified before the directory name.",
"");
int cur = clop.parse(argv);
// Since local classes cannot modify these, we have to do
// this ugly thing...
boolean newspace=clop.seen("new"); // New client space?
boolean textlog=clop.seen("textlog");
boolean verboselog=clop.seen("verboselog");
boolean pda=clop.seen("pda");
boolean span=clop.seen("span"); // Use a SpanSet to find overlapping spans?
boolean ztpserv=clop.seen("ztpserv");
boolean dump=clop.seen("dump");
String slurpf=clop.getArg("slurp", 0);
int ztpport=Integer.parseInt(clop.getArg("ztpserv", 0, "0"));
boolean recreateMasterDimList=clop.seen("recrmdim");
boolean convert=clop.seen("convert"); // Convert client space version?
boolean foolconvert=clop.seen("foolconvert");
String importFile=clop.getArg("import", 0, ""); // If nonempty, it'll be imported
String exportFile=clop.getArg("export", 0, ""); // If nonempty, we'll export
String encoding = clop.getArg("xmlencoding", 0, "UTF-8"); // for exporting
clop = null; // we no longer need it, throw it away
if ( argv[cur].startsWith("-") ){
// we don't want filenames to look like args
System.err.println("Unknown arg "+argv[cur]);
cur++;
//SafeExit.exit(1);
}
if(argv.length < cur+1) {
System.err.println("No filename given");
SafeExit.exit(1);
}
if(textlog) {
ZZPrimitiveActions.textlog =
new PrintWriter(new FileOutputStream(
argv[cur] + ".txt", true));
}
if(verboselog) {
ZZLogger.logfile =
new PrintWriter(new FileOutputStream(argv[cur] + ".log", true));
}
// *** Prepare the space: create ZZSpace objects
System.runFinalizersOnExit(true); // needed for unlocking a space
String[] slices = null;
ZZSpace space;
if(argv[cur].equals(":DS")) {
space = new ZZDimSpace();
newspace = true;
} else { // Space stored in file
File f = new File(argv[cur]);
if(!f.exists()) newspace = true;
if(argv.length > cur+1) {
System.err.println("SLICE TEST MODE!!!!!");
slices = new String[argv.length-(cur+1)];
System.arraycopy(argv, cur+1, slices, 0,
argv.length-(cur+1));
}
space = new ZZCacheDimSpace(new DirStreamSet(f));
if(span) ((ZZDimSpace)space).spanset = new SpanTree();
}
// And now we switch spaces if there are slices.
if(slices != null) {
throw new ZZError("slices are broken");
//ZZDimSpace[] ss = new ZZDimSpace[slices.length+1];
//ss[0] = (ZZDimSpace)space;
//for(int i=0; i<slices.length; i++)
// ss[i+1] = new ZZPersistentDimSpace(
// new DirSet(new File(slices[i]), "rw"));
// space = new ZZSlicedDimSpace(ss);
//throw new Error("No sliced stuff right now!");
}
if (slurpf != null) {
if (!newspace)
throw new ZZError("cannot slurp into an existing space");
Reader r = new FileReader(slurpf);
SpaceDump sd = new SpaceDump(r);
// First, instantiate the scrolls
for (Enumeration e = sd.stringscrolls.keys(); e.hasMoreElements();) {
String name = (String)e.nextElement();
StringScroll ss = space.getStringScroll(name);
String content = (String)sd.stringscrolls.get(name);
if (ss == null)
throw new ZZError("cannot create the string scroll " + name);
if (ss.curEnd() != 0)
throw new ZZError("the string scroll `" + name
+ "' is not initially empty!");
ss.append(content);
}
// Then put in cell content
for (Enumeration e = sd.content.keys(); e.hasMoreElements();) {
String cid = (String)e.nextElement();
ZZCell c = space.getCellByID(cid);
String content = (String)sd.content.get(cid);
if (sd.cellswithspans.containsKey(cid)) {
c.setSpan(Span.parse(content));
} else {
c.setText(content);
}
}
// Then make the connections
for (Enumeration e = sd.dims.keys(); e.hasMoreElements();) {
String dname = (String)e.nextElement();
// we ignore d.cellcreation
if (dname.equals("d.cellcreation")) continue;
Hashtable dim = (Hashtable)sd.dims.get(dname);
for (Enumeration f = dim.keys(); f.hasMoreElements();) {
String cid = (String)f.nextElement();
String did = (String)dim.get(cid);
ZZCell c = space.getCellByID(cid);
ZZCell d = space.getCellByID(did);
c.connect(dname, d);
}
}
space.commit();
ZZLogger.log("Done.");
SafeExit.exit(0);
}
if (dump) {
PrintWriter pw = new
PrintWriter(new FileOutputStream(argv[cur]
+ ".dmp", false));
SpaceDump.dump(pw, space);
pw.flush();
pw.close();
ZZLogger.log("Done.");
SafeExit.exit(0);
}
if (ztpserv && ztpport > 0) {
ZZLogger.tryInitSyslog();
try {
Class ztpcl = Class.forName("org.gzigzag.module.ZTPserver");
Constructor ztpcons = ztpcl.getConstructor(new Class[] {
Class.forName("org.gzigzag.ZZSpace"),
Class.forName("java.lang.Integer")});
Object ztp = ztpcons.newInstance(new Object[] { space, new Integer(ztpport) });
p("main exiting - waiting for connections");
} catch (ClassNotFoundException e) {
ZZLogger.exc(e);
}
return;
}
if (recreateMasterDimList) {
space.recreateMasterDimList();
space.commit();
}
if(newspace && !importFile.equals("")) {
// XXX should be "if a space with only the home cell"
// NOTE: Introspection is used so that there will not
// be a compile-time dependency on the XML module.
Object xml = (Class.forName("org.gzigzag.module.XML").newInstance());
Method get = xml.getClass().getMethod("load", new Class[] {Class.forName("java.io.File"), Class.forName("org.gzigzag.ZZCell")});
get.invoke(xml, new Object[] {new File(importFile), space.getHomeCell()});
space.commit();
newspace = false; // It's old then
}
if(!exportFile.equals("")) {
// NOTE: Introspection is used so that there will not
// be a compile-time dependency on the XML module.
Object xml = (Class.forName("org.gzigzag.module.XML").newInstance());
Method get = xml.getClass().getMethod("export", new Class[] {Class.forName("java.io.File"), Class.forName("org.gzigzag.ZZSpace"), Class.forName("java.lang.String")});
p("Invoke: "+get+" "+xml);
get.invoke(xml, new Object[] {new File(exportFile), space, encoding});
p("File exported - exiting.");
SafeExit.exit(0);
}
// *** Prepare client space and check version
// Ted asked for this kind of an announcement
if(newspace) {
System.err.println("Creating or recreating the system part of the space");
}
ZZCell home = space.getHomeCell();
if(newspace) {
ZZDefaultSpace.create(space.getHomeCell());
}
if (newspace) { // Might be re-init
home.getOrNewCell("d.gzz-space-version", 1)
.setText(""+zzclientversion);
} else {
ZZCell vercell = home.s("d.gzz-space-version",1);
if(vercell==null) {
pa("Version cell not found: setting version to zero.");
vercell = home.N("d.gzz-space-version", 1);
vercell.setText("0");
}
pa("Clientspace versionstring: '"+vercell.getText()+"'");
int fileversion;
try {
fileversion = Integer.parseInt(vercell.getText());
} catch (NumberFormatException e) {
fileversion = 0;
}
pa("Clientspace version in prepared space is "+fileversion+".");
if(fileversion > zzclientversion)
quit("Can't use files from clients that new. " +
"Please upgrade client.");
if(fileversion < zzclientversion && !convert)
quit("Files from older client. Please use " +
"option -convert after backing up the file.");
if(convert && !foolconvert) {
if(fileversion == zzclientversion)
quit("File is already of same version.");
convert(fileversion, zzclientversion, space);
vercell.setText(""+zzclientversion);
vercell.getSpace().commit();
SafeExit.exit(0);
}
}
/* // Print all cells
ZZCell i;
p("Cells:");
for(Enumeration e=space.cells();e.hasMoreElements();)
{
i=(ZZCell)e.nextElement();
p(i.getID()+": "+i.getText());
}*/
// Print all dimensions
String[] dims = space.dims();
p("Dimensions: ("+dims.length+")");
for(int s=0;s<dims.length;s++) p(dims[s]);
// *** Start client
// ZZCell clientcell = space.getHomeCell().findText(
// "d.2",1,"ClientCell");
// if(clientcell==null)
// throw new ZZError("No client cell found on d.2!!");
// ZZCell dimlist = ZZDefaultSpace.findDefaultDimlist(home);
// ZZSimpleClient2 cli = new ZZSimpleClient2(clientcell,
// dimlist, dimlist);
// *** Initialize ZZWindows
if(pda) {
pa("PDA mode: Creating root window.");
Container c=new Window((Frame)null);
c.setLocation(0,0);
c.setSize(320,240);
c.setVisible(true);
ZZWindows.init(space,c);
} else ZZWindows.init(space,null);
// Make sure no undo to before start.
space.commit();
} catch(Throwable e) {
ZZLogger.exc(e, "In main");
}
ZZObsTrigger.runObsQueue();
p("Main exiting - on to the events..");
}
/** Convert a space from older client to be compatible with the current
* form. Classes org.gzigzag.transform.Version[number] will be used
* one after another until the current version is reached.
* @param fileversion Version of loaded space
* @param zzclientversion Version of current client
* @param s The space to be converted
*/
static void convert(int fileversion, int zzclientversion, ZZSpace s) {
try {
org.gzigzag.transform.VersionChanger vchg;
System.out.println("Converting space to current version:");
while(fileversion < zzclientversion) {
vchg = (org.gzigzag.transform.VersionChanger)
(Class.forName(
"org.gzigzag.transform.Version"+fileversion)
).newInstance();
fileversion = vchg.changeVersion(s);
}
System.out.println("Conversion done, new file version "+zzclientversion+".");
} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
}
}
static void quit(String msg) {
System.out.println(msg);
SafeExit.exit(5);
}
}