How do I traverse a SortedTree list with a public String method?
I'm trying to write a method that determined the next item in the list, sets cursor to null, an returns the item. I then set the cursor to null and return null. Ideally, I'd like to append to a string (like toString), and then select an entry that follows the one matching cursor. (If there is an entry).
Or would I be better off using something like an ArrayList?
My tree driver:
SortedTree tree = new SortedTree();
// Populate the tree
tree.add("Jim");
tree.add("John");
tree.add("Henry");
tree.add("Paul");
tree.add("Ringo");
tree.add("Mick");
tree.add("Keith");
tree.add("Charlie");
tree.add("Eric");
tree.add("David");
tree.add("George");
// traverse it looking for eeeeerie names
System.out.println("Eeeeerie items in our tree:");
String item = tree.first();
while (item != null) {
if (item.contains("e") || item.contains("E"))
System.out.println(item);
item = tree.next();
}
Here is the method I'm trying to make to return the appropriate names.
As it stands, my method is only returning the name "Charlie", but the output should be:
"Eeeeerie items in our tree:
Charlie
Eric
George
Henry
Keith"
public String next() {
cursor = root;
String nodeList = null;
while (cursor.right != null) {
cursor = cursor.right;
nodeList += cursor.item;
}
if (cursor == null) {
return null;
}
return null;
}
I should also include my code for the cursor:
TreeNode cursor; // node to remember the current traversal position
public void setCursor(TreeNode node) {
this.cursor = node;
}
Here is my first() method which is called in the driver
public String first() {
TreeNode beginning = root;
do {
beginning = beginning.left;
} while (beginning.left != null);
setCursor(beginning);
return beginning.item;
}
Like this:
List<String> names = new ArrayList<String>();
names.add("Jim");
// ... add rest here ...
List<String> eerie = new ArrayList<String>(); // a new empty list
// Collect the ones you want
for(String name : names) {
// I'm not sure what 'contains()' is (in your code), but this will work.
if(item.indexOf("e") != -1 || item.indexOf("E") != -1) {
eerie.add(name);
}
}
// Sort 'em
Collections.sort(eerie);
// Print 'em
for(String name : eerie) {
System.out.println(name);
}
Related
I have a categorized Notes view, let say the first categorized column is TypeOfVehicle the second categorized column is Model and the third categorized column is Manufacturer.
I would like to collect only the values for the first category and return it as json object:
I am facing two problems:
- I can not read the value for the category, the column values are emptry and when I try to access the underlying document it is null
the script won't hop over to the category/sibling on the same level.
can someone explain me what am I doing wrong here?
private Object getFirstCategory() {
JsonJavaObject json = new JsonJavaObject();
try{
String server = null;
String filepath = null;
server = props.getProperty("server");
filepath = props.getProperty("filename");
Database db;
db = utils.getSession().getDatabase(server, filepath);
if (db.isOpen()) {
View vw = db.getView("transport");
if (null != vw) {
vw.setAutoUpdate(false);
ViewNavigator nav;
nav = vw.createViewNav();
JsonJavaArray arr = new JsonJavaArray();
Integer count = 0;
ViewEntry tmpentry;
ViewEntry entry = nav.getFirst();
while (null != entry) {
Vector<?> columnValues = entry.getColumnValues();
if(entry.isCategory()){
System.out.println("entry notesid = " + entry.getNoteID());
Document doc = entry.getDocument();
if(null != doc){
if (doc.hasItem("TypeOfVehicle ")){
System.out.println("category has not " + "TypeOfVehicle ");
}
else{
System.out.println("category IS " + doc.getItemValueString("TypeOfVehicle "));
}
} else{
System.out.println("doc is null");
}
JsonJavaObject row = new JsonJavaObject();
JsonJavaObject jo = new JsonJavaObject();
String TypeOfVehicle = String.valueOf(columnValues.get(0));
if (null != TypeOfVehicle ) {
if (!TypeOfVehicle .equals("")){
jo.put("TypeOfVehicle ", TypeOfVehicle );
} else{
jo.put("TypeOfVehicle ", "Not categorized");
}
} else {
jo.put("TypeOfVehicle ", "Not categorized");
}
row.put("request", jo);
arr.put(count, row);
count++;
tmpentry = nav.getNextSibling(entry);
entry.recycle();
entry = tmpentry;
} else{
//tmpentry = nav.getNextCategory();
//entry.recycle();
//entry = tmpentry;
}
}
json.put("data", arr);
vw.setAutoUpdate(true);
vw.recycle();
}
}
} catch (Exception e) {
OpenLogUtil.logErrorEx(e, JSFUtil.getXSPContext().getUrl().toString(), Level.SEVERE, null);
}
return json;
}
What you're doing wrong is trying to treat any single view entry as both a category and a document. A single view entry can only be one of a category, a document, or a total.
If you have an entry for which isCategory() returns true, then for the same entry:
isDocument() will return false.
getDocument() will return null.
getNoteID() will return an empty string.
If the only thing you need is top-level categories, then get the first entry from the navigator and iterate over entries using nav.getNextSibling(entry) as you're already doing, but:
Don't try to get documents, note ids, or fields.
Use entry.getColumnValues().get(0) to get the value of the first column for each category.
If the view contains any uncategorised documents, it's possible that entry.getColumnValues().get(0) might throw an exception, so you should also check that entry.getColumnValues().size() is at least 1 before trying to get a value.
If you need any extra data beyond just top-level categories, then note that subcategories and documents are children of their parent categories.
If an entry has a subcategory, nav.getChild(entry) will get the first subcategory of that entry.
If an entry has no subcategories, but is a category which contains documents, nav.getChild(entry) will get the first document in that category.
I have the HTML string like
<b>test</b><b>er</b>
<span class="ab">continue</span><span> without</span>
I want to collapse the Tags which are similar and belong to each other. In the above sample I want to have
<b>tester</b>
since the tags have the same tag withouth any further attribute or style. But for the span Tag it should remain the same because it has a class attribute. I am aware that I can iterate via Jsoup over the tree.
Document doc = Jsoup.parse(input);
for (Element element : doc.select("b")) {
}
But I'm not clear how look forward (I guess something like nextSibling) but than how to collapse the elements?
Or exists a simple regexp merge?
The attributes I can specify on my own. It's not required to have a one-fits-for-all Tag solution.
My approach would be like this. Comments in the code
public class StackOverflow60704600 {
public static void main(final String[] args) throws IOException {
Document doc = Jsoup.parse("<b>test</b><b>er</b><span class=\"ab\">continue</span><span> without</span>");
mergeSiblings(doc, "b");
System.out.println(doc);
}
private static void mergeSiblings(Document doc, String selector) {
Elements elements = doc.select(selector);
for (Element element : elements) {
// get the next sibling
Element nextSibling = element.nextElementSibling();
// merge only if the next sibling has the same tag name and the same set of attributes
if (nextSibling != null && nextSibling.tagName().equals(element.tagName())
&& nextSibling.attributes().equals(element.attributes())) {
// your element has only one child, but let's rewrite all of them if there's more
while (nextSibling.childNodes().size() > 0) {
Node siblingChildNode = nextSibling.childNodes().get(0);
element.appendChild(siblingChildNode);
}
// remove because now it doesn't have any children
nextSibling.remove();
}
}
}
}
output:
<html>
<head></head>
<body>
<b>tester</b>
<span class="ab">continue</span>
<span> without</span>
</body>
</html>
One more note on why I used loop while (nextSibling.childNodes().size() > 0). It turned out for or iterator couldn't be used here because appendChild adds the child but removes it from the source element and remaining childen are be shifted. It may not be visible here but the problem will appear when you try to merge: <b>test</b><b>er<a>123</a></b>
I tried to update the code from #Krystian G but my edit was rejected :-/ Therefore I post it as an own post. The code is an excellent starting point but it fails if between the tags a TextNode appears, e.g.
<span> no class but further</span> (in)valid <span>spanning</span> would result into a
<span> no class but furtherspanning</span> (in)valid
Therefore the corrected code looks like:
public class StackOverflow60704600 {
public static void main(final String[] args) throws IOException {
String test1="<b>test</b><b>er</b><span class=\"ab\">continue</span><span> without</span>";
String test2="<b>test</b><b>er<a>123</a></b>";
String test3="<span> no class but further</span> <span>spanning</span>";
String test4="<span> no class but further</span> (in)valid <span>spanning</span>";
Document doc = Jsoup.parse(test1);
mergeSiblings(doc, "b");
System.out.println(doc);
}
private static void mergeSiblings(Document doc, String selector) {
Elements elements = doc.select(selector);
for (Element element : elements) {
Node nextElement = element.nextSibling();
// if the next Element is a TextNode but has only space ==> we need to preserve the
// spacing
boolean addSpace = false;
if (nextElement != null && nextElement instanceof TextNode) {
String content = nextElement.toString();
if (!content.isBlank()) {
// the next element has some content
continue;
} else {
addSpace = true;
}
}
// get the next sibling
Element nextSibling = element.nextElementSibling();
// merge only if the next sibling has the same tag name and the same set of
// attributes
if (nextSibling != null && nextSibling.tagName().equals(element.tagName())
&& nextSibling.attributes().equals(element.attributes())) {
// your element has only one child, but let's rewrite all of them if there's more
while (nextSibling.childNodes().size() > 0) {
Node siblingChildNode = nextSibling.childNodes().get(0);
if (addSpace) {
// since we have had some space previously ==> preserve it and add it
if (siblingChildNode instanceof TextNode) {
((TextNode) siblingChildNode).text(" " + siblingChildNode.toString());
} else {
element.appendChild(new TextNode(" "));
}
}
element.appendChild(siblingChildNode);
}
// remove because now it doesn't have any children
nextSibling.remove();
}
}
}
}
I'm in the process of building a basic database using csv files, and i'm currently testing the select function out when i ran into something strange.
private ArrayList<Record> selectField(String selectTerm)
{
Log.log("Selection " + selectTerm,2,"DB_io");
ArrayList<Record> ret = new ArrayList<Record>();
if (titleRow.values.contains(selectTerm))
{
Log.log("Adding values to " + selectTerm);
int ordinal = titleRow.values.indexOf(selectTerm);
Log.log("Ordinal " + ordinal);
List<String> tempList = new ArrayList<String>();
for (Record r : data)
{
List<String> tempList = new ArrayList<String>();
tempList.add(r.values.get(ordinal));
Record s = new Record(tempList);
ret.add(s);
tempList.clear();
}
Log.log("Number of records in ret " + ret.size());
for (Record t : ret)
{
Log.log(t.toString());
}
}
else
{
Log.log("keyField does not contain that field");
return null;
}
Log.log("Values " + ret.toString());
return ret;
}
When i do this, the part where it logs t.ToString() shows the record to be empty, whereas if i log it before tempList.clear(), it shows the record to be containing data like it should.
If i move the tempList declaration into the Record r : data loop, then it works fine and the Record t : ret loop works outputs the contents of the record like it should
Why is this?
Edit : Record class
public class Record
{
List<String> values = new ArrayList<String>();
public Record(List<String> terms)
{
this.values = terms;
}
public Record(String[] s)
{
this.values = Arrays.asList(s);
}
public String toString()
{
return values.toString();
}
}
Your Record instance holds a reference to the ArrayList instance you passed to its constructor. Therefore, when you call tempList.clear(), you clear the same List that your Record instance is holding a reference to.
You shouldn't call tempList.clear(), since you are creating a new ArrayList in each iteration of your loop anyway.
you are referencing object from more than one place and clear method is cleaning object by setting its reference to null:
instead of ret.add(s); you can use ret.add(s.clone());
I have created twitter stream filtered by some keywords as follows.
TwitterStream twitterStream = getTwitterStreamInstance();
FilterQuery filtre = new FilterQuery();
String[] keywordsArray = { "iphone", "samsung" , "apple", "amazon"};
filtre.track(keywordsArray);
twitterStream.filter(filtre);
twitterStream.addListener(listener);
What is the best way to segregate tweets based on keywords matched. e.g. All the tweets that matches "iphone" should be stored into "IPHONE" table and all the tweets that matches "samsung" will be stored into "SAMSUNG" table and so on. NOTE: The no of filter keywords is about 500.
It seems that the only way to find out to which keyword a tweet belongs to is iterating over multiple properties of the Status object. The following code requires a database service with a method insertTweet(String tweetText, Date createdAt, String keyword) and every tweet is stored in the database multiple times, if multiple keywords are found. If at least one keyword is found in the tweet text, the additional properties are not searched for more keywords.
// creates a map of the keywords with a compiled pattern, which matches the keyword
private Map<String, Pattern> keywordsMap = new HashMap<>();
private TwitterStream twitterStream;
private DatabaseService databaseService; // implement and add this service
public void start(List<String> keywords) {
stop(); // stop the streaming first, if it is already running
if(keywords.size() > 0) {
for(String keyword : keywords) {
keywordsMap.put(keyword, Pattern.compile(keyword, Pattern.CASE_INSENSITIVE));
}
twitterStream = new TwitterStreamFactory().getInstance();
StatusListener listener = new StatusListener() {
#Override
public void onStatus(Status status) {
insertTweetWithKeywordIntoDatabase(status);
}
/* add the unimplemented methods from the interface */
};
twitterStream.addListener(listener);
FilterQuery filterQuery = new FilterQuery();
filterQuery.track(keywordsMap.keySet().toArray(new String[keywordsMap.keySet().size()]));
filterQuery.language(new String[]{"en"});
twitterStream.filter(filterQuery);
}
else {
System.err.println("Could not start querying because there are no keywords.");
}
}
public void stop() {
keywordsMap.clear();
if(twitterStream != null) {
twitterStream.shutdown();
}
}
private void insertTweetWithKeywordIntoDatabase(Status status) {
// search for keywords in tweet text
List<String> keywords = getKeywordsFromTweet(status.getText());
if (keywords.isEmpty()) {
StringBuffer additionalDataFromTweets = new StringBuffer();
// get extended urls
if (status.getURLEntities() != null) {
for (URLEntity url : status.getURLEntities()) {
if (url != null && url.getExpandedURL() != null) {
additionalDataFromTweets.append(url.getExpandedURL());
}
}
}
// get retweeted status -> text
if (status.getRetweetedStatus() != null && status.getRetweetedStatus().getText() != null) {
additionalDataFromTweets.append(status.getRetweetedStatus().getText());
}
// get retweeted status -> quoted status -> text
if (status.getRetweetedStatus() != null && status.getRetweetedStatus().getQuotedStatus() != null
&& status.getRetweetedStatus().getQuotedStatus().getText() != null) {
additionalDataFromTweets.append(status.getRetweetedStatus().getQuotedStatus().getText());
}
// get retweeted status -> quoted status -> extended urls
if (status.getRetweetedStatus() != null && status.getRetweetedStatus().getQuotedStatus() != null
&& status.getRetweetedStatus().getQuotedStatus().getURLEntities() != null) {
for (URLEntity url : status.getRetweetedStatus().getQuotedStatus().getURLEntities()) {
if (url != null && url.getExpandedURL() != null) {
additionalDataFromTweets.append(url.getExpandedURL());
}
}
}
// get quoted status -> text
if (status.getQuotedStatus() != null && status.getQuotedStatus().getText() != null) {
additionalDataFromTweets.append(status.getQuotedStatus().getText());
}
// get quoted status -> extended urls
if (status.getQuotedStatus() != null && status.getQuotedStatus().getURLEntities() != null) {
for (URLEntity url : status.getQuotedStatus().getURLEntities()) {
if (url != null && url.getExpandedURL() != null) {
additionalDataFromTweets.append(url.getExpandedURL());
}
}
}
String additionalData = additionalDataFromTweets.toString();
keywords = getKeywordsFromTweet(additionalData);
}
if (keywords.isEmpty()) {
System.err.println("ERROR: No Keyword found for: " + status.toString());
} else {
// insert into database
for(String keyword : keywords) {
databaseService.insertTweet(status.getText(), status.getCreatedAt(), keyword);
}
}
}
// returns a list of keywords which are found in a tweet
private List<String> getKeywordsFromTweet(String tweet) {
List<String> result = new ArrayList<>();
for (String keyword : keywordsMap.keySet()) {
Pattern p = keywordsMap.get(keyword);
if (p.matcher(tweet).find()) {
result.add(keyword);
}
}
return result;
}
Here's how you'd use a StatusListener to interrogate the received Status objects:
final Set<String> keywords = new HashSet<String>();
keywords.add("apple");
keywords.add("samsung");
// ...
final StatusListener listener = new StatusAdapter() {
#Override
public void onStatus(Status status) {
final String statusText = status.getText();
for (String keyword : keywords) {
if (statusText.contains(keyword)) {
dao.insert(keyword, statusText);
}
}
}
};
final TwitterStream twitterStream = getTwitterStreamInstance();
final FilterQuery fq = new FilterQuery();
fq.track(keywords.toArray(new String[0]));
twitterStream.addListener(listener);
twitterStream.filter(fq);
I see the DAO being defined along the lines of:
public interface StatusDao {
void insert(String tableSuffix, Status status);
}
You would then have a DB table corresponding with each keyword. The implementation would use the tableSuffix to store the Status in the correct table, the sql would roughly look like:
INSERT INTO status_$tableSuffix$ VALUES (...)
Notes:
This implementation would insert a Status into multiple tables if a Tweet contained 'apple' and 'samsung' for instance.
Additionally, this is quite a naive implementation, you might want to consider batching inserts into the tables... but it depends on the volume of Tweets you'll be receiving.
As noted in the comments, the API considers other attributes when matching e.g. URLs and an embedded Tweet (if present) so searching the status text for a keyword match may not be sufficient.
Well, you could create a class similar to an ArrayList but make it so you can create an array of ArrayLists, call it TweetList. This class will need an insert function.
Then use two for loops to search through the tweets and find matching keywords that are contained in a normal array list, and then add them to the TweetList that matches the index of the keyword in the keywords ArrayList
for (int i = 0; i < tweets.length; i++)
{
String[] split = tweets[i].split(" ");// split the tweet up
for (int j = 0; j < split.length; j++)
if (keywords.contains(split[j]))//check each word against the keyword list
list[keywords.indexOf(j)].insert[tweets[i]];//add the tweet to the tree index that matches index of the keyword
}
I need help for this case below :
I have 2 method :
private void calculateTime(Map.Entry<List<String>, List<LogRecord>> entry, List<LogProcess> processList) {
List<List<LogRecord>> processSpentTime = new ArrayList<List<LogRecord>>();
processSpentTime = subListProcess(entry, processSpentTime);
for (List<LogRecord> item : processSpentTime) {
processList = parse(item, DEFAULT_START_LEVEL);
}
}
and the second method
private List<LogProcess> parse(List<LogRecord> recordList, int level) {
List<LogProcess> processList = new ArrayList<LogProcess>();
if(!recordList.isEmpty()) {
LogProcess process = findProcess(recordList, level);
if(!(process instanceof NullLogProcess)) {
if(!(process instanceof IncompleteLogProcess)) {
processList.add(process);
}
int fromIndex = recordList.indexOf(process.returnStartIndexOfNextProcess()) + 1;
processList.addAll(parse(recordList.subList(fromIndex, recordList.size()), level));
}
}
return processList;
}
public LogProcess findProcess(List<LogRecord> recordList, int level) {
LogRecord endRecord = null;
LogRecord startRecord = findStartRecord(recordList);
if(startRecord instanceof NullLogRecord) {
return new NullLogProcess();
}
List<LogRecord> startEndRecord = findStartEndRecord(startRecord, recordList);
startRecord = startEndRecord.get(0);
endRecord = startEndRecord.get(1);
LogProcess process = returnLogProcess(startRecord, endRecord);
process.setLevel(level);
process.setChildren(findChildProcess(recordList, startRecord, endRecord, level + 1));
return process;
}
private List<LogProcess> findChildProcess(List<LogRecord> recordList, LogRecord startRecord, LogRecord endRecord, int level) {
int fromIndex = recordList.indexOf(startRecord) + 1;
int toIndex = recordList.indexOf(endRecord);
if(toIndex > fromIndex) {
List<LogRecord> recordSubList = recordList.subList(fromIndex, toIndex);
return parse(recordSubList, level);
} else {
return new ArrayList<LogProcess>();
}
}
private List<LogRecord> findStartEndRecord(LogRecord startRecord, List<LogRecord> recordList) {
List<LogRecord> startEndRecord = new ArrayList<LogRecord>();
if (!recordList.isEmpty()) {
startEndRecord.add(startRecord);
for (LogRecord record : recordList) {
boolean isStartRecord = record.isStartPoint() && record.hasSameActionName(startRecord);
if(isStartRecord){
startEndRecord = new ArrayList<LogRecord>();;
startEndRecord.add(record);
continue;
}
boolean isEndRecord = record.isEndPoint() && record.hasSameActionName(startRecord);
if (isEndRecord) {
startEndRecord.add(record);
return startEndRecord;
}
}
return startEndRecord;
}
return startEndRecord;
}
private LogRecord findStartRecord(List<LogRecord> recordList) {
for (LogRecord record : recordList) {
if (record.isStartPoint()){
recordList.remove(record);
return record;
}
}
return new NullLogRecord();
}
at the method calculatime in the for loop I just get the result for the first item, and after that I got error the same the title . please help me and explain me more for this case .
The name of this exception is a bit confusing, because it isn't related to multi threading.
What happens is that you are iterating over a collection which is being modified while you are iterating over it.
If performance is not your highest concern, a simple way out would be to copy the list and iterate over that copy and add items to the original list.
My guess is it's related to recordList.subList():
Returns a view of the portion of this list. [..] The returned list is backed by this list. [..] The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. [..] All methods first check to see if the actual modCount of the backing list is equal to its expected value, and throw a ConcurrentModificationException if it is not.
I don't see any modification, so it probably happens in findProcess(). Consider creating a copy of that list:
new ArrayList(recordList.subList())
You are getting the exception because of this :
for (LogRecord record : recordList) {
if (record.isStartPoint()){
recordList.remove(record); <--- This is the cause
return record;
}
}
Use an Iterator Instead
Iterator<LogRecord> iterator = recordList.iterator();
while(iterator.hasNext()){
LogRecord logRecord = iterator.next();
if(record.isStartPoint()){
iterator.remove();
return logRecord;
}
Check if this works