<commit_before>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.template.soy.base;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.auto.value.AutoValue;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ComparisonChain;
import com.google.errorprone.annotations.CheckReturnValue;
import com.google.errorprone.annotations.Immutable;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

/**
 * Describes a source location in a Soy input file.
 *
 * @author <EMAIL> (<NAME>)
 */
@ParametersAreNonnullByDefault
@Immutable
@CheckReturnValue
public final class SourceLocation implements Comparable<SourceLocation> {

  /** A file path or URI useful for error messages. */
  @Nonnull private final String filePath;

  private final Point begin;
  private final Point end;

  /**
   * A nullish source location.
   *
   * <p>This is useful for default initialization of field values or when dealing with situations
   * when you may or may not have a location. Obviously, associating real locations is always
   * preferred when possible.
   */
  public static final SourceLocation UNKNOWN = new SourceLocation("unknown");

  /**
   * @param filePath A file path or URI useful for error messages.
   * @param beginLine The line number in the source file where this location begins (1-based), or -1
   *     if associated with the entire file instead of a line.
   * @param beginColumn The column number in the source file where this location begins (1-based),
   *     or -1 if associated with the entire file instead of a line.
   * @param endLine The line number in the source file where this location ends (1-based), or -1 if
   *     associated with the entire file instead of a line.
   * @param endColumn The column number in the source file where this location ends (1-based), or -1
   *     if associated with the entire file instead of a line.
   */
  public SourceLocation(
      String filePath, int beginLine, int beginColumn, int endLine, int endColumn) {
    this(filePath, Point.create(beginLine, beginColumn), Point.create(endLine, endColumn));
  }

  public SourceLocation(String filePath) {
    this(filePath, -1, -1, -1, -1);
  }

  public SourceLocation(String filePath, Point begin, Point end) {
    checkNotNull(filePath, "filePath is null");
    checkNotNull(begin, "begin is null");
    checkNotNull(end, "end is null");
    checkArgument(
        begin.isKnown() == end.isKnown(),
        "Either both the begin and end locations should be known, or neither should be. Got [%s,"
            + " %s]",
        begin,
        end);
    checkArgument(
        begin.compareTo(end) <= 0,
        "begin %s should be before end %s in file %s",
        begin,
        end,
        filePath);
    this.filePath = filePath;
    this.begin = begin;
    this.end = end;
  }

  /**
   * Returns a file path or URI useful for error messages. This should not be used to fetch content
   * from the file system.
   */
  @Nonnull
  public String getFilePath() {
    return filePath;
  }

  @Nullable
  public String getFileName() {
    if (UNKNOWN.equals(this)) {
      // This is to replicate old behavior where SoyFileNode#getFileName returns null when an
      // invalid SoyFileNode is created.
      return null;
    }
    // TODO(lukes): consider using Java 7 File APIs here.
    int lastSlashIndex = CharMatcher.anyOf("/\\").lastIndexIn(filePath);
    if (lastSlashIndex != -1 && lastSlashIndex != filePath.length() - 1) {
      return filePath.substring(lastSlashIndex + 1);
    }
    return filePath;
  }

  /** Returns the line number in the source file where this location begins (1-based). */
  public int getBeginLine() {
    return begin.line();
  }

  /** Returns the column number in the source file where this location begins (1-based). */
  public int getBeginColumn() {
    return begin.column();
  }

  /** Returns the line number in the source file where this location ends (1-based). */
  public int getEndLine() {
    return end.line();
  }

  /** Returns the column number in the source file where this location ends (1-based). */
  public int getEndColumn() {
    return end.column();
  }

  /** Returns true if this location ends one character before the other location starts. */
  public boolean isJustBefore(SourceLocation that) {
    if (!this.filePath.equals(that.filePath)) {
      return false;
    }

    return this.getEndLine() == that.getBeginLine()
        && this.getEndColumn() + 1 == that.getBeginColumn();
  }

  /** True iff this location has valid begin and end locations. */
  public boolean isKnown() {
    // our ctor enforces that if begin is known then end is known, so we only need to check one.
    return begin.isKnown();
  }

