You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何识别并统计Java特定条件注释内的缩进模式?

Java Conditional Comment Block Indent Pattern Detection & Statistics

Problem Breakdown

We need to analyze a single Java file containing conditional comment blocks wrapped in //#if defined(IDENTIFIER) and //#endif. Each block falls into one of two indent patterns:

  • Bubble: Indentation follows a nested "peak" shape (e.g., 4→8→12→8→4)
  • Straight: All lines in the block have 0 indentation

Our goals are to:

  1. Identify the indent pattern for each block
  2. Aggregate counts per identifier (e.g., LOGGING: Bubble 1, Straight 1)
  3. Optimize or implement this logic using an existing lineIndents method that retrieves a line's indentation.

Implementation & Optimization Suggestions

Step 1: Locate Conditional Blocks

First, scan the file line by line to track block boundaries:

  • Trigger block start when encountering //#if defined(...); extract the identifier (like LOGGING) from this line
  • Use a stack to handle nested blocks (critical if your code has nested conditional comments)
  • Capture all lines between the start //#if and its matching //#endif

Step 2: Extract Relevant Indent Data

For each line inside the block (excluding the start/end comment lines):

  • Use lineIndents to get the indent value
  • Filter out empty lines or comment-only lines—these can skew pattern detection

Step 3: Classify the Indent Pattern

Straight Pattern Check

A block is Straight if every non-empty, non-comment line has an indentation of 0. Optimize this by terminating early: as soon as you find a line with non-zero indent, mark it as not Straight.

Bubble Pattern Check

A Bubble requires indentation to strictly increase to a peak, then strictly decrease back. Here's how to detect it:

  1. Find the peak indent value and its position in the block
  2. Verify all lines before the peak have strictly increasing indent
  3. Verify all lines after the peak have strictly decreasing indent
  • Edge case: Blocks with only 1 or 2 lines can't form a bubble—mark these as UNKNOWN or handle them per your requirements

Step 4: Aggregate Statistics

Use a nested map (or custom data class) to track counts:

  • Outer map key: The identifier (e.g., LOGGING)
  • Inner map keys: Bubble and Straight, with values as their respective counts

Optimization Tips

  1. Cache Indent Values: If lineIndents is expensive (e.g., reads the file each time), precompute and cache indent values for all lines upfront. This avoids redundant calls.
  2. Early Pattern Termination: For both patterns, stop checking as soon as the pattern is invalidated (e.g., non-zero indent for Straight, a non-increasing line before the peak for Bubble).
  3. Nested Block Handling: Use a stack to track block depth—only process lines for the current top-level block unless you need to analyze nested blocks separately.

Example Pseudocode Snippet

// Precompute indent values for all lines (optimization)
List<Integer> allLineIndents = new ArrayList<>();
for (String line : Files.readAllLines(Paths.get("your-file.java"))) {
    allLineIndents.add(lineIndents(line));
}

// Stats storage
Map<String, PatternStats> identifierStats = new HashMap<>();
Deque<String> blockStack = new ArrayDeque<>();
List<Integer> currentBlockIndents = new ArrayList<>();
String currentIdentifier = null;

// Scan lines to process blocks
List<String> allLines = Files.readAllLines(Paths.get("your-file.java"));
for (int i = 0; i < allLines.size(); i++) {
    String line = allLines.get(i).trim();
    if (line.startsWith("//#if defined(")) {
        // Extract identifier
        int start = line.indexOf("defined(") + 8;
        int end = line.indexOf(")", start);
        currentIdentifier = line.substring(start, end).trim();
        blockStack.push(currentIdentifier);
        currentBlockIndents.clear();
    } else if (line.startsWith("//#endif")) {
        if (!blockStack.isEmpty()) {
            blockStack.pop();
            // Classify and update stats
            Pattern pattern = classifyPattern(currentBlockIndents);
            identifierStats.computeIfAbsent(currentIdentifier, k -> new PatternStats())
                .increment(pattern);
            currentIdentifier = blockStack.isEmpty() ? null : blockStack.peek();
            currentBlockIndents.clear();
        }
    } else if (!blockStack.isEmpty()) {
        // Only collect indents for non-empty, non-comment lines
        String rawLine = allLines.get(i);
        if (!rawLine.trim().isEmpty() && !rawLine.trim().startsWith("//")) {
            currentBlockIndents.add(allLineIndents.get(i));
        }
    }
}

// Helper: Classify pattern
private Pattern classifyPattern(List<Integer> indents) {
    if (indents.size() < 3) return Pattern.UNKNOWN;
    
    // Check Straight
    boolean isStraight = indents.stream().allMatch(indent -> indent == 0);
    if (isStraight) return Pattern.STRAIGHT;
    
    // Check Bubble
    int peakValue = Collections.max(indents);
    int peakIndex = indents.indexOf(peakValue);
    
    // Verify increasing up to peak
    boolean increasing = true;
    for (int i = 1; i <= peakIndex; i++) {
        if (indents.get(i) <= indents.get(i-1)) {
            increasing = false;
            break;
        }
    }
    if (!increasing) return Pattern.UNKNOWN;
    
    // Verify decreasing after peak
    boolean decreasing = true;
    for (int i = peakIndex + 1; i < indents.size(); i++) {
        if (indents.get(i) >= indents.get(i-1)) {
            decreasing = false;
            break;
        }
    }
    return increasing && decreasing ? Pattern.BUBBLE : Pattern.UNKNOWN;
}

// Helper classes
enum Pattern { BUBBLE, STRAIGHT, UNKNOWN }

class PatternStats {
    int bubbleCount = 0;
    int straightCount = 0;
    
    void increment(Pattern pattern) {
        switch(pattern) {
            case BUBBLE: bubbleCount++; break;
            case STRAIGHT: straightCount++; break;
            default: break;
        }
    }
    
    @Override
    public String toString() {
        return String.format("Bubble %d, Straight %d", bubbleCount, straightCount);
    }
}

// Print results
identifierStats.forEach((id, stats) -> {
    System.out.printf("%s: %s%n", id, stats);
});

Key Notes

  • Edge Cases: Define how to handle empty blocks, single-line blocks, or blocks that don't fit either pattern (add an UNKNOWN category if needed).
  • Accuracy: Ensure you're only analyzing code lines—skip comments and empty lines to avoid false pattern matches.
  • Performance: For large files, precomputing indent values is a must to avoid redundant I/O or processing.

内容的提问来源于stack exchange,提问作者Avah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:28:54