001: /*
002: * Author: Chris Seguin
003: *
004: * This software has been developed under the copyleft
005: * rules of the GNU General Public License. Please
006: * consult the GNU General Public License for more
007: * details about use and distribution of this software.
008: */
009: package org.acm.seguin.pretty;
010:
011: import net.sourceforge.jrefactory.parser.Token;
012: import org.acm.seguin.util.SettingNotFoundException;
013: import org.acm.seguin.util.Settings;
014:
015: /**
016: * Prints a description from a java doc comment with HTML tags formatted.
017: *
018: * @author Chris Seguin
019: * @author Mike Atkinson
020: * @created July 23, 1999
021: */
022: public class JavadocDescriptionPrinter {
023: /*<Instance Variables>*/
024: private PrintData printData;
025: private StringBuffer buffer;
026: private int indent;
027: private int mode;
028: private boolean newline;
029: private int owedLines;
030: private boolean withinID;
031: // are we within a "$Id: JavadocDescriptionPrinter.java,v 1.8 2004/05/05 20:56:29 mikeatkinson Exp $" sequence (if so don't do line breaks)
032: private boolean lastLineWasCComment = false;
033: /*</Instance Variables>*/
034:
035: /*<Class Variables>*/
036: private static int NORMAL = 0;
037: private static int PARA = 1;
038: private static int LIST = 2;
039: private static int END_LIST = 3;
040: private static int TABLE = 4;
041: private static int END_TAG = 5;
042: private static int LINE_BREAK = 6;
043: private static int PREFORMATTED = 7;
044:
045: /*</Class Variables>*/
046:
047: /*<Constructors>*/
048: /**
049: * Constructor for the JavadocDescriptionPrinter object
050: *
051: * @param data Description of Parameter
052: * @param description Description of Parameter
053: */
054: public JavadocDescriptionPrinter(PrintData data, String description) {
055: this (data, description, data.getJavadocIndent());
056: }
057:
058: /**
059: * Constructor for the JavadocDescriptionPrinter object
060: *
061: * @param data Description of Parameter
062: * @param description Description of Parameter
063: * @param initIndent Description of Parameter
064: */
065: public JavadocDescriptionPrinter(PrintData data,
066: String description, int initIndent) {
067: printData = data;
068: buffer = new StringBuffer(description);
069: indent = initIndent;
070: newline = true;
071: withinID = false;
072: mode = NORMAL;
073: }
074:
075: /*</Constructors>*/
076:
077: /*<Methods>*/
078: /** This is the main program. */
079: public void run() {
080: if (printData.isReformatComments()) {
081: int MIN = printData.getJavadocWordWrapMinimum();
082: int MAX = printData.getJavadocWordWrapMaximum();
083:
084: JavadocTokenizer tok = new JavadocTokenizer(buffer
085: .toString());
086: mode = NORMAL;
087: boolean first = true;
088: while (tok.hasNext()) {
089: Token nextToken = tok.next();
090: first = printToken(nextToken, MIN, MAX, first);
091: }
092: } else {
093: maintainCurrentFormat();
094: }
095: }
096:
097: /** Indents the line and inserts the required "*" */
098: protected void indent() {
099: if (printData.isCurrentSingle()) {
100: return;
101: }
102:
103: newline = true;
104: printData.indent();
105: if (!printData.isStarsAlignedWithSlash()) {
106: printData.space();
107: }
108: printData.appendComment("*", PrintData.JAVADOC_COMMENT);
109:
110: if (printData.isReformatComments() && (mode != PREFORMATTED)) {
111: for (int ndx = 0; ndx < indent; ndx++) {
112: printData.space();
113: }
114: }
115: }
116:
117: /**
118: * Certain tags require that we insert a new line after them.
119: *
120: * @param forToken the tag that we are considering
121: * @return true if we just printed a space or a newline
122: */
123: protected boolean startMode(Token forToken) {
124: String token = forToken.image.toUpperCase();
125: if (startsWith(token, "<PRE") || startsWith(token, "<CODE")) {
126: mode = PREFORMATTED;
127: } else if (startsWith(token, "</PRE")
128: || startsWith(token, "</CODE")) {
129: mode = NORMAL;
130: } else if (startsWith(token, "<P")) {
131: mode = PARA;
132: } else if (startsWith(token, "<BR")) {
133: mode = LINE_BREAK;
134: } else if (startsWith(token, "<UL")) {
135: mode = LIST;
136: indent();
137: indent += 2;
138: return true;
139: } else if (startsWith(token, "<OL")) {
140: mode = LIST;
141: indent();
142: indent += 2;
143: return true;
144: } else if (startsWith(token, "<DL")) {
145: mode = LIST;
146: indent();
147: indent += 2;
148: return true;
149: } else if (startsWith(token, "</UL")) {
150: mode = END_LIST;
151: indent -= 2;
152: indent();
153: return true;
154: } else if (startsWith(token, "</OL")) {
155: mode = END_LIST;
156: indent -= 2;
157: indent();
158: return true;
159: } else if (startsWith(token, "</DL")) {
160: mode = END_LIST;
161: indent -= 2;
162: indent();
163: return true;
164: } else if (startsWith(token, "<LI")) {
165: indent();
166: mode = END_TAG;
167: return true;
168: } else if (startsWith(token, "<DD")) {
169: indent();
170: mode = END_TAG;
171: return true;
172: } else if (startsWith(token, "<DT")) {
173: indent();
174: mode = END_TAG;
175: return true;
176: } else if (startsWith(token, "<TABLE")) {
177: mode = TABLE;
178: indent();
179: indent += 2;
180: return true;
181: } else if (startsWith(token, "<TR")) {
182: mode = TABLE;
183: indent();
184: indent += 2;
185: return true;
186: } else if (startsWith(token, "<TD")) {
187: mode = TABLE;
188: indent();
189: indent += 2;
190: return true;
191: } else if (startsWith(token, "<TH")) {
192: mode = TABLE;
193: indent();
194: indent += 2;
195: return true;
196: } else if (startsWith(token, "</TABLE")) {
197: mode = TABLE;
198: indent -= 2;
199: indent();
200: return true;
201: } else if (startsWith(token, "</TR")) {
202: mode = TABLE;
203: indent -= 2;
204: indent();
205: return true;
206: } else if (startsWith(token, "</TD")) {
207: mode = TABLE;
208: indent -= 2;
209: indent();
210: return true;
211: } else if (startsWith(token, "</TH")) {
212: mode = TABLE;
213: indent -= 2;
214: indent();
215: return true;
216: } else if (startsWith(token, "</") && !newline) {
217: mode = END_TAG;
218: }
219:
220: return false;
221: }
222:
223: /**
224: * Certain tags require that we insert a space after them.
225: *
226: * @param token the tag that we are considering
227: * @return true if we just printed a space or a newline
228: * @since JRefactory 2.7.00
229: */
230: protected boolean spaceRequired(Token token) {
231: return spaceRequired(token.image.toUpperCase());
232: }
233:
234: /**
235: * Detects the end of the tag marker
236: *
237: * @param forToken the token
238: * @return <code>true</code> if a newline has been issued.
239: */
240: protected boolean endMode(Token forToken) {
241: String token = forToken.image;
242: if (mode == END_TAG) {
243: mode = NORMAL;
244: printData.space();
245: return true;
246: }
247: if (mode == PARA) {
248: mode = NORMAL;
249: indent();
250: indent();
251: return true;
252: }
253: if (mode == LINE_BREAK) {
254: mode = NORMAL;
255: indent();
256: return true;
257: }
258: if (mode == LIST) {
259: mode = NORMAL;
260: }
261: if (mode == END_LIST) {
262: mode = NORMAL;
263: indent();
264: return true;
265: }
266: if (mode == TABLE) {
267: mode = NORMAL;
268: indent();
269: return true;
270: }
271:
272: return false;
273: }
274:
275: /**
276: * Description of the Method
277: *
278: * @param nextToken Description of Parameter
279: * @param MIN Description of Parameter
280: * @param MAX Description of Parameter
281: * @param isFirst Description of Parameter
282: * @return Description of the Returned Value
283: */
284: private boolean printToken(Token nextToken, int MIN, int MAX,
285: boolean isFirst) {
286: if (nextToken.kind == JavadocTokenizer.WORD) {
287: newline = false;
288: if (nextToken.image.equals("$Id:")) {
289: withinID = true;
290: }
291: int length = nextToken.image.length();
292: if (!withinID && (printData.getLineLength() > MIN)
293: && (printData.getLineLength() + length > MAX)
294: && (mode != PREFORMATTED)) {
295: indent();
296: newline = true;
297: }
298: if (nextToken.image.charAt(0) == '<') {
299: newline = startMode(nextToken);
300: } else {
301: newline = false;
302: }
303: if (nextToken.image.equals("$")) {
304: withinID = false;
305: }
306:
307: if ((newline && (indent == 0) && nextToken.image
308: .startsWith("/"))) {
309: printData.space();
310: }
311: if ((!newline && (indent == 0) && nextToken.image
312: .startsWith("//"))) {
313: indent();
314: newline = true;
315: printData.space();
316: lastLineWasCComment = true;
317: } else if (lastLineWasCComment) {
318: indent();
319: newline = true;
320: lastLineWasCComment = false;
321: }
322:
323: printData.appendComment(nextToken.image,
324: PrintData.JAVADOC_COMMENT);
325:
326: if (spaceRequired(nextToken)) {
327: printData.space();
328: }
329:
330: if (nextToken.image.charAt(nextToken.image.length() - 1) == '>') {
331: newline = endMode(nextToken) || newline;
332: }
333: return newline;
334: } else {
335: if (mode != PREFORMATTED) {
336: if (!isFirst) {
337: printData.space();
338: return true;
339: }
340: } else if (nextToken.kind == JavadocTokenizer.SPACE) {
341: printData.appendComment(nextToken.image,
342: PrintData.JAVADOC_COMMENT);
343: } else {
344: indent();
345: }
346:
347: return isFirst;
348: }
349: }
350:
351: /** Maintains the current format */
352: private void maintainCurrentFormat() {
353: JavadocTokenizer tok = new JavadocTokenizer(buffer.toString());
354: owedLines = 0;
355:
356: Token last = null;
357: Token current = tok.next();
358:
359: while (current.kind != JavadocTokenizer.WORD) {
360: last = current;
361: if (!tok.hasNext()) {
362: return;
363: }
364: current = tok.next();
365: }
366:
367: if ((last != null) && (last.kind != JavadocTokenizer.NEWLINE)) {
368: mcfOutputToken(last, printData);
369: }
370: mcfOutputToken(current, printData);
371:
372: while (tok.hasNext()) {
373: Token nextToken = tok.next();
374: mcfOutputToken(nextToken, printData);
375: }
376: }
377:
378: /**
379: * Description of the Method
380: *
381: * @param nextToken Description of Parameter
382: * @param printData Description of Parameter
383: */
384: private void mcfOutputToken(Token nextToken, PrintData printData) {
385: if (nextToken.kind == JavadocTokenizer.NEWLINE) {
386: owedLines++;
387: } else {
388: while (owedLines > 0) {
389: indent();
390: owedLines--;
391: }
392:
393: printData.appendComment(nextToken.image,
394: PrintData.JAVADOC_COMMENT);
395: }
396: }
397:
398: /**
399: * Certain tags require that we insert a space after them.
400: *
401: * @param token the tag that we are considering (should be upper case).
402: * @return true if we just printed a space or a newline
403: * @since JRefactory 2.9.17
404: */
405: public static boolean spaceRequired(String token) {
406: if (startsWithThenSpace(token, "<PRE")) {
407: return true;
408: } else if (startsWithThenSpace(token, "<P")) {
409: return true;
410: } else if (startsWithThenSpace(token, "<BR")) {
411: return true;
412: } else if (startsWithThenSpace(token, "<UL")) {
413: return true;
414: } else if (startsWithThenSpace(token, "<OL")) {
415: return true;
416: } else if (startsWithThenSpace(token, "<DL")) {
417: return true;
418: } else if (startsWithThenSpace(token, "<LI")) {
419: return true;
420: } else if (startsWithThenSpace(token, "<DD")) {
421: return true;
422: } else if (startsWithThenSpace(token, "<DT")) {
423: return true;
424: } else if (startsWithThenSpace(token, "<TABLE")) {
425: return true;
426: } else if (startsWithThenSpace(token, "<TR")) {
427: return true;
428: } else if (startsWithThenSpace(token, "<TD")) {
429: return true;
430: } else if (startsWithThenSpace(token, "<TH")) {
431: return true;
432: } else if (startsWithThenSpace(token, "//")) {
433: return true;
434: }
435:
436: return false;
437: }
438:
439: /**
440: * Checks to see if this tag is the same as what we want and ignores case troubles
441: *
442: * @param have the token that we have
443: * @param want the token that we are interested in
444: * @return <code>true</code> if what we have is the same as what we want
445: */
446: private static boolean startsWith(String have, String want) {
447: return have.startsWith(want);
448: }
449:
450: /**
451: * Checks to see if this tag is the same as what we want and ignores case troubles
452: *
453: * @param have the token that we have
454: * @param want the token that we are interested in
455: * @return true if what we have is the same as what we want
456: * @since JRefactory 2.7.00
457: */
458: private static boolean startsWithThenSpace(String have, String want) {
459: return have.startsWith(want) && have.length() == want.length();
460: }
461: }
|