Migrate to java.time

This commit is contained in:
FooIbar authored and tarsin committed 2022-11-28 07:00:54 +08:00
1 parent 3ca0189f28
commit 086c0d9f28
4 files changed
+66 -175

No files matched your search

@@ -46,14 +46,12 @@ import org.jsoup.select.Elements;
import org.jsoup.select.NodeTraversor;
import org.jsoup.select.NodeVisitor;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -64,9 +62,6 @@ public class GalleryDetailParser {
private static final Pattern PATTERN_TORRENT = Pattern.compile("<a[^<>]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Torrent Download[^<]+(\\d+)[^<]+</a");
private static final Pattern PATTERN_ARCHIVE = Pattern.compile("<a[^<>]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Archive Download</a>");
private static final Pattern PATTERN_COVER = Pattern.compile("width:(\\d+)px; height:(\\d+)px.+?url\\((.+?)\\)");
private static final Pattern PATTERN_TAG_GROUP = Pattern.compile("<tr><td[^<>]+>([\\w\\s]+):</td><td>(?:<div[^<>]+><a[^<>]+>[\\w\\s]+</a></div>)+</td></tr>");
private static final Pattern PATTERN_TAG = Pattern.compile("<div[^<>]+><a[^<>]+>([\\w\\s]+)</a></div>");
private static final Pattern PATTERN_COMMENT = Pattern.compile("<div class=\"c3\">Posted on ([^<>]+) by: &nbsp; <a[^<>]+>([^<>]+)</a>.+?<div class=\"c6\"[^>]*>(.+?)</div><div class=\"c[78]\"");
private static final Pattern PATTERN_PAGES = Pattern.compile("<tr><td[^<>]*>Length:</td><td[^<>]*>([\\d,]+) pages</td></tr>");
private static final Pattern PATTERN_PREVIEW_PAGES = Pattern.compile("<td[^>]+><a[^>]+>([\\d,]+)</a></td><td[^>]+>(?:<a[^>]+>)?&gt;(?:</a>)?</td>");
private static final Pattern PATTERN_NORMAL_PREVIEW = Pattern.compile("<div class=\"gdtm\"[^<>]*><div[^<>]*width:(\\d+)[^<>]*height:(\\d+)[^<>]*\\((.+?)\\)[^<>]*-(\\d+)px[^<>]*><a[^<>]*href=\"(.+?)\"[^<>]*><img alt=\"([\\d,]+)\"");
@@ -76,16 +71,13 @@ public class GalleryDetailParser {
private static final GalleryTagGroup[] EMPTY_GALLERY_TAG_GROUP_ARRAY = new GalleryTagGroup[0];
private static final GalleryCommentList EMPTY_GALLERY_COMMENT_ARRAY = new GalleryCommentList(new GalleryComment[0], false);
private static final DateFormat WEB_COMMENT_DATE_FORMAT = new SimpleDateFormat("dd MMMMM yyyy, HH:mm", Locale.US);
private static final DateTimeFormatter WEB_COMMENT_DATE_FORMAT = DateTimeFormatter
.ofPattern("dd MMMM yyyy, HH:mm", Locale.US).withZone(ZoneOffset.UTC);
private static final String OFFENSIVE_STRING =
"<p>(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)</p>";
private static final String PINING_STRING =
"<p>This gallery is pining for the fjords.</p>";
static {
WEB_COMMENT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}
public static GalleryDetail parse(String body) throws EhException {
if (body.contains(OFFENSIVE_STRING)) {
throw new OffensiveException();
@@ -196,14 +188,13 @@ public class GalleryDetailParser {
gd.posted = "";
gd.parent = "";
gd.visible = "";
gd.visible = "";
gd.size = "";
gd.pages = 0;
gd.favoriteCount = 0;
try {
Elements es = gdd.child(0).child(0).children();
for (int i = 0, n = es.size(); i < n; i++) {
parseDetailInfo(gd, es.get(i), body);
parseDetailInfo(gd, es.get(i));
}
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
@@ -291,7 +282,7 @@ public class GalleryDetailParser {
}
}
private static void parseDetailInfo(GalleryDetail gd, Element e, String body) {
private static void parseDetailInfo(GalleryDetail gd, Element e) {
Elements es = e.children();
if (es.size() < 2) {
return;
@@ -321,20 +312,16 @@ public class GalleryDetailParser {
}
} else if (key.startsWith("Favorited")) {
switch (value) {
case "Never":
gd.favoriteCount = 0;
break;
case "Once":
gd.favoriteCount = 1;
break;
default:
case "Never" -> gd.favoriteCount = 0;
case "Once" -> gd.favoriteCount = 1;
default -> {
int index = value.indexOf(' ');
if (index == -1) {
gd.favoriteCount = 0;
} else {
gd.favoriteCount = NumberUtils.parseIntSafely(value.substring(0, index), 0);
}
break;
}
}
}
}
@@ -375,6 +362,7 @@ public class GalleryDetailParser {
public static GalleryTagGroup[] parseTagGroups(Document document) {
try {
Element taglist = document.getElementById("taglist");
assert taglist != null;
Elements tagGroups = taglist.child(0).child(0).children();
return parseTagGroups(tagGroups);
} catch (Throwable e) {
@@ -394,7 +382,7 @@ public class GalleryDetailParser {
list.add(group);
}
}
return list.toArray(new GalleryTagGroup[list.size()]);
return list.toArray(new GalleryTagGroup[0]);
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
e.printStackTrace();
@@ -402,31 +390,6 @@ public class GalleryDetailParser {
}
}
/**
* Parse tag groups with regular expressions
*/
@NonNull
private static GalleryTagGroup[] parseTagGroups(String body) throws EhException {
List<GalleryTagGroup> list = new LinkedList<>();
Matcher m = PATTERN_TAG_GROUP.matcher(body);
while (m.find()) {
GalleryTagGroup tagGroup = new GalleryTagGroup();
tagGroup.groupName = ParserUtils.trim(m.group(1));
parseGroup(tagGroup, m.group(0));
list.add(tagGroup);
}
return list.toArray(new GalleryTagGroup[list.size()]);
}
private static void parseGroup(GalleryTagGroup tagGroup, String body) {
Matcher m = PATTERN_TAG.matcher(body);
while (m.find()) {
tagGroup.addTag(ParserUtils.trim(m.group(1)));
}
}
@Nullable
@SuppressWarnings("ConstantConditions")
public static GalleryComment parseComment(Element element) {
@@ -444,17 +407,15 @@ public class GalleryDetailParser {
}
for (Element e : c4.children()) {
switch (e.text()) {
case "Vote+":
case "Vote+" -> {
comment.voteUpAble = true;
comment.voteUpEd = !StringUtils.trim(e.attr("style")).isEmpty();
break;
case "Vote-":
}
case "Vote-" -> {
comment.voteDownAble = true;
comment.voteDownEd = !StringUtils.trim(e.attr("style")).isEmpty();
break;
case "Edit":
comment.editable = true;
break;
}
case "Edit" -> comment.editable = true;
}
}
}
@@ -475,7 +436,7 @@ public class GalleryDetailParser {
Element c3 = JsoupUtils.getElementByClass(element, "c3");
String temp = c3.ownText();
temp = temp.substring("Posted on ".length(), temp.length() - " by:".length());
comment.time = WEB_COMMENT_DATE_FORMAT.parse(temp).getTime();
comment.time = Instant.from(WEB_COMMENT_DATE_FORMAT.parse(temp)).toEpochMilli();
// user
comment.user = c3.child(0).text();
// comment
@@ -485,7 +446,7 @@ public class GalleryDetailParser {
if (c8 != null) {
Element e = c8.children().first();
if (e != null) {
comment.lastEdited = WEB_COMMENT_DATE_FORMAT.parse(temp).getTime();
comment.lastEdited = Instant.from(WEB_COMMENT_DATE_FORMAT.parse(temp)).toEpochMilli();
}
}
return comment;
@@ -503,6 +464,7 @@ public class GalleryDetailParser {
public static GalleryCommentList parseComments(Document document) {
try {
Element cdiv = document.getElementById("cdiv");
assert cdiv != null;
Elements c1s = cdiv.getElementsByClass("c1");
List<GalleryComment> list = new ArrayList<>(c1s.size());
@@ -515,20 +477,21 @@ public class GalleryDetailParser {
Element chd = cdiv.getElementById("chd");
MutableBoolean hasMore = new MutableBoolean(false);
assert chd != null;
NodeTraversor.traverse(new NodeVisitor() {
@Override
public void head(Node node, int depth) {
public void head(@NonNull Node node, int depth) {
if (node instanceof Element && ((Element) node).text().equals("click to show all")) {
hasMore.value = true;
}
}
@Override
public void tail(Node node, int depth) {
public void tail(@NonNull Node node, int depth) {
}
}, chd);
return new GalleryCommentList(list.toArray(new GalleryComment[list.size()]), hasMore.value);
return new GalleryCommentList(list.toArray(new GalleryComment[0]), hasMore.value);
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
e.printStackTrace();
@@ -536,38 +499,14 @@ public class GalleryDetailParser {
}
}
/**
* Parse comments with regular expressions
*/
@NonNull
public static GalleryComment[] parseComments(String body) {
List<GalleryComment> list = new LinkedList<>();
Matcher m = PATTERN_COMMENT.matcher(body);
while (m.find()) {
String webDateString = ParserUtils.trim(m.group(1));
Date date;
try {
date = WEB_COMMENT_DATE_FORMAT.parse(webDateString);
} catch (java.text.ParseException e) {
date = new Date(0L);
}
GalleryComment comment = new GalleryComment();
comment.time = date.getTime();
comment.user = ParserUtils.trim(m.group(2));
comment.comment = m.group(3);
list.add(comment);
}
return list.toArray(new GalleryComment[list.size()]);
}
/**
* Parse preview pages with html parser
*/
public static int parsePreviewPages(Document document, String body) throws ParseException {
try {
Elements elements = document.getElementsByClass("ptt").first().child(0).child(0).children();
Element ptt = document.getElementsByClass("ptt").first();
assert ptt != null;
Elements elements = ptt.child(0).child(0).children();
return Integer.parseInt(elements.get(elements.size() - 2).text());
} catch (Throwable e) {
ExceptionUtils.throwIfFatal(e);
@@ -634,9 +573,10 @@ public class GalleryDetailParser {
try {
LargePreviewSet largePreviewSet = new LargePreviewSet();
Element gdt = d.getElementById("gdt");
assert gdt != null;
Elements gdtls = gdt.getElementsByClass("gdtl");
int n = gdtls.size();
if (n <= 0) {
if (n == 0) {
throw new ParseException("Can't parse large preview", body);
}
for (int i = 0; i < n; i++) {
@@ -19,19 +19,18 @@ package com.hippo.ehviewer.client.parser;
import com.hippo.yorozuya.NumberUtils;
import com.hippo.yorozuya.StringUtils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.TimeZone;
public class ParserUtils {
public static final DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
public static final DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm", Locale.US).withZone(ZoneOffset.UTC);
public static synchronized String formatDate(long time) {
sDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return sDateFormat.format(new Date(time));
return formatter.format(Instant.ofEpochMilli(time));
}
public static String trim(String str) {
@@ -50,7 +49,4 @@ public class ParserUtils {
return NumberUtils.parseLongSafely(trim(str).replace(",", ""), defValue);
}
public static float parseFloat(String str, float defValue) {
return NumberUtils.parseFloatSafely(trim(str).replace(",", ""), defValue);
}
}
@@ -16,8 +16,6 @@
package com.hippo.ehviewer.widget
import android.annotation.SuppressLint
import android.icu.text.SimpleDateFormat
import android.icu.util.Calendar
import android.os.Bundle
import android.os.Parcelable
import com.hippo.ehviewer.EhApplication
@@ -25,7 +23,12 @@ import com.hippo.ehviewer.FavouriteStatusRouter
import com.hippo.ehviewer.client.data.GalleryInfo
import com.hippo.widget.ContentLayout.ContentHelper
import com.hippo.yorozuya.IntIdGenerator
import java.time.Instant
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.Locale
import kotlin.math.max
import kotlin.math.min
abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
@JvmField
@@ -64,8 +67,8 @@ abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
info?.let {
if (maxGid == -1) maxGid = info.gid.toInt()
if (minGid == -1) minGid = info.gid.toInt()
maxGid = Math.max(maxGid.toLong(), info.gid).toInt()
minGid = Math.min(minGid.toLong(), info.gid).toInt()
maxGid = max(maxGid.toLong(), info.gid).toInt()
minGid = min(minGid.toLong(), info.gid).toInt()
map[info.gid] = info
}
}
@@ -121,11 +124,9 @@ abstract class GalleryInfoContentHelper : ContentHelper<GalleryInfo?>() {
}
fun goTo(time: Long) {
val calendar = Calendar.getInstance()
calendar.timeInMillis = time
val date = calendar.time
val format = SimpleDateFormat("yyyy-MM-dd", Locale.US)
jumpTo = format.format(date)
val formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd", Locale.US).withZone(ZoneOffset.UTC)
jumpTo = formatter.format(Instant.ofEpochMilli(time))
doGoToPage(Int.MAX_VALUE / 2)
}
@@ -16,18 +16,17 @@
package com.hippo.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import com.hippo.ehviewer.R;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
@SuppressLint("SimpleDateFormat")
public final class ReadableTime {
public static final long SECOND_MILLIS = 1000;
@@ -51,38 +50,19 @@ public final class ReadableTime {
R.plurals.minute,
R.plurals.second
};
private static final Calendar sCalendar = Calendar.getInstance();
private static final Object sCalendarLock = new Object();
private static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR = new SimpleDateFormat("MMM d");
private static final SimpleDateFormat DATE_FORMAT_WITH_YEAR = new SimpleDateFormat("MMM d, yyyy");
private static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR_ZH = new SimpleDateFormat("M月d日");
private static final SimpleDateFormat DATE_FORMAT_WITH_YEAR_ZH = new SimpleDateFormat("yyyy年M月d日");
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yy-MM-dd HH:mm");
private static final Object sDateFormatLock1 = new Object();
private static final SimpleDateFormat FILENAMABLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
private static final Object sDateFormatLock2 = new Object();
private static final DateTimeFormatter DATE_FORMAT_WITHOUT_YEAR = DateTimeFormatter.ofPattern("MMM d");
private static final DateTimeFormatter DATE_FORMAT_WITH_YEAR = DateTimeFormatter.ofPattern("MMM d, yyyy");
private static final DateTimeFormatter DATE_FORMAT_WITHOUT_YEAR_ZH = DateTimeFormatter.ofPattern("M月d日");
private static final DateTimeFormatter DATE_FORMAT_WITH_YEAR_ZH = DateTimeFormatter.ofPattern("yyyy年M月d日");
private static final DateTimeFormatter FILENAMABLE_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss-SSS");
private static final Object sDateFormatLock = new Object();
private static Resources sResources;
public static void initialize(Context context) {
sResources = context.getApplicationContext().getResources();
}
/*
public static String getDisplayTime(long time) {
if (Settings.getPrettyTime()) {
return getTimeAgo(time);
} else {
return getPlainTime(time);
}
}
*/
public static String getPlainTime(long time) {
synchronized (sDateFormatLock1) {
return DATE_FORMAT.format(new Date(time));
}
}
public static String getTimeAgo(long time) {
Resources resources = sResources;
@@ -111,49 +91,23 @@ public final class ReadableTime {
return resources.getString(R.string.some_days_ago, days);
} else {
synchronized (sCalendarLock) {
Date nowDate = new Date(now);
Date timeDate = new Date(time);
sCalendar.setTime(nowDate);
int nowYear = sCalendar.get(Calendar.YEAR);
sCalendar.setTime(timeDate);
int timeYear = sCalendar.get(Calendar.YEAR);
OffsetDateTime nowDate = Instant.ofEpochMilli(now).atOffset(ZoneOffset.UTC);
OffsetDateTime timeDate = Instant.ofEpochMilli(time).atOffset(ZoneOffset.UTC);
int nowYear = nowDate.getYear();
int timeYear = timeDate.getYear();
boolean isZh = Locale.getDefault().getLanguage().equals("zh");
if (nowYear == timeYear) {
return (isZh ? DATE_FORMAT_WITHOUT_YEAR_ZH : DATE_FORMAT_WITHOUT_YEAR).format(timeDate);
return (isZh ? DATE_FORMAT_WITHOUT_YEAR_ZH : DATE_FORMAT_WITHOUT_YEAR)
.format(timeDate.toInstant().atOffset(ZoneOffset.UTC));
} else {
return (isZh ? DATE_FORMAT_WITH_YEAR_ZH : DATE_FORMAT_WITH_YEAR).format(timeDate);
return (isZh ? DATE_FORMAT_WITH_YEAR_ZH : DATE_FORMAT_WITH_YEAR)
.format(timeDate.toInstant().atOffset(ZoneOffset.UTC));
}
}
}
}
public static String getTimeInterval(long time) {
StringBuilder sb = new StringBuilder();
Resources resources = sResources;
long leftover = time;
boolean start = false;
for (int i = 0; i < SIZE; i++) {
long multiple = MULTIPLES[i];
long quotient = leftover / multiple;
long remainder = leftover % multiple;
if (start || quotient != 0 || i == SIZE - 1) {
if (start) {
sb.append(" ");
}
sb.append(quotient)
.append(" ")
.append(resources.getQuantityString(UNITS[i], (int) quotient));
start = true;
}
leftover = remainder;
}
return sb.toString();
}
public static String getShortTimeInterval(long time) {
StringBuilder sb = new StringBuilder();
Resources resources = sResources;
@@ -173,8 +127,8 @@ public final class ReadableTime {
}
public static String getFilenamableTime(long time) {
synchronized (sDateFormatLock2) {
return FILENAMABLE_DATE_FORMAT.format(new Date(time));
synchronized (sDateFormatLock) {
return FILENAMABLE_DATE_FORMAT.format(Instant.ofEpochMilli(time));
}
}
}