  @Override
  public int compareTo(SourceLocation o) {
    // TODO(user): use Comparator.comparing(...)
    return ComparisonChain.start()
        .compare(this.filePath, o.filePath)
        .compare(this.begin, o.begin)
        // These last two are unlikely to make a difference, but if they do it means we sort smaller
        // source locations first.
        .compare(this.end, o.end)
        .result();<commit_msg>Create an imports rectangle that includes alias+ imports so that all imports are sorted on partial formatting.<commit_after>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.template.soy.base;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.auto.value.AutoValue;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ComparisonChain;
import com.google.errorprone.annotations.CheckReturnValue;
import com.google.errorprone.annotations.Immutable;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

/**
 * Describes a source location in a Soy input file.
 *
 * @author <EMAIL> (<NAME>)
 */
@ParametersAreNonnullByDefault
@Immutable
@CheckReturnValue
public final class SourceLocation implements Comparable<SourceLocation> {

  /** A file path or URI useful for error messages. */
  @Nonnull private final String filePath;

  private final Point begin;
  private final Point end;

  /**
   * A nullish source location.
   *
   * <p>This is useful for default initialization of field values or when dealing with situations
   * when you may or may not have a location. Obviously, associating real locations is always
   * preferred when possible.
   */
  public static final SourceLocation UNKNOWN = new SourceLocation("unknown");

  /**
   * @param filePath A file path or URI useful for error messages.
   * @param beginLine The line number in the source file where this location begins (1-based), or -1
   *     if associated with the entire file instead of a line.
   * @param beginColumn The column number in the source file where this location begins (1-based),
   *     or -1 if associated with the entire file instead of a line.
   * @param endLine The line number in the source file where this location ends (1-based), or -1 if
   *     associated with the entire file instead of a line.
   * @param endColumn The column number in the source file where this location ends (1-based), or -1
   *     if associated with the entire file instead of a line.
   */
  public SourceLocation(
      String filePath, int beginLine, int beginColumn, int endLine, int endColumn) {
    this(filePath, Point.create(beginLine, beginColumn), Point.create(endLine, endColumn));
  }

  public SourceLocation(String filePath) {
    this(filePath, -1, -1, -1, -1);
  }

  public SourceLocation(String filePath, Point begin, Point end) {
    checkNotNull(filePath, "filePath is null");
    checkNotNull(begin, "begin is null");
    checkNotNull(end, "end is null");
    checkArgument(
        begin.isKnown() == end.isKnown(),
        "Either both the begin and end locations should be known, or neither should be. Got [%s,"
            + " %s]",
        begin,
        end);
    checkArgument(
        begin.compareTo(end) <= 0,
        "begin %s should be before end %s in file %s",
        begin,
        end,
        filePath);
    this.filePath = filePath;
    this.begin = begin;
    this.end = end;
  }

  /**
   * Returns a file path or URI useful for error messages. This should not be used to fetch content
   * from the file system.
   */
  @Nonnull
  public String getFilePath() {
    return filePath;
  }

  @Nullable
  public String getFileName() {
    if (UNKNOWN.equals(this)) {
      // This is to replicate old behavior where SoyFileNode#getFileName returns null when an
      // invalid SoyFileNode is created.
      return null;
    }
    // TODO(lukes): consider using Java 7 File APIs here.
    int lastSlashIndex = CharMatcher.anyOf("/\\").lastIndexIn(filePath);
    if (lastSlashIndex != -1 && lastSlashIndex != filePath.length() - 1) {
      return filePath.substring(lastSlashIndex + 1);
    }
    return filePath;
  }

  /** Returns the line number in the source file where this location begins (1-based). */
  public int getBeginLine() {
    return begin.line();
  }

  /** Returns the column number in the source file where this location begins (1-based). */
  public int getBeginColumn() {
    return begin.column();
  }

  /** Returns the line number in the source file where this location ends (1-based). */
  public int getEndLine() {
    return end.line();
  }

  /** Returns the column number in the source file where this location ends (1-based). */
  public int getEndColumn() {
    return end.column();
  }

  /** Returns true if this location ends one character before the other location starts. */
  public boolean isJustBefore(SourceLocation that) {
    if (!this.filePath.equals(that.filePath)) {
      return false;
    }

    return this.getEndLine() == that.getBeginLine()
        && this.getEndColumn() + 1 == that.getBeginColumn();
  }

  public boolean isBefore(SourceLocation that) {
    if (!this.filePath.equals(that.filePath)) {
      return false;
    }
    return this.getEndPoint().isBefore(that.getBeginPoint());
  }

  /** True iff this location has valid begin and end locations. */
  public boolean isKnown() {
    // our ctor enforces that if begin is known then end is known, so we only need to check one.
    return begin.isKnown();
  }

  @Override
  public int compareTo(SourceLocation o) {
    // TODO(user): use Comparator.comparing(...)
    return ComparisonChain.start()
        .compare(this.filePath, o.filePath)
        .compare(this.begin, o.begin)
        // These last two are unlikely to make a difference, but if they do it means we sort smaller
        // source locations first.
        .compare(this.end, o.end)
        .result();
{'fraction_non_alphanumeric': 0.09435199606525126, 'fraction_numerical': 0.004918435937371916, 'mean_word_length': 3.193881058783087, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4820757', 'n_tokens_mistral': 3388, 'n_tokens_neox': 3309, 'n_words': 1317}