Package com.almworks.jira.structure.api

Source Code of com.almworks.jira.structure.api.StructureException$Builder

package com.almworks.jira.structure.api;

import com.almworks.jira.structure.util.StructureUtil;
import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.MutableIssue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Locale;

/**
* <p>{@code StructureException} can be thrown for different causes that involve Structure plugin. It usually means that the
* operation that is traced back to the user's action, cannot be performed and should be reported as error.</p>
*
* <p>Each exception is associated with a specific value from enumeration {@link StructureError}. Exception may also
* carry additional message description and information about affected structure, view or issue. The general method
* {@link #getMessage()}, returns all information included with the exception, except for the stack trace and cause.
* </p>
*
* <h4>Displaying User-Friendly Errors</h4>
*
* <p>To display an error on the user interface: use {@link #getLocalizedMessage()} or {@link #getLocalizedMessage(com.atlassian.crowd.embedded.api.User)}
*   to get a human-friendly error in the user's locale. However, in many cases the error message will not be localized
*   and the result of {@code getLocalizedMessage()} will contain the problem description as a developer had put it in English.
*   <em>(It might not be human-friendly even for English audience!)</em></p>
*
*   <p>So for the best result, you should check {@link #isLocalized()}
*   method and if exception is not localized, use some wrapper text to display the error to the user in a good way:
*   </p>
*
* <pre>
* Java code:
*   try {
*     ...
*   } catch (StructureException e) {
*     if (e.isLocalized()) {
*       setDisplayedError(e.getLocalizedMessage());
*     } else {
*       setDisplayedError(getText("my.errors.structure-error", e.getLocalizedMessage()));
*     }
*   }
*
* I18N Properties:
*  my.errors.structure-error=Some problem happened with structure, sorry! ({0})
* </pre>
*
* <h4>Throwing StructureException</h4>
*
* <p>An instance of {@code StructureException} may be created by using one of the available constructors, but
* it might be more convenient to start from {@link StructureError} and use chained builder commands, ending with
* message specification:</p>
*
* <pre>
*   throw StructureError.GENERIC_ERROR.withMessage("cannot foo bar");
*   ...
*   throw StructureError.INVALID_JQL.causedBy(caughtException).forStructure(id).withoutMessage();
*   ...
*   throw StructureError.VIEW_EDIT_DENIED.forView(id).withLocalizedMessage("error.view.edit.denied", id, name);
* </pre>
*
* @see StructureError
*/
public class StructureException extends Exception {
  // used by tests to avoid exceptions
  private static final boolean ISSUE_LOOKUP_DISABLED = "true".equalsIgnoreCase(System.getProperty("structure.exception.disable.lookup"));

  private final StructureError myError;
  private final String myProblemDetails;
  private final long myStructure;
  private final long myView;
  private final long myIssue;
  private final String myMessageKey;
  private final Object[] myMessageParameters;


  /**
   * Constructs an instance of exception.
   *
   * @param error structure error code
   */
  public StructureException(@Nullable StructureError error) {
    this(error, null, null, null, null, null, null);
  }

  /**
   * Constructs an instance of exception.
   *
   * @param error structure error code
   * @param message additional message text
   */
  public StructureException(@Nullable StructureError error, @Nullable String message) {
    this(error, null, null, null, null, message, null);
  }

  /**
   * Constructs an instance of exception.
   *
   * @param error structure error code
   * @param structure structure in question
   */
  public StructureException(@Nullable StructureError error, @Nullable Long structure) {
    this(error, null, structure, null, null, null, null);
  }

  /**
   * Constructs an instance of exception.
   *
   * @param error structure error code
   * @param structure structure in question
   * @param issue related issue
   */
  public StructureException(@Nullable StructureError error, @Nullable Long structure, @Nullable Long issue) {
    this(error, null, structure, null, issue, null, null);
  }

  /**
   * Constructs an instance of exception.
   *
   * @param error structure error code
   * @param structure structure in question
   * @param issue related issue
   * @param message additional message text
   */
  public StructureException(@Nullable StructureError error, @Nullable Long structure, @Nullable Long issue, @Nullable String message) {
    this(error, null, structure, null, issue, message, null);
  }

  /**
   * Constructs an instance of exception. For convenience, use one of the overloaded constructors.
   *
   * @param error structure error code
   * @param structure structure in question
   * @param issue related issue
   * @param message additional message text
   * @param cause throwable cause
   */
  public StructureException(@Nullable StructureError error, @Nullable Long structure, @Nullable Long issue, @Nullable String message, @Nullable Throwable cause) {
    this(error, cause, structure, null, issue, message, null);
  }

  public StructureException(@Nullable StructureError error, @Nullable Long structure, @Nullable Long issue, @Nullable Long view) {
    this(error, null, structure, view, issue, null, null);
  }

  public StructureException(@Nullable StructureError error, @Nullable Long structure, @Nullable Long issue, @Nullable Long view, @Nullable String message) {
    this(error, null, structure, view, issue, message, null);
  }

