/*
 * Copyright (c) 2002 - 2014 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 */

/*
 * Range.java
 *
 * Created on 21. Juni 2005, 12:20
 */

package com.ibm.wala.sourcepos;

/**
 * This class represents a range in the source file.
 *
 * @author Siegfried Weber
 * @author Juergen Graf &lt;juergen.graf@gmail.com&gt;
 */
public class Range {

  /** stores the start position */
  private Position start;
  /** stores the end position */
  private Position end;

  /** Creates an empty range. */
  Range() {
    start = new Position();
    end = new Position();
  }

  /**
   * Creates a new instance of Range with the given start and end position.
   *
   * @param start the start position
   * @param end the end position
   * @throws InvalidRangeException if end is before start or if the range is not undefined and start
   *     or end is undefined.
   */
  Range(Position start, Position end) throws InvalidRangeException {
    if (start == null) throw new InvalidRangeException(InvalidRangeException.Cause.START_UNDEFINED);
    if (end == null) throw new InvalidRangeException(InvalidRangeException.Cause.END_UNDEFINED);
    if (end.isBefore(start))
      throw new InvalidRangeException(InvalidRangeException.Cause.END_BEFORE_START);
    else if (start.isUndefined() && !end.isUndefined())
      throw new InvalidRangeException(InvalidRangeException.Cause.START_UNDEFINED);
    else if (!start.isUndefined() && end.isUndefined())
      throw new InvalidRangeException(InvalidRangeException.Cause.END_UNDEFINED);
    this.start = start;
    this.end = end;
  }

  /**
   * Returns whether this range is undefined.
   *
   * @return whether this range is undefined
   */
  boolean isUndefined() {
    return start.isUndefined();
  }

  /**
   * Tests whether this range is within the given range. Returns false if a range is undefined or
   * {@code r} is null.
   *
   * @param r the range to test with
   * @return {@code true} if this range is within the given range
   */
  boolean isWithin(Range r) {
    return (r != null) && !start.isBefore(r.start) && !r.end.isBefore(end);
  }

  /**
   * Returns this range as an array of integers.
   *
   * @return an array with the following entries: start line number, start column number, end line
   *     number, end column number
   */
  int[] toArray() {
    return new int[] {start.getLine(), start.getColumn(), end.getLine(), end.getColumn()};
  }

  /**
   * Returns the start position.
   *
   * @return the start position
   */
  public Position getStartPosition() {
    return start;
  }

  /**
   * Returns the end position.
   *
   * @return the end position
   */
  public Position getEndPosition() {
    return end;
  }

  @Override
  public String toString() {
    return (start.isUndefined()
        ? "<undefined>"
        : "(" + getStartPosition() + ") - (" + getEndPosition() + ')');
  }
}
