Package com.almworks.jira.structure.api.view

Source Code of com.almworks.jira.structure.api.view.ViewSettings$AssociatedView

package com.almworks.jira.structure.api.view;

import com.almworks.jira.structure.api.settings.StructurePage;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.*;

import static com.almworks.jira.structure.api.settings.StructurePage.*;

/**
* <p>View settings define a structure's parameters with regards to views - which
* views are associated with the structure and which views are default on which pages.</p>
*
* <p>{@code ViewSettings} class contains a list of {@link AssociatedView} records,
* with each record referencing a view by ID. Additionally, the record has "menu" markers,
* while define on which "structured" pages is the view offered to the user, and "default" markers,
* which define on which pages it is the default view.</p>
*
* <p>View settings can be set for a specific structure with {@link StructureViewManager#setViewSettings}.
* If per-structure view settings are not defined, then the global default view settings
* are in effect for that structure. Global view settings can be modified with
* {@link StructureViewManager#setDefaultViewSettings}.</p>
*
* <p>To get the current view settings for a structure, use {@link StructureViewManager#getViewSettings}.
* To construct a menu of views for the user, you would usually want to use {@link StructureViewManager#getMenuItems}.</p>
*
* <p>An instance of {@code ViewSettings} may be "undefined", which for the
* per-structure view settings means "use default".</p>
*
* <p>Note that views are referenced in {@code ViewSettings} by view ID. It is not guaranteed
* that a view with that ID exists, or that it is visible to the user. So before using the information
* from {@code ViewSettings}, check that the view is accessible.</p>
*
* <p>{@code ViewSettings} instance is immutable and thread-safe. To construct a new
* instance, use {@link Builder}.</p>
*
* @author Igor Sereda
*/
public class ViewSettings {
  public static final Set<StructurePage> NO_PAGES = Collections.emptySet();
  public static final Set<StructurePage> ALL_PAGES = Collections.unmodifiableSet(EnumSet.of(
    STRUCTURE_BOARD, STRUCTURE_BOARD_WITH_DETAILS, ISSUE_VIEW, PROJECT_TAB, GADGET));
  public static final Set<StructurePage> PAGES_WITH_DEFAULT_VIEW = Collections.unmodifiableSet(EnumSet.of(
    STRUCTURE_BOARD, STRUCTURE_BOARD_WITH_DETAILS, ISSUE_VIEW, PROJECT_TAB));
  public static final ViewSettings EMPTY_SETTINGS = new Builder().build();

  /**
   * A list of associated views. A {@code null} value means "use default", while
   * empty list means "no associated views".
   */
  @Nullable
  private final List<AssociatedView> myAssociatedViews;

  private ViewSettings(@Nullable List<AssociatedView> associatedViews, boolean reuseList) {
    myAssociatedViews = associatedViews == null ? null
      : Collections.unmodifiableList(reuseList ? associatedViews : new ArrayList<AssociatedView>(associatedViews));
  }

  /**
   * @return {@code true} if the view settings are defined, {@code false} means "use default settings"
   */
  public boolean isDefined() {
    return myAssociatedViews != null;
  }

  /**
   * @return a list of associated views, or an empty list if settings are not defined
   */
  @NotNull
  public List<AssociatedView> getAssociatedViews() {
    return myAssociatedViews == null ? Collections.<AssociatedView>emptyList() : myAssociatedViews;
  }

  /**
   * Retrieves a default view ID for a given page. If any of the associated
   * views has a view that is marked as the default for the specified page, the view ID
   * is returned.
   *
   * @param page structure page for which default is needed
   * @return default view ID, or null if the view settings do not define the default for that page
   */
  @Nullable
  public Long getDefaultViewForPage(StructurePage page) {
    if (myAssociatedViews == null) return null;
    for (AssociatedView view : myAssociatedViews) {
      if (view.isDefault(page)) return view.getViewId();
    }
    return null;
  }

  /**
   * Checks if the view settings contain a record for the specified view ID -
   * that is, if the view is associated with the structure that is represented by this view settings instance.
   *
   * @param viewId the ID of the view
   * @return true if this view settings instance includes the specified view
   */
  public boolean hasView(Long viewId) {
    if (viewId == null || myAssociatedViews == null) return false;
    for (AssociatedView view : myAssociatedViews) {
      if (view.getViewId() == viewId) return true;
    }
    return false;
  }

  private static StructurePage adjustPage(StructurePage page) {
    if (page == COMPONENT_TAB || page == VERSION_TAB) return PROJECT_TAB;
    if (ALL_PAGES.contains(page)) return page;
    return STRUCTURE_BOARD;
  }

  public String toString() {
    return "ViewSettings{" +
      "associatedViews=" + myAssociatedViews +
      '}';
  }

  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    ViewSettings that = (ViewSettings) o;

    if (myAssociatedViews != null ? !myAssociatedViews.equals(that.myAssociatedViews) : that.myAssociatedViews != null)
      return false;

    return true;
  }

  public int hashCode() {
    return myAssociatedViews != null ? myAssociatedViews.hashCode() : 0;
  }


  /**
   * <p>View settings builder allows to construct instances of {@link ViewSettings} and serialize them
   * into JSON format.</p>
   *
   * <p>Builder is not thread-safe.</p>
   *
   * @see ViewSettings
   */
  @XmlRootElement
  public static class Builder {
    private List<AssociatedView.Builder> myViews;

    /**
     * Creates an empty builder
     */
    public Builder() {
    }

    /**
     * Creates a builder that copies the contents of an already existing {@code ViewSettings} instance.
     *
     * @param copyFrom view settings to copy
     */
    public Builder(@Nullable ViewSettings copyFrom) {
      if (copyFrom != null && copyFrom.isDefined()) {
        List<AssociatedView> copyViews = copyFrom.getAssociatedViews();
        myViews = new ArrayList<AssociatedView.Builder>(copyViews.size());
        for (AssociatedView view : copyViews) {
          myViews.add(new AssociatedView.Builder(view));
        }
      }
    }

    /**
     * @return a copy of list of {@link AssociatedView.Builder}, or {@code null} if no views have been added.
     * Each individual builder is not copied but referenced in the resulting list.
     */
    @Nullable
    @SuppressWarnings("UnusedDeclaration")
    @XmlElement
    public List<AssociatedView.Builder> getViews() {
      return myViews == null ? null : new ArrayList<AssociatedView.Builder>(myViews);
    }

    /**
     * Sets a list of builders of associated views.
     *
     * @param views list of builders, or {@code null} to make the constructed view settings undefined.
     */
    @SuppressWarnings("UnusedDeclaration")
    public void setViews(List<AssociatedView.Builder> views) {
      myViews = views == null ? null : new ArrayList<AssociatedView.Builder>(views);
    }

    /**
     * Constructs an instance of {@link ViewSettings}. If a builder for any of the associated views
     * is invalid, that associated view record is ignored.
     *
     * @return an instance of {@code ViewSettings}
     */
    @NotNull
    public ViewSettings build() {
      List<AssociatedView> list = null;
      if (myViews != null) {
        list = new ArrayList<AssociatedView>(myViews.size());
        for (AssociatedView.Builder view : myViews) {
          AssociatedView v = view.build();
          if (v != null) list.add(v);
        }
      }
      return new ViewSettings(list, true);
    }

    /**
     * Adds specified views to the list of associated views. Each view is available on
     * every page ("menu" marker is set for all pages), and each view is not the default
     * on any page ("default" marker is unset for all pages).
     *
     * @param viewIds a list of view IDs
     * @return this builder
     */
    public Builder addViews(long... viewIds) {
      if (viewIds != null) {
        for (long viewId : viewIds) {
          addView(viewId, false);
        }
      }
      return this;
    }

    /**
     * Adds the specified view to the list of associated views, optionally making it the default
     * for all pages. The view is made available on every page ("menu" marker is set for all pages),
     * and if {@code defaultView} parameter is {@code true}, the view is also marked as the default
     * for all pages ("default" marker is set for all pages).
     *
     * @param viewId view ID
     * @param defaultView if {@code}, the added view will be the default
     * @return this builder
     */
    public Builder addView(long viewId, boolean defaultView) {
      return addView(-1, viewId, null, defaultView ? ALL_PAGES : null);
    }

    /**
     * <p>Adds specified view to the list of associated views, or inserts it as a specific position in the list.
     * The caller may specify on which pages the view is available in the drop-down list (via {@code menuPages}
     * parameter), and on which pages the view is the default (via {@code defaultPages} parameter.</p>
     *
     * <p>Hint: to construct a collection of pages, use {@code EnumSet.of(...)}.</p>
     *
     * @param index the position in the list where to add the view, or {@code -1} to add the view to the end of the list
     * @param viewId the ID of the view
     * @param menuPages a collection of pages on which this view is offered to the user, {@code null} means all pages
     * @param defaultPages a collection of pages on which this view is the default, {@code null} means no pages
     *
     * @return this builder
     * @throws IndexOutOfBoundsException if the specified index is invalid
     */
    public Builder addView(int index, long viewId,
      @Nullable Collection<StructurePage> menuPages, @Nullable Collection<StructurePage> defaultPages)
    {
      if (viewId <= 0) throw new IllegalArgumentException("cannot set parameters for view " + viewId);
      makeDefined();
      if (index < 0) {
        index = myViews.size();
      } else {
        if (index > myViews.size()) throw new IndexOutOfBoundsException("bad index " + index);
      }
      for (int i = 0; i < myViews.size(); i++) {
        AssociatedView.Builder builder = myViews.get(i);
        if (builder.getViewId() == viewId) {
          myViews.remove(i);
          if (index > i) index--;
        }
      }
      AssociatedView.Builder builder = new AssociatedView.Builder();
      builder.setViewId(viewId);
      builder.setMenuPages(enumSet(menuPages));
      builder.setDefaultPages(enumSet(defaultPages));
      myViews.add(index, builder);
      return this;
    }

    /**
     * Makes this view settings instance defined, by making sure the views list is not {@code null}.
     * Even if you don't add any views after that and build an instance of {@link ViewSettings},
     * the instance will be defined and override the default settings.
     */
    public void makeDefined() {
      if (myViews == null) myViews = new ArrayList<AssociatedView.Builder>(5);
    }

    /**
     * @return true if the current state of the builder would create a defined instance of view settings
     */
    @JsonIgnore
    public boolean isDefined() {
      return myViews != null;
    }

    public String toString() {
      return "ViewSettings.Builder{" +
        "views=" + myViews +
        '}';
    }
  }


  /**
   * <p>{@code AssociatedView} is a record of a view association within {@link ViewSettings}. A
   * record contains a view ID and "menu" and "default" per-page markers that tell if the specified
   * view is offered to the user in the menu on a page and if the view is the default on a page.</p>
   *
   * <p>The markers are represented as two sets of {@link StructurePage} - {@code menuPages}
   * set contains pages on which the view is offered in drop-down, and {@code defaultPages}
   * set contains pages on which the view is the default.</p>
   *
   * <p>This class is immutable and thread-safe. To construct a new instance, use
   * {@link AssociatedView.Builder}.</p>
   */
  public static class AssociatedView {
    private final long myViewId;

    @NotNull
    private final Set<StructurePage> myMenuPages;

    @NotNull
    private final Set<StructurePage> myDefaultPages;

    private AssociatedView(long viewId, @Nullable Set<StructurePage> menuPages,
      @Nullable Set<StructurePage> defaultPages, boolean reuseSets)
    {
      myViewId = viewId;
      myMenuPages = menuPages == null ? ALL_PAGES :
        reuseSets ? Collections.unmodifiableSet(menuPages) : Collections.unmodifiableSet(enumSet(menuPages));
      myDefaultPages = defaultPages == null ? NO_PAGES :
        reuseSets ? Collections.unmodifiableSet(defaultPages) : Collections.unmodifiableSet(enumSet(defaultPages));
    }

    /**
     * @return unmodifiable set of pages which have this view in the drop-down
     */
    @NotNull
    public Set<StructurePage> getMenuPages() {
      return myMenuPages;
    }

    /**
     * @return unmodifiable set of pages which have this view as the default
     */
    @NotNull
    public Set<StructurePage> getDefaultPages() {
      return myDefaultPages;
    }

    /**
     * @return the view ID
     */
    public long getViewId() {
      return myViewId;
    }

    /**
     * Checks if the view should be displayed in the menu on the specified page.
     *
     * @param page page with Structure widget
     * @return true if the view should be offered in the drop-down
     */
    public boolean isOnMenu(StructurePage page) {
      return myMenuPages.contains(adjustPage(page));
    }

    /**
     * Checks if the view is the default on the specified page.
     *
     * @param page page with Structure widget
     * @return true if the view is the default
     */
    public boolean isDefault(StructurePage page) {
      return myDefaultPages.contains(adjustPage(page));
    }

    public String toString() {
      return "ViewSettings.AssociatedView{" +
        "viewId=" + myViewId +
        ", menuPages=" + myMenuPages +
        ", defaultPages=" + myDefaultPages +
        '}';
    }

    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      AssociatedView that = (AssociatedView) o;

      if (myViewId != that.myViewId) return false;
      if (!myDefaultPages.equals(that.myDefaultPages)) return false;
      if (!myMenuPages.equals(that.myMenuPages)) return false;

      return true;
    }

    public int hashCode() {
      int result = (int) (myViewId ^ (myViewId >>> 32));
      result = 31 * result + myMenuPages.hashCode();
      result = 31 * result + myDefaultPages.hashCode();
      return result;
    }


    /**
     * The builder for {@link AssociatedView} record.
     */
    @XmlRootElement
    @JsonPropertyOrder({"view", "menuPages", "defaultPages"})
    @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
    public static class Builder {
      private long myViewId;
      private Set<StructurePage> myMenuPages;
      private Set<StructurePage> myDefaultPages;

      /**
       * Creates an empty builder.
       */
      public Builder() {
      }

      /**
       * Creates a builder that copies the state of an already existing {@code AssociatedView} record.
       *
       * @param view view record to copy
       */
      public Builder(AssociatedView view) {
        if (view != null) {
          myViewId = view.getViewId();
          setMenuPages(view.getMenuPages());
          setDefaultPages(view.getDefaultPages());
        }
      }

      /**
       * Creates an instance of {@link AssociatedView}.
       *
       * @return new instance of associated view record, or {@code null} if the builder is in invalid state
       * (no view ID is set)
       */
      public AssociatedView build() {
        if (!isValid()) return null;
        return new AssociatedView(myViewId, myMenuPages, myDefaultPages, true);
      }

      @JsonIgnore
      private boolean isValid() {
        return myViewId != 0;
      }

      /**
       * @return set of pages on which the associated view will be in the menu, or {@code null} if
       * the view should be in the menu on all pages.
       */
      @Nullable
      @XmlElement
      public Set<StructurePage> getMenuPages() {
        return enumSet(myMenuPages);
      }

      /**
       * Updates the set of pages on which the associated view will be in the menu.
       *
       * @param menuPages set of pages, on which the associated view should be in the Views drop-down menu. If {@code null}
       * is passed, the view will be in the menu on all pages.
       */
      public void setMenuPages(@Nullable Set<StructurePage> menuPages) {
        if (menuPages == null || menuPages.equals(ALL_PAGES)) {
          myMenuPages = null;
        } else {
          myMenuPages = enumSet(menuPages);
        }
        validatePages(myMenuPages);
      }

      /**
       * @return set of pages on which the associated view will be the default, or {@code null} if
       * the view should not be default on any page.
       */
      @Nullable
      @XmlElement
      public Set<StructurePage> getDefaultPages() {
        return enumSet(myDefaultPages);
      }

      /**
       * Updates the set of pages on which the associated view will be the default view.
       *
       * @param defaultPages set of pages, on which the associated view should be the default view. If {@code null}
       * is passed, the view will not be the default on any page.
       */
      public void setDefaultPages(@Nullable Set<StructurePage> defaultPages) {
        myDefaultPages = enumSet(defaultPages);
        validatePages(myDefaultPages);
      }

      /**
       * @return the view ID
       */
      @XmlElement
      @JsonProperty("view")
      public long getViewId() {
        return myViewId;
      }

      /**
       * Sets the ID of the view. No checks are made to make sure a view with such ID exists.
       *
       * @param viewId the id of the view
       */
      public void setViewId(long viewId) {
        myViewId = viewId;
      }

      private static void validatePages(Collection<StructurePage> pages) {
        if (pages == null) return;
        for (Iterator<StructurePage> ii = pages.iterator(); ii.hasNext(); ) {
          StructurePage page = ii.next();
          if (!ALL_PAGES.contains(page)) ii.remove();
        }
      }

      public String toString() {
        return "ViewSettings.AssociatedView.Builder{" +
          "viewId=" + myViewId +
          ", menuPages=" + myMenuPages +
          ", defaultPages=" + myDefaultPages +
          '}';
      }
    }
  }
}
TOP

Related Classes of com.almworks.jira.structure.api.view.ViewSettings$AssociatedView

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.