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

com.almworks.jira.structure.api.item.ItemIdentity Maven / Gradle / Ivy

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

import com.almworks.jira.structure.api.util.Limits;
import com.atlassian.annotations.PublicApi;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.annotation.concurrent.Immutable;
import java.io.Serializable;
import java.text.ParseException;

/**
 * 

{@code ItemIdentity} represents an item, a core concept in Structure's architecture.

* *

An item is an abstract notion that generalizes everything that can be put into a structure. Issues, projects, * users and other JIRA objects — all can be represented as items. Structures themselves may be represented * as items. Structure can be extended and new types of items can be added to it — for example, Structure.Pages * extension adds "Confluence page" item type.

* *

Because of such diversity, there's no single class that represents an item in the low-level API. Instead, * there's {@code ItemIdentity} which represents the only single property that each item must have — its * unique ID.

* *

Anatomy of the item ID

* *

Each {@code ItemIdentity} is a pair of item type and item id within that * type.

* *

Item type is nothing else but the complete module key of the {@code } module, which * supports this item type. Main item types are listed in {@link CoreItemTypes}.

* *

The actual item ID can be either {@code long} or {@code String}. Numeric item IDs are used whenever possible, * as that allows Structure to save consumed memory and speed up calculations. Issues, Folders, Projects, Sprints * and most other items are identified with {@code long} ID.

* *

Text item IDs are used in other cases. For example, Users and Special Folders are represented with * {@code String} IDs.

* *

Item type does not prescribe whether IDs are long or string based. For one type there might be issues with * long IDs and string IDs.

* *

Uniqueness

* *

It is important to remember that an item ID is unique only within a single instance of JIRA (or within a * single cluster running JIRA Data Center). It is not globally unique. So when synchronizing multiple JIRAs or * when restoring Structure data on another instance, all items must be mapped to the new instance.

* *

Value Range

* *
    *
  • Both item type and string ID must not be null or empty and must not exceed 190 characters.
  • *
  • There are no limit on the value of long ID, it can be 0.
  • *
* *

Canonical notation

* *

{@code ItemIdentity} can be serialized into a {@code String} by calling {@code toString()} on it. The serialized * form is either:

* *
    *
  • {@code /} for long-based identities, for example: {@code com.almworks.jira.structure:type-issue/10000}
  • *
  • {@code //} for string-based identities, for example: {@code com.almworks.jira.structure:type-user//admin}
  • *
* * @see CoreItemTypes * @see CoreIdentities */ @PublicApi @Immutable public abstract class ItemIdentity implements Serializable { private static final long serialVersionUID = 2016_11_18_0000L; /** * Represents non-existing item. */ public static final ItemIdentity ITEM_ZERO = longId("0", 0); /** * Item type. */ @NotNull private final String myItemType; private ItemIdentity(@NotNull String itemType) { assert !StringUtils.isBlank(itemType) : itemType; if (StringUtils.isEmpty(itemType)) { // not using isBlank() here to avoid relatively costly isWhitespace() check throw new IllegalArgumentException("item type must not be empty"); } if (itemType.length() > Limits.MAX_MODULE_KEY_LENGTH) { throw new IllegalArgumentException("item type must not be longer than " + Limits.MAX_MODULE_KEY_LENGTH + " chars"); } myItemType = itemType; } /** * Returns item type. */ @NotNull public String getItemType() { return myItemType; } /** * Returns {@code true} if this ID is string-based. */ public boolean isStringId() { return false; } /** * Gets the string ID from a string-based {@code ItemIdentity}. * * @return string ID, not null, not empty * @throws UnsupportedOperationException if this is not a string-based ID */ @NotNull public String getStringId() { throw new UnsupportedOperationException("Not a string ID"); } /** * Returns {@code true} if this ID is long-based. */ public boolean isLongId() { return false; } /** * Gets the long ID from a long-based {@code ItemIdentity}. * * @return long ID, not {@code 0} * @throws UnsupportedOperationException if this is not a long-based ID */ public long getLongId() { throw new UnsupportedOperationException("Not a long ID"); } /** * Creates a new string-based ID. * * @param itemType item type * @param stringId item ID * @return identity * @throws IllegalArgumentException if the parameters are invalid */ @NotNull public static ItemIdentity stringId(@NotNull String itemType, @NotNull String stringId) { return new StringIdentity(itemType, stringId); } /** * Creates a new long-based ID. * * @param itemType item type * @param longId item ID * @return identity * @throws IllegalArgumentException if the parameters are invalid */ @NotNull public static ItemIdentity longId(@NotNull String itemType, long longId) { return new LongIdentity(itemType, longId); } /** * Parses canonical string representation of the item ID, which can be retrieved with {@code toString()} method. * * @param id string representation of the item ID. * @return identity * @throws ParseException if there were errors parsing the string or if the parsed parameters were invalids */ @NotNull public static ItemIdentity parse(@Nullable String id) throws ParseException { if (id == null || id.isEmpty()) { throw new ParseException("cannot parse empty id", 0); } int k = id.indexOf('/'); if (k < 0) { try { long longId = Long.parseLong(id); return CoreIdentities.issue(longId); } catch (NumberFormatException e) { throw new ParseException("unknown id format [" + id + "]", id.length()); } } if (k == 0) { throw new ParseException("empty type id [" + id + "]", 0); } if (k == id.length() - 1) { throw new ParseException("unexpected end of line [" + id + "]", id.length()); } String typeId = id.substring(0, k); if (id.charAt(k + 1) == '/') { String sid = id.substring(k + 2); if (sid.isEmpty()) { throw new ParseException("empty sid [" + id + "]", k + 2); } try { return stringId(typeId, sid); } catch (IllegalArgumentException e) { throw new ParseException(e.getMessage(), k + 1); } } else { String lid = id.substring(k + 1); if (lid.isEmpty()) { throw new ParseException("empty id [" + id + "]", k + 1); } try { return longId(typeId, Long.parseLong(lid)); } catch (NumberFormatException e) { throw new ParseException("bad id format [" + id + "]", k + 1); } catch (IllegalArgumentException e) { throw new ParseException(e.getMessage(), k + 1); } } } public abstract String toSimplifiedString(); /** * Represents string-based ID. * * @see ItemIdentity */ @PublicApi public static final class StringIdentity extends ItemIdentity implements Serializable { private static final long serialVersionUID = 2016_11_18_0000L; @NotNull private final String myStringId; private StringIdentity(@NotNull String itemType, @NotNull String stringId) { super(itemType); if (StringUtils.isBlank(stringId)) { throw new IllegalArgumentException("string id must not be empty"); } if (stringId.length() > Limits.MAX_ITEM_STRING_ID_LENGTH) { throw new IllegalArgumentException("string id must not be longer than " + Limits.MAX_ITEM_STRING_ID_LENGTH + " chars"); } myStringId = stringId; } @Override public boolean isStringId() { return true; } @Override @NotNull public String getStringId() { return myStringId; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } StringIdentity that = (StringIdentity) o; return myStringId.equals(that.myStringId) && getItemType().equals(that.getItemType()); } @Override public int hashCode() { return myStringId.hashCode() * 31 + getItemType().hashCode(); } @Override public String toString() { return getItemType() + "//" + myStringId; } @Override public String toSimplifiedString() { return CoreItemTypes.simplifyType(getItemType()) + "//" + myStringId; } } /** * Represents long-based ID. * * @see ItemIdentity */ @PublicApi public static final class LongIdentity extends ItemIdentity implements Serializable { private static final long serialVersionUID = 2016_11_18_0000L; private final long myLongId; private LongIdentity(@NotNull String itemType, long longId) { super(itemType); // watch out for reasons to establish a contract where longId cannot be 0 myLongId = longId; } @Override public boolean isLongId() { return true; } @Override public long getLongId() { return myLongId; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } LongIdentity that = (LongIdentity) o; return myLongId == that.myLongId && getItemType().equals(that.getItemType()); } @Override public int hashCode() { return (int) (myLongId ^ (myLongId >>> 32)) * 31 + getItemType().hashCode(); } @Override public String toString() { return getItemType() + "/" + myLongId; } @Override public String toSimplifiedString() { return CoreItemTypes.simplifyType(getItemType()) + "/" + myLongId; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy