|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * This code is free software; you can redistribute it and/or modify it
|
|
27 | 27 |
|
28 | 28 | import com.sun.tools.javac.parser.Tokens.Comment;
|
29 | 29 | import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
|
30 |
| -import com.sun.tools.javac.util.*; |
| 30 | +import com.sun.tools.javac.util.JCDiagnostic; |
| 31 | +import com.sun.tools.javac.util.Position; |
31 | 32 |
|
32 | 33 | import java.nio.CharBuffer;
|
33 | 34 | import java.util.Arrays;
|
34 |
| -import java.util.regex.Pattern; |
35 | 35 |
|
36 | 36 | /**
|
37 | 37 | * An extension to the base lexical analyzer (JavaTokenizer) that
|
@@ -170,6 +170,11 @@ protected void scanDocComment() {
|
170 | 170 | offsetMap.trim();
|
171 | 171 | }
|
172 | 172 | }
|
| 173 | + |
| 174 | + @Override |
| 175 | + public Comment stripIndent() { |
| 176 | + return StrippedComment.of(this); |
| 177 | + } |
173 | 178 | }
|
174 | 179 |
|
175 | 180 | /**
|
@@ -354,4 +359,154 @@ int getSourcePos(int pos) {
|
354 | 359 | return map[startScaled + POS_OFFSET] + (pos - map[startScaled + SB_OFFSET]);
|
355 | 360 | }
|
356 | 361 | }
|
| 362 | + |
| 363 | + /** |
| 364 | + * A Comment derived from a JavadocComment with leading whitespace removed from all lines. |
| 365 | + * A new OffsetMap is used in combination with the OffsetMap of the original comment to |
| 366 | + * translate comment locations to positions in the source file. |
| 367 | + * |
| 368 | + * Note: This class assumes new lines are encoded as {@code '\n'}, which is the case |
| 369 | + * for comments created by {@code JavadocTokenizer}. |
| 370 | + */ |
| 371 | + static class StrippedComment implements Comment { |
| 372 | + String text; |
| 373 | + final OffsetMap strippedMap; |
| 374 | + final OffsetMap sourceMap; |
| 375 | + // Copy these fields to not hold a reference to the original comment with its text |
| 376 | + final JCDiagnostic.DiagnosticPosition diagPos; |
| 377 | + final CommentStyle style; |
| 378 | + final boolean deprecated; |
| 379 | + |
| 380 | + /** |
| 381 | + * Returns a stripped version of the comment, or the comment itself if there is no |
| 382 | + * whitespace that can be stripped. |
| 383 | + * |
| 384 | + * @param comment the original comment |
| 385 | + * @return stripped or original comment |
| 386 | + */ |
| 387 | + static Comment of(JavadocComment comment) { |
| 388 | + if (comment.getStyle() != CommentStyle.JAVADOC_BLOCK) { |
| 389 | + return comment; |
| 390 | + } |
| 391 | + int indent = getIndent(comment); |
| 392 | + return indent > 0 ? new StrippedComment(comment, indent) : comment; |
| 393 | + } |
| 394 | + |
| 395 | + private StrippedComment(JavadocComment comment, int indent) { |
| 396 | + this.diagPos = comment.getPos(); |
| 397 | + this.style = comment.getStyle(); |
| 398 | + this.deprecated = comment.isDeprecated(); |
| 399 | + this.strippedMap = new OffsetMap(); |
| 400 | + this.sourceMap = comment.offsetMap; |
| 401 | + stripComment(comment, indent); |
| 402 | + } |
| 403 | + |
| 404 | + /** |
| 405 | + * Determines the number of leading whitespace characters that can be removed from |
| 406 | + * all non-blank lines of the original comment. |
| 407 | + * |
| 408 | + * @param comment the original comment |
| 409 | + * @return number of leading whitespace characters that can be reomved |
| 410 | + */ |
| 411 | + static int getIndent(Comment comment) { |
| 412 | + String txt = comment.getText(); |
| 413 | + int len = txt.length(); |
| 414 | + int indent = Integer.MAX_VALUE; |
| 415 | + |
| 416 | + for (int i = 0; i < len; ) { |
| 417 | + int next; |
| 418 | + boolean inIndent = true; |
| 419 | + for (next = i; next < len && txt.charAt(next) != '\n'; next++) { |
| 420 | + if (inIndent && !Character.isWhitespace(txt.charAt(next))) { |
| 421 | + indent = Math.min(indent, next - i); |
| 422 | + inIndent = false; |
| 423 | + } |
| 424 | + } |
| 425 | + i = next + 1; |
| 426 | + } |
| 427 | + |
| 428 | + return indent == Integer.MAX_VALUE ? 0 : indent; |
| 429 | + } |
| 430 | + |
| 431 | + /** |
| 432 | + * Strips {@code indent} whitespace characters from every line of the original comment |
| 433 | + * and initializes an OffsetMap to translate positions to the original comment's OffsetMap. |
| 434 | + * This method does not distinguish between blank and non-blank lines except for the fact |
| 435 | + * that blank lines are not required to contain the number of leading whitespace indicated |
| 436 | + * by {@indent}. |
| 437 | + * |
| 438 | + * @param comment the original comment |
| 439 | + * @param indent number of whitespace characters to remove from each non-blank line |
| 440 | + */ |
| 441 | + private void stripComment(JavadocComment comment, int indent) { |
| 442 | + String txt = comment.getText(); |
| 443 | + int len = txt.length(); |
| 444 | + StringBuilder sb = new StringBuilder(len); |
| 445 | + |
| 446 | + for (int i = 0; i < len; ) { |
| 447 | + int startOfLine = i; |
| 448 | + // Advance till start of stripped line, or \n if line is blank |
| 449 | + while (startOfLine < len |
| 450 | + && startOfLine < i + indent |
| 451 | + && txt.charAt(startOfLine) != '\n') { |
| 452 | + assert(Character.isWhitespace(txt.charAt(startOfLine))); |
| 453 | + startOfLine++; |
| 454 | + } |
| 455 | + if (startOfLine == len) { |
| 456 | + break; |
| 457 | + } |
| 458 | + |
| 459 | + // Copy stripped line (terminated by \n or end of input) |
| 460 | + i = startOfLine + 1; |
| 461 | + while (i < len && txt.charAt(i - 1) != '\n') { |
| 462 | + i++; |
| 463 | + } |
| 464 | + // Add new offset if necessary |
| 465 | + strippedMap.add(sb.length(), startOfLine); |
| 466 | + sb.append(txt, startOfLine, i); |
| 467 | + } |
| 468 | + |
| 469 | + text = sb.toString(); |
| 470 | + strippedMap.trim(); |
| 471 | + } |
| 472 | + |
| 473 | + @Override |
| 474 | + public String getText() { |
| 475 | + return text; |
| 476 | + } |
| 477 | + |
| 478 | + @Override |
| 479 | + public Comment stripIndent() { |
| 480 | + return this; |
| 481 | + } |
| 482 | + |
| 483 | + @Override |
| 484 | + public int getSourcePos(int pos) { |
| 485 | + if (pos == Position.NOPOS) { |
| 486 | + return Position.NOPOS; |
| 487 | + } |
| 488 | + |
| 489 | + if (pos < 0 || pos > text.length()) { |
| 490 | + throw new StringIndexOutOfBoundsException(String.valueOf(pos)); |
| 491 | + } |
| 492 | + |
| 493 | + return sourceMap.getSourcePos(strippedMap.getSourcePos(pos)); |
| 494 | + } |
| 495 | + |
| 496 | + @Override |
| 497 | + public JCDiagnostic.DiagnosticPosition getPos() { |
| 498 | + return diagPos; |
| 499 | + } |
| 500 | + |
| 501 | + @Override |
| 502 | + public CommentStyle getStyle() { |
| 503 | + return style; |
| 504 | + } |
| 505 | + |
| 506 | + @Override |
| 507 | + public boolean isDeprecated() { |
| 508 | + return deprecated; |
| 509 | + } |
| 510 | + } |
| 511 | + |
357 | 512 | }
|
0 commit comments