  protected StructureException(StructureError error, @Nullable Throwable cause, @Nullable Long structure,
    @Nullable Long view, @Nullable Long issue, @Nullable String message, @Nullable String messageKey,
    @Nullable Object... messageParameters)
  {
    super(createMessage(error, structure, view, issue, message, messageKey, messageParameters), cause);
    myError = error == null ? StructureError.GENERIC_ERROR : error;
    // avoid double calculation of root-locale message by providing message that already contains it
    String details = createDetails(message, messageKey, messageParameters);
    myProblemDetails = details.isEmpty() ? "error " + myError : details;
    myStructure = structure == null ? 0L : structure;
    myView = view == null ? 0L : view;
    myIssue = issue == null ? 0L : issue;
    myMessageKey = messageKey;
    myMessageParameters = messageParameters;
  }

  private static String createMessage(StructureError error, Long structure, Long view, Long issue,
    @Nullable String message, @Nullable String messageKey, @Nullable Object... messageParameters)
  {
    StringBuilder b = new StringBuilder();
    if (error == null) error = StructureError.GENERIC_ERROR;
    String details = createDetails(message, messageKey, messageParameters);
    if (!details.isEmpty()) b.append(details).append(" - ");
    b.append("structure ").append(error.name()).append(" (code:").append(error.getCode());
    if (structure != null && structure > 0) b.append(" structure:").append(structure);
    if (view != null && view > 0) b.append(" view:").append(view);
    if (issue != null && issue > 0) {
      b.append(" issue:").append(issue);
      if (!ISSUE_LOOKUP_DISABLED) {
        appendIssueKey(b, issue);
      }
    }
    b.append(')');
    return b.toString();
  }

  @NotNull
  private static String createDetails(String message, String messageKey, Object... messageParameters) {
    if (message == null) {
      message = getRootLocaleMessage(messageKey, messageParameters);
      if (message == null) message = "";
    }
    return message;
  }

  private static void appendIssueKey(StringBuilder b, Long issue) {
    try {
      IssueManager issueManager = ComponentAccessor.getIssueManager();
      MutableIssue io = issueManager.getIssueObject(issue);
      if (io != null) b.append(" key:").append(io.getKey());
    } catch (ThreadDeath e) {
      throw e;
    } catch (Throwable e) {
      // ignore
    }
  }

  private static String getRootLocaleMessage(String messageKey, Object... messageParameters) {
    // we could use Locale.ROOT here, but it won't work in development environment because there's no StructureMessages.properties
    try {
      return StructureUtil.getText(Locale.ENGLISH, null, messageKey, messageParameters);
    } catch (ThreadDeath e) {
      throw e;
    } catch (Throwable e) {
      // protect from errors and exceptions in the exception initializer
      return "";
    }
  }

  // overriding default toString(), which uses getLocalizedMessage()
  public String toString() {
    String s = getClass().getName();
    String message = getMessage();
    return message != null ? s + ": " + message : s;
  }

  /**
   * @return error code
   */
  public StructureError getError() {
    return myError;
  }

  /**
   * @return the part of {@link #getMessage()} that corresponds to the original description of the problem.
   *  Does not contain structure ID, error code, and related issue information, so is more user-friendly than {@link #getMessage()}
   *  in case when that information can be more suitably described by the caller.
   * */
  public String getProblemDetails() {
    return myProblemDetails;
  }

  /**
   * @return related structure, or 0 if no structure is related
   */
  public long getStructure() {
    return myStructure;
  }

  public long getView() {
    return myView;
  }

  /**
   * @return related issue, or 0 if no issue is related
   */
  public long getIssue() {
    return myIssue;
  }

  public boolean isLocalized() {
    return myMessageKey != null;
  }

  @Override
  public String getLocalizedMessage() {
    return isLocalized() ? StructureUtil.getTextInCurrentUserLocale(myMessageKey, myMessageParameters) : getProblemDetails();
  }

  public String getLocalizedMessage(@Nullable User user) {
    return isLocalized() ? StructureUtil.getText(null, user, myMessageKey, myMessageParameters) : getProblemDetails();
  }


  public static class Builder {
    private StructureError myError;
    private String myMessage;
    private Long myStructure;
    private Long myView;
    private Long myIssue;
    private String myMessageKey;
    private Object[] myMessageParameters;
    private Throwable myCause;

    public Builder(StructureError error) {
      myError = error;
    }

    public StructureException withLocalizedMessage(String messageKey, Object... messageParameters) {
      myMessage = messageKey == null ? "" : getRootLocaleMessage(messageKey, messageParameters);
      myMessageKey = messageKey;
      myMessageParameters = messageParameters;
      return build();
    }

    public StructureException withMessage(String message) {
      myMessage = message;
      myMessageKey = null;
      myMessageParameters = null;
      return build();
    }

    public StructureException withoutMessage() {
      myMessage = null;
      myMessageKey = null;
      myMessageParameters = null;
      return build();
    }

    public Builder forStructure(Long structure) {
      myStructure = structure;
      return this;
    }

    public Builder forView(Long view) {
      myView = view;
      return this;
    }

    public Builder forIssue(Long issue) {
      myIssue = issue;
      return this;
    }

    public Builder causedBy(Throwable cause) {
      myCause = cause;
      return this;
    }

    private StructureException build() {
      return new StructureException(myError, myCause, myStructure, myView, myIssue, myMessage, myMessageKey, myMessageParameters);
    }
  }
}
TOP

Related Classes of com.almworks.jira.structure.api.StructureException$Builder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.