package org.wikipedia.page;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.page.PageLeadProperties;
import org.wikipedia.util.DimenUtil;
import java.text.ParseException;
import java.util.Date;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.wikipedia.util.DateUtil.getIso8601DateFormat;
/**
* Immutable class that contains metadata associated with a PageTitle.
*/
public class PageProperties implements Parcelable {
private static final String JSON_NAME_TITLE_PRONUNCIATION_URL = "titlePronunciationUrl";
private static final String JSON_NAME_GEO = "geo";
private static final String JSON_NAME_NAMESPACE = "namespace";
private final int pageId;
@NonNull private final Namespace namespace;
private final long revisionId;
private final Date lastModified;
private final String displayTitleText;
private final String editProtectionStatus;
private final int languageCount;
private final boolean isMainPage;
private final boolean isDisambiguationPage;
/** Nullable URL with no scheme. For example, foo.bar.com/ instead of http://foo.bar.com/. */
@Nullable private final String leadImageUrl;
private final String leadImageName;
@Nullable private final String titlePronunciationUrl;
@Nullable private final Location geo;
@Nullable private final String wikiBaseItem;
/**
* True if the user who first requested this page can edit this page
* FIXME: This is not a true page property, since it depends on current user.
*/
private final boolean canEdit;
/**
* Side note: Should later be moved out of this class but I like the similarities with
* PageProperties(JSONObject).
*/
public PageProperties(@NonNull WikiSite wiki, PageLeadProperties core) {
pageId = core.getId();
namespace = core.getNamespace(wiki);
revisionId = core.getRevision();
displayTitleText = defaultString(core.getDisplayTitle());
titlePronunciationUrl = core.getTitlePronunciationUrl();
geo = core.getGeo();
editProtectionStatus = core.getFirstAllowedEditorRole();
languageCount = core.getLanguageCount();
// todo: don't hardcode this here
leadImageUrl = core.getLeadImageUrl(DimenUtil.calculateLeadImageWidth());
leadImageName = core.getLeadImageName();
lastModified = new Date();
String lastModifiedText = core.getLastModified();
if (lastModifiedText != null) {
try {
lastModified.setTime(getIso8601DateFormat().parse(lastModifiedText).getTime());
} catch (ParseException e) {
Log.d("PageProperties", "Failed to parse date: " + lastModifiedText);
}
}
// assume formatversion=2 is used so we get real booleans from the API
canEdit = core.isEditable();
isMainPage = core.isMainPage();
isDisambiguationPage = core.isDisambiguation();
wikiBaseItem = core.getWikiBaseItem();
}
/**
* Create a new PageProperties object.
* @param json JSON object from which this item will be built.
*/
public PageProperties(JSONObject json) {
pageId = json.optInt("id");
namespace = Namespace.of(json.optInt(JSON_NAME_NAMESPACE));
revisionId = json.optLong("revision");
displayTitleText = json.optString("displaytitle");
titlePronunciationUrl = json.optString(JSON_NAME_TITLE_PRONUNCIATION_URL, null);
geo = GeoUnmarshaller.unmarshal(json.optString(JSON_NAME_GEO, null));
if (json.optJSONObject("protection") != null && json.optJSONObject("protection").has("edit")) {
editProtectionStatus = json.optJSONObject("protection").optJSONArray("edit").optString(0);
} else {
editProtectionStatus = null;
}
languageCount = json.optInt("languagecount");
JSONObject thumb = json.optJSONObject("thumb");
leadImageUrl = thumb != null ? thumb.optString("url") : null;
JSONObject image = json.optJSONObject("image");
leadImageName = image != null ? image.optString("file") : null;
lastModified = new Date();
String lastModifiedText = json.optString("lastmodified");
try {
lastModified.setTime(getIso8601DateFormat().parse(lastModifiedText).getTime());
} catch (ParseException e) {
Log.d("PageProperties", "Failed to parse date: " + lastModifiedText);
}
// There's something really screwy going on with the "editable" key in the API response.
// It's not always returning a boolean, sadly.
// If the key is the empty string, or true, then the page is editable.
// If the key is not in the response, or is false, then the page is not editable.
// This solution, while stupid, will work even if the API starts returning a boolean.
canEdit = (json.has("editable") && json.optString("editable").equals(""))
|| json.optString("editable").equals("true");
isMainPage = json.has("mainpage");
isDisambiguationPage = json.has("disambiguation");
wikiBaseItem = json.optString("wikibase_item");
}
public int getPageId() {
return pageId;
}
@NonNull public Namespace getNamespace() {
return namespace;
}
public long getRevisionId() {
return revisionId;
}
public Date getLastModified() {
return lastModified;
}
public String getDisplayTitle() {
return displayTitleText;
}
@Nullable
public String getTitlePronunciationUrl() {
return titlePronunciationUrl;
}
@Nullable
public Location getGeo() {
return geo;
}
public String getEditProtectionStatus() {
return editProtectionStatus;
}
public int getLanguageCount() {
return languageCount;
}
public boolean canEdit() {
return canEdit;
}
public boolean isMainPage() {
return isMainPage;
}
public boolean isDisambiguationPage() {
return isDisambiguationPage;
}
/**
* @return Nullable URL with no scheme. For example, foo.bar.com/ instead of
* http://foo.bar.com/.
*/
@Nullable
public String getLeadImageUrl() {
return leadImageUrl;
}
public String getLeadImageName() {
return leadImageName;
}
@Nullable
public String getWikiBaseItem() {
return wikiBaseItem;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(pageId);
parcel.writeInt(namespace.code());
parcel.writeLong(revisionId);
parcel.writeLong(lastModified.getTime());
parcel.writeString(displayTitleText);
parcel.writeString(titlePronunciationUrl);
parcel.writeString(GeoMarshaller.marshal(geo));
parcel.writeString(editProtectionStatus);
parcel.writeInt(languageCount);
parcel.writeInt(canEdit ? 1 : 0);
parcel.writeInt(isMainPage ? 1 : 0);
parcel.writeInt(isDisambiguationPage ? 1 : 0);
parcel.writeString(leadImageUrl);
parcel.writeString(leadImageName);
parcel.writeString(wikiBaseItem);
}
private PageProperties(Parcel in) {
pageId = in.readInt();
namespace = Namespace.of(in.readInt());
revisionId = in.readLong();
lastModified = new Date(in.readLong());
displayTitleText = in.readString();
titlePronunciationUrl = in.readString();
geo = GeoUnmarshaller.unmarshal(in.readString());
editProtectionStatus = in.readString();
languageCount = in.readInt();
canEdit = in.readInt() == 1;
isMainPage = in.readInt() == 1;
isDisambiguationPage = in.readInt() == 1;
leadImageUrl = in.readString();
leadImageName = in.readString();
wikiBaseItem = in.readString();
}
public static final Parcelable.Creator<PageProperties> CREATOR
= new Parcelable.Creator<PageProperties>() {
@Override
public PageProperties createFromParcel(Parcel in) {
return new PageProperties(in);
}
@Override
public PageProperties[] newArray(int size) {
return new PageProperties[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PageProperties that = (PageProperties) o;
return pageId == that.pageId
&& namespace == that.namespace
&& revisionId == that.revisionId
&& lastModified.equals(that.lastModified)
&& displayTitleText.equals(that.displayTitleText)
&& TextUtils.equals(titlePronunciationUrl, that.titlePronunciationUrl)
&& (geo == that.geo || geo != null && geo.equals(that.geo))
&& languageCount == that.languageCount
&& canEdit == that.canEdit
&& isMainPage == that.isMainPage
&& isDisambiguationPage == that.isDisambiguationPage
&& TextUtils.equals(editProtectionStatus, that.editProtectionStatus)
&& TextUtils.equals(leadImageUrl, that.leadImageUrl)
&& TextUtils.equals(leadImageName, that.leadImageName)
&& TextUtils.equals(wikiBaseItem, that.wikiBaseItem);
}
@Override
public int hashCode() {
int result = lastModified.hashCode();
result = 31 * result + displayTitleText.hashCode();
result = 31 * result + (titlePronunciationUrl != null ? titlePronunciationUrl.hashCode() : 0);
result = 31 * result + (geo != null ? geo.hashCode() : 0);
result = 31 * result + (editProtectionStatus != null ? editProtectionStatus.hashCode() : 0);
result = 31 * result + languageCount;
result = 31 * result + (isMainPage ? 1 : 0);
result = 31 * result + (isDisambiguationPage ? 1 : 0);
result = 31 * result + (leadImageUrl != null ? leadImageUrl.hashCode() : 0);
result = 31 * result + (leadImageName != null ? leadImageName.hashCode() : 0);
result = 31 * result + (wikiBaseItem != null ? wikiBaseItem.hashCode() : 0);
result = 31 * result + (canEdit ? 1 : 0);
result = 31 * result + pageId;
result = 31 * result + namespace.code();
result = 31 * result + (int) revisionId;
return result;
}
@Override
public String toString() {
return toJSON().toString();
}
public JSONObject toJSON() {
JSONObject json = new JSONObject();
try {
json.put("id", pageId);
json.put(JSON_NAME_NAMESPACE, namespace.code());
json.put("revision", revisionId);
json.put("lastmodified", getIso8601DateFormat().format(getLastModified()));
json.put("displaytitle", displayTitleText);
json.put(JSON_NAME_TITLE_PRONUNCIATION_URL, titlePronunciationUrl);
json.put(JSON_NAME_GEO, GeoMarshaller.marshal(geo));
json.put("wikibase_item", wikiBaseItem);
if (editProtectionStatus == null) {
json.put("protection", new JSONArray());
} else {
JSONObject protectionStatusObject = new JSONObject();
JSONArray editProtectionStatusArray = new JSONArray();
editProtectionStatusArray.put(editProtectionStatus);
protectionStatusObject.put("edit", editProtectionStatusArray);
json.put("protection", protectionStatusObject);
}
json.put("languagecount", languageCount);
json.put("editable", canEdit);
if (isMainPage) {
json.put("mainpage", "");
}
if (isDisambiguationPage) {
json.put("disambiguation", "");
}
if (leadImageUrl != null) {
JSONObject thumbObject = new JSONObject();
thumbObject.put("url", leadImageUrl);
json.put("thumb", thumbObject);
}
if (leadImageName != null) {
JSONObject imageObject = new JSONObject();
imageObject.put("file", leadImageName);
json.put("image", imageObject);
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
return json;
}
}