1:  import java.util.*;
  2:  import java.io.*;
  3:  
  4:  
  5:  /**
  6:   * JMarkup converts Java source code to HTML.
  7:   *
  8:   * @author: Stefan Zeiger
  9:   */
 10:  
 11:  public final class JMarkup
 12:  {
 13:    public static void main(String[] args) throws Exception
 14:    {
 15:      String instr = null, outstr = null;
 16:      JMarkup j = new JMarkup();
 17:  
 18:      for(int i=0; i<args.length; i++)
 19:      {
 20:        if(args[i].equals("?") || args[i].equals("-h") ||
 21:           args[i].equals("--help"))
 22:        {
 23:          System.err.println
 24:            ("Syntax: JMarkup [options]\n"+
 25:             "Options: ?, -h, --help   Show this help\n"+
 26:             "         -i <filename>   Input file (default: stdin)\n"+
 27:             "         -o <filename>   Output file (default: stdout)\n"+
 28:             "         -c              Don't mark constants\n"+
 29:             "         -l              Don't add line numbers\n"+
 30:             "         -t <int>        Tab size (default: 8)\n"+
 31:             "         -n <int>        First line number (default: 0)\n"+
 32:             "         -w <int>        Line number width (default: 3)\n"+
 33:             "         --seprn         Separate lines by \\r\\n (default: \\n)\n"+
 34:             "         --rwcol <hex>   Reserved word color (default: 0000C8)\n"+
 35:             "         --cmcol <hex>   Comment color (default: 6820A8)\n"+
 36:             "         --slcol <hex>   String literal color (default: 008800)\n"+
 37:             "         --cncol <hex>   Constant color (default: 880000)\n"+
 38:             "         --lncol <hex>   Line number color (default: 880000)\n"+
 39:             "         --header <text> Header code\n"+
 40:             "         --footer <text> Footer code\n"+
 41:             "infile defaults to stdin, outfile to stdout");
 42:          System.exit(0);
 43:        }
 44:        else if(args[i].equals("-i")) instr = args[++i];
 45:        else if(args[i].equals("-o")) outstr = args[++i];
 46:        else if(args[i].equals("-c")) j.setMarkConstants(false);
 47:        else if(args[i].equals("-l")) j.setAddLineNumbers(false);
 48:        else if(args[i].equals("-t")) j.setTabSize(Integer.parseInt(args[++i]));
 49:        else if(args[i].equals("-n"))
 50:          j.setLineNumber(Integer.parseInt(args[++i]));
 51:        else if(args[i].equals("-w"))
 52:          j.setLineNumberWidth(Integer.parseInt(args[++i]));
 53:        else if(args[i].equals("--seprn")) j.setLineSep("\r\n");
 54:        else if(args[i].equals("--rwcol")) j.setReservedWordColor(args[++i]);
 55:        else if(args[i].equals("--cmcol")) j.setCommentColor(args[++i]);
 56:        else if(args[i].equals("--slcol")) j.setStringColor(args[++i]);
 57:        else if(args[i].equals("--cncol")) j.setConstantColor(args[++i]);
 58:        else if(args[i].equals("--lncol")) j.setLineNumberColor(args[++i]);
 59:        else if(args[i].equals("--header")) j.setHeader(args[++i]);
 60:        else if(args[i].equals("--footer")) j.setFooter(args[++i]);
 61:        else
 62:        {
 63:          System.err.println("Illegal argument: "+args[i]);
 64:          System.exit(1);
 65:        }
 66:      }
 67:  
 68:      Reader in;
 69:      if(instr != null) in = new FileReader(instr);
 70:      else in = new InputStreamReader(System.in);
 71:  
 72:      Writer out;
 73:      if(outstr != null) out = new FileWriter(outstr);
 74:      else out = new OutputStreamWriter(System.out);
 75:  
 76:      j.parse(new BufferedReader(in), new PrintWriter(new BufferedWriter(out)));
 77:    }
 78:  
 79:  
 80:    private static final int STATE_NORMAL = 0;
 81:    private static final int STATE_INLINECOMMENT = 1;
 82:    private static final int STATE_INSTARCOMMENT = 2;
 83:    private static final int STATE_INSTRINGLITERAL = 3;
 84:    private static final int STATE_INCHARLITERAL = 4;
 85:  
 86:    private Reader in;
 87:    private PrintWriter out;
 88:    private Hashtable reservedWordTable = new Hashtable();
 89:    private String appliedFG = "", currentFG = "";
 90:    private int tabSize = 8, xpos = 0, state = STATE_NORMAL, lineno = 1;
 91:    private int lineNumberWidth = 3;
 92:    private String linesep = "\n";
 93:    private String reservedWordColor = "0000C8";
 94:    private String commentColor = "6820A8";
 95:    private String stringColor = "008800";
 96:    private String constantColor = "880000";
 97:    private String lineNumberColor = "880000";
 98:    private boolean addLineNumbers = true, markConstants = true;
 99:  
100:    private String header =
101:    "<TABLE BGCOLOR=\"#E0E0E0\" BORDER=0 CELLPADDING=8 CELLSPACING=0>"+
102:    "<TR><TD><PRE><FONT COLOR=\"#000000\">";
103:  
104:    private String footer = "</FONT></PRE></TD></TR></TABLE>";
105:  
106:  
107:    public JMarkup()
108:    {
109:      reservedWordTable.put("abstract", "");
110:      reservedWordTable.put("boolean", "");
111:      reservedWordTable.put("break", "");
112:      reservedWordTable.put("byte", "");
113:      reservedWordTable.put("case", "");
114:      reservedWordTable.put("catch", "");
115:      reservedWordTable.put("char", "");
116:      reservedWordTable.put("class", "");
117:      reservedWordTable.put("const", "");
118:      reservedWordTable.put("continue", "");
119:      reservedWordTable.put("default", "");
120:      reservedWordTable.put("do", "");
121:      reservedWordTable.put("double", "");
122:      reservedWordTable.put("else", "");
123:      reservedWordTable.put("extends", "");
124:      reservedWordTable.put("false", "");
125:      reservedWordTable.put("final", "");
126:      reservedWordTable.put("finally", "");
127:      reservedWordTable.put("float", "");
128:      reservedWordTable.put("for", "");
129:      reservedWordTable.put("goto", "");
130:      reservedWordTable.put("if", "");
131:      reservedWordTable.put("implements", "");
132:      reservedWordTable.put("import", "");
133:      reservedWordTable.put("instanceof", "");
134:      reservedWordTable.put("int", "");
135:      reservedWordTable.put("interface", "");
136:      reservedWordTable.put("long", "");
137:      reservedWordTable.put("native", "");
138:      reservedWordTable.put("new", "");
139:      reservedWordTable.put("null", "");
140:      reservedWordTable.put("package", "");
141:      reservedWordTable.put("private", "");
142:      reservedWordTable.put("protected", "");
143:      reservedWordTable.put("public", "");
144:      reservedWordTable.put("return", "");
145:      reservedWordTable.put("short", "");
146:      reservedWordTable.put("static", "");
147:      reservedWordTable.put("super", "");
148:      reservedWordTable.put("switch", "");
149:      reservedWordTable.put("synchronized", "");
150:      reservedWordTable.put("this", "");
151:      reservedWordTable.put("throw", "");
152:      reservedWordTable.put("throws", "");
153:      reservedWordTable.put("transient", "");
154:      reservedWordTable.put("true", "");
155:      reservedWordTable.put("try", "");
156:      reservedWordTable.put("void", "");
157:      reservedWordTable.put("volatile", "");
158:      reservedWordTable.put("while", "");
159:    }
160:  
161:  
162:    public void parse(Reader in, PrintWriter out) throws IOException
163:    {
164:      this.in = in;
165:      this.out = out;
166:  
167:      int prevttype = 0;
168:      out.println(header);
169:      StreamTokenizer tok = new StreamTokenizer(in);
170:      tok.ordinaryChars('0','9');
171:      tok.ordinaryChars(0,' ');
172:      tok.ordinaryChar('.');
173:      tok.ordinaryChar('-');
174:      tok.ordinaryChar('/');
175:      tok.ordinaryChar('\'');
176:      tok.ordinaryChar('\"');
177:      tok.whitespaceChars('\r','\r');
178:      tok.wordChars('_','_');
179:      while(tok.nextToken() != tok.TT_EOF)
180:      {
181:        switch(state)
182:        {
183:          case STATE_NORMAL:
184:            switch(tok.ttype)
185:            {
186:              case tok.TT_WORD:
187:                if(reservedWordTable.containsKey(tok.sval))
188:                  currentFG = reservedWordColor;
189:                else if(markConstants && tok.sval.toUpperCase().equals(tok.sval))
190:                  currentFG = constantColor;
191:                else
192:                  currentFG = "";
193:                write(tok.sval);
194:                break;
195:  
196:              case tok.TT_EOL: write('\n'); break;
197:  
198:              case '/':
199:                int next = tok.nextToken();
200:                if(next == '/')
201:                {
202:                  currentFG = commentColor;
203:                  state = STATE_INLINECOMMENT;
204:                  write('/');
205:                  write('/');
206:                }
207:                else if(next == '*')
208:                {
209:                  currentFG = commentColor;
210:                  state = STATE_INSTARCOMMENT;
211:                  write('/');
212:                  write('*');
213:                }
214:                else
215:                {
216:                  tok.pushBack();
217:                  currentFG = "";
218:                  write('/');
219:                }
220:                break;
221:  
222:              case '\"':
223:                currentFG = "";
224:                write('\"');
225:                state = STATE_INSTRINGLITERAL;
226:                currentFG = stringColor;
227:                break;
228:  
229:              case '\'':
230:                currentFG = "";
231:                write('\'');
232:                state = STATE_INCHARLITERAL;
233:                currentFG = stringColor;
234:                break;
235:  
236:              default:
237:                currentFG = "";
238:                write((char)tok.ttype);
239:                break;
240:            }
241:            break;
242:  
243:          case STATE_INLINECOMMENT:
244:            switch(tok.ttype)
245:            {
246:              case tok.TT_WORD: write(tok.sval); break;
247:  
248:              case tok.TT_EOL:
249:                write('\n');
250:                currentFG = "";
251:                state = STATE_NORMAL;
252:                break;
253:  
254:              default: write((char)tok.ttype);
255:            }
256:            break;
257:  
258:          case STATE_INSTARCOMMENT:
259:            switch(tok.ttype)
260:            {
261:              case tok.TT_WORD: write(tok.sval); break;
262:  
263:              case tok.TT_EOL: write('\n'); break;
264:  
265:              case '*':
266:                int next = tok.nextToken();
267:                if(next == '/')
268:                {
269:                  write('*');
270:                  write('/');
271:                  currentFG = "";
272:                  state = STATE_NORMAL;
273:                }
274:                else
275:                {
276:                  tok.pushBack();
277:                  write('*');
278:                }
279:                break;
280:  
281:              default:
282:                write((char)tok.ttype);
283:            }
284:            break;
285:  
286:          case STATE_INSTRINGLITERAL:
287:            switch(tok.ttype)
288:            {
289:              case tok.TT_WORD: write(tok.sval); break;
290:  
291:              case tok.TT_EOL: write('\n'); break;
292:  
293:              case '\"':
294:                if(prevttype != '\\')
295:                {
296:                  currentFG = "";
297:                  state = STATE_NORMAL;
298:                }
299:  
300:              default: write((char)tok.ttype);
301:            }
302:            break;
303:  
304:          case STATE_INCHARLITERAL:
305:            switch(tok.ttype)
306:            {
307:              case tok.TT_WORD: write(tok.sval); break;
308:  
309:              case tok.TT_EOL: write('\n'); break;
310:  
311:              case '\'':
312:                if(prevttype != '\\')
313:                {
314:                  currentFG = "";
315:                  state = STATE_NORMAL;
316:                }
317:  
318:              default: write((char)tok.ttype);
319:            }
320:            break;
321:        }
322:        if(prevttype == '\\' && tok.ttype == '\\') prevttype = 0;
323:        else prevttype = tok.ttype;
324:      }
325:      unapplyFG();
326:      out.println(footer);
327:      out.flush();
328:    }
329:  
330:  
331:    private void write(String s)
332:    {
333:      for(int i=0; i<s.length(); i++) write(s.charAt(i));
334:    }
335:  
336:  
337:    private void write(char c)
338:    {
339:      if(addLineNumbers && xpos == 0)
340:      {
341:        String rememberedFG = currentFG;
342:        currentFG = lineNumberColor;
343:        applyFG();
344:        String linenoStr = Integer.toString(lineno, 10);
345:        for(int i=linenoStr.length(); i<lineNumberWidth; i++) out.print(' ');
346:        out.print(linenoStr);
347:        out.print(":  ");
348:        currentFG = rememberedFG;
349:      }
350:  
351:      if(" \r\n\t\f".indexOf(c) == -1) applyFG();
352:      switch(c)
353:      {
354:        case '<':
355:          out.print("<");
356:          break;
357:  
358:        case '>':
359:          out.print(">");
360:          break;
361:  
362:        case '\t':
363:          if(tabSize > 0) // untabify
364:          {
365:            int nextstop = ((xpos/tabSize)+1)*tabSize;
366:            for(int i=xpos; i<nextstop; i++) out.print(' ');
367:            xpos = nextstop - 1;
368:          }
369:          else out.print('\t');
370:          break;
371:  
372:        case '\n':
373:          xpos = -1;
374:          lineno++;
375:          out.print(linesep);
376:          break;
377:  
378:        default:
379:          out.print(c);
380:      }
381:      xpos++;
382:    }
383:  
384:  
385:    private void applyFG()
386:    {
387:      if(currentFG.equals(appliedFG)) return;
388:      unapplyFG();
389:      if(currentFG.length() != 0) out.print("<FONT COLOR=\"#"+currentFG+"\">");
390:      appliedFG = currentFG;
391:    }
392:  
393:  
394:    private void unapplyFG()
395:    {
396:      if(appliedFG.length() != 0)
397:      {
398:        out.print("</FONT>");
399:        appliedFG = "";
400:      }
401:    }
402:  
403:  
404:    public void setMarkConstants(boolean b) { markConstants = b; }
405:    public void setAddLineNumbers(boolean b) { addLineNumbers = b; }
406:    public void setTabSize(int i) { tabSize = i; }
407:    public void setLineNumber(int i) { lineno = i; }
408:    public void setLineNumberWidth(int i) { lineNumberWidth = i; }
409:    public void setLineSep(String s) { linesep = s; }
410:    public void setReservedWordColor(String s) { reservedWordColor = s; }
411:    public void setCommentColor(String s) { commentColor = s; }
412:    public void setStringColor(String s) { stringColor = s; }
413:    public void setConstantColor(String s) { constantColor = s; }
414:    public void setLineNumberColor(String s) { lineNumberColor = s; }
415:    public void setHeader(String s) { header = s; }
416:    public void setFooter(String s) { footer = s; }
417:  }