Browse Source

add default request in NetworkTask, refactor main activity

pull/54/head
Thodoris1999 6 years ago
parent
commit
5129c06cd4
  1. 87
      app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java
  2. 64
      app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java
  3. 69
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
  4. 9
      app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NetworkTask.java

87
app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java

@ -10,6 +10,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.Toast;
import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter; import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter;
@ -17,21 +18,26 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.Board; import gr.thmmy.mthmmy.model.Board;
import gr.thmmy.mthmmy.model.Category; import gr.thmmy.mthmmy.model.Category;
import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.CustomRecyclerView; import gr.thmmy.mthmmy.utils.CustomRecyclerView;
import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.Parcel;
import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseTask;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
/** /**
@ -83,7 +89,7 @@ public class ForumFragment extends BaseFragment {
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
if (categories.isEmpty()) { if (categories.isEmpty()) {
forumTask = new ForumTask(); forumTask = new ForumTask(this::onForumTaskStarted, this::onForumTaskFinished);
forumTask.execute(); forumTask.execute();
} }
@ -106,7 +112,7 @@ public class ForumFragment extends BaseFragment {
if (BaseActivity.getSessionManager().isLoggedIn()) { if (BaseActivity.getSessionManager().isLoggedIn()) {
if (forumTask.getStatus() == AsyncTask.Status.RUNNING) if (forumTask.getStatus() == AsyncTask.Status.RUNNING)
forumTask.cancel(true); forumTask.cancel(true);
forumTask = new ForumTask(); forumTask = new ForumTask(ForumFragment.this::onForumTaskStarted, ForumFragment.this::onForumTaskFinished);
forumTask.setUrl(categories.get(parentPosition).getCategoryURL()); forumTask.setUrl(categories.get(parentPosition).getCategoryURL());
forumTask.execute(); forumTask.execute();
} }
@ -117,7 +123,7 @@ public class ForumFragment extends BaseFragment {
if (BaseActivity.getSessionManager().isLoggedIn()) { if (BaseActivity.getSessionManager().isLoggedIn()) {
if (forumTask.getStatus() == AsyncTask.Status.RUNNING) if (forumTask.getStatus() == AsyncTask.Status.RUNNING)
forumTask.cancel(true); forumTask.cancel(true);
forumTask = new ForumTask(); forumTask = new ForumTask(ForumFragment.this::onForumTaskStarted, ForumFragment.this::onForumTaskFinished);
forumTask.setUrl(categories.get(parentPosition).getCategoryURL()); forumTask.setUrl(categories.get(parentPosition).getCategoryURL());
forumTask.execute(); forumTask.execute();
} }
@ -135,17 +141,13 @@ public class ForumFragment extends BaseFragment {
swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh); swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary); swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary);
swipeRefreshLayout.setColorSchemeResources(R.color.accent); swipeRefreshLayout.setColorSchemeResources(R.color.accent);
swipeRefreshLayout.setOnRefreshListener( swipeRefreshLayout.setOnRefreshListener(() -> {
new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
if (forumTask != null && forumTask.getStatus() != AsyncTask.Status.RUNNING) { if (forumTask != null && forumTask.getStatus() != AsyncTask.Status.RUNNING) {
forumTask = new ForumTask(); forumTask = new ForumTask(ForumFragment.this::onForumTaskStarted, ForumFragment.this::onForumTaskFinished);
forumTask.execute(SessionManager.indexUrl.toString()); //forumTask.execute(SessionManager.indexUrl.toString());
forumTask.execute();
} }
} }
}
); );
} }
@ -163,33 +165,38 @@ public class ForumFragment extends BaseFragment {
void onForumFragmentInteraction(Board board); void onForumFragmentInteraction(Board board);
} }
//---------------------------------------ASYNC TASK----------------------------------- public void onForumTaskStarted() {
progressBar.setVisibility(ProgressBar.VISIBLE);
private class ForumTask extends ParseTask {
private HttpUrl forumUrl = SessionManager.forumUrl; //may change upon collapse/expand
private final List<Category> fetchedCategories;
ForumTask() {
fetchedCategories = new ArrayList<>();
} }
protected void onPreExecute() { public void onForumTaskFinished(int resultCode, ArrayList<Category> fetchedCategories) {
progressBar.setVisibility(ProgressBar.VISIBLE); if (resultCode == Parcel.ResultCode.SUCCESSFUL) {
categories.clear();
categories.addAll(fetchedCategories);
forumAdapter.notifyParentDataSetChanged(false);
} else if (resultCode == Parcel.ResultCode.NETWORK_ERROR) {
Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
} }
@Override progressBar.setVisibility(ProgressBar.INVISIBLE);
protected Request prepareRequest(String... params) { swipeRefreshLayout.setRefreshing(false);
return new Request.Builder()
.url(forumUrl)
.build();
} }
//---------------------------------------ASYNC TASK-----------------------------------
private class ForumTask extends NewParseTask<ArrayList<Category>> {
private HttpUrl forumUrl = SessionManager.forumUrl; //may change upon collapse/expand
public ForumTask(OnParseTaskStartedListener onParseTaskStartedListener,
OnParseTaskFinishedListener<ArrayList<Category>> onParseTaskFinishedListener) {
super(onParseTaskStartedListener, onParseTaskFinishedListener);
}
@Override @Override
public void parse(Document document) throws ParseException { protected ArrayList<Category> parse(Document document) throws ParseException {
Elements categoryBlocks = document.select(".tborder:not([style])>table[cellpadding=5]"); Elements categoryBlocks = document.select(".tborder:not([style])>table[cellpadding=5]");
if (categoryBlocks.size() != 0) { if (categoryBlocks.size() != 0) {
ArrayList<Category> fetchedCategories = new ArrayList<>();
for (Element categoryBlock : categoryBlocks) { for (Element categoryBlock : categoryBlocks) {
Element categoryElement = categoryBlock.select("td[colspan=2]>[name]").first(); Element categoryElement = categoryBlock.select("td[colspan=2]>[name]").first();
String categoryUrl = categoryElement.attr("href"); String categoryUrl = categoryElement.attr("href");
@ -207,24 +214,26 @@ public class ForumFragment extends BaseFragment {
fetchedCategories.add(category); fetchedCategories.add(category);
} }
categories.clear(); return fetchedCategories;
categories.addAll(fetchedCategories);
fetchedCategories.clear();
} else } else
throw new ParseException("Parsing failed"); throw new ParseException("Parsing failed");
} }
@Override @Override
protected void postExecution(ParseTask.ResultCode result) { protected Response sendRequest(OkHttpClient client, String... input) throws IOException {
if (result == ResultCode.SUCCESS) Request request = new Request.Builder()
forumAdapter.notifyParentDataSetChanged(false); .url(forumUrl)
.build();
return client.newCall(request).execute();
}
progressBar.setVisibility(ProgressBar.INVISIBLE); @Override
swipeRefreshLayout.setRefreshing(false); protected int getResultCode(Response response, ArrayList<Category> data) {
return Parcel.ResultCode.SUCCESSFUL;
} }
public void setUrl(String string) //TODO delete and simplify e.g. in prepareRequest possible? //TODO delete and simplify e.g. in prepareRequest possible?
{ public void setUrl(String string) {
forumUrl = HttpUrl.parse(string); forumUrl = HttpUrl.parse(string);
} }
} }

64
app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java

@ -10,23 +10,30 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.Toast;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.model.TopicSummary;
import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.CustomRecyclerView; import gr.thmmy.mthmmy.utils.CustomRecyclerView;
import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.Parcel;
import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseTask;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
@ -79,7 +86,7 @@ public class RecentFragment extends BaseFragment {
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
if (topicSummaries.isEmpty()) { if (topicSummaries.isEmpty()) {
recentTask = new RecentTask(); recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished);
recentTask.execute(SessionManager.indexUrl.toString()); recentTask.execute(SessionManager.indexUrl.toString());
} }
@ -109,17 +116,12 @@ public class RecentFragment extends BaseFragment {
swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh); swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary); swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary);
swipeRefreshLayout.setColorSchemeResources(R.color.accent); swipeRefreshLayout.setColorSchemeResources(R.color.accent);
swipeRefreshLayout.setOnRefreshListener( swipeRefreshLayout.setOnRefreshListener(() -> {
new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
if (recentTask != null && recentTask.getStatus() != AsyncTask.Status.RUNNING) { if (recentTask != null && recentTask.getStatus() != AsyncTask.Status.RUNNING) {
recentTask = new RecentTask(); recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished);
recentTask.execute(SessionManager.indexUrl.toString()); recentTask.execute(SessionManager.indexUrl.toString());
} }
} }
}
); );
} }
@ -138,18 +140,34 @@ public class RecentFragment extends BaseFragment {
void onRecentFragmentInteraction(TopicSummary topicSummary); void onRecentFragmentInteraction(TopicSummary topicSummary);
} }
private void onRecentTaskStarted() {
progressBar.setVisibility(ProgressBar.VISIBLE);
}
private void onRecentTaskFinished(int resultCode, ArrayList<TopicSummary> fetchedRecent) {
if (resultCode == Parcel.ResultCode.SUCCESSFUL) {
topicSummaries.clear();
topicSummaries.addAll(fetchedRecent);
recentAdapter.notifyDataSetChanged();
} else if (resultCode == Parcel.ResultCode.NETWORK_ERROR) {
Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
}
progressBar.setVisibility(ProgressBar.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
}
//---------------------------------------ASYNC TASK----------------------------------- //---------------------------------------ASYNC TASK-----------------------------------
private class RecentTask extends ParseTask { private class RecentTask extends NewParseTask<ArrayList<TopicSummary>> {
private List<TopicSummary> fetchedRecent;
@Override public RecentTask(OnParseTaskStartedListener onParseTaskStartedListener,
protected void onPreExecute() { OnParseTaskFinishedListener<ArrayList<TopicSummary>> onParseTaskFinishedListener) {
progressBar.setVisibility(ProgressBar.VISIBLE); super(onParseTaskStartedListener, onParseTaskFinishedListener);
fetchedRecent = new ArrayList<>();
} }
@Override @Override
public void parse(Document document) throws ParseException { protected ArrayList<TopicSummary> parse(Document document) throws ParseException {
ArrayList<TopicSummary> fetchedRecent = new ArrayList<>();
Elements recent = document.select("#block8 :first-child div"); Elements recent = document.select("#block8 :first-child div");
if (!recent.isEmpty()) { if (!recent.isEmpty()) {
for (int i = 0; i < recent.size(); i += 3) { for (int i = 0; i < recent.size(); i += 3) {
@ -184,22 +202,14 @@ public class RecentFragment extends BaseFragment {
fetchedRecent.add(new TopicSummary(link, title, lastUser, dateTime)); fetchedRecent.add(new TopicSummary(link, title, lastUser, dateTime));
} }
return; return fetchedRecent;
} }
throw new ParseException("Parsing failed"); throw new ParseException("Parsing failed");
} }
@Override @Override
protected void postExecution(ParseTask.ResultCode result) { protected int getResultCode(Response response, ArrayList<TopicSummary> data) {
if (result == ResultCode.SUCCESS) return Parcel.ResultCode.SUCCESSFUL;
{
topicSummaries.clear();
topicSummaries.addAll(fetchedRecent);
recentAdapter.notifyDataSetChanged();
}
progressBar.setVisibility(ProgressBar.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
} }
} }
} }

69
app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java

@ -22,13 +22,19 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.model.TopicSummary;
import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.CustomRecyclerView; import gr.thmmy.mthmmy.utils.CustomRecyclerView;
import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.Parcel;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseTask; import gr.thmmy.mthmmy.utils.parsing.ParseTask;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
/** /**
@ -83,7 +89,7 @@ public class UnreadFragment extends BaseFragment {
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
if (topicSummaries.isEmpty()) { if (topicSummaries.isEmpty()) {
unreadTask = new UnreadTask(); unreadTask = new UnreadTask(this::onUnreadTaskStarted, this::onUnreadTaskFinished);
assert SessionManager.unreadUrl != null; assert SessionManager.unreadUrl != null;
unreadTask.execute(SessionManager.unreadUrl.toString()); unreadTask.execute(SessionManager.unreadUrl.toString());
} }
@ -126,7 +132,7 @@ public class UnreadFragment extends BaseFragment {
topicSummaries.clear(); topicSummaries.clear();
numberOfPages = 0; numberOfPages = 0;
loadedPages = 0; loadedPages = 0;
unreadTask = new UnreadTask(); unreadTask = new UnreadTask(this::onUnreadTaskStarted, this::onUnreadTaskFinished);
assert SessionManager.unreadUrl != null; assert SessionManager.unreadUrl != null;
unreadTask.execute(SessionManager.unreadUrl.toString()); unreadTask.execute(SessionManager.unreadUrl.toString());
} }
@ -151,13 +157,42 @@ public class UnreadFragment extends BaseFragment {
} }
//---------------------------------------ASYNC TASK----------------------------------- //---------------------------------------ASYNC TASK-----------------------------------
private class UnreadTask extends ParseTask {
protected void onPreExecute() { private void onUnreadTaskStarted() {
progressBar.setVisibility(ProgressBar.VISIBLE); progressBar.setVisibility(ProgressBar.VISIBLE);
} }
private void onUnreadTaskFinished(int resultCode, Void data) {
if (resultCode == Parcel.ResultCode.SUCCESSFUL) {
unreadAdapter.notifyDataSetChanged();
++loadedPages;
if (loadedPages < numberOfPages) {
unreadTask = new UnreadTask(this::onUnreadTaskStarted, this::onUnreadTaskFinished);
assert SessionManager.unreadUrl != null;
unreadTask.execute(SessionManager.unreadUrl.toString() + ";start=" + loadedPages * 20);
}
else {
progressBar.setVisibility(ProgressBar.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
}
}
else{
progressBar.setVisibility(ProgressBar.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
if (resultCode == Parcel.ResultCode.NETWORK_ERROR)
Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
}
}
private class UnreadTask extends NewParseTask<Void> {
UnreadTask(OnParseTaskStartedListener onParseTaskStartedListener, OnParseTaskFinishedListener<Void> onParseTaskFinishedListener) {
super(onParseTaskStartedListener, onParseTaskFinishedListener);
}
@Override @Override
public void parse(Document document) { protected Void parse(Document document) throws ParseException {
Elements unread = document.select("table.bordercolor[cellspacing=1] tr:not(.titlebg)"); Elements unread = document.select("table.bordercolor[cellspacing=1] tr:not(.titlebg)");
if (!unread.isEmpty()) { if (!unread.isEmpty()) {
//topicSummaries.clear(); //topicSummaries.clear();
@ -217,28 +252,12 @@ public class UnreadFragment extends BaseFragment {
} }
topicSummaries.add(new TopicSummary(null, null, null, message)); topicSummaries.add(new TopicSummary(null, null, null, message));
} }
return null;
} }
@Override @Override
protected void postExecution(ParseTask.ResultCode result) { protected int getResultCode(Response response, Void data) {
if (result == ResultCode.SUCCESS) { return Parcel.ResultCode.SUCCESSFUL;
unreadAdapter.notifyDataSetChanged();
++loadedPages;
if (loadedPages < numberOfPages) {
unreadTask = new UnreadTask();
assert SessionManager.unreadUrl != null;
unreadTask.execute(SessionManager.unreadUrl.toString() + ";start=" + loadedPages * 20);
}
else {
progressBar.setVisibility(ProgressBar.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
}
}
else{
progressBar.setVisibility(ProgressBar.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
}
} }
} }
@ -282,7 +301,7 @@ public class UnreadFragment extends BaseFragment {
, "Fatal error!\n Task aborted...", Toast.LENGTH_LONG).show(); , "Fatal error!\n Task aborted...", Toast.LENGTH_LONG).show();
} else { } else {
if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) { if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) {
unreadTask = new UnreadTask(); unreadTask = new UnreadTask(UnreadFragment.this::onUnreadTaskStarted, UnreadFragment.this::onUnreadTaskFinished);
assert SessionManager.unreadUrl != null; assert SessionManager.unreadUrl != null;
unreadTask.execute(SessionManager.unreadUrl.toString()); unreadTask.execute(SessionManager.unreadUrl.toString());
} }

9
app/src/main/java/gr/thmmy/mthmmy/utils/parsing/NetworkTask.java

@ -7,6 +7,7 @@ import java.io.IOException;
import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseApplication;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
@ -67,7 +68,13 @@ public abstract class NetworkTask<T> extends ExternalAsyncTask<String, Parcel<T>
super.onPostExecute(tParcel); super.onPostExecute(tParcel);
} }
protected abstract Response sendRequest(OkHttpClient client, String... input) throws IOException; protected Response sendRequest(OkHttpClient client, String... input) throws IOException {
String url = input[0];
Request request = new Request.Builder()
.url(url)
.build();
return client.newCall(request).execute();
}
protected abstract T performTask(Document document); protected abstract T performTask(Document document);

Loading…
Cancel
Save