• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 43

20 Jun 2025 06:39PM UTC coverage: 78.375% (-0.002%) from 78.377%
43

push

github

adangel
Fix #1639 #5832: Use filtered comment text for UnnecessaryImport (#5833)

Merged pull request #5833 from adangel:java/issue-5832-unnecessaryimport

17714 of 23438 branches covered (75.58%)

Branch coverage included in aggregate %.

3 of 3 new or added lines in 1 file covered. (100.0%)

109 existing lines in 17 files now uncovered.

38908 of 48807 relevant lines covered (79.72%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.78
/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/impl/javacc/EscapeTranslator.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.lang.ast.impl.javacc;
6

7
import static java.lang.Integer.min;
8

9
import net.sourceforge.pmd.lang.document.Chars;
10
import net.sourceforge.pmd.lang.document.FileLocation;
11
import net.sourceforge.pmd.lang.document.FragmentedDocBuilder;
12
import net.sourceforge.pmd.lang.document.TextDocument;
13
import net.sourceforge.pmd.util.AssertionUtil;
14

15
/**
16
 * An object that can translate an input document into an output document,
17
 * typically by replacing escape sequences with the character they represent.
18
 *
19
 * <p>This is an abstract class because the default implementation does not
20
 * perform any escape processing. Subclasses refine this behavior.
21
 */
22
public abstract class EscapeTranslator {
1✔
23
    // Note that this can easily be turned into a java.io.Reader with
24
    // efficient block IO, optimized for the common case where there are
25
    // few or no escapes. This is part of the history of this file, but
26
    // was removed for simplicity.
27

28
    /**
29
     * Source characters. When there is an escape, eg \ u00a0, the
30
     * first backslash is replaced with the translated value of the
31
     * escape. The bufpos is updated so that we read the next char
32
     * after the escape.
33
     */
34
    protected Chars input;
35
    /** Position of the next char to read in the input. */
36
    protected int bufpos;
37
    /** Keep track of adjustments to make to the offsets, caused by unicode escapes. */
38
    final FragmentedDocBuilder builder;
39

40
    private Chars curEscape;
41
    private int offInEscape;
42

43
    /**
44
     * Create a translator that will read from the given document.
45
     *
46
     * @param original Original document
47
     *
48
     * @throws NullPointerException If the parameter is null
49
     */
50
    public EscapeTranslator(TextDocument original) {
1✔
51
        AssertionUtil.requireParamNotNull("builder", original);
1✔
52
        this.input = original.getText();
1✔
53
        this.bufpos = 0;
1✔
54
        this.builder = new FragmentedDocBuilder(original);
1✔
55
    }
1✔
56

57

58
    /**
59
     * Translate all the input in the buffer. This consumes this object.
60
     *
61
     * @return The translated text document. If there is no escape, returns the original text
62
     *
63
     * @throws IllegalStateException    If this method is called more than once on the same object
64
     * @throws MalformedSourceException If there are invalid escapes in the source
65
     */
66
    public TextDocument translateDocument() throws MalformedSourceException {
67
        ensureOpen();
1✔
68
        try {
69
            return translateImpl();
1✔
70
        } finally {
71
            close();
1✔
72
        }
73
    }
74

75
    private TextDocument translateImpl() {
76
        if (this.bufpos == input.length()) {
1!
UNCOV
77
            return builder.build();
×
78
        }
79

80
        final int len = input.length(); // remove Integer.MAX_VALUE
1✔
81

82
        int readChars = 0;
1✔
83
        while (readChars < len && (this.bufpos < input.length() || curEscape != null)) {
1✔
84
            if (curEscape != null) {
1✔
85
                int toRead = min(len - readChars, curEscape.length() - offInEscape);
1✔
86

87
                readChars += toRead;
1✔
88
                offInEscape += toRead;
1✔
89

90
                if (curEscape.length() == offInEscape) {
1!
91
                    curEscape = null;
1✔
92
                    continue;
1✔
93
                } else {
94
                    break; // len cut us off, we'll retry next time
95
                }
96
            }
97

98
            int bpos = this.bufpos;
1✔
99
            int nextJump = gobbleMaxWithoutEscape(min(input.length(), bpos + len - readChars));
1✔
100
            int newlyReadChars = nextJump - bpos;
1✔
101

102
            assert newlyReadChars >= 0 && (readChars + newlyReadChars) <= len;
1!
103

104
            if (newlyReadChars == 0 && nextJump == input.length()) {
1!
105
                // eof
UNCOV
106
                break;
×
107
            }
108
            readChars += newlyReadChars;
1✔
109
        }
1✔
110
        return builder.build();
1✔
111
    }
112

113
    /**
114
     * Returns the max offset, EXclusive, up to which we can cut the input
115
     * array from the bufpos to dump it into the output array.
116
     *
117
     * @param maxOff Max offset up to which to read ahead
118
     */
119
    protected int gobbleMaxWithoutEscape(int maxOff) throws MalformedSourceException {
UNCOV
120
        this.bufpos = maxOff;
×
121
        return maxOff;
×
122
    }
123

124
    protected int recordEscape(final int startOffsetInclusive, int endOffsetExclusive, Chars translation) {
125
        assert endOffsetExclusive > startOffsetInclusive && startOffsetInclusive >= 0;
1!
126
        this.builder.recordDelta(startOffsetInclusive, endOffsetExclusive, translation);
1✔
127
        this.bufpos = endOffsetExclusive;
1✔
128
        this.curEscape = translation;
1✔
129
        this.offInEscape = 0;
1✔
130
        return startOffsetInclusive;
1✔
131
    }
132

133
    /**
134
     * Closing a translator does not close the underlying document, it just
135
     * clears the intermediary state.
136
     */
137
    private void close() {
138
        this.bufpos = -1;
1✔
139
        this.input = null;
1✔
140
    }
1✔
141

142

143
    /** Check to make sure that the stream has not been closed */
144
    protected final void ensureOpen() {
145
        if (input == null) {
1!
UNCOV
146
            throw new IllegalStateException("Closed");
×
147
        }
148
    }
1✔
149

150
    protected FileLocation locationAt(int indexInInput) {
UNCOV
151
        return builder.toLocation(indexInInput);
×
152
    }
153

154
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc