Jsoup Issues in Android AsyncTask - java

Summary:
My Jsoup parser works perfectly on its own, but fails to gather any values once copy-pasted into one of my Android application's AsyncTask task classes. The 2d array is returned filled with nothing but nulls.
Long version:
I have been working on an application that uses page-scraping via Jsoup to pull and display content from various blogs. I have written a few parsers so far, and all seem to work as expected. Unfortunately, my most recent parser (written for nyc-shows.brooklynvegan.com), has been having issues.
Here is the parser method itself, invoked by a main method with print statements added. Run this yourself. It works (not perfectly, but it works).
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class Main {
static String TAG_EVENT = "li.ds-entry";
static String TAG_TITLE = ".ds-entry-title";
static String TAG_LOCATION = ".location";
static String TAG_DATE_AND_TIME = ".ds-date";
static String TAG_TICKET_URL = ".ds-buy-tickets";
static String FEED_URL = "http://nyc-shows.brooklynvegan.com/";
public static void main(String[] args) throws IOException {
String values[][] = new String[50][6];
values = getFeedItems();
for (int i=0; i<values.length; i++) {
for (int j=0; j<6; j++) {
System.out.println(values[i][j]);
}
System.out.println("-----------------");
}
}
public static String[][] getFeedItems() throws IOException {
Document doc = null;
String values[][] = new String[50][6];
try{
doc = Jsoup.connect(FEED_URL).timeout(0).get();
Elements events = doc.select(TAG_EVENT);
String delimSpace = "[ ]";
int i = 0;
for (Element event : events) {
//Set event title
Element title = event.select(TAG_TITLE).first();
String titleString = title.text();
if (title != null) {
boolean isFake = checkFake(titleString);
if (!isFake) {
values[i][0] = titleString;
}
else {
continue;
}
}
//Set event date and time i guess
Element dateAndTime = event.select(TAG_DATE_AND_TIME).first();
if (dateAndTime != null) {
String[] dateAndTimeTokens = dateAndTime.text().split(delimSpace);
String date = dateAndTimeTokens[1];
String time = dateAndTimeTokens[3];
values[i][1] = date;
values[i][2] = time;
}
//Set price (tbd)
values[i][3] = "See Ticket";
//Set location
Element location = event.select(TAG_LOCATION).first();
if (location != null) {
values[i][4] = location.text();
}
//Set ticket urls
Element ticketContainer = event.select(TAG_TICKET_URL).first();
if (ticketContainer != null) {
String ticket = ticketContainer.select("a").attr("href");
values[i][5] = ticket;
}
else {
values[i][3] = "Free";
}
i++;
} //End of event loop
} //End of try clause
catch (IOException e) {
e.printStackTrace();
}
return values;
}
public static boolean checkFake(String s) {
boolean isFake = false;
String[] days = {"Today", "Tomorrow", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
for (int i=0; i<days.length; i++) {
if (s.contains(days[i])) {
isFake = true;
return isFake;
}
}
return isFake;
}
}
Now, here is the same exact method transported into an AsyncTask to be run in the background by my application while a loading screen is displayed.
package com.example.nylist;
import java.io.IOException;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class BVParser extends AsyncTask<Void, Void, String[][]> {
static String TAG_EVENT = "li.ds-entry";
static String TAG_TITLE = ".ds-entry-title";
static String TAG_LOCATION = ".location";
static String TAG_DATE_AND_TIME = ".ds-date";
static String TAG_TICKET_URL = ".ds-buy-tickets";
static String FEED_URL = "http://nyc-shows.brooklynvegan.com/";
Context context;
Activity activity;
public BVParser(Activity context) {
this.context = context.getApplicationContext();
this.activity = context;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
Toast.makeText(context, "Fetching...", Toast.LENGTH_LONG).show();
}
#Override
protected String[][] doInBackground(Void... param) {
String values[][] = new String[50][6];
try {
values = getFeedItems();
}
catch (IOException e) {
Log.d("ASSERT", "Exception occured during doInBackground", e);
e.printStackTrace();
}
Log.d("ASSERT", ("values successfully returned by doInBackground, first title is: "+values[0][0]));
return values;
}
protected void onPostExecute(String[][] result) {
super.onPostExecute(result);
int eventCount = result.length;
Log.d("ASSERT", ("event count in onPostExecute is: "+eventCount));
ListRow[] listrow_data = new ListRow[eventCount];
ListRow temp;
for (int i=0; i<eventCount; i++) {
if (result[i] != null) {
temp = new ListRow(context, result[i][0], result[i][1], result[i][2],
result[i][3], result[i][4], result[i][5], i);
listrow_data[i] = temp;
}
}
((EventList) activity).setList(listrow_data);
}
public String[][] getFeedItems() throws IOException {
Document doc = null;
String values[][] = new String[50][6];
int i = 0;
try{
Log.d("ASSERT","Made it to try block");
doc = Jsoup.connect(FEED_URL).timeout(0).get();
Elements events = doc.select(TAG_EVENT);
Log.d("ASSERT","printing events, whatever it is: "+events);
String delimSpace = "[ ]";
//******THIS LOOP NEVER BEGINS*****//
for (Element event : events) {
Log.d("ASSERT","Made it to getFeedItems's main for loop");
//Set event title
Element title = event.select(TAG_TITLE).first();
String titleString = title.text();
Log.d("ASSERT","This title is: "+titleString);
boolean isFake = checkFake(titleString);
if (!isFake) {
values[i][0] = titleString;
}
else {
continue;
}
//Set event date and time i guess
Element dateAndTime = event.select(TAG_DATE_AND_TIME).first();
if (dateAndTime != null) {
String[] dateAndTimeTokens = dateAndTime.text().split(delimSpace);
String date = dateAndTimeTokens[1];
String time = dateAndTimeTokens[3];
values[i][1] = date;
values[i][2] = time;
}
//Set price
values[i][3] = "See Ticket";
//Set location
Element location = event.select(TAG_LOCATION).first();
if (location != null) {
values[i][4] = location.text();
}
//Set ticket urls
Element ticketContainer = event.select(TAG_TICKET_URL).first();
if (ticketContainer != null) {
String ticket = ticketContainer.select("a").attr("href");
values[i][5] = ticket;
}
else {
values[i][3] = "Free";
}
i++;
} //End of event loop
} //End of try clause
catch (IOException e) {
Log.d("ASSERT","Exception during getFeedItems");
e.printStackTrace();
}
Log.d("ASSERT","The first title in getFeedItems before returning is: "+values[0][0]);
return values;
}
private static boolean checkFake(String s) {
boolean isFake = false;
String[] days = {"Today", "Tomorrow", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
for (int i=0; i<days.length; i++) {
if (s.contains(days[i])) {
isFake = true;
return isFake;
}
}
return isFake;
}
}
Debugging Attempts:
I added log statements throughout the code in order to debug the problem. If you run this, you will see that the problem seems to occur somewhere within getFeedItems() itself, specifically within the "try" block. Although the log statement at the beginning of the try statement appears, the for loop that runs through events isn't running at all, because the log statement at it's beginning never prints.
Question:
Can someone explain why the loop through events doesn't begin? Is events null, and if so, why? Why is there a discrepancy between the method running on it's own and the method running within my AsyncTask? I have been tearing my hair out. The logic in this parser is almost identical to the logic in the (working) others that I have written, and yet this is returning a 2d array with nothing but nulls. I have trouble even beginning to understand where the logic error might be, and yet I just can't seem to find the typo.
PS:
If comparing this with my other parser would help, let me know and I'll post the source. Thanks in advance.

Related

arraylist.isEmpty works only in a loop and not outside

In this class I store a number and content belonging to this number. In the method deleteContentIfOlderThen() I check if the ArrayList(opexContent) is empty. But this works only if the check occurs inside the for loop. If I check it outside it doesn't work. I use a workStealingExecutorService to execute this method. But the list is only accessed by this class while the threads do the work.
So what am I doing wrong that it doesn't work outside the loop?
version with list.removeIf()
package org.excelAnalyser;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.function.Predicate;
public class OpexLine implements Runnable{
public static final int DELETE_CONTENT_TASK=0;
public final int SAPNumber;
public final ArrayList<String[]> opexContent;
private final DeletionRequestHandler m_deletionRequestHandler;
private String[] m_taskParameters=null;
private int m_taskid=-1;
public OpexLine(int SAPNumber, ArrayList<String[]> opexContent, DeletionRequestHandler deletionRequestHandler) {
this.SAPNumber=SAPNumber;
this.opexContent=opexContent;
m_deletionRequestHandler=deletionRequestHandler;
}
public void setupTask(int taskID, String[] parameters) {
m_taskid=taskID;
m_taskParameters=parameters;
}
public void run() {
if(m_taskid<0) {
System.out.println("aborted task because no taskID was given");
return;
}
if(m_taskParameters==null) {
System.out.println("no taskparameters were specified");
return;
}
switch (m_taskid) {
case DELETE_CONTENT_TASK:
deleteContentIfOlderThen();
break;
}
}
private void deleteContentIfOlderThen() {
final long unixTime=Long.parseLong(m_taskParameters[0]);
final int indexOfTime=Integer.parseInt(m_taskParameters[1]);
final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
final Date checkDate=new Date(System.currentTimeMillis()-unixTime);
opexContent.removeIf(new Predicate<String[]>() {
public boolean test(String[] content) {
final String timeString=content[indexOfTime];
if(timeString==null || timeString.isEmpty()) {
return false;
}
Date date = null;
try {
date = sdf.parse(timeString);
} catch (Exception e) {
return false;
//date = new Date();
}
return date.before(checkDate);
}
});
if(opexContent.isEmpty()) {
m_deletionRequestHandler.requestDeletionFor(this);
}
}
public String toString() {
final StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append(SAPNumber);
stringBuilder.append(": ");
final int length=String.valueOf(SAPNumber).length();
//gen offset
final StringBuilder offsetBuilder=new StringBuilder();
for(int i=0; i<length+2; i++) {
offsetBuilder.append(' ');
}
final String offset=offsetBuilder.toString();
for (String[] strings : opexContent) {
for (String string : strings) {
stringBuilder.append(string+"; ");
}
stringBuilder.append("\n"+offset);
}
return stringBuilder.toString();
}
}
version with iterator:
package org.excelAnalyser;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
public class OpexLine implements Runnable{
public static final int DELETE_CONTENT_TASK=0;
public final int SAPNumber;
public final ArrayList<String[]> opexContent;
private final DeletionRequestHandler m_deletionRequestHandler;
private String[] m_taskParameters=null;
private int m_taskid=-1;
public OpexLine(int SAPNumber, ArrayList<String[]> opexContent, DeletionRequestHandler deletionRequestHandler) {
this.SAPNumber=SAPNumber;
this.opexContent=opexContent;
m_deletionRequestHandler=deletionRequestHandler;
}
public void setupTask(int taskID, String[] parameters) {
m_taskid=taskID;
m_taskParameters=parameters;
}
public void run() {
if(m_taskid<0) {
System.out.println("aborted task because no taskID was given");
return;
}
if(m_taskParameters==null) {
System.out.println("no taskparameters were specified");
return;
}
switch (m_taskid) {
case DELETE_CONTENT_TASK:
deleteContentIfOlderThen();
break;
}
}
private void deleteContentIfOlderThen() {
final long unixTime=Long.parseLong(m_taskParameters[0]);
final int indexOfTime=Integer.parseInt(m_taskParameters[1]);
final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
Iterator<String[]> opexContentIterator=opexContent.iterator();
contentLoop: while(opexContentIterator.hasNext()) {
String[] content=(String[])opexContentIterator.next();
final String timeString=content[indexOfTime];
if(timeString==null || timeString.isEmpty()) {
continue contentLoop;
}
Date date = null;
try {
date = sdf.parse(timeString);
} catch (Exception e) {
continue contentLoop;
//date = new Date();
}
Date checkDate=new Date(System.currentTimeMillis()-unixTime);
if(date.before(checkDate)) {
opexContentIterator.remove();
}
}
if(opexContent.isEmpty()) {
m_deletionRequestHandler.requestDeletionFor(this);
}
}
public String toString() {
final StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append(SAPNumber);
stringBuilder.append(": ");
final int length=String.valueOf(SAPNumber).length();
//gen offset
final StringBuilder offsetBuilder=new StringBuilder();
for(int i=0; i<length+2; i++) {
offsetBuilder.append(' ');
}
final String offset=offsetBuilder.toString();
for (String[] strings : opexContent) {
for (String string : strings) {
stringBuilder.append(string+"; ");
}
stringBuilder.append("\n"+offset);
}
return stringBuilder.toString();
}
}
version without iterator:
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class OpexLine implements Runnable{
public static final int DELETE_CONTENT_TASK=0;
public final int SAPNumber;
public final ArrayList<String[]> opexContent;
private final DeletionRequestHandler m_deletionRequestHandler;
private String[] m_taskParameters=null;
private int m_taskid=-1;
public OpexLine(int SAPNumber, ArrayList<String[]> opexContent, DeletionRequestHandler deletionRequestHandler) {
this.SAPNumber=SAPNumber;
this.opexContent=opexContent;
m_deletionRequestHandler=deletionRequestHandler;
}
public void setupTask(int taskID, String[] parameters) {
m_taskid=taskID;
m_taskParameters=parameters;
}
public void run() {
if(m_taskid<0) {
System.out.println("aborted task because no taskID was given");
return;
}
if(m_taskParameters==null) {
System.out.println("no taskparameters were specified");
return;
}
switch (m_taskid) {
case DELETE_CONTENT_TASK:
deleteContentIfOlderThen();
break;
}
}
private void deleteContentIfOlderThen() {
final long unixTime=Long.parseLong(m_taskParameters[0]);
final int indexOfTime=Integer.parseInt(m_taskParameters[1]);
final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
contentLoop: for(final String[] content : opexContent) {
//final String[] content=opexContent.get(i);
final String timeString=content[indexOfTime];
if(timeString==null || timeString.isEmpty()) {
continue contentLoop;
}
Date date = null;
try {
date = sdf.parse(timeString);
} catch (Exception e) {
continue contentLoop;
//date = new Date();
}
Date checkDate=new Date(System.currentTimeMillis()-unixTime);
//System.out.println(date+" before "+checkDate+": "+date.before(checkDate));
if(date.before(checkDate)) {
System.out.println("removed");
opexContent.remove(content);
if(opexContent.isEmpty()) {
System.out.println("isEmptyAfterRemoval");
m_deletionRequestHandler.requestDeletionFor(this);
}
}
}
//System.out.println("isexecuted");
if(opexContent.isEmpty()) {
System.out.println("isEmpty");
m_deletionRequestHandler.requestDeletionFor(this);
}
}
public String toString() {
final StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append(SAPNumber);
stringBuilder.append(": ");
final int length=String.valueOf(SAPNumber).length();
//gen offset
final StringBuilder offsetBuilder=new StringBuilder();
for(int i=0; i<length+2; i++) {
offsetBuilder.append(' ');
}
final String offset=offsetBuilder.toString();
for (String[] strings : opexContent) {
for (String string : strings) {
stringBuilder.append(string+"; ");
}
stringBuilder.append("\n"+offset);
}
return stringBuilder.toString();
}
}
You should never mutate a list while you are iterating it. This type of code:
final ArrayList<String[]> opexContent;
private void deleteContentIfOlderThen() {
for(final String[] content : opexContent) {
Date date = /* some date */;
Date checkDate=new Date(System.currentTimeMillis()-unixTime);
if(date.before(checkDate)) {
opexContent.remove(content); //CME!
}
}
}
Will likely cause a ConcurrentModificationException, as the list was mutated while an Iterator was in use (behind the scenes). Instead, you should use the Iterator directly, such that you have the ability to use Iterator#remove:
final ArrayList<String[]> opexContent;
private void deleteContentIfOlderThen() {
Iterator<String[]> itr = opexContent.iterator();
while (itr.hasNext()) {
String[] content = itr.next();
Date date = /* some date */;
Date checkDate=new Date(System.currentTimeMillis()-unixTime);
if(date.before(checkDate)) {
itr.remove(); //No more CME!
}
}
}
Whilst Rogue's answer is correct in saying that you shouldn't remove elements from the list while iterating it, there's a neater way of removing the elements since Java 8 that avoids using an iterator explicitly:
// Doesn't have to be moved outside, but at least doing this means
// you're comparing against a fixed check date, rather than potentially
// changing as you iterate.
Date checkDate=new Date(System.currentTimeMillis()-unixTime);
opexContent.removeIf(content -> {
Date date = /* some date */;
return date.before(checkDate);
});
This is better because
it's clearer, because you're not having to deal with the iterator
it's safer, as it leaves the list untouched if an exception is thrown for some element (i.e. it's failure atomic);
it's more efficient, because it avoids resizing the list repeatedly

javafx ComboBox with cellfactory does not display selected item

I have a small proof of concept app which contains 6 Labels a ComboBox and a Button, all created using SceneBuilder.
By clicking the Button, the app makes a Json api call to return a list of countries and their related details (apla2code, apla3code, name etc). I created a CountryDetails object which holds 3 String elements. I use that to return an array of the CountryDetails which I then load into an array of ObserbavleList. I then apply that to a ComboBox and I load the CountryDetails elements into 3 labels each time an item is selected in the ComboBox. The All of this works fine (although there is probably a much better way of doing this).
The problem I am having is that the ComboBox is not displaying the selected item and I cannot figure out how to correct this. The image below shows what the issue is.
The code which makes the api call is as follows:
import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class GetCountries {
public CountryDetails[] getDetails() {
String inputLine = "";
StringBuilder jsonString = new StringBuilder();
HttpURLConnection urlConnection;
try {
URL urlObject = new URL("https://restcountries.eu/rest/v2/all");
urlConnection = (HttpURLConnection) urlObject.openConnection();
urlConnection.setRequestMethod("GET");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
while ((inputLine = bufferedReader.readLine()) != null) {
jsonString.append(inputLine);
}
urlConnection.getInputStream().close();
} catch(IOException ioe) {
System.out.println(ioe.getMessage());
}
Countries[] countries = new Gson().fromJson(jsonString.toString(), Countries[].class);
CountryDetails[] countryDetails = new CountryDetails[countries.length];
for(int i = 0; i < countries.length; i++){
countryDetails[i] = new CountryDetails(
countries[i].getAlpha2Code(),
countries[i].getAlpha3Code(),
countries[i].getName()
);
}
return countryDetails;
}
}
The code for the CountryDetails object is as follows:
public class CountryDetails {
private String alpha2Code;
private String alpha3Code;
private String name;
public CountryDetails(String strAlpha2Code, String strAlpha3Code, String strName) {
this.alpha2Code = strAlpha2Code;
this.alpha3Code = strAlpha3Code;
this.name = strName;
}
public String getAlpha2Code() { return alpha2Code; }
public void setAlpha2Code(String alpha2Code) { this.alpha2Code = alpha2Code; }
public String getAlpha3Code() { return alpha3Code; }
public void setAlpha3Code(String alpha3Code) { this.alpha3Code = alpha3Code; }
public String getName() { return name; }
public void setName(String name) {this.name = name; }
}
The code which loads the ObservableList is as follows:
GetCountries countries = new GetCountries();
CountryDetails[] countryDetails = countries.getDetails();
for (CountryDetails countryDetail : countryDetails) {
countriesObservableList.add(new CountryDetails(
countryDetail.getAlpha2Code(),
countryDetail.getAlpha3Code(),
countryDetail.getName())
);
}
The code which loads the ComboBox and displays the elements in the Labels is as follows:
cbCountryList.setCellFactory(new Callback<ListView<CountryDetails>, ListCell<CountryDetails>>() {
#Override public ListCell<CountryDetails> call(ListView<CountryDetails> p) {
return new ListCell<CountryDetails>() {
#Override
protected void updateItem(CountryDetails item, boolean empty) {
super.updateItem(item, empty);
if (empty || (item == null) || (item.getName() == null)) {
setText(null);
} else {
setText(item.getName());
}
}
};
}
});
public void comboAction(ActionEvent event) {
lblAlpha2Code.setText(cbCountryList.getValue().getAlpha2Code());
lblAlpha3Code.setText(cbCountryList.getValue().getAlpha3Code());
lblCountryName.setText(cbCountryList.getValue().getName());
}
Below is the image of the app:
The problem I am having is that the ComboBox is not displaying the selected item and I cannot figure out how to correct this.
You need to set a StringConverter for your cbCountryList.
cbCountryList.setConverter(new StringConverter<CountryDetails>() {
#Override
public String toString(CountryDetails object) {
return object.getName();
}
#Override
public CountryDetails fromString(String string) {
return null;
}
});
The All of this works fine (although there is probably a much better way of doing this).
You may consider updating the following things,
Asynchronous call for HTTP request and load items
You can cache your fetched-country list as you're calling every time when the button gets triggered. You can make a Singleton object for GetCountries.
Modified GetCountries class,
It caches the countries list and uses the cached data for multiple requests,
public static class GetCountries {
private static final String API_URL = "https://restcountries.eu/rest/v2/all";
private static CountryDetails[] countryDetails;
public static CountryDetails[] getDetails() {
//uses cached countryDetails once it gets loaded
if (countryDetails != null) {
return countryDetails;
}
StringBuilder jsonString = new StringBuilder();
HttpURLConnection urlConnection;
try {
URL urlObject = new URL(API_URL);
urlConnection = (HttpURLConnection) urlObject.openConnection();
urlConnection.setRequestMethod(HttpMethod.GET.name());
String inputLine = "";
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
while ((inputLine = bufferedReader.readLine()) != null) {
jsonString.append(inputLine);
}
urlConnection.getInputStream().close();
} catch (IOException ioe) {
System.out.println(ioe.getMessage());
}
Countries[] countries = new Gson().fromJson(jsonString.toString(), Countries[].class);
countryDetails = new CountryDetails[countries.length];
for (int i = 0; i < countries.length; i++) {
countryDetails[i] = new CountryDetails(
countries[i].getAlpha2Code(),
countries[i].getAlpha3Code(),
countries[i].getName()
);
}
return countryDetails;
}
}
Use Task to fetch your countries asynchronously,
Task<CountryDetails[]> fetchCountryTask = new Task<CountryDetails[]>() {
#Override
protected CountryDetails[] call() throws Exception {
return GetCountries.getDetails();
}
};
fetchButton.setOnAction(event -> new Thread(fetchCountryTask).start());
fetchCountryTask.setOnRunning(event -> cbCountryList.setDisable(true));
fetchCountryTask.setOnSucceeded(e -> {
cbCountryList.getItems().addAll(fetchCountryTask.getValue());
cbCountryList.setDisable(false);
});

Get preferences older than the most recent entry using java.util.prefs.Preferences.get

With the java.util.prefs.Preferences API is it possible to get historic results which are older than the last entry?
eg if you had
prefs.put("key", "value1");
prefs.put("key", "value2");
return prefs.get("key");
will return "value2"
Can I ever get "value1" ?
If no then what is the best alternative to achieve this functionality.
If anyone can do better I am happy to accept their answer, in the mean time:
import java.util.ArrayList;
import java.util.List;
import java.util.prefs.Preferences;
public class RecentStrings
{
private final Preferences prefs;
private final int noRecent;
private static final int defaultNo = 10;
private final String indexingKey;
public RecentStrings()
{
this(Preferences.userRoot(), defaultNo, "r");
}
public RecentStrings(Preferences prefs, int noRecent, String indexingKey)
{
this.noRecent = noRecent;
this.prefs = prefs;
this.indexingKey = indexingKey;
}
public void setMostRecentString(String file)
{
if (!getAllRecentStrings().contains(file))
{
for (int i = noRecent-1; i >= 0; i--)
{
prefs.put(indexingKey+(i+1), prefs.get(indexingKey+i, ""));
}
prefs.put(indexingKey+0, file);
}
else
{
removeRecentString(file);
setMostRecentString(file);
}
}
private void removeRecentString(String file)
{
List<String> recents = getAllRecentStrings();
recents.remove(file);
for (int i=0; i < noRecent; i++)
{
String value;
if (i < recents.size())
{
value = recents.get(i);
}
else
{
value = "";
}
prefs.put(indexingKey+i,value);
}
}
public String getMostRecentString()
{
return prefs.get(indexingKey+0, "");
}
public List<String> getAllRecentStrings()
{
List<String> mostRecent = new ArrayList<>();
for (int i = 0; i < 10; i++)
{
String value = prefs.get(indexingKey+i, "");
if (!value.equals(""))
{
mostRecent.add(value);
}
}
return mostRecent;
}
}
Which also handles duplicates and keeps things from most recent to oldest.

How to iterate column values in an object's list?

Here's my code:
public String generateEISReports_PDF(){
surveyReportService.setSurveyReportDA(surveyReportDA);
surveyReportList = surveyReportService.getSurveyReport(surveyType, surveyDate, projectCode, employeeCode);
if(surveyReportList != null){
System.out.println(surveyReportList + "testing");
System.out.println(surveyReportList.size() + "size ito");
for (SurveyReport surveyReport : surveyReportList) {
System.out.println(surveyReport.getRiskRank().toString() + "asdf");
surveyReports.add(surveyReport);
}
}
this.compileTheJasperReports();
return SUCCESS;
}
I am getting the whole row values with this code as an object. I want to iterate the field values in every list of objects. How can I do that?
By using reflection you can achieve that. Following is the code. This might help you.
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SurveyReport {
public int localServeryCont; // if you make it private then changes have to be done in 'SurveyIterator' inner class.
public String surveyReporterName;
public static void main(String[] args) {
List<SurveyReport> surveyReportList = new ArrayList<SurveyReport>();
SurveyReport sr = new SurveyReport(); //first object creation
sr.localServeryCont = 10;
sr.surveyReporterName = "AAA";
surveyReportList.add(sr);
sr = new SurveyReport(); //second object creation
sr.localServeryCont = 100;
sr.surveyReporterName = "BBB";
surveyReportList.add(sr); //two objects are in the list-object.
for (SurveyReport surveyReport : surveyReportList) {
Iterator<String> itr = surveyReport.iterator(); //You can work around with 'java.lang.Iterable' to use 'foreach' loop
while (itr.hasNext()) { //this is what you might be expecting
System.out.println("SurveyReport's object's values : " + itr.next());
}
}
}
public Iterator<String> iterator() { //here is method to get iterator object.
return new SurveyIterator();
}
private class SurveyIterator implements Iterator<String> { //creating 'SurveyIterator' INNER class
private int totalAvailableField = SurveyReport.class.getDeclaredFields().length;
int cursor = 0;
Field[] surveyReportFields = SurveyReport.class.getFields();
#Override
public boolean hasNext() {
return cursor != totalAvailableField;
}
#Override
public String next() {
String next = null;
try {
next = (surveyReportFields[cursor].get(SurveyReport.this)).toString();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
cursor++;
return next;
}
#Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}

Java: Why isn't this method recognized?

I am having a problem where a method from another class is not being recognized as existing by Eclipse, even though all the other methods are. I have no idea why this is happening, and cannot figure it out. I get the error The method getData() is undefined for the type TMXReader.TMXHandler when I try doing this:
package tiled.simple.reader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import tiled.simple.core.Map;
import tiled.simple.core.MapLayer;
import tiled.simple.core.TileSet;
import davidiserovich.TMXLoader.TMXHandler;
import davidiserovich.TMXLoader.TileMapData;
TMXHandler handler = new TMXHandler();
handler.getData();
Here is the TMXHandler class:
package davidiserovich.TMXLoader;
import java.util.HashMap;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
//import android.sax.StartElementListener;
public class TMXHandler extends DefaultHandler {
/*
* This is an SAX2 XML parser that interprets the input TMX file and creates
* a TileMapData object.
*/
// NOTE: Map Object loading is not yet implemented
// Markers for which tag we're in
private boolean inTileSet, inTile, inLayer, inData, inObjectGroup,
inProperties;
// ID of the current tile that we're adding properties to.
// This is actually an OFFSET from firstGID of the tile in
// the tileset. Beware.
private String currentTileID;
private String currentObjectGroupName;
TileMapData.TMXObject currentObject;
TileMapData.TileSet currentTileSet;
TileMapData.Layer currentLayer;
HashMap<String, HashMap<String, String>> currentTileSetProperties;
HashMap<String, String> currentLayerProperties;
private TileMapData data;
/*
* These fields hold the buffer and data to help decode the long stream of
* gids in the data field
*/
private char buffer[];
private int bufferIndex;
private int currentX;
private int currentY;
public int MAX_INT_DECIMAL_LENGTH = 10;
public TMXHandler() {
super();
buffer = new char[MAX_INT_DECIMAL_LENGTH];
bufferIndex = 0;
currentX = 0;
currentY = 0;
}
public TileMapData getData() {
return data;
}
#Override
public void startDocument() throws SAXException {
data = new TileMapData();
}
#Override
public void endDocument() throws SAXException {
}
#Override
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts) throws SAXException {
if (localName.equals("map")) {
// inMap = true;
// Check that the orientation is orthogonal
if (!(atts.getValue("orientation").equals("orthogonal"))) {
throw new SAXException(
"Unsupported orientation. Parse Terminated.");
}
data.orientation = atts.getValue("orientation");
data.height = Integer.parseInt(atts.getValue("height"));
data.width = Integer.parseInt(atts.getValue("width"));
data.tilewidth = Integer.parseInt(atts.getValue("tilewidth"));
data.tileheight = Integer.parseInt(atts.getValue("tileheight"));
// data.sectionId = atts.getValue("id");
} else if (localName.equals("tileset")) {
inTileSet = true;
currentTileSet = new TileMapData.TileSet();
currentTileSet.firstGID = Integer.parseInt(atts
.getValue("firstgid"));
currentTileSet.tileWidth = Integer.parseInt(atts
.getValue("tilewidth"));
currentTileSet.tileHeight = Integer.parseInt(atts
.getValue("tileheight"));
currentTileSet.name = atts.getValue("name");
currentTileSetProperties = new HashMap<String, HashMap<String, String>>();
} else if (inTileSet && localName.equals("image")) {
currentTileSet.ImageFilename = atts.getValue("source");
currentTileSet.imageWidth = Integer
.parseInt(atts.getValue("width"));
currentTileSet.imageHeight = Integer.parseInt(atts
.getValue("height"));
} else if (inTileSet && localName.equals("tile")) {
inTile = true;
currentTileID = atts.getValue("id");
} else if (inTile && localName.equals("properties")) {
inProperties = true;
currentTileSetProperties.put(currentTileID,
new HashMap<String, String>());
} else if (inLayer && localName.equals("properties")) {
inProperties = true;
} else if (inTile && inProperties && localName.equals("property")) {
(currentTileSetProperties.get(currentTileID)).put(
atts.getValue("name"), atts.getValue("value"));
} else if (inLayer && inProperties && localName.equals("property")) {
currentLayerProperties.put(atts.getValue("name"),
atts.getValue("value"));
} else if (localName.equals("layer")) {
inLayer = true;
currentLayer = new TileMapData.Layer();
currentLayer.name = atts.getValue("name");
currentLayer.width = Integer.parseInt(atts.getValue("width"));
currentLayer.height = Integer.parseInt(atts.getValue("height"));
if (atts.getValue("opacity") != null)
currentLayer.opacity = Double.parseDouble(atts
.getValue("opacity"));
currentLayer.tiles = new long[currentLayer.height][currentLayer.width];
currentLayerProperties = new HashMap<String, String>();
} else if (localName.equals("data")) {
/*
* Data is loaded directly into the int array in characters() We
* just check if the encoding is supported here.
*/
inData = true;
String encoding = atts.getValue("encoding");
if (!encoding.equals("csv")) {
throw new SAXException(
"Unsupported encoding. Parse Terminated.");
}
} else if (localName.equals("objectgroup")) {
inObjectGroup = true;
currentObjectGroupName = atts.getValue("name");
} else if (localName.equals("object")) {
currentObject = new TileMapData.TMXObject();
currentObject.name = atts.getValue("name");
currentObject.type = atts.getValue("type");
currentObject.x = Integer.parseInt(atts.getValue("x"));
currentObject.y = Integer.parseInt(atts.getValue("y"));
currentObject.width = Integer.parseInt(atts.getValue("width"));
currentObject.height = Integer.parseInt(atts.getValue("height"));
if (inObjectGroup) {
currentObject.objectGroup = currentObjectGroupName;
} else {
currentObject.objectGroup = null;
}
}
}
#Override
public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {
if (localName.equals("map")) {
// inMap = false;
} else if (localName.equals("tileset")) {
inTileSet = false;
currentTileSet.properties = currentTileSetProperties;
currentTileSetProperties = null;
data.tilesets.add(currentTileSet);
currentTileSet = null; // Clear it just in case
} else if (localName.equals("tile")) {
inTile = false;
currentTileID = "-1"; // -1 won't be produced when searching for
// properties. Just a safeguard for
// improperly formatted files.
} else if (localName.equals("properties")) {
inProperties = false;
} else if (localName.equals("layer")) {
inLayer = false;
currentLayer.properties = currentLayerProperties;
data.layers.add(currentLayer);
currentLayer = null; // Clear it just in case
} else if (localName.equals("data")) {
inData = false;
// In case we missed the last entry (no non-numeral chars before tag
// end)
if (bufferIndex > 0) {
currentLayer.tiles[currentY][currentX] = Long
.parseLong(new String(buffer, 0, bufferIndex));
}
// Clear buffer
bufferIndex = 0;
currentX = 0;
currentY = 0;
} else if (localName.equals("objectgroup")) {
inObjectGroup = false;
} else if (localName.equals("object")) {
data.objects.add(currentObject);
}
}
#Override
public void characters(char ch[], int start, int length) {
/*
* Java has no unsigned types, so we have to use a long instead of an
* int so we can "simulate" an unsigned int. Disgusting. Anyway, we're
* going to add the numbers from the character stream to a buffer until
* we hit a comma, at which point we empty the buffer and convert it to
* a long, and dump it into the array. These are raw, so the horizontal
* and vertical flip bits may be set - to get the actual GID number,
* we'll use TileMapData's getGIDAt(x, y), which will mask it properly.
*/
if (inData) {
for (int i = 0; i < length; i++) {
if (ch[start + i] <= '9' && ch[start + i] >= '0') {
buffer[bufferIndex] = ch[start + i];
// Log.d("Wrote to index", String.valueOf(bufferIndex));
bufferIndex++;
} else {
// When we hit a comma or any non-number character, empty
// the buffer and enter the relevant
// GID into the data field
// int what = Integer.parseInt(new String(buffer, 0,
// bufferIndex));
// Log.d("Number", new String(buffer, 0, bufferIndex));
String nextNumber = new String(buffer, 0, bufferIndex);
if ((nextNumber != null) && ((nextNumber.trim()) != "")
&& (bufferIndex != 0)) {
// Log.d("Checking", nextNumber + " yes");
currentLayer.tiles[currentY][currentX] = Long
.parseLong(nextNumber);
bufferIndex = 0;
// Move to the next tile
if (currentX < (currentLayer.width - 1)) {
currentX++;
} else if (currentY < (currentLayer.height - 1)) {
currentX = 0;
currentY++;
}
}
}
}
}
}
}
Auto-complete shows all the other methods as usable, but for some reason getData "doesn't exist"...
I'm sure this is apparent for experienced Java programmers, but I have no idea why this is happening... so if anyone could tell me why, I'd be grateful. Thanks!
Change this:
TMXHandler handler = new TMXHandler;
To this:
TMXHandler handler = new TMXHandler();
EDIT
If it's just a typo issue... and if your question is complete... You may have forgot to put it in a class ? :<
import davidiserovich.TMXLoader.TMXHandler;
public class Main {
public static void main(String[] args) {
TMXHandler handler = new TMXHandler();
handler.getData();
}
}
You have to create object. You have forget ()
TMXHandler handler = new TMXHandler();
Are you sure the java code which is referring TMXHandler class have correct TMXHandler.class file in Runtime. This may be caused by old TMXHandler.class which dont have getData() method defined.
maybe try a clean build
TMXHandler handler = new TMXHandler;
The above line is the culprit.
- When we call new on a Class name its always accompanied by () as postfix, which indicates the constructor of the class whose object is now created on the heap.
- And this line gives the opportunity to step in, to initialize the state of the object before the Actual object is assigned to the Object reference variable.
So it should be....
TMXHandler handler = new TMXHandler();
I solved it... turns out that the one project already had a class named TMXHandler, so it was referring to that, and not my other project... A very stupid mistake. :

Categories