package com.jbirdvegas.mgerrit.objects;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import com.jbirdvegas.mgerrit.Prefs;
import com.jbirdvegas.mgerrit.database.Config;
import com.jbirdvegas.mgerrit.search.SearchKeyword;
import org.jetbrains.annotations.Nullable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* A class that helps to deconstruct Gerrit queries and assemble them
* when necessary. This allows for setting individual parts of a query
* without knowing other query parameters.
*/
public class GerritURL implements Parcelable
{
private static Context sContext;
private String mStatus = "";
private boolean mRequestDetailedAccounts = false;
private String mSortkey = "";
private int mChangeNo = 0;
private Set<SearchKeyword> mSearchKeywords;
private int mLimit;
private enum ChangeDetailLevels {
DISABLED, // Do not fetch change details
LEGACY, // Fetch change details and use legacy URL (Gerrit 2.7 or lower)
ENABLED // Fetch change details and use new change details endpoint (Gerrit 2.8+)
}
private ChangeDetailLevels mRequestChangeDetail = ChangeDetailLevels.DISABLED;
public static final String DETAILED_ACCOUNTS_ARG = "&o=DETAILED_ACCOUNTS";
// used to query commit message
public static final String CURRENT_PATCHSET_ARGS = new StringBuilder(0)
.append("?o=CURRENT_REVISION")
.append("&o=CURRENT_COMMIT")
.append("&o=CURRENT_FILES")
.toString();
public static final String OLD_CHANGE_DETAIL_ARGS = new StringBuilder(0)
.append("&o=CURRENT_REVISION")
.append("&o=CURRENT_COMMIT")
.append("&o=CURRENT_FILES")
.append("&o=DETAILED_LABELS")
.append("&o=MESSAGES")
.toString();
// Default constructor to facilitate instantiation
public GerritURL() { }
public GerritURL(GerritURL url) {
mStatus = url.mStatus;
mRequestDetailedAccounts = url.mRequestDetailedAccounts;
mChangeNo = url.mChangeNo;
mRequestChangeDetail = url.mRequestChangeDetail;
}
public static void setContext(Context context) {
GerritURL.sContext = context;
}
public void addSearchKeyword(SearchKeyword keyword) {
if (mSearchKeywords == null) {
mSearchKeywords = new HashSet<>();
}
mSearchKeywords.add(keyword);
}
public void addSearchKeywords(Set<SearchKeyword> keywords) {
if (keywords == null) return;
for (SearchKeyword keyword : keywords) addSearchKeyword(keyword);
}
public void setStatus(String status) {
if (status == null) status = "";
mStatus = status;
}
/**
* DETAILED_ACCOUNTS: include _account_id and email fields when referencing accounts.
* @param requestDetailedAccounts true if to include the additional fields in the response
*/
public void setRequestDetailedAccounts(boolean requestDetailedAccounts) {
mRequestDetailedAccounts = requestDetailedAccounts;
}
public void requestChangeDetail(boolean request, Boolean useLegacyUrl) {
if (!request) {
mRequestChangeDetail = ChangeDetailLevels.DISABLED;
} else if (!useLegacyUrl) {
mRequestChangeDetail = ChangeDetailLevels.ENABLED;
mRequestDetailedAccounts = false;
} else {
mRequestChangeDetail = ChangeDetailLevels.LEGACY;
mRequestDetailedAccounts = true;
}
}
public void setChangeNumber(int changeNumber) {
mChangeNo = changeNumber;
}
/**
* Use the sortKey to resume a query from a given change. This is only valid
* for requesting change lists.
* @param sortKey The sortkey of a given change.
*/
public void setSortKey(String sortKey) {
mSortkey = sortKey;
}
/**
* @param limit The maximum number of changes to include in the response
*/
public void setLimit(int limit) {
this.mLimit = limit;
}
@Override
@Nullable
public String toString()
{
boolean addSeperator;
StringBuilder builder = new StringBuilder(0).append(Prefs.getCurrentGerrit(sContext));
builder.append("changes/");
if (mRequestChangeDetail == ChangeDetailLevels.ENABLED) {
if (mChangeNo > 0) {
builder.append(mChangeNo).append("/detail/")
.append(GerritURL.CURRENT_PATCHSET_ARGS);
}
// Cannot request change detail without a change number.
else return "";
} else {
builder.append("?q=");
addSeperator = appendStatus(builder, false);
try {
appendSearchKeywords(builder, addSeperator);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
appendArgs(builder);
return builder.toString();
}
public String getStatus() {
return mStatus;
}
@Nullable
public String getQuery() {
if (mStatus == null) return null;
else {
return JSONCommit.KEY_STATUS + ":" + mStatus;
}
}
public boolean equals(String str) {
return str != null && str.equals(this.toString());
}
private boolean appendStatus(StringBuilder builder, boolean addSeperator) {
if (mStatus != null && !mStatus.isEmpty()) {
if (addSeperator) builder.append('+');
builder.append(JSONCommit.KEY_STATUS)
.append(":")
.append(mStatus);
return true;
}
return false;
}
private boolean appendSearchKeywords(StringBuilder builder, boolean addSeperator)
throws UnsupportedEncodingException {
ServerVersion version = Config.getServerVersion(sContext);
if (mSearchKeywords != null && !mSearchKeywords.isEmpty()) {
if (addSeperator) {
builder.append('+');
addSeperator = false;
}
for (SearchKeyword keyword : mSearchKeywords) {
if (addSeperator) {
builder.append('+');
addSeperator = false;
}
String operator = URLEncoder.encode(keyword.getGerritQuery(version),"UTF-8");
if (operator != null && !operator.isEmpty()) {
builder.append(operator);
addSeperator = true;
}
}
}
return addSeperator;
}
private void appendArgs(StringBuilder builder) {
if (mSortkey != null && !mSortkey.isEmpty()) {
builder.append("&P=").append(mSortkey);
}
if (mRequestChangeDetail == ChangeDetailLevels.LEGACY) {
builder.append(GerritURL.OLD_CHANGE_DETAIL_ARGS);
}
if (mRequestDetailedAccounts) {
builder.append(GerritURL.DETAILED_ACCOUNTS_ARG);
}
if (mLimit > 0) {
builder.append("&n=").append(mLimit);
}
}
// --- Parcelable stuff so we can send this object through intents ---
public static final Creator<GerritURL> CREATOR
= new Creator<GerritURL>() {
public GerritURL createFromParcel(Parcel in) {
return new GerritURL(in);
}
@Override
public GerritURL[] newArray(int size) {
return new GerritURL[0];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mChangeNo);
dest.writeString(mStatus);
dest.writeInt(mRequestDetailedAccounts ? 1 : 0);
dest.writeString(mSortkey);
dest.writeString(mRequestChangeDetail.name());
dest.writeInt(mLimit);
int size;
if (mSearchKeywords == null) size = 0;
else size = mSearchKeywords.size();
dest.writeInt(size);
if (size > 0) {
SearchKeyword[] keywords = new SearchKeyword[size];
dest.writeTypedArray(mSearchKeywords.toArray(keywords), flags);
}
}
public GerritURL(Parcel in) {
mChangeNo = in.readInt();
mStatus = in.readString();
mRequestDetailedAccounts = in.readInt() == 1;
mSortkey = in.readString();
mRequestChangeDetail = ChangeDetailLevels.valueOf(in.readString());
mLimit = in.readInt();
int size = in.readInt();
if (size > 0) {
SearchKeyword[] keywords = new SearchKeyword[size];
in.readTypedArray(keywords, SearchKeyword.CREATOR);
mSearchKeywords = new HashSet<>(Arrays.asList(keywords));
}
}
}