/* ======================================================================== bisl_gen.c - code-generation routines for BISL compiler Global routines: gen_code - write Java code to file Copyright 1996 Micah Beck / Simon D. Levy University of Tennessee All distributions of this product must include source code. Source code is not to be distributed in any modified form. ======================================================================== */ #include "bisl.h" #include "y.tab.h" #include #define DBG /* leave in for stdout output; comment out for file output */ /* ------------------------------------------------------------------------ declare_vars - declare variables. We declare a numbered variable for each node and use constraint names to declare boolean variables that encode the state (on/off) of a constraint. We also use a node-selector variable, "which", so that paint() will draw only what needs to be drawn each time it is invoked, a constraint-selector variable "last_roll", to deal with missed calls to repaint() caused by fast rollovers, and a Boolean variable for each text and image node, to prevent unnecessary erasing when a complex constraint isn't satisfied. Finally, we need a count of unfulfilled calls to repaint(); this count is incremented in handleEvent() each time repaint() is called, and is decremented at the end of paint(). The last thing that handleEvent() does is to call paint() repeatedly until the count variable is zero. ------------------------------------------------------------------------ */ static void declare_vars(FILE *fp, Dlist node_list, Dlist cstr_list) { NODE *node; CSTR *cstr; SNDSTRUCT *sndptr; URLSTRUCT *urlptr; STRSTRUCT *strptr; BAKSTRUCT *bakptr; PLASTRUCT *plaptr; Dlist tmp, tmp2; int node_count = 1, constraint_count = 1; /* *** declare node- and constraint- selector variables *** */ fprintf(fp, "int which, last_roll, repaint_needed=1;"); /* *** write variables for nodes *** */ dl_traverse(tmp, node_list) { node = (NODE *)tmp->val; switch (node->what) { case n_Back: bakptr = node->uval.bakptr; node->count = node_count++; fprintf(fp, "Image back%d;", node->count); break; case n_Image: node->count = node_count++; fprintf(fp, "Image image%d;", node->count); if (node->constraint) fprintf(fp, "boolean image_drawn%d = false;\n", node->count); break; case n_Sound: sndptr = node->uval.sndptr; node->count = node_count++; fprintf(fp, "AudioClip sound%d;", node->count); break; case n_URL: urlptr = node->uval.urlptr; node->count = node_count++; fprintf(fp, "URL url%d;", node->count); break; case n_Text: plaptr = node->uval.plaptr; strptr = plaptr->visptr->uval.strptr; node->count = node_count++; fprintf(fp, "String string%d = \"%s\";", node->count, strptr->string); fprintf(fp, "Font font%d = new Font(\"%s\", Font.%s, %d);", node->count, strptr->font, strptr->style, strptr->size); fprintf(fp, "FontMetrics fm%d;", node->count); if (node->constraint) fprintf(fp, "boolean text_drawn%d = false;\n", node->count); break; } /* switch (node->what) */ } /* dl_traverse(tmp, node_list) */ /* *** write variables for constraints *** */ dl_traverse(tmp, cstr_list) { cstr = (CSTR *)tmp->val; fprintf(fp, "boolean %s = false;", cstr->name); dl_traverse(tmp2, cstr->nodes_to) { node = (NODE *)tmp2->val; if (node->what == n_Image || node->what == n_Text) if (!cstr->count) { cstr->count = constraint_count++; fprintf(fp, "int ulx_%d, uly_%d, lrx_%d, lry_%d;", cstr->count, cstr->count, cstr->count, cstr->count); } } /* dl_traverse(tmp2, cstr->nodes_to) */ } /* dl_traverse(tmp, node_list) */ } /* declare_vars */ /* ------------------------------------------------------------------------ write_init - write init() method. We load images and sounds here, using a MediaTracker instance to force immediate loading. For each constraint that points to image- or text-nodes, we compute the extreme upper-left and lower-right coordinates of all texts/images pointed to, so we can create the proper clipping rect later. ------------------------------------------------------------------------ */ static void write_init(FILE *fp, Dlist node_list, Dlist cstr_list) { NODE *node; CSTR *cstr; SNDSTRUCT *sndptr; URLSTRUCT *urlptr; BAKSTRUCT *bakptr; PLASTRUCT *plaptr; int ncount, ecount, min_x; Dlist tmp, tmp2; fprintf(fp, "public void init() {"); fprintf(fp, "MediaTracker tracker = new MediaTracker(this);"); /* *** load images, sounds, et al. *** */ dl_traverse(tmp, node_list) { node = (NODE *)tmp->val; ncount = node->count; switch (node->what) { case n_Image: plaptr = node->uval.plaptr; fprintf(fp, "image%d = getImage(getCodeBase(), \"%s\");", ncount, plaptr->visptr->uval.gifptr->fname); fprintf(fp, "tracker.addImage(image%d, 0);", ncount); break; case n_Back: bakptr = node->uval.bakptr; fprintf(fp, "back%d = getImage(getCodeBase(), \"%s\");", ncount, bakptr->visptr->uval.gifptr->fname); fprintf(fp, "tracker.addImage(back%d, 0);", ncount); break; case n_Sound: sndptr = node->uval.sndptr; fprintf(fp, " sound%d = getAudioClip(getCodeBase(), \"%s\");", ncount, sndptr->fname); if (!node->constraint) fprintf(fp, "if (sound%d != null) sound%d.play();", ncount, ncount); break; case n_URL: urlptr = node->uval.urlptr; fprintf(fp, "try { url%d = new URL(\"%s\"); }", ncount, urlptr->uname); fprintf(fp, "catch (MalformedURLException e) {"); fprintf(fp, " System.out.println(\"Bad URL: \" + \n\"%s\");", urlptr->uname); fprintf(fp, "}"); if (!node->constraint) { fprintf(fp, "if (url%d != null)\n", ncount); fprintf(fp, "getAppletContext().showDocument(url%d);", ncount); } break; case n_Text: fprintf(fp, "fm%d = getFontMetrics(font%d);", ncount, ncount); break; } /* switch (node->what) */ } /* dl_traverse(tmp, node_list) */ fprintf(fp, "try {"); fprintf(fp, "tracker.waitForAll();"); fprintf(fp, "} catch(InterruptedException e) {"); fprintf(fp, "System.out.println(\"Image loading failed\");"); fprintf(fp, "}"); fprintf(fp, "tracker.checkAll(true);"); /* *** compute upper-left coordinate for each CSTR *** */ dl_traverse(tmp, cstr_list) { cstr = (CSTR *)tmp->val; if (!(ecount=cstr->count)) continue; min_x = INT_MAX; fprintf(fp, "uly_%d = this.getSize().height;", ecount); dl_traverse(tmp2, cstr->nodes_to) { node = (NODE *)tmp2->val; ncount = node->count; plaptr = node->uval.plaptr; if (plaptr->ul_point->x < min_x) min_x = plaptr->ul_point->x; switch(node->what) { case n_Image: fprintf(fp, "if (uly_%d > %d)", ecount, plaptr->ul_point->y); fprintf(fp, "uly_%d = %d;", ecount, plaptr->ul_point->y); break; case n_Text: fprintf(fp, "if (uly_%d > (%d-fm%d.getAscent()))", ecount, plaptr->ul_point->y, ncount); fprintf(fp, "uly_%d = %d-fm%d.getAscent();", ecount, plaptr->ul_point->y, ncount); break; } /* switch(node->what) */ } /* dl_traverse(tmp2, cstr->nodes_to) */ fprintf(fp, "ulx_%d = %d;", ecount, min_x); } /* dl_traverse(tmp, cstr_list) */ /* *** compute lower-right coordinate for each CSTR *** */ dl_traverse(tmp, cstr_list) { cstr = (CSTR *)tmp->val; if (!(ecount=cstr->count)) continue; fprintf(fp, "lrx_%d = 0;", ecount); fprintf(fp, "lry_%d = 0;", ecount); dl_traverse(tmp2, cstr->nodes_to) { node = (NODE *)tmp2->val; ncount = node->count; plaptr = node->uval.plaptr; switch(node->what) { case n_Image: fprintf(fp, "if (lrx_%d < (%d + image%d.getWidth(this))){", ecount, plaptr->ul_point->x, ncount); fprintf(fp, "lrx_%d = %d + image%d.getWidth(this);}", ecount, plaptr->ul_point->x, ncount); fprintf(fp, "if (lry_%d < (%d + ", ecount, plaptr->ul_point->y); fprintf(fp, "image%d.getHeight(this))){", ncount); fprintf(fp, "lry_%d = %d + image%d.getHeight(this);}", ecount, plaptr->ul_point->y, ncount); break; case n_Text: fprintf(fp, "if (lrx_%d < (%d + ", ecount, plaptr->ul_point->x); fprintf(fp, "fm%d.stringWidth(string%d))){", ncount, ncount); fprintf(fp, "lrx_%d = %d + fm%d.stringWidth(string%d);}", ecount, plaptr->ul_point->x, ncount, ncount); fprintf(fp, "if (lry_%d < (%d + fm%d.getDescent())){", ecount, plaptr->ul_point->y, ncount); fprintf(fp, "lry_%d = %d + fm%d.getDescent();}", ecount, plaptr->ul_point->y, ncount); break; } /* switch(node->what) */ } /* dl_traverse(tmp2, cstr->nodes_to) */ } /* dl_traverse(tmp, cstr_list) */ fprintf(fp, "}"); } /* write_init */ /* ------------------------------------------------------------------------ write_offs - write code for hiding images, halting sounds, etc. ------------------------------------------------------------------------ */ static void write_offs(FILE *fp, CSTR *cstr) { NODE *node; SNDSTRUCT *sndptr; Dlist tmp; int repainted = 0; dl_traverse(tmp, cstr->nodes_to) { node = (NODE *)tmp->val; switch (node->what) { case n_Image: case n_Text: case n_Back: if (!repainted) { fprintf(fp, "which = %d;", cstr->count); fprintf(fp, "repaint();"); fprintf(fp, "repaint_needed++;"); repainted = 1; } break; case n_Sound: sndptr = node->uval.sndptr; fprintf(fp, "if (sound%d != null) sound%d.stop();", node->count, node->count); break; } /* switch (node->what) */ } /* dl_traverse(tmp, node_list) */ } /* write_offs */ /* ------------------------------------------------------------------------ r_print_constraint - recursivley print out Boolean expression over constraints ------------------------------------------------------------------------ */ static void r_print_constraint(FILE *fp, BEXP *bexp) { switch(bexp->oper) { case b_And: /* & */ r_print_constraint(fp, bexp->left); fprintf(fp, "&&"); r_print_constraint(fp, bexp->right); break; case b_Or: /* | */ fprintf (fp, "("); /* only need parens for OR */ r_print_constraint(fp, bexp->left); fprintf(fp, "||"); r_print_constraint(fp, bexp->right); fprintf (fp, ")"); break; case b_Not: /* ~ */ fprintf(fp, "!"); r_print_constraint(fp, bexp->left); break; default: /* constraint name */ fprintf(fp, "%s", bexp->cstr->name); } /* switch(bexp->oper) */ } /* r_print_CSTR */ /* ------------------------------------------------------------------------ write_ons - write code for showing images, playing sounds, etc. ------------------------------------------------------------------------ */ static void write_ons(FILE *fp, CSTR *cstr) { NODE *node; CSTR *cstr_to; SNDSTRUCT *sndptr; URLSTRUCT *urlptr; Dlist tmp; int repainted = 0, ncount; /* *** handle nodes affected by this constraint *** */ dl_traverse(tmp, cstr->nodes_to) { node = (NODE *)tmp->val; ncount = node->count; switch (node->what) { case n_Image: case n_Text: case n_Back: if (!repainted) { fprintf(fp, "which = %d;", cstr->count); fprintf(fp, "repaint();"); fprintf(fp, "repaint_needed++;"); repainted = 1; } break; case n_Sound: sndptr = node->uval.sndptr; fprintf(fp, "if ("); r_print_constraint(fp, node->constraint); fprintf(fp, " && sound%d != null) sound%d.play();", ncount, ncount); break; case n_URL: urlptr = node->uval.urlptr; fprintf(fp, "if ("); r_print_constraint(fp, node->constraint); fprintf(fp, " && url%d != null)\n", ncount); fprintf(fp, "getAppletContext().showDocument(url%d);", ncount); break; } /* switch (node->what) */ } /* dl_traverse(tmp, node_list) */ /* *** handle constraints affected by this constraint *** */ if (cstr_to=cstr->cstr_to) if (cstr_to->what == c_Tog) { fprintf(fp, "if (%s) %s = !%s;", cstr->name, cstr_to->name, cstr_to->name); write_ons(fp, cstr_to); } } /* write_ons */ /* ------------------------------------------------------------------------ write_handleEvent - write handleEvent() method. We first traverse the node list, checking each image and text node to see whether the pointer is within its boundaries. We then traverse the constraint-list, grabbing the tuple that the constraint comes out of, and writing code that will set a boolean flag based on whether the mouse is inside the area defined by the tuple, and not inside the area(s) defined by any other tuple(s) of higher count; in this way, we implement opacity of visual nodes. Then we write code appropriate to the constraint-type (click, rollover, et al.). This code will use the boolean flag, in combination with the constraint-type, to take the appropriate action. Events are not "blocked" by a constrained visual node. This makes it possible for a button to catch the up-click event if it is being blocked by an image or text string whose appearance was caused by clicking the button. Finally, we call repaint() repeatedly until no more calls to it are unfulfilled. ------------------------------------------------------------------------ */ static void write_handleEvent(FILE *fp, Dlist node_list, Dlist cstr_list) { NODE *node; CSTR *cstr; TUPL *tupl; Dlist tmp, tmp2; REGSTRUCT *regptr; RECSTRUCT *recptr; CIRSTRUCT *cirptr; PLASTRUCT *plaptr; char *name, tmpstr[100], csrstr[100]; int xoff = 0, yoff = 0, count, depth; fprintf(fp, "public boolean handleEvent(Event evt) {"); /* *** check what images / text strings event occurred in *** */ dl_traverse(tmp, node_list) { node = (NODE *)tmp->val; if (node->what != n_Image && node->what != n_Text) continue; count = node->count; plaptr = node->uval.plaptr; fprintf(fp, "boolean in_%d = (evt.x > %d && evt.x < (%d +", count, plaptr->ul_point->x, plaptr->ul_point->x); switch (plaptr->visptr->what) { case v_Giff: fprintf(fp, "image%d.getWidth(this))\n", count); fprintf(fp, "&& evt. y > %d && evt.y < (%d + ", plaptr->ul_point->y, plaptr->ul_point->y); fprintf(fp, "image%d.getHeight(this))", count); break; case v_String: fprintf(fp, "fm%d.stringWidth(string%d))\n", count, count); fprintf(fp, "&& evt. y < %d && evt.y > (%d - ", plaptr->ul_point->y, plaptr->ul_point->y); fprintf(fp, "fm%d.getHeight())", count); break; } /* switch (plaptr->visptr->what) */ fprintf(fp, ");"); } /* dl_traverse(tmp, node_list) */ /* *** traverse constraints, checking relevance of event *** */ dl_traverse(tmp, cstr_list) { cstr = (CSTR *)tmp->val; if (cstr->what == c_Tog) continue; /* don't handle toggle here */ tupl = cstr->tupl_from; switch (tupl->what) { case t_Region: regptr = tupl->uval.regptr; if (plaptr=regptr->plaptr) { depth = plaptr->depth; xoff = plaptr->ul_point->x; yoff = plaptr->ul_point->y; } else { depth = 0; } switch (regptr->what) { case r_Circ: cirptr = regptr->uval.cirptr; fprintf(fp, "int xd = evt.x - %d;", cirptr->center->x + xoff); fprintf(fp, "int yd = evt.y - %d;", cirptr->center->y + yoff); fprintf(fp, "in_%s = (Math.sqrt((double)(xd * xd + ", cstr->name); fprintf(fp, "yd * yd)) < %d);", cirptr->radius); break; case r_Rect: recptr = regptr->uval.recptr; fprintf(fp, "in_%s = (evt.x > %d ", cstr->name, recptr->ul_point->x + xoff); fprintf(fp, "&& evt.x < %d ", recptr->lr_point->x + xoff); fprintf(fp, "&& evt.y > %d ", recptr->ul_point->y + yoff); fprintf(fp, "&& evt.y < %d);", recptr->lr_point->y + yoff); break; } /* switch (regptr->what) */ break; case t_Place: plaptr = tupl->uval.plaptr; if (!(node=plaptr->node)) break; depth = plaptr->depth; sprintf(csrstr, "in_%d", node->count); dl_traverse(tmp2, node_list) { NODE *node2 = (NODE *)tmp2->val; if (node2->what == n_Image || node2->what == n_Text) if (node2->uval.plaptr->depth < depth) { sprintf(tmpstr, " && !in_%d", node2->count); strcat(csrstr, tmpstr); } } break; } /* switch (tupl->what) */ name = cstr->name; count = cstr->count; fprintf(fp, "switch(evt.id) {"); switch (cstr->what) { case c_Click: fprintf(fp, "case Event.MOUSE_UP:"); fprintf(fp, "if (%s) {", csrstr); write_ons(fp, cstr); fprintf(fp, "}"); fprintf(fp, "break;"); fprintf(fp, "case Event.MOUSE_DOWN:"); fprintf(fp, "%s = %s;", name, csrstr); break; case c_Dclick: fprintf(fp, "case Event.MOUSE_DOWN:"); fprintf(fp, "%s = %s;", name, csrstr); fprintf(fp, "if (%s) {", csrstr); write_ons(fp, cstr); fprintf(fp, "}"); fprintf(fp, "break;"); fprintf(fp, "case Event.MOUSE_UP:"); fprintf(fp, "%s = false;", name); fprintf(fp, "if (%s) {", csrstr); write_offs(fp, cstr); fprintf(fp, "}"); fprintf(fp, "break;"); fprintf(fp, "default:"); fprintf(fp, "if (%s && !%s) {", name, csrstr); write_offs(fp, cstr); fprintf(fp, "%s = false;", name); fprintf(fp, "}"); break; case c_Roll: fprintf(fp, "case Event.MOUSE_MOVE:"); fprintf(fp, "if (%s) {", csrstr); fprintf(fp, "if (!%s) {", name); fprintf(fp, "%s = true;", name); write_ons(fp, cstr); fprintf(fp, "}"); fprintf(fp, "}"); fprintf(fp, "else {"); fprintf(fp, "if (%s) {", name); fprintf(fp, "%s = false;", name); fprintf(fp, "last_roll = %d;", count); write_offs(fp, cstr); fprintf(fp, "}"); fprintf(fp, "}"); fprintf(fp, "break;"); break; } /* switch (cstr->what) */ fprintf(fp, "}"); } /* dl_traverse(tmp, cstr_list) */ fprintf(fp, "for (int i=0; icount; PLASTRUCT *plaptr = node->uval.plaptr; STRSTRUCT *strptr; switch (node->what) { case n_Image: fprintf(fp, "if (image%d != null)\ng.drawImage(image%d, %d, %d, this);", count, count, plaptr->ul_point->x, plaptr->ul_point->y); break; case n_Text: strptr = plaptr->visptr->uval.strptr; fprintf(fp, "g.setFont(font%d);", count); fprintf(fp, "g.setColor(Color.%s);", strptr->color); fprintf(fp, "g.drawString(string%d, %d, %d);", count, plaptr->ul_point->x, plaptr->ul_point->y); break; } /* switch (node->what) */ } /* write_placenode */ /* ------------------------------------------------------------------------ write_clip - write code to create a clipping-rect for the specified constraint-number ------------------------------------------------------------------------ */ static void write_clip(FILE *fp, int count) { fprintf(fp, "g.clipRect(ulx_%d, uly_%d,\nlrx_%d-ulx_%d, lry_%d-uly_%d);", count, count, count, count, count, count); } /* write_clip */ /* ------------------------------------------------------------------------ write_paint - write paint() method. Nodes are sorted in pane order, and by statement order within pane. We decrement repaint_needed at the end to indicate one less unfulfilled call to paint(). ------------------------------------------------------------------------ */ static void write_paint(FILE *fp, Dlist node_list, Dlist cstr_list) { NODE *node, *baknode = NULL; CSTR *cstr; STRSTRUCT *strptr; PLASTRUCT *plaptr; Dlist tmp, tmp2; char drawnstr[80]; /* *** find final background node, if any *** */ dl_traverse(tmp, node_list) { node = (NODE *)tmp->val; if (node->constraint) continue; if (node->what == n_Back) baknode = node; } /* dl_traverse(tmp, node_list) */ /* *** write code for drawing nodes *** */ fprintf(fp, "void draw(Graphics g) {"); if (baknode) { int count = baknode->count; fprintf(fp, "for (int x=0; xval; if (node->constraint) continue; write_placenode(fp, node); } /* dl_traverse(tmp, node_list) */ dl_traverse(tmp, node_list) { node = (NODE *)tmp->val; if (node->what != n_Text && node->what != n_Image) continue; if (!node->constraint) continue; fprintf(fp, "if ("); r_print_constraint(fp, node->constraint); fprintf(fp, ") {"); write_placenode(fp, node); fprintf(fp, "}"); } /* dl_traverse(tmp, node_list) */ fprintf(fp, "}"); /* *** write code for handling last rollover *** */ fprintf(fp, "void handle_last_roll(Graphics g) {"); fprintf(fp, "switch (last_roll) {"); dl_traverse(tmp, cstr_list) { cstr = (CSTR *)tmp->val; if (cstr->what == c_Roll && cstr->count > 0) { fprintf(fp, "case %d:", cstr->count); write_clip(fp, cstr->count); fprintf(fp, "draw(g);"); fprintf(fp, "break;"); } } fprintf(fp, "}}"); /* *** begin paint() *** */ fprintf(fp, "public void paint(Graphics g) {"); fprintf(fp, "switch (which) {"); /* *** write code for constraint-less visual nodes *** */ fprintf(fp, "case 0:"); fprintf(fp, "draw(g);"); fprintf(fp, "break;"); /* *** write code for visual nodes having inbound constraints *** */ dl_traverse(tmp, cstr_list) { cstr = (CSTR *)tmp->val; if (!cstr->count) continue; fprintf(fp, "case %d:", cstr->count); dl_traverse(tmp2, cstr->nodes_to) { node = (NODE *)tmp2->val; sprintf(drawnstr, "%s_drawn%d", node->what == n_Image ? "image" : "text", node->count); fprintf(fp, "if ("); r_print_constraint(fp, node->constraint); fprintf(fp, ") {"); write_placenode(fp, node); fprintf(fp, "handle_last_roll(g);"); fprintf(fp, "%s = true;}", drawnstr); fprintf(fp, "else if (%s) {", drawnstr); write_clip(fp, cstr->count); fprintf(fp, "draw(g);"); fprintf(fp, "%s = false;}", drawnstr); } /* dl_traverse(tmp2, cstr->nodes_to) */ fprintf(fp, "break;"); } /* dl_traverse(tmp, cstr_list) */ /* *** done: reset "which" for re-paint on exposure *** */ fprintf(fp, "}"); fprintf(fp, "which = 0;"); fprintf(fp, "last_roll = 0;"); fprintf(fp, "repaint_needed--;"); /* one less unfulfilled call! */ fprintf(fp, "}"); } /* write_paint */ /* ------------------------------------------------------------------------ gen_code - write Java code to file based on input file name, node-list, constraint-list, and tuple-list. We override the update() method to eliminate erasing, and we override the start() method to ensure that the entire page is re-drawn if we leave and re-enter the applet (by setting the global switch "which" back to zero). ------------------------------------------------------------------------ */ void gen_code(char *iname, Dlist node_list, Dlist cstr_list) { char cname[80], jname[80], *cp; FILE *fp; strcpy(cname, iname); /* make class name from input file name */ cp = strchr(cname, '.'); *cp = 0; strcpy(jname, cname); /* make output file name from class name */ strcat(jname, ".java"); #ifdef DBG fp = stdout; #else if (!(fp=fopen(jname, "w"))) /* attempt open; exit on failure */ { fprintf(stderr, "Error opening %s for output", jname); exit(-1); } #endif /* print generic leading code */ fprintf(fp, "import java.awt.Image;"); fprintf(fp, "import java.awt.Font;"); fprintf(fp, "import java.awt.FontMetrics;"); fprintf(fp, "import java.awt.MediaTracker;"); fprintf(fp, "import java.awt.Event;"); fprintf(fp, "import java.awt.Graphics;"); fprintf(fp, "import java.awt.Color;"); fprintf(fp, "import java.net.URL;"); fprintf(fp, "import java.net.MalformedURLException;"); fprintf(fp, "import java.applet.AudioClip;"); fprintf(fp, "public class %s extends java.applet.Applet {", cname); declare_vars(fp, node_list, cstr_list); /* declare variables */ write_init(fp, node_list, cstr_list); /* write init() method */ write_handleEvent(fp, /* write handleEvent() method */ node_list, cstr_list); fprintf(fp, "public void update(Graphics g) {"); fprintf(fp, "paint(g);"); fprintf(fp, "}"); /* override update() method */ fprintf(fp, "public void start() {"); fprintf(fp, "which = 0;"); fprintf(fp, "last_roll = 0;"); fprintf(fp, "}"); /* override start() method */ write_paint(fp, node_list, cstr_list); /* write paint() method */ fprintf(fp, "}"); /* print generic trailing code */ fclose(fp); /* done */ } /* gen_code */