package org.testng;

import org.testng.internal.AutoCloseableLock;

/**
 * The root exception for special skip handling. In case a @Test or @Configuration throws this
 * exception the method will be considered a skip or a failure according to the return of {@link
 * #isSkip()}. Users may provide extensions to this mechanism by extending this class.
 *
 * @since 5.6
 */
public class SkipException extends RuntimeException {

  private static final long serialVersionUID = 4052142657885527260L;

  private StackTraceElement[] m_stackTrace;
  private volatile boolean m_stackReduced;

  public SkipException(String skipMessage) {
    super(skipMessage);
  }

  public SkipException(String skipMessage, Throwable cause) {
    super(skipMessage, cause);
  }

  /**
   * Flag if the current exception marks a skipped method (<code>true</code>) or a failure (<code>
   * false</code>). By default Subclasses should override this method in order to provide smarter
   * behavior.
   *
   * @return <code>true</code> if the method should be considered a skip, <code>false</code> if the
   *     method should be considered failed. If not overwritten it returns <code>true</code>
   */
  public boolean isSkip() {
    return true;
  }

  private final AutoCloseableLock internalLock = new AutoCloseableLock();

  /**
   * Subclasses may use this method to reduce the printed stack trace. This method keeps only the
   * last frame. <b>Important</b>: after calling this method the preserved internally and can be
   * restored called {@link #restoreStackTrace}.
   */
  protected void reduceStackTrace() {
    if (!m_stackReduced) {
      try (AutoCloseableLock ignore = internalLock.lock()) {
        StackTraceElement[] newStack = new StackTraceElement[1];
        StackTraceElement[] originalStack = getStackTrace();
        if (originalStack.length > 0) {
          m_stackTrace = originalStack;
          newStack[0] = getStackTrace()[0];
          setStackTrace(newStack);
        }
        m_stackReduced = true;
      }
    }
  }

  /**
   * Restores the original exception stack trace after a previous call to {@link
   * #reduceStackTrace()}.
   */
  protected void restoreStackTrace() {
    if (m_stackReduced && null != m_stackTrace) {
      try (AutoCloseableLock ignore = internalLock.lock()) {
        setStackTrace(m_stackTrace);
        m_stackReduced = false;
      }
    }
  }
}
