| /* Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package tools.util; |
| |
| // Diff -- text file difference utility. |
| // See full docu-comment at beginning of Diff class. |
| |
| // $Id$ |
| |
| import java.io.*; |
| import java.util.Vector; |
| |
| /** |
| * This is the info kept per-file. |
| */ |
| class fileInfo |
| { |
| |
| static final int MAXLINECOUNT = 20000; |
| |
| //DataInputStream file; /* File handle that is open for read. */ |
| File file; |
| public int maxLine; /* After input done, # lines in file. */ |
| node symbol[]; /* The symtab handle of each line. */ |
| int other[]; /* Map of line# to line# in other file */ |
| /* ( -1 means don't-know ). */ |
| /* Allocated AFTER the lines are read. */ |
| |
| /** |
| * Normal constructor with one filename |
| * Tests whether file actually exists |
| */ |
| fileInfo(String filename) |
| { |
| symbol = new node[MAXLINECOUNT + 2]; |
| other = null; // allocated later! |
| file = new File(filename); |
| } |
| |
| // This is done late, to be same size as # lines in input file. |
| void alloc() |
| { |
| other = new int[symbol.length + 2]; |
| } |
| } |
| |
| ; |
| |
| /** |
| * diff Text file difference utility. |
| * ---- Copyright 1987, 1989 by Donald C. Lindsay, |
| * School of Computer Science, Carnegie Mellon University. |
| * Copyright 1982 by Symbionics. |
| * Use without fee is permitted when not for direct commercial |
| * advantage, and when credit to the source is given. Other uses |
| * require specific permission. |
| * |
| * Converted from C to Java by Ian F. Darwin, ian@darwinsys.com, January, 1997. |
| * Copyright 1997, Ian F. Darwin. |
| * |
| * Conversion is NOT FULLY TESTED. |
| * |
| * USAGE: diff oldfile newfile |
| * |
| * This program assumes that "oldfile" and "newfile" are text files. |
| * The program writes to stdout a description of the changes which would |
| * transform "oldfile" into "newfile". |
| * |
| * The printout is in the form of commands, each followed by a block of |
| * text. The text is delimited by the commands, which are: |
| * |
| * DELETE AT n |
| * ..deleted lines |
| * |
| * INSERT BEFORE n |
| * ..inserted lines |
| * |
| * n MOVED TO BEFORE n |
| * ..moved lines |
| * |
| * n CHANGED FROM |
| * ..old lines |
| * CHANGED TO |
| * ..newer lines |
| * |
| * The line numbers all refer to the lines of the oldfile, as they are |
| * numbered before any commands are applied. |
| * The text lines are printed as-is, without indentation or prefixing. The |
| * commands are printed in upper case, with a prefix of ">>>>", so that |
| * they will stand out. Other schemes may be preferred. |
| * Files which contain more than MAXLINECOUNT lines cannot be processed. |
| * This can be fixed by changing "symbol" to a Vector. |
| * The algorithm is taken from Communications of the ACM, Apr78 (21, 4, 264-), |
| * "A Technique for Isolating Differences Between Files." |
| * Ignoring I/O, and ignoring the symbol table, it should take O(N) time. |
| * This implementation takes fixed space, plus O(U) space for the symbol |
| * table (where U is the number of unique lines). Methods exist to change |
| * the fixed space to O(N) space. |
| * Note that this is not the only interesting file-difference algorithm. In |
| * general, different algorithms draw different conclusions about the |
| * changes that have been made to the oldfile. This algorithm is sometimes |
| * "more right", particularly since it does not consider a block move to be |
| * an insertion and a (separate) deletion. However, on some files it will be |
| * "less right". This is a consequence of the fact that files may contain |
| * many identical lines (particularly if they are program source). Each |
| * algorithm resolves the ambiguity in its own way, and the resolution |
| * is never guaranteed to be "right". However, it is often excellent. |
| * This program is intended to be pedagogic. Specifically, this program was |
| * the basis of the Literate Programming column which appeared in the |
| * Communications of the ACM (CACM), in the June 1989 issue (32, 6, |
| * 740-755). |
| * By "pedagogic", I do not mean that the program is gracefully worded, or |
| * that it showcases language features or its algorithm. I also do not mean |
| * that it is highly accessible to beginners, or that it is intended to be |
| * read in full, or in a particular order. Rather, this program is an |
| * example of one professional's style of keeping things organized and |
| * maintainable. |
| * The program would be better if the "print" variables were wrapped into |
| * a struct. In general, grouping related variables in this way improves |
| * documentation, and adds the ability to pass the group in argument lists. |
| * This program is a de-engineered version of a program which uses less |
| * memory and less time. The article points out that the "symbol" arrays |
| * can be implemented as arrays of pointers to arrays, with dynamic |
| * allocation of the subarrays. (In C, macros are very useful for hiding |
| * the two-level accesses.) In Java, a Vector would be used. This allows an |
| * extremely large value for MAXLINECOUNT, without dedicating fixed arrays. |
| * (The "other" array can be allocated after the input phase, when the exact |
| * sizes are known.) The only slow piece of code is the "strcmp" in the tree |
| * descent: it can be speeded up by keeping a hash in the tree node, and |
| * only using "strcmp" when two hashes happen to be equal. |
| * |
| * Change Log |
| * ---------- |
| * 1Jan97 Ian F. Darwin: first working rewrite in Java, based entirely on |
| * D.C.Lindsay's reasonable C version. |
| * Changed comments from /***************** to /**, shortened, added |
| * whitespace, used tabs more, etc. |
| * 6jul89 D.C.Lindsay, CMU: fixed portability bug. Thanks, Gregg Wonderly. |
| * Just changed "char ch" to "int ch". |
| * Also added comment about way to improve code. |
| * 10jun89 D.C.Lindsay, CMU: posted version created. |
| * Copyright notice changed to ACM style, and Dept. is now School. |
| * ACM article referenced in docn. |
| * 26sep87 D.C.Lindsay, CMU: publication version created. |
| * Condensed all 1982/83 change log entries. |
| * Removed all command line options, and supporting code. This |
| * simplified the input code (no case reduction etc). It also |
| * simplified the symbol table, which was capable of remembering |
| * offsets into files (instead of strings), and trusting (!) hash |
| * values to be unique. |
| * Removed dynamic allocation of arrays: now fixed static arrays. |
| * Removed speed optimizations in symtab package. |
| * Removed string compression/decompression code. |
| * Recoded to Unix standards from old Lattice/MSDOS standards. |
| * (This affected only the #include's and the IO.) |
| * Some renaming of variables, and rewording of comments. |
| * 1982/83 D.C.Lindsay, Symbionics: created. |
| * |
| * @author Ian F. Darwin, Java version |
| * @version Java version 0.9, 1997 |
| * @author D. C. Lindsay, C version (1982-1987) |
| */ |
| public class Diff |
| { |
| |
| /** |
| * block len > any possible real block len |
| */ |
| final int UNREAL = Integer.MAX_VALUE; |
| |
| /** |
| * Keeps track of information about file1 and file2 |
| */ |
| fileInfo oldinfo, newinfo; |
| |
| /** |
| * blocklen is the info about found blocks. It will be set to 0, except |
| * at the line#s where blocks start in the old file. At these places it |
| * will be set to the # of lines in the block. During printout , |
| * this # will be reset to -1 if the block is printed as a MOVE block |
| * (because the printout phase will encounter the block twice, but |
| * must only print it once.) |
| * The array declarations are to MAXLINECOUNT+2 so that we can have two |
| * extra lines (pseudolines) at line# 0 and line# MAXLINECOUNT+1 |
| * (or less). |
| */ |
| int blocklen[]; |
| |
| /** |
| * Controls how trailing whitespace lines is handled |
| */ |
| boolean trimLines; |
| |
| /** |
| * main - entry point when used standalone. |
| * NOTE: no routines return error codes or throw any local |
| * exceptions. Instead, any routine may complain |
| * to stderr and then exit with error to the system. |
| */ |
| public static void main(String argstrings[]) |
| { |
| if (argstrings.length != 2) |
| { |
| System.err.println("Usage: diff oldfile newfile"); |
| System.exit(1); |
| } |
| Diff d = new Diff(false); |
| d.doDiff(argstrings[0], argstrings[1]); |
| return; |
| } |
| |
| /** |
| * Construct a Diff object. |
| */ |
| public Diff(boolean trimLines) |
| { |
| this.trimLines = trimLines; |
| } |
| |
| /** |
| * Construct a Diff object. |
| */ |
| public Diff() |
| { |
| this.trimLines = false; |
| } |
| |
| /** |
| * Do one file comparison. Called with both filenames. |
| */ |
| public void doDiff(String oldFile, String newFile) |
| { |
| println(">>>> Difference of file \"" + oldFile + |
| "\" and file \"" + newFile + "\".\n"); |
| oldinfo = new fileInfo(oldFile); |
| newinfo = new fileInfo(newFile); |
| /* we don't process until we know both files really do exist. */ |
| try |
| { |
| inputscan(oldinfo); |
| inputscan(newinfo); |
| } |
| catch (IOException e) |
| { |
| System.err.println("Read error: " + e); |
| } |
| |
| /* Now that we've read all the lines, allocate some arrays. |
| */ |
| blocklen = new int[(oldinfo.maxLine > newinfo.maxLine ? |
| oldinfo.maxLine : newinfo.maxLine) + 2]; |
| oldinfo.alloc(); |
| newinfo.alloc(); |
| |
| /* Now do the work, and print the results. */ |
| transform(); |
| printout(); |
| } |
| |
| public String compareFiles(String oldFile, String newFile) |
| throws IOException |
| { |
| //println("Diff of \"" + oldFile + "\" and \"" + newFile); |
| oldinfo = new fileInfo(oldFile); |
| newinfo = new fileInfo(newFile); |
| /* we don't process until we know both files really do exist. */ |
| inputscan(oldinfo); |
| inputscan(newinfo); |
| |
| /* Now that we've read all the lines, allocate some arrays. |
| */ |
| blocklen = new int[(oldinfo.maxLine > newinfo.maxLine ? |
| oldinfo.maxLine : newinfo.maxLine) + 2]; |
| oldinfo.alloc(); |
| newinfo.alloc(); |
| |
| /* Now do the work, and print the results. */ |
| transform(); |
| return printout(); |
| } |
| |
| |
| /** |
| * inputscan Reads the file specified by pinfo.file. |
| * --------- Places the lines of that file in the symbol table. |
| * Sets pinfo.maxLine to the number of lines found. |
| */ |
| void inputscan(fileInfo pinfo) |
| throws IOException |
| { |
| pinfo.maxLine = 0; |
| |
| BufferedReader reader = new BufferedReader(new FileReader(pinfo.file)); |
| Vector v = new Vector(); |
| |
| while (true) |
| { |
| String s = reader.readLine(); |
| if (s == null) break; |
| v.addElement(s); |
| } |
| reader.close(); |
| // Discard all trailing lines that are only whitespaces.. |
| if (trimLines) |
| { |
| int i = v.size(); |
| while (--i >= 0) |
| if (Util.isWhiteSpace((String) v.get(i))) |
| v.removeElementAt(i); |
| } |
| |
| int i = 0; |
| while (i < v.size()) |
| storeline((String) v.get(i++), pinfo); |
| |
| } |
| |
| /** |
| * storeline Places line into symbol table. |
| * --------- Expects pinfo.maxLine initted: increments. |
| * Places symbol table handle in pinfo.ymbol. |
| * Expects pinfo is either oldinfo or newinfo. |
| */ |
| void storeline(String linebuffer, fileInfo pinfo) |
| { |
| int linenum = ++pinfo.maxLine; /* note, no line zero */ |
| if (linenum > fileInfo.MAXLINECOUNT) |
| { |
| System.err.println("MAXLINECOUNT exceeded, must stop."); |
| System.exit(1); |
| } |
| pinfo.symbol[linenum] = |
| node.addSymbol(linebuffer, pinfo == oldinfo, linenum); |
| } |
| |
| /* |
| * transform |
| * Analyzes the file differences and leaves its findings in |
| * the global arrays oldinfo.other, newinfo.other, and blocklen. |
| * Expects both files in symtab. |
| * Expects valid "maxLine" and "symbol" in oldinfo and newinfo. |
| */ |
| void transform() |
| { |
| int oldline, newline; |
| int oldmax = oldinfo.maxLine + 2; /* Count pseudolines at */ |
| int newmax = newinfo.maxLine + 2; /* ..front and rear of file */ |
| |
| for (oldline = 0; oldline < oldmax; oldline++) |
| oldinfo.other[oldline] = -1; |
| for (newline = 0; newline < newmax; newline++) |
| newinfo.other[newline] = -1; |
| |
| scanunique(); /* scan for lines used once in both files */ |
| scanafter(); /* scan past sure-matches for non-unique blocks */ |
| scanbefore(); /* scan backwards from sure-matches */ |
| scanblocks(); /* find the fronts and lengths of blocks */ |
| } |
| |
| /* |
| * scanunique |
| * Scans for lines which are used exactly once in each file. |
| * Expects both files in symtab, and oldinfo and newinfo valid. |
| * The appropriate "other" array entries are set to the line# in |
| * the other file. |
| * Claims pseudo-lines at 0 and XXXinfo.maxLine+1 are unique. |
| */ |
| void scanunique() |
| { |
| int oldline, newline; |
| node psymbol; |
| |
| for (newline = 1; newline <= newinfo.maxLine; newline++) |
| { |
| psymbol = newinfo.symbol[newline]; |
| if (psymbol.symbolIsUnique()) |
| { // 1 use in each file |
| oldline = psymbol.linenum; |
| newinfo.other[newline] = oldline; // record 1-1 map |
| oldinfo.other[oldline] = newline; |
| } |
| } |
| newinfo.other[0] = 0; |
| oldinfo.other[0] = 0; |
| newinfo.other[newinfo.maxLine + 1] = oldinfo.maxLine + 1; |
| oldinfo.other[oldinfo.maxLine + 1] = newinfo.maxLine + 1; |
| } |
| |
| /* |
| * scanafter |
| * Expects both files in symtab, and oldinfo and newinfo valid. |
| * Expects the "other" arrays contain positive #s to indicate |
| * lines that are unique in both files. |
| * For each such pair of places, scans past in each file. |
| * Contiguous groups of lines that match non-uniquely are |
| * taken to be good-enough matches, and so marked in "other". |
| * Assumes each other[0] is 0. |
| */ |
| void scanafter() |
| { |
| int oldline, newline; |
| |
| for (newline = 0; newline <= newinfo.maxLine; newline++) |
| { |
| oldline = newinfo.other[newline]; |
| if (oldline >= 0) |
| { /* is unique in old & new */ |
| for (; ;) |
| { /* scan after there in both files */ |
| if (++oldline > oldinfo.maxLine) break; |
| if (oldinfo.other[oldline] >= 0) break; |
| if (++newline > newinfo.maxLine) break; |
| if (newinfo.other[newline] >= 0) break; |
| |
| /* oldline & newline exist, and |
| aren't already matched */ |
| |
| if (newinfo.symbol[newline] != |
| oldinfo.symbol[oldline]) |
| break; // not same |
| |
| newinfo.other[newline] = oldline; // record a match |
| oldinfo.other[oldline] = newline; |
| } |
| } |
| } |
| } |
| |
| /** |
| * scanbefore |
| * As scanafter, except scans towards file fronts. |
| * Assumes the off-end lines have been marked as a match. |
| */ |
| void scanbefore() |
| { |
| int oldline, newline; |
| |
| for (newline = newinfo.maxLine + 1; newline > 0; newline--) |
| { |
| oldline = newinfo.other[newline]; |
| if (oldline >= 0) |
| { /* unique in each */ |
| for (; ;) |
| { |
| if (--oldline <= 0) break; |
| if (oldinfo.other[oldline] >= 0) break; |
| if (--newline <= 0) break; |
| if (newinfo.other[newline] >= 0) break; |
| |
| /* oldline and newline exist, |
| and aren't marked yet */ |
| |
| if (newinfo.symbol[newline] != |
| oldinfo.symbol[oldline]) |
| break; // not same |
| |
| newinfo.other[newline] = oldline; // record a match |
| oldinfo.other[oldline] = newline; |
| } |
| } |
| } |
| } |
| |
| /** |
| * scanblocks - Finds the beginnings and lengths of blocks of matches. |
| * Sets the blocklen array (see definition). |
| * Expects oldinfo valid. |
| */ |
| void scanblocks() |
| { |
| int oldline, newline; |
| int oldfront = 0; // line# of front of a block in old, or 0 |
| int newlast = -1; // newline's value during prev. iteration |
| |
| for (oldline = 1; oldline <= oldinfo.maxLine; oldline++) |
| blocklen[oldline] = 0; |
| blocklen[oldinfo.maxLine + 1] = UNREAL; // starts a mythical blk |
| |
| for (oldline = 1; oldline <= oldinfo.maxLine; oldline++) |
| { |
| newline = oldinfo.other[oldline]; |
| if (newline < 0) |
| oldfront = 0; /* no match: not in block */ |
| else |
| { /* match. */ |
| if (oldfront == 0) oldfront = oldline; |
| if (newline != (newlast + 1)) oldfront = oldline; |
| ++blocklen[oldfront]; |
| } |
| newlast = newline; |
| } |
| } |
| |
| /* The following are global to printout's subsidiary routines */ |
| // enum{ idle, delete, insert, movenew, moveold, |
| // same, change } printstatus; |
| public static final int |
| idle = 0, delete = 1, insert = 2, movenew = 3, moveold = 4, |
| same = 5, change = 6; |
| int printstatus; |
| boolean anyprinted; |
| int printoldline, printnewline; // line numbers in old & new file |
| |
| /** |
| * printout - Prints summary to stdout. |
| * Expects all data structures have been filled out. |
| */ |
| String printout() |
| { |
| printstatus = idle; |
| anyprinted = false; |
| PrintStream _sysout = System.out; |
| _sysout.flush(); |
| String ret; |
| try |
| { |
| ByteArrayOutputStream bout = new ByteArrayOutputStream(); |
| System.setOut(new PrintStream(bout)); |
| for (printoldline = printnewline = 1; ;) |
| { |
| if (printoldline > oldinfo.maxLine) |
| { |
| newconsume(); |
| break; |
| } |
| if (printnewline > newinfo.maxLine) |
| { |
| oldconsume(); |
| break; |
| } |
| if (newinfo.other[printnewline] < 0) |
| { |
| if (oldinfo.other[printoldline] < 0) |
| showchange(); |
| else |
| showinsert(); |
| } else if (oldinfo.other[printoldline] < 0) |
| showdelete(); |
| else if (blocklen[printoldline] < 0) |
| skipold(); |
| else if (oldinfo.other[printoldline] == printnewline) |
| showsame(); |
| else |
| showmove(); |
| } |
| |
| if (anyprinted) |
| ret = bout.toString(); |
| else |
| ret = null; |
| } |
| finally |
| { |
| System.setOut(_sysout); |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * newconsume Part of printout. Have run out of old file. |
| * Print the rest of the new file, as inserts and/or moves. |
| */ |
| void newconsume() |
| { |
| for (; ;) |
| { |
| if (printnewline > newinfo.maxLine) |
| break; /* end of file */ |
| if (newinfo.other[printnewline] < 0) |
| showinsert(); |
| else |
| showmove(); |
| } |
| } |
| |
| /** |
| * oldconsume Part of printout. Have run out of new file. |
| * Process the rest of the old file, printing any |
| * parts which were deletes or moves. |
| */ |
| void oldconsume() |
| { |
| for (; ;) |
| { |
| if (printoldline > oldinfo.maxLine) |
| break; /* end of file */ |
| printnewline = oldinfo.other[printoldline]; |
| if (printnewline < 0) |
| showdelete(); |
| else if (blocklen[printoldline] < 0) |
| skipold(); |
| else |
| showmove(); |
| } |
| } |
| |
| /** |
| * showdelete Part of printout. |
| * Expects printoldline is at a deletion. |
| */ |
| void showdelete() |
| { |
| if (printstatus != delete) |
| println(">>>> DELETE AT " + printoldline); |
| printstatus = delete; |
| oldinfo.symbol[printoldline].showSymbol(); |
| anyprinted = true; |
| printoldline++; |
| } |
| |
| /* |
| * showinsert Part of printout. |
| * Expects printnewline is at an insertion. |
| */ |
| void showinsert() |
| { |
| if (printstatus == change) |
| println(">>>> CHANGED TO"); |
| else if (printstatus != insert) |
| println(">>>> INSERT BEFORE " + printoldline); |
| printstatus = insert; |
| newinfo.symbol[printnewline].showSymbol(); |
| anyprinted = true; |
| printnewline++; |
| } |
| |
| /** |
| * showchange Part of printout. |
| * Expects printnewline is an insertion. |
| * Expects printoldline is a deletion. |
| */ |
| void showchange() |
| { |
| if (printstatus != change) |
| println(">>>> " + printoldline + " CHANGED FROM"); |
| printstatus = change; |
| oldinfo.symbol[printoldline].showSymbol(); |
| anyprinted = true; |
| printoldline++; |
| } |
| |
| /** |
| * skipold Part of printout. |
| * Expects printoldline at start of an old block that has |
| * already been announced as a move. |
| * Skips over the old block. |
| */ |
| void skipold() |
| { |
| printstatus = idle; |
| for (; ;) |
| { |
| if (++printoldline > oldinfo.maxLine) |
| break; /* end of file */ |
| if (oldinfo.other[printoldline] < 0) |
| break; /* end of block */ |
| if (blocklen[printoldline] != 0) |
| break; /* start of another */ |
| } |
| } |
| |
| /** |
| * skipnew Part of printout. |
| * Expects printnewline is at start of a new block that has |
| * already been announced as a move. |
| * Skips over the new block. |
| */ |
| void skipnew() |
| { |
| int oldline; |
| printstatus = idle; |
| for (; ;) |
| { |
| if (++printnewline > newinfo.maxLine) |
| break; /* end of file */ |
| oldline = newinfo.other[printnewline]; |
| if (oldline < 0) |
| break; /* end of block */ |
| if (blocklen[oldline] != 0) |
| break; /* start of another */ |
| } |
| } |
| |
| /** |
| * showsame Part of printout. |
| * Expects printnewline and printoldline at start of |
| * two blocks that aren't to be displayed. |
| */ |
| void showsame() |
| { |
| int count; |
| printstatus = idle; |
| if (newinfo.other[printnewline] != printoldline) |
| { |
| System.err.println("BUG IN LINE REFERENCING"); |
| System.exit(1); |
| } |
| count = blocklen[printoldline]; |
| printoldline += count; |
| printnewline += count; |
| } |
| |
| /** |
| * showmove Part of printout. |
| * Expects printoldline, printnewline at start of |
| * two different blocks ( a move was done). |
| */ |
| void showmove() |
| { |
| int oldblock = blocklen[printoldline]; |
| int newother = newinfo.other[printnewline]; |
| int newblock = blocklen[newother]; |
| |
| if (newblock < 0) |
| skipnew(); // already printed. |
| else if (oldblock >= newblock) |
| { // assume new's blk moved. |
| blocklen[newother] = -1; // stamp block as "printed". |
| println(">>>> " + newother + |
| " THRU " + (newother + newblock - 1) + |
| " MOVED TO BEFORE " + printoldline); |
| for (; newblock > 0; newblock--, printnewline++) |
| newinfo.symbol[printnewline].showSymbol(); |
| anyprinted = true; |
| printstatus = idle; |
| |
| } else /* assume old's block moved */ |
| skipold(); /* target line# not known, display later */ |
| } |
| |
| /** |
| * Convenience wrapper for println |
| */ |
| public void println(String s) |
| { |
| System.out.println(s); |
| } |
| } |
| |
| ; // end of main class! |
| |
| /** |
| * Class "node". The symbol table routines in this class all |
| * understand the symbol table format, which is a binary tree. |
| * The methods are: addSymbol, symbolIsUnique, showSymbol. |
| */ |
| class node |
| { /* the tree is made up of these nodes */ |
| node pleft, pright; |
| int linenum; |
| |
| static final int freshnode = 0, |
| oldonce = 1, newonce = 2, bothonce = 3, other = 4; |
| |
| int /* enum linestates */ linestate; |
| String line; |
| |
| static node panchor = null; /* symtab is a tree hung from this */ |
| |
| /** |
| * Construct a new symbol table node and fill in its fields. |
| * |
| * @param string A line of the text file |
| */ |
| node(String pline) |
| { |
| pleft = pright = null; |
| linestate = freshnode; |
| /* linenum field is not always valid */ |
| line = pline; |
| } |
| |
| /** |
| * matchsymbol Searches tree for a match to the line. |
| * |
| * @param String pline, a line of text |
| * If node's linestate == freshnode, then created the node. |
| */ |
| static node matchsymbol(String pline) |
| { |
| int comparison; |
| node pnode = panchor; |
| if (panchor == null) return panchor = new node(pline); |
| for (; ;) |
| { |
| comparison = pnode.line.compareTo(pline); |
| if (comparison == 0) return pnode; /* found */ |
| |
| if (comparison < 0) |
| { |
| if (pnode.pleft == null) |
| { |
| pnode.pleft = new node(pline); |
| return pnode.pleft; |
| } |
| pnode = pnode.pleft; |
| } |
| if (comparison > 0) |
| { |
| if (pnode.pright == null) |
| { |
| pnode.pright = new node(pline); |
| return pnode.pright; |
| } |
| pnode = pnode.pright; |
| } |
| } |
| /* NOTE: There are return stmts, so control does not get here. */ |
| } |
| |
| /** |
| * addSymbol(String pline) - Saves line into the symbol table. |
| * Returns a handle to the symtab entry for that unique line. |
| * If inoldfile nonzero, then linenum is remembered. |
| */ |
| static node addSymbol(String pline, boolean inoldfile, int linenum) |
| { |
| node pnode; |
| pnode = matchsymbol(pline); /* find the node in the tree */ |
| if (pnode.linestate == freshnode) |
| { |
| pnode.linestate = inoldfile ? oldonce : newonce; |
| } else |
| { |
| if ((pnode.linestate == oldonce && !inoldfile) || |
| (pnode.linestate == newonce && inoldfile)) |
| pnode.linestate = bothonce; |
| else |
| pnode.linestate = other; |
| } |
| if (inoldfile) pnode.linenum = linenum; |
| return pnode; |
| } |
| |
| /** |
| * symbolIsUnique Arg is a ptr previously returned by addSymbol. |
| * -------------- Returns true if the line was added to the |
| * symbol table exactly once with inoldfile true, |
| * and exactly once with inoldfile false. |
| */ |
| boolean symbolIsUnique() |
| { |
| return (linestate == bothonce); |
| } |
| |
| /** |
| * showSymbol Prints the line to stdout. |
| */ |
| void showSymbol() |
| { |
| System.out.println(line); |
| } |
| } |
| |