All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.java.model.GeneratedFile Maven / Gradle / Ivy

The newest version!
/*
 * SonarQube Java
 * Copyright (C) 2012-2024 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the Sonar Source-Available License for more details.
 *
 * You should have received a copy of the Sonar Source-Available License
 * along with this program; if not, see https://sonarsource.com/license/ssal/
 */
package org.sonar.java.model;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.annotation.CheckForNull;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.TextPointer;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.java.annotations.VisibleForTesting;
import org.sonar.plugins.java.api.SourceMap;
import org.sonar.plugins.java.api.SourceMap.Location;
import org.sonar.plugins.java.api.tree.Tree;

import static java.lang.Math.max;
import static java.lang.Math.min;

public class GeneratedFile implements InputFile {

  private final Path path;
  private String contents = null;
  private String md5 = null;

  @VisibleForTesting
  final List smapFiles = new ArrayList<>();

  private SourceMap sourceMap;

  public GeneratedFile(Path path) {
    this.path = path;
  }

  public SourceMap sourceMap() {
    if (sourceMap == null) {
      sourceMap = new SourceMapImpl();
    }
    return sourceMap;
  }

  public void addSmap(SmapFile smap) {
    smapFiles.add(smap);
  }

  class SourceMapImpl implements SourceMap {

    final Map lines = new HashMap<>();

    private SourceMapImpl() {
      for (SmapFile sm : smapFiles) {
        for (SmapFile.LineInfo lineInfo : sm.getLineSection()) {
          sm.getInputFile(lineInfo.lineFileId).ifPresent(inputFile -> processLineInfo(inputFile, lineInfo));
        }
      }
    }

    private void processLineInfo(InputFile inputFile, SmapFile.LineInfo lineInfo) {
      for (int i = 0; i < lineInfo.repeatCount; i++) {
        int inputLine = lineInfo.inputStartLine + i;
        LocationImpl location = new LocationImpl(inputFile, inputLine, inputLine);
        int outputStart = lineInfo.outputStartLine + (i * lineInfo.outputLineIncrement);
        int outputEnd = lineInfo.outputStartLine + ((i + 1) * lineInfo.outputLineIncrement) - 1;
        // when outputLineIncrement == 0, end will be less than start (looks like bug in spec)
        outputEnd = max(outputStart, outputEnd);
        for (int j = outputStart; j <= outputEnd; j++) {
          lines.merge(j, location, LocationImpl::mergeLocations);
        }
      }
    }

    @Override
    public Optional sourceMapLocationFor(Tree tree) {
      return getLocation(LineUtils.startLine(tree.firstToken()), LineUtils.startLine(tree.lastToken()));
    }

    @VisibleForTesting
    Optional getLocation(int startLine, int endLine) {
      Location startLoc = lines.get(startLine);
      Location endLoc = lines.get(endLine);
      if (startLoc == null || endLoc == null) {
        return Optional.empty();
      }
      return Optional.of(new LocationImpl(startLoc.file(), startLoc.startLine(), endLoc.endLine()));
    }
  }


  private static final class LocationImpl implements Location {

    private final InputFile inputFile;
    private final int startLine;
    private final int endLine;

    private LocationImpl(InputFile inputFile, int startLine, int endLine) {
      this.inputFile = inputFile;
      this.startLine = startLine;
      this.endLine = endLine;
    }

    @Override
    public InputFile file() {
      return inputFile;
    }

    @Override
    public int startLine() {
      return startLine;
    }

    @Override
    public int endLine() {
      return endLine;
    }

    private static Location mergeLocations(Location loc1, Location loc2) {
      return new LocationImpl(loc1.file(),
        min(loc1.startLine(), loc2.startLine()),
        max(loc1.endLine(), loc2.endLine()));
    }
  }

  @Override
  public String relativePath() {
    return path.toString();
  }

  @Override
  public String absolutePath() {
    return path.toAbsolutePath().toString();
  }

  @Override
  public File file() {
    return path.toFile();
  }

  @Override
  public Path path() {
    return path;
  }

  @Override
  public URI uri() {
    return path.toUri();
  }

  @Override
  public String filename() {
    return path.getFileName().toString();
  }

  @CheckForNull
  @Override
  public String language() {
    return "java";
  }

  @Override
  public Type type() {
    throw new UnsupportedOperationException();
  }

  @Override
  public InputStream inputStream() throws IOException {
    return Files.newInputStream(path);
  }

  @Override
  public String contents() throws IOException {
    if (contents == null) {
      contents = new String(Files.readAllBytes(path), charset());
    }
    return contents;
  }

  @Override
  public String md5Hash() {
    if (md5 == null) {
      md5 = InputFileUtils.md5Hash(this);
    }
    return md5;
  }

  @Override
  public Status status() {
    throw new UnsupportedOperationException();
  }

  @Override
  public int lines() {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean isEmpty() {
    return false;
  }

  @Override
  public TextPointer newPointer(int line, int lineOffset) {
    throw new UnsupportedOperationException();
  }

  @Override
  public TextRange newRange(TextPointer start, TextPointer end) {
    throw new UnsupportedOperationException();
  }

  @Override
  public TextRange newRange(int startLine, int startLineOffset, int endLine, int endLineOffset) {
    throw new UnsupportedOperationException();
  }

  @Override
  public TextRange selectLine(int line) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Charset charset() {
    return StandardCharsets.UTF_8;
  }

  @Override
  public String key() {
    return absolutePath();
  }

  @Override
  public boolean isFile() {
    return Files.isRegularFile(path);
  }

  @Override
  public String toString() {
    return path.toString();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy