All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almworks.jira.structure.api.view.ViewSettings Maven / Gradle / Ivy

The newest version!
package com.almworks.jira.structure.api.view;

import com.almworks.jira.structure.api.settings.StructurePage;
import com.atlassian.annotations.PublicApi;
import com.fasterxml.jackson.annotation.*;
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.*;

/**
 * 

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.

* *

{@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.

* *

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}.

* *

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}.

* *

An instance of {@code ViewSettings} may be "undefined", which for the * per-structure view settings means "use default".

* *

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.

* *

{@code ViewSettings} instance is immutable and thread-safe. To construct a new * instance, use {@link Builder}.

* * @author Igor Sereda */ @PublicApi public class ViewSettings { public static final Set NO_PAGES = Collections.emptySet(); public static final Set ALL_PAGES = Collections.unmodifiableSet(EnumSet.of( STRUCTURE_BOARD, ISSUE_VIEW, PROJECT_TAB, GADGET)); public static final Set PAGES_WITH_DEFAULT_VIEW = Collections.unmodifiableSet(EnumSet.of( STRUCTURE_BOARD, 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 myAssociatedViews; private ViewSettings(@Nullable List associatedViews, boolean reuseList) { myAssociatedViews = associatedViews == null ? null : Collections.unmodifiableList(reuseList ? associatedViews : new ArrayList(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 getAssociatedViews() { return myAssociatedViews == null ? Collections.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; } /** *

View settings builder allows to construct instances of {@link ViewSettings} and serialize them * into JSON format.

* *

Builder is not thread-safe.

* * @see ViewSettings */ @XmlRootElement public static class Builder { private List 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 copyViews = copyFrom.getAssociatedViews(); myViews = new ArrayList(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 getViews() { return myViews == null ? null : new ArrayList(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 views) { myViews = views == null ? null : new ArrayList(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 list = null; if (myViews != null) { list = new ArrayList(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 ? PAGES_WITH_DEFAULT_VIEW : null); } /** *

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.

* *

Hint: to construct a collection of pages, use {@code EnumSet.of(...)}.

* * @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} or empty means all pages * @param defaultPages a collection of pages on which this view is the default, {@code null} or empty means no pages * * @return this builder * @throws IndexOutOfBoundsException if the specified index is invalid */ public Builder addView(int index, long viewId, @Nullable Collection menuPages, @Nullable Collection 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(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 + '}'; } } /** *

{@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.

* *

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.

* *

This class is immutable and thread-safe. To construct a new instance, use * {@link AssociatedView.Builder}.

*/ public static class AssociatedView { private final long myViewId; @NotNull private final Set myMenuPages; @NotNull private final Set myDefaultPages; private AssociatedView(long viewId, @Nullable Set menuPages, @Nullable Set defaultPages) { myViewId = viewId; myMenuPages = menuPages == null ? ALL_PAGES : Collections.unmodifiableSet(menuPages); myDefaultPages = defaultPages == null ? NO_PAGES : Collections.unmodifiableSet(defaultPages); } /** * @return unmodifiable set of pages which have this view in the drop-down */ @NotNull public Set getMenuPages() { return myMenuPages; } /** * @return unmodifiable set of pages which have this view as the default */ @NotNull public Set 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"}) @JsonInclude(value = JsonInclude.Include.NON_NULL) public static class Builder { private long myViewId; private Set myMenuPages; private Set 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); } @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 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 menuPages) { if (menuPages == null || menuPages.equals(ALL_PAGES)) { myMenuPages = null; } else { myMenuPages = enumSet(menuPages); } replaceDetailsPage(myMenuPages); validatePages(myMenuPages, ALL_PAGES); } /** * @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 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 defaultPages) { myDefaultPages = enumSet(defaultPages); validatePages(myDefaultPages, PAGES_WITH_DEFAULT_VIEW); } /** * @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 replaceDetailsPage(Set pages) { if (pages != null && pages.remove(STRUCTURE_BOARD_WITH_DETAILS)) { pages.addAll(EnumSet.of(STRUCTURE_BOARD, PROJECT_TAB)); } } private static void validatePages(Collection pages, Set universe) { if (pages == null) return; pages.retainAll(universe); } public String toString() { return "ViewSettings.AssociatedView.Builder{" + "viewId=" + myViewId + ", menuPages=" + myMenuPages + ", defaultPages=" + myDefaultPages + '}'; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy