Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.almworks.jira.structure.util.StructureUtil Maven / Gradle / Ivy
package com.almworks.jira.structure.util;
import com.almworks.integers.*;
import com.almworks.jira.structure.api.*;
import com.almworks.jira.structure.api2g.attribute.*;
import com.almworks.jira.structure.api2g.forest.*;
import com.almworks.jira.structure.api2g.item.ItemIdentity;
import com.almworks.jira.structure.api2g.row.StructureRow;
import com.almworks.jira.structure.api2g.structure.Structure;
import com.almworks.jira.structure.api2g.structure.StructureManager;
import com.atlassian.crowd.embedded.api.Group;
import com.atlassian.crowd.embedded.api.User;
import com.atlassian.crowd.embedded.impl.IdentifierUtils;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.config.properties.ApplicationProperties;
import com.atlassian.jira.issue.*;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.user.ApplicationUsers;
import com.atlassian.jira.util.I18nHelper;
import com.opensymphony.workflow.loader.ActionDescriptor;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.TypeReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;
import static org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* Contains miscellaneous utility methods.
*/
public class StructureUtil {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(StructureUtil.class);
public static final int MAX_PROPERTY_LENGTH = 250;
public static final La STRING_LONG = new La() {
public Long la(String argument) {
try {
return argument == null ? null : Long.parseLong(argument.trim());
} catch (NumberFormatException e) {
return null;
}
}
};
public static final La INT_ITERATOR_TO_INT = new La() {
@Override
public Integer la(IntIterator intIterator) {
return intIterator.value();
}
};
public static final La LONG_ITERATOR_TO_LONG = new La() {
@Override
public Long la(LongIterator longIterator) {
return longIterator.value();
}
};
public static final TypeReference> JSON_MAP = new TypeReference>() {};
public static PermissionLevel applyPermissions(List permissions, User user,
List stack, La> resolver, PermissionLevel pass)
{
PermissionLevel r = pass;
if (permissions != null) {
for (PermissionRule permission : permissions) {
r = permission.apply(user, r, stack, resolver);
}
}
return r;
}
public static JAXBContext createJAXBContext(Class contextClass) {
Thread thread = Thread.currentThread();
ClassLoader contextLoader = thread.getContextClassLoader();
ClassLoader[] loaders = {ClassLoader.getSystemClassLoader(), contextLoader, JAXBContext.class.getClassLoader(), contextClass.getClassLoader()};
JAXBContext context = null;
try {
context = JAXBContext.newInstance(contextClass);
} catch (JAXBException e) {
for (int i = 1; i < loaders.length; i++) {
try {
thread.setContextClassLoader(loaders[i]);
context = JAXBContext.newInstance(contextClass);
if (context != null) break;
} catch (JAXBException e2) {
// ignore
}
}
if (context == null) {
logger.error("cannot initialize JAXB context for " + contextClass + " (tried loaders: " + Arrays.asList(loaders) + ")", e);
}
} finally {
thread.setContextClassLoader(contextLoader);
}
return context;
}
@Nullable
public static Long getSingleParameterLong(@Nullable Map map, @Nullable String name) {
if (map == null || name == null) return null;
Object r = map.get(name);
if (r instanceof Long || r instanceof Integer) {
return ((Number) r).longValue();
}
String value = getSingleString(r);
if (value != null && value.length() > 0) {
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
logger.warn("bad parameter " + name + " [" + value + "]");
}
}
return null;
}
@Nullable
public static Integer getSingleParameterInteger(@Nullable Map map, @Nullable String name) {
if (map == null || name == null) return null;
Object r = map.get(name);
if (r instanceof Integer) {
return (Integer) r;
}
String value = getSingleString(r);
if (value != null && value.length() > 0) {
value = value.trim();
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
logger.warn("bad parameter " + name + " [" + value + "]");
}
}
return null;
}
public static boolean getSingleParameterBoolean(@Nullable Map map, @Nullable String name) {
if (map == null || name == null) return false;
Object r = map.get(name);
if (r instanceof Boolean) {
return (Boolean) r;
}
String value = getSingleString(r);
return value != null && "true".equalsIgnoreCase(value.trim());
}
@Nullable
public static String getSingleParameter(@Nullable Map map, @Nullable String name) {
if (map == null || name == null) return null;
return getSingleString(map.get(name));
}
@Nullable
private static String getSingleString(Object r) {
if (r instanceof Collection) {
Collection c = (Collection) r;
r = c.isEmpty() ? null : c.iterator().next();
} else if (r instanceof String[]) {
r = ((String[]) r).length == 0 ? null : ((String[]) r)[0];
}
return r == null ? null : String.valueOf(r);
}
@NotNull
public static List getMultiParameter(@Nullable Map map, @Nullable String name) {
if (map == null || name == null) return Collections.emptyList();
Object r = map.get(name);
if (r instanceof Collection) {
Collection c = (Collection) r;
ArrayList result = new ArrayList(c.size());
for (Object o : c) {
if (o != null) result.add(String.valueOf(o));
}
return result;
} else if (r instanceof String[]) {
String[] array = (String[]) r;
return array.length == 0 ? Collections.emptyList() : Arrays.asList(array);
}
return r == null ? Collections.emptyList() : Collections.singletonList(String.valueOf(r));
}
@NotNull
public static List getMultiParameterLong(@Nullable Map map, @Nullable String name) {
return STRING_LONG.arrayList(getMultiParameter(map, name));
}
/**
* Sets a possibly long value (template) as an application property. Because properties may be limited to 255
* characters, this method breaks up long property into several properties each no longer than allowed.
*
* @param properties properties interface
* @param name the name of the property
* @param value the value
*/
public static void setLongProperty(ApplicationProperties properties, String name, String value) {
if (value == null) value = "";
int len = value.length();
int chunk = Math.min(len, MAX_PROPERTY_LENGTH);
properties.setString(name, value.substring(0, chunk));
value = value.substring(chunk);
len -= chunk;
int step = 1;
while (len > 0) {
chunk = Math.min(len, MAX_PROPERTY_LENGTH);
properties.setString(name + "." + (step++), value.substring(0, chunk));
value = value.substring(chunk);
len -= chunk;
}
while (true) {
String n = name + "." + (step++);
String s = properties.getDefaultBackedString(n);
if (s == null || s.length() <= 0) {
break;
}
properties.setString(n, null);
}
}
/**
* Reads a value previously stored in properties using setLongValue
*
* @param properties application propeties
* @param name the name of the property to read
* @return property value or an empty string
*/
@NotNull
public static String getLongProperty(ApplicationProperties properties, String name) {
StringBuilder r = new StringBuilder();
String s = properties.getDefaultBackedString(name);
int step = 1;
while (s != null && s.length() > 0) {
r.append(s);
s = properties.getDefaultBackedString(name + "." + (step++));
}
return r.toString();
}
/**
* Returns the last element of the given list if it's not empty, 0 otherwise.
*
* @param list the list of longs
* @return last value of list
or 0 if it's empty.
*/
public static long lastOrZero(LongList list) {
return (list == null || list.isEmpty()) ? 0 : list.get(list.size() - 1);
}
/**
* Tests whether the given lists are equal (contain the same numbers in the same order).
* An empty list is considered equal to a null
list.
*
* @param list1 the first list
* @param list2 the second list
* @return true
if the lists are equal, false
otherwise.
*/
public static boolean listsEqual(LongList list1, LongList list2) {
boolean empty1 = list1 == null || list1.isEmpty();
boolean empty2 = list2 == null || list2.isEmpty();
if (empty1 && empty2) return true;
if (empty1 || empty2) return false;
return list1.equals(list2);
}
@Nullable
public static List copyPermissionsOrNull(@Nullable Collection extends PermissionRule> permissions) {
return permissions == null ? null : copyPermissions(permissions);
}
@NotNull
public static List copyPermissions(@Nullable Collection extends PermissionRule> permissions) {
ArrayList r = new ArrayList(permissions == null ? 0 : permissions.size());
if (permissions != null) {
for (PermissionRule permission : permissions) {
r.add(permission.clone());
}
}
return r;
}
/**
* Returns a string representation of the issue that is used to write log messages. Writes issue ID and, if possible,
* issue key.
* @param issue the ID of the issue
* @return string that can be used in output
*/
@NotNull
public static String getDebugIssueString(@Nullable Long issue) {
return issue == null ? "null" : appendDebugIssueString(issue, new StringBuilder()).toString();
}
public static StringBuilder appendDebugIssueString(@Nullable Long issue, StringBuilder sb) {
if (issue == null) sb.append("null");
else appendDebugIssueString(issue, getDebugIssueKey(issue), sb);
return sb;
}
/** Returns issue key if possible. Use for debug output. */
@Nullable
public static String getDebugIssueKey(long issueId) {
try {
IssueManager issueManager = ComponentAccessor.getIssueManager();
MutableIssue io = issueManager == null ? null : issueManager.getIssueObject(issueId);
return io == null ? null : io.getKey();
} catch (Exception|LinkageError ignored) {}
return null;
}
/**
* Returns a string representation of the issue that is used to write log messages. Writes issue ID and, if possible,
* issue key.
* @param issue the issue
* @return string that can be used in output
*/
@NotNull
public static String getDebugIssueString(@Nullable Issue issue) {
return issue == null ? "null" : appendDebugIssueString(issue.getId(), issue.getKey(), new StringBuilder()).toString();
}
/**
* @return issue key if the issue object is not {@code null}. Otherwise, if issue ID is not {@code null}, returns issue ID in the form "issue #123". Otherwise,
* returns string "(no issue ID)".
* Better suited for high-level log messages than {@link #getDebugIssueString(java.lang.Long)} in that it is easily recognizable as issue representation to JIRA admin.
* */
public static String issueKeyOrId(@Nullable Issue issue, @Nullable Long issueId) {
return issue != null ? issue.getKey() : issueId != null ? "issue #" + issueId : "(no issue ID)";
}
@NotNull
public static StringBuilder appendDebugIssueString(@Nullable Long issueId, @Nullable String issueKey, StringBuilder r) {
if (issueKey != null) {
r.append('(').append(issueKey).append(' ').append(issueId).append(')');
} else {
r.append(issueId);
}
return r;
}
@NotNull
public static StringBuilder appendDebugStructureString(final long structureId, final StructureManager manager,
final StringBuilder sb)
{
return StructureAuth.sudo(new SimpleCallable() {
@Override
public StringBuilder call() {
try {
Structure structure = manager.getStructure(structureId, PermissionLevel.NONE);
String name = structure.getName();
return sb.append('\'').append(name).append("' (#").append(structureId).append(')');
} catch (StructureException ignored) {
return sb.append("#").append(structureId);
}
}
});
}
public static String getDebugStructureString(long structureId, StructureManager structureManager) {
return appendDebugStructureString(structureId, structureManager, new StringBuilder()).toString();
}
@NotNull
public static String username(@Nullable User user) {//j6 ok
String username = user != null ? user.getName() : "anonymous"; //j6 ok
return username == null ? "user[name = null]" : username;
}
@NotNull
public static String username(@Nullable ApplicationUser user) {//j6 ok
String username = user != null ? user.getName() : "anonymous"; //j6 ok
return username == null ? "user[name = null]" : username;
}
public static String debugConstant(IssueConstant constant) {
return constant == null ? "" : constant.getName() + "(" + constant.getId() + ")";
}
public static String debugAction(ActionDescriptor action) {
return action == null ? "" : action.getName() + "(" + action.getId() + ")";
}
public static String getTextInCurrentUserLocale(String key, Object... parameters) {
return getText2(null, StructureAuth.getUser(), key, parameters);
}
@Deprecated
public static String getText(@Nullable Locale locale, @Nullable User user, String textKey, Object... messageParameters) {
return getText2(locale, ApplicationUsers.from(user), textKey, messageParameters);
}
/**
* Formats the text taken from the i18n bundle with the specified parameters in the locale that is selected as follows:
*
* If locale is not null , it is used.
* Otherwise, if user is not null , the user locale (set up in the user preferences) is used.
* If both locale and user are null , JIRA default locale is used. In a highly unlikely
* event when JIRA application parameters are inaccessible, {@link java.util.Locale#ROOT root} (neutral) locale is used.
*
* @param messageParameters can be the same as {@link com.atlassian.jira.util.I18nHelper#getText(String, Object)}
* */
@NotNull
public static String getText2(@Nullable Locale locale, @Nullable ApplicationUser user, String textKey, Object... messageParameters) {
I18nHelper.BeanFactory factory = ComponentAccessor.getI18nHelperFactory();
if (factory == null) return textKey + ' ' + Arrays.toString(messageParameters);
I18nHelper i18nHelper = locale != null || user == null
? factory.getInstance(locale != null ? locale : getJiraDefaultLocale())
: factory.getInstance(user);
return i18nHelper.getText(textKey, messageParameters);
}
public static Locale getJiraDefaultLocale() {
ApplicationProperties appProps = ComponentAccessor.getApplicationProperties();
if (appProps == null) {
logger.warn("No application properties: probably a System 2 plugin now? Using root (neutral) locale.");
return Locale.ROOT;
}
Locale defaultLocale = appProps.getDefaultLocale();
if (defaultLocale == null) {
logger.warn("No JIRA default locale. Using root (neutral) locale.");
return Locale.ROOT;
}
return defaultLocale;
}
@Nullable
public static String getUserKey(@Nullable User user) {
ApplicationUser applicationUser = ApplicationUsers.from(user);
return applicationUser == null ? null : applicationUser.getKey();
}
public static String getUserKey(@Nullable ApplicationUser user) {
return user == null ? null : user.getKey();
}
@Nullable
public static String getUserName(@Nullable User user) {
return user == null ? null : user.getName();
}
@Nullable
public static String getUserName(@Nullable ApplicationUser user) {
return user == null ? null : user.getUsername();
}
@Nullable
public static User getUserByKey(@Nullable String userKey) {
ApplicationUser user = ApplicationUsers.byKey(userKey);
return user == null ? null : user.getDirectoryUser();
}
@Nullable
public static User getUserByName(@Nullable String userName) {
return ApplicationUsers.toDirectoryUser(ComponentAccessor.getUserManager().getUserByName(userName));
}
@Nullable
public static String getUserNameByKey(@Nullable String userKey) {
User user = getUserByKey(userKey);
return user == null ? null : user.getName();
}
@Nullable
public static String migrateUserNameToUserKey(@Nullable String userName) {
return userName == null ? null : IdentifierUtils.toLowerCase(userName);
}
@Deprecated
@Nullable
public static User getUserOldObject(@Nullable ApplicationUser user) {
return ApplicationUsers.toDirectoryUser(user);
}
/**
* @return a fairly random number that is used to identify the scope of some other data for external components
* with longer lifecycle (such as browsers or other addons). The signature is retrieved once per component
* lifecycle and all other data it provides is valid only within the same signature.
*/
public static int createRuntimeSignature() {
// todo for easier debugging and ensuring that the IDs are unique within the system, allocate a base number and then increment
// static context is ok because the plugin is reloaded with the class
long v = System.currentTimeMillis() + 1299827L * System.nanoTime() + 31 * new Object().hashCode();
return new Long(v).hashCode();
}
public static String getDebugRowString(long row) {
// todo
return "(row " + row + ")";
}
public static String getDebugRowString(StructureRow row) {
// todo
return row == null ? "" : row.toString();
}
@NotNull
public static String toJson(@Nullable Object object) {
return toJson(object, defaultMapper());
}
@NotNull
public static String toJson(@Nullable Object object, ObjectMapper mapper) {
if (object == null) return "";
try {
return mapper.writeValueAsString(object);
} catch (IOException e) {
logger.warn("failed to serialize " + object, e);
return "";
}
}
@Nullable
public static T fromJson(String specJson, Class expectedClass) {
return fromJson(specJson, expectedClass, defaultMapper());
}
@Nullable
public static T fromJson(String specJson, Class expectedClass, ObjectMapper mapper) {
if (specJson == null || specJson.length() == 0) return null;
try {
return mapper.readValue(specJson, expectedClass);
} catch (IOException e) {
logger.warn("failed to deserialize " + specJson + " for " + expectedClass, e);
return null;
}
}
@Nullable
public static T fromJson(String json, TypeReference typeRef) {
return fromJson(json, typeRef, defaultMapper());
}
@Nullable
public static T fromJson(String json, TypeReference typeRef, ObjectMapper mapper) {
if (json == null || json.isEmpty()) {
return null;
}
try {
return mapper.readValue(json, typeRef);
} catch (IOException e) {
logger.warn("failed to deserialize " + json + " for " + typeRef, e);
return null;
}
}
public static Map fromJson(String json) {
return fromJson(json, defaultMapper());
}
@Nullable
public static Map fromJson(String json, ObjectMapper mapper) {
return fromJson(json, JSON_MAP, mapper);
}
@Nullable
public static T fromMap(Map json, Class expectedClass) {
return fromMap(json, expectedClass, defaultMapper());
}
@Nullable
public static T fromMap(Map json, Class expectedClass, ObjectMapper mapper) {
if (json == null || json.isEmpty()) {
return null;
}
try {
return mapper.convertValue(json, expectedClass);
} catch (IllegalArgumentException e) {
logger.warn("failed to convert " + json + " to " + expectedClass, e);
return null;
}
}
@Nullable
public static Map toMap(Object object) {
return toMap(object, defaultMapper());
}
@Nullable
public static Map toMap(Object object, ObjectMapper mapper) {
try {
return mapper.convertValue(object, JSON_MAP);
} catch (IllegalArgumentException e) {
logger.warn("failed to convert " + object + " to " + JSON_MAP, e);
return null;
}
}
public static ObjectMapper defaultMapper() {
return new ObjectMapper().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
}
public static ObjectMapper withUnknownPropertiesMapper() {
return defaultMapper().disable(FAIL_ON_UNKNOWN_PROPERTIES);
}
@SuppressWarnings("unchecked")
public static Class> setType() {
return (Class)Set.class;
}
@SuppressWarnings("unchecked")
public static Class> mapType() {
return (Class)Map.class;
}
@SuppressWarnings("unchecked")
public static Class> listType() {
return (Class)List.class;
}
public static boolean onlyOneIsTrue(boolean... booleans) {
boolean result = false;
for (boolean b : booleans) {
if (b) {
if (result) {
result = false;
break;
} else {
result = true;
}
}
}
return result;
}
public static void swap(List list, int a, int b) {
T tmp = list.get(a);
list.set(a, list.get(b));
list.set(b, tmp);
}
public static void warnExceptionIfDebug(Logger log, String message, Throwable t) {
if (log.isDebugEnabled()) {
log.warn(message, t);
} else if (t != null) {
log.warn(message + " " + getMessageWithCauseChain(t));
} else {
log.warn(message);
}
}
public static String getMessageWithCauseChain(Throwable t) {
StringBuilder sb = new StringBuilder();
String pre = "";
while (t != null) {
sb.append(pre).append(t);
t = t.getCause();
pre = "; ";
}
return sb.toString();
}
public static ItemDisplayable getItemDisplayable(ItemIdentity itemId, StructureAttributeService attributeService) {
return getItemDisplayable(new ItemForestBuilderImpl().nextRow(itemId).build(), attributeService);
}
public static ItemDisplayable getItemDisplayable(StructureRow row, StructureAttributeService attributeService) {
return getItemDisplayable(ImmutableItemForest.of(row), attributeService);
}
public static ItemDisplayable getItemDisplayable(ItemForest fragment, StructureAttributeService attributeService) {
assert fragment.getForest().size() == 1 : fragment;
VersionedRowValues values = attributeService.getAttributeValues(
fragment, fragment.getForest().getRows(), Arrays.asList(CoreAttributeSpecs.DISPLAYABLE_TEXT, CoreAttributeSpecs.URL_TEXT));
long rowId = fragment.getForest().getRow(0);
String description = values.get(rowId, CoreAttributeSpecs.DISPLAYABLE_TEXT);
if (StringUtils.isEmpty(description)) {
description = fragment.getRow(rowId).getItemId().toString();
}
String url = values.get(rowId, CoreAttributeSpecs.URL_TEXT);
return new ItemDisplayable(description, url);
}
@Nullable
public static User getUserObject(Object object) {
if (object instanceof User) return (User) object;
if (object instanceof ApplicationUser) return ((ApplicationUser) object).getDirectoryUser();
return null;
}
@Nullable
public static String getGroupName(Group group) {
return group == null ? null : group.getName();
}
public static boolean isMutuallyExclusive(LongList set1, LongList set2) {
if (set2.isEmpty() || set1.isEmpty()) return true;
LongList hashed, iterated;
boolean insertedIsHashed = set1.size() < set2.size();
hashed = insertedIsHashed ? set1 : set2;
iterated = insertedIsHashed ? set2 : set1;
LongOpenHashSet set = LongOpenHashSet.createFrom(hashed);
return !set.containsAny(iterated);
}
// todo add to integers
public static boolean isMutuallyExclusive(LongSet set1, LongSet set2) {
boolean set1Iterated = set1.size() < set2.size();
LongSet iterated = set1Iterated ? set1 : set2;
LongSet checked = set1Iterated ? set2 : set1;
return !checked.containsAny(iterated);
}
@NotNull
public static String toDebugJson(@Nullable Object object) {
if (object == null) return "";
if (object instanceof String) return (String)object;
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(object);
} catch (IOException e) {
return "?" + e + "?";
}
}
public static long nn(@Nullable Long value, long defaultValue) {
return value == null ? defaultValue : value;
}
public static int nn(@Nullable Integer value, int defaultValue) {
return value == null ? defaultValue : value;
}
@NotNull
public static T nnv(@Nullable T value, @NotNull T defaultValue) {
return value == null ? defaultValue : value;
}
public static long lv(String v, long defaultValue) {
if (v == null) return defaultValue;
try {
return Long.parseLong(v);
} catch (NumberFormatException e) {
return defaultValue;
}
}
public static int iv(String v, int defaultValue) {
if (v == null) return defaultValue;
try {
return Integer.parseInt(v);
} catch (NumberFormatException e) {
return defaultValue;
}
}
public static Long lvn(String v) {
if (v == null) return null;
try {
return Long.parseLong(v);
} catch (NumberFormatException e) {
return null;
}
}
public static String nn(String value) {
return value == null ? "" : value;
}
public static long nnl(Long value) {
return value == null ? 0L : value;
}
public static int nni(Integer value) {
return value == null ? 0 : value;
}
@NotNull
public static String getBaseUrl() {
com.atlassian.sal.api.ApplicationProperties ap = ComponentAccessor.getOSGiComponentInstanceOfType(
com.atlassian.sal.api.ApplicationProperties.class);
if (ap != null) {
return nn(ap.getBaseUrl());
} else {
logger.warn("sal ApplicationProperties is null");
return "";
}
}
public static String decodeURL(String url) {
try {
return url == null ? "" : URLDecoder.decode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("NO UTF-8?", e);
return url;
} catch (IllegalArgumentException e) {
logger.error("when decoding [" + url + "]", e);
return url;
}
}
public static String encodeURL(String url) {
try {
return url == null ? "" : URLEncoder.encode(url, "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("NO UTF-8?", e);
return url;
} catch (IllegalArgumentException e) {
logger.error("when encoding [" + url + "]", e);
return url;
}
}
}