This is an excerpt from my project:
import javafx.util.Pair;
import org.w3c.dom.*;
private Pair<Element, Integer> findBestAlbumElement(Element recording) {
Pair<Element, Integer> best = new Pair<>(null, Integer.MIN_VALUE);
NodeList list = recording.getElementsByTagName("release");
for (int i = 0; i < list.getLength(); i++) {
System.out.println((best.getKey() == null ? "null" : best.getKey().getTextContent()) + "; " + best.getValue());
Element album = (Element) list.item(i);
int mark = getAlbumAndYearMark(recording, album);
if (mark > best.getValue()) best = new Pair<>(album, mark);
System.out.println((best.getKey() == null ? "null" : best.getKey().getTextContent()) + "; " + best.getValue());
}
return best;
}
and I'm running into a strange problem in this piece of code. The variable best resets between loop iterations, as seen in the beginning of the printout to console:
null; -2147483648
Live USABootlegAlbumLive1990DE1990GermanyGermanyDE212CD2T.N.T255240; 6
null; -2147483648
...
The first line is the first System.out.println(), the second line is the second one (where the variable best is properly set as expected) and the third line is the first one again (where the variable best seemingly just resets of its own accord).
I've tried to replicate the problem with the following code:
Pair<String, Integer> best = new Pair<>("", Integer.MIN_VALUE);
String[] strings = {"asdf", "fdsa", "dsaf"};
int[] marks = {1, 5, 3};
for (int i = 0; i < strings.length; i++) {
System.out.println(best.getKey() + " " + best.getValue());
if (marks[i] > best.getValue()) best = new Pair<>(strings[i], marks[i]);
System.out.println(best.getKey() + " " + best.getValue());
}
which replaces the NodeList with a String array, but this code works as expected.
My problem is, I don't even know how to approach this issue. I don't know how to debug this further or even reproduce the problem in a smaller example, as I don't know how to create a valid NodeList (since it's an interface, so I can't just new NodeList).
I'm also at a bit of a loss, as it looks to me like the bug appears in a place where it shouldn't even be possible, since the only code that is supposed to execute between the two println calls is i++ (not altering or even accessing best in any way). Am I wrong about this?
Does anyone have any idea what could be going on, or even how I would get closer to pinpointing the issue?
EDIT
As per request, here's getAlbumAndYearMark, which uses the jaudiotagger library (apologies for the ugly long lined code, this is a fairly old project).
private Tag tag;
private int getAlbumAndYearMark(Element recording, Element album) {
int mark = 0;
if (album == null) return tag.hasField(FieldKey.YEAR) ? getYearMark(album) : 0;
if (contains(album.getElementsByTagName("primary-type"), "Album")) mark += 2;
else if (!contains(album.getElementsByTagName("secondary-type"), "Album")) return Integer.MIN_VALUE;
Node title = album.getElementsByTagName("title").item(0);
if (title != null && tag.hasField(FieldKey.ALBUM)) mark += title.getTextContent().equals(tag.getFirst(FieldKey.ALBUM)) ? 7 : -4;
Node date = album.getElementsByTagName("date").item(0);
if (date != null && tag.hasField(FieldKey.YEAR)) mark += date.getTextContent().equals(tag.getFirst(FieldKey.YEAR).trim()) ? 3 : -3;
Node track = album.getElementsByTagName("number").item(0);
if (track != null && tag.hasField(FieldKey.TRACK)) mark += track.getTextContent().equals(tag.getFirst(FieldKey.TRACK).trim()) ? 3 : -1;
return mark;
}
private int getYearMark(Element element) {
NodeList dates = element.getElementsByTagName("date");
for (int i = 0; i < dates.getLength(); i++)
if (dates.item(i).getTextContent().substring(0, 4).equals(tag.getFirst(FieldKey.YEAR))) return 7;
return -7;
}
private static boolean contains(NodeList list, String string) {
for (int i = 0; i < list.getLength(); i++)
if (list.item(i).getTextContent().trim().equalsIgnoreCase(string)) return true;
return false;
}
but I don't believe this method is the problem, as I still have the same issue if I replace int mark = getAlbumYearMark(recording, album); with int mark = (int) (Math.random() * 10);
Here's a (heavily trimmed) example XML file, printed directly from the program:
<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#" xmlns:ext="http://musicbrainz.org/ns/ext#-2.0" created="2018-02-16T02:07:28.816Z">
<recording-list count="72" offset="0">
<recording ext:score="100" id="6e702972-00c2-4725-b3e5-60e85ef0de25">
<title>T.N.T</title>
<artist-credit>
<name-credit>
<artist id="66c662b6-6e2f-4930-8610-912e24c63ed1">
<name>AC/DC</name>
</artist>
</name-credit>
</artist-credit>
<release-list>
<release id="ddaa5690-df97-4bb2-b93d-396fe5fb49d5">
<title>Live USA</title>
<release-group id="6b1ace64-bf92-3c42-8a1f-aea6fa08edec" type="Live">
<primary-type>Album</primary-type>
<secondary-type-list>
<secondary-type>Live</secondary-type>
</secondary-type-list>
</release-group>
<date>1990</date>
<country>DE</country>
<release-event-list>
<release-event>
<date>1990</date>
<area id="85752fda-13c4-31a3-bee5-0e5cb1f51dad">
<name>Germany</name>
<sort-name>Germany</sort-name>
<iso-3166-1-code-list>
<iso-3166-1-code>DE</iso-3166-1-code>
</iso-3166-1-code-list>
</area>
</release-event>
</release-event-list>
<medium-list>
<track-count>21</track-count>
<medium>
<position>2</position>
<format>CD</format>
<track-list count="11" offset="1">
<track id="caadf3b8-4a44-34c6-b9dc-c9870c5d9bc0">
<number>2</number>
</track>
</track-list>
</medium>
</medium-list>
</release>
</release-list>
</recording>
</recording-list>
</metadata>
You can see an untrimmed example by querying the musicbrainz database directly, for example this query.
It's not an answer (yet). I'm trying with next xml:
<root>
<release-list>
<release id="ddaa5690-df97-4bb2-b93d-396fe5fb49d5">
<title>Live USA</title>
<date>1990</date>
<country>DE</country>
</release>
<release id="qqqa5690-df97-4bb2-b93d-396fe5fb49d5">
<title>German collections</title>
<date>1991</date>
<country>DE</country>
</release>
</release-list>
<release-list>
<release id="zzza5690-df97-4bb2-b93d-396fe5fb49d5">
<title>Just USA</title>
<date>1995</date>
<country>US</country>
</release>
</release-list>
<release-list>
<release id="aaaa5690-df97-4bb2-b93d-396fe5fb49d5">
<title>Anoother USA</title>
<primary-type>Album</primary-type>
<date>1999</date>
<country>RUS</country>
</release>
</release-list>
And I have no issues. Could you please try with this xml?
Also I'm using both incremental mark like return mockMark++; and array-based mark like
private int getAlbumAndYearMark(Element recording, Element album) {
int[] arr = {1,0,5,7,3,6,8,9,10};
return arr[mockMark++];
}
Related
I only want to check for:
if (lore.contains("§eSigned of ")) {
but it doesn't get that it does contain "§eSigned of "
I wrote a Minecraft Command /sign you can add a lore to an item ("Signed of playerrank | playername").
Then i wanted to add an /unsign command to remove this lore.
ItemStack is = p.getItemInHand();
ItemMeta im = is.getItemMeta();
List<String> lore = im.hasLore() ? im.getLore() : new ArrayList<String>();
if (lore.contains("§eSigned of " + getChatName(p))) { // this line is important!
for (int i = 0; i < 3; i++) {
int size = lore.size();
lore.remove(size - 1);
}
im.setLore(lore);
is.setItemMeta(im);
p.setItemInHand(is);
sendMessage(p, "§aThis item is no longer signed");
} else {
sendMessage(p, "§aThis item is not signed!");
}
return CommandResult.None;
Everything works fine until you e.g. change your name. than you can't remove the sign because getChatName(p) has changed.
To fix this i only want to check
if (lore.contains("§eSigned of ")) {
but than it doesn't get it and returns false. (it says lore does not contain "§eSigned of ")
I tried a lot but it only works with the string "§eSigned of " and getChatName(p).
As the documentation "contains" searches for the specific string so it should work as I thought right?
Add:
getChatName(p) returns the rank of the player and the playername like: "Member | domi"
sendMessage(p, "") sends a simple message in the Minecraft chat
The problem you run into is that contains(String) looks for a matching string. What you search for is a check if any string in the list starts with "§eSigned of ".
I would suggest adding a function isSignedItem like this:
private boolean isSignedItem(List<String> lore) {
for (String st : lore)
if (st.startsWith("§eSigned of "))
return true;
return false;
}
and then to use this function to check if the item is signed or not:
[...]
List<String> lore = im.hasLore() ? im.getLore() : new ArrayList<String>();
if (isSignedItem(lore)) { // this line is important!
for (int i = 0; i < 3; i++) {
int size = lore.size();
lore.remove(size - 1);
}
[...]
I have an Element list of which i'm using jsoup's method attr() to get the href attribute.
Here is part of my code:
String searchTerm = "tutorial+programming+"+i_SearchPhrase;
int num = 10;
String searchURL = GOOGLE_SEARCH_URL + "?q="+searchTerm+"&num="+num;
Document doc = Jsoup.connect(searchURL).userAgent("chrome/5.0").get();
Elements results = doc.select("h3.r > a");
String linkHref;
for (Element result : results) {
linkHref = result.attr("href").replace("/url?q=","");
//some more unrelated code...
}
So for example, when i use the search prase "test", the attr("href") produces (first in the list):
linkHref = https://www.tutorialspoint.com/software_testing/&sa=U&ved=0ahUKEwi_lI-T69jTAhXIbxQKHU1kBlAQFggTMAA&usg=AFQjCNHr6EzeYegPDdpHJndLJ-889Sj3EQ
where i only want: https://www.tutorialspoint.com/software_testing/
What is the best way to fix this? Do i just add some string operations on linkHref (which i know how) or is there a way to make the href attribute contain the shorter link to begin with?
Thank you in advanced
If you always want to remove the query parameters you can make use of String.indexOf() e.g.
int lastPos;
if(linkHref.indexOf("?") > 0) {
lastPos = linkHref.indexOf("?");
} else if (linkHref.indexOf("&") > 0){
lastPos = linkHref.indexOf("&");
}
else lastPos = -1;
if(lastPos != -1)
linkHref = linkHref.subsring(0, lastPos);
When the code is ran the nested loop causes it to create occasional duplicate entries to the system, i have spent a while looking through this but still cant find what is causing this, would greatly appreciate any help?
for(int i = 0; i < subWorkItemElement.getChildNodes().getLength(); i++) {
Boolean test = false;
WorkItemCommon existingChild = null;
String summary = null;
if(subWorkItemElement.getChildNodes().item(i).getNodeName().equals("workitem")) {
// We know it's a work item - but is it in the existing list?
Element childWorkItem = (Element) subWorkItemElement.getChildNodes().item(i);
for(int j = 0; j < subWorkItemElement.getChildNodes().getLength(); j++) {
if(childWorkItem.getChildNodes().item(j) instanceof Element) {
if(((Element)childWorkItem.getChildNodes().item(j)).getNodeName().equals("details")) {
summary = ((Element) childWorkItem.getChildNodes().item(j)).getElementsByTagName("summary")
.item(0).getTextContent();
for(String k : userInfoHashMap.keySet()) {
summary = summary.replace("${" + k + "}", userInfoHashMap.get(k));
}
if(childHashTable.containsKey(summary)) {
test = true;
existingChild = childHashTable.get(summary);
IWorkItem workItem = existingChild.getWorkItem();
System.out.println("INFO: The task with summary \"" + summary + "\" already exists. Skipping creation.");
System.out.println("this task is work item: " + workItem.getId());
//either check the tasks in the xml for updated details and then modify the existing workitem
//or just modify the work item without checking for updates
makeChildTask(childWorkItem, existingChild, childHashTable, userInfoHashMap, workItemHashMap, rtc, false);
break;
}
}
}
}
if(!test) {
System.out.println("INFO: The task with summary " + summary + " does not currently exist. Creating.");
makeChildTask(childWorkItem, thisItem, childHashTable, userInfoHashMap, workItemHashMap, rtc, true);
} else makeFromExistingChildTask(childWorkItem, existingChild, userInfoHashMap, workItemHashMap, rtc);
}
}
You are possibly (not sure what makeChildTask() does) changing an XML structure while iterating through the children list. While not necessarily incorrect, this can mean you get entries inserted while you process the list. Since you call the subWorkItemElement.getChildNodes().getLength() each time instead of cache'ing it, this might result in the length changing in between the loop iterations.
I'm trying to implement an expandable listview with data from a remote server. I've already have the JSON part covered. My sample has three value sets returned (confirmed by checking logcat on the original JSON response). My problem now is while dividing the JSON return into header and child datas, the first value set is skipped. My code is as follows:
int lisDataHeaderCounter = 0;
String searchKey;
for (int i = 0; i < components.length(); i++) {
List<String> component_value = new ArrayList<String>();
searchKey = main_components.get(i);
if (!listDataHeader.contains(searchKey)) {
listDataHeader.add(searchKey);
Iterator<Map.Entry<String, String>> entries = sub_components.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, String> entry = entries.next();
Log.d("getValue() ", "+ " + entry.getValue());
if (searchKey == entry.getKey())
component_value.add(entry.getValue());
}
listDataChild.put(listDataHeader.get(lisDataHeaderCounter), component_value);
lisDataHeaderCounter++;
}
}
I've also tried the code below and it still has the same result.
for (Map.Entry<String, String> entry : sub_components.entrySet()) {
if (searchKey == entry.getKey())
component_value.add(entry.getValue());
}
Here is a sample of the JSON response that is being process by the above codes:
[{"activity_code":"1","activity_name":"Midterm Exam"},
{"activity_code":"1","activity_name":"Final Exam"},
{"activity_code":"2","activity_name":"Project"}]
With the current codes, in the for loop, the first value of searchKey is '1'. When I placed a Log.d(); in the while loop to check what the first value is read, I found that it is "Final Exam" and not "Midterm Exam". Is there a way for me to get the value of the first data set before it goes into the while loop?
Here is a workaround I've made to ensure that the first value would be included to the sub_components. But I guess it doesn't look neat. If anyone has a better solution, please feel free to share.
for (int i = 0; i < components.length(); i++) {
JSONObject c = components.getJSONObject(i);
String formula_code = c.getString(TAG_FORMULA_CODE);
String component_name = c.getString(TAG_ACTIVITY_NAME);
main_components.add(formula_code);
sub_components.put(formula_code, component_name);
if (!listDataHeader.contains(formula_code))
listDataHeader.add(formula_code);
if (i == 0) {
component_value.add(component_name);
}
}
for (int i = 0; i < listDataHeader.size(); i++) {
for (Map.Entry<String, String> entry : sub_components.entrySet()) {
if (listDataHeader.get(i) == entry.getKey())
component_value.add(entry.getValue());
}
listDataChild.put(listDataHeader.get(i), component_value);
component_value = new ArrayList<String>();
}
I can't see any off by one error on the two, but perhaps it's the searchKey == entry.getKey(), that might have to be searchKey.equals(entry.getKey()), but I would need to see more code to know for sure.
i have a list of url's i need to filter specific domain and subdomain. say i have some domains like
http://www.example.com
http://test.example.com
http://test2.example.com
I need to extract urls which from domain example.com.
Working on project that required me to determine if two URLs are from the same sub domain (even when there are nested domains). I worked up a modification from the guide above. This holds out pretty well thus far:
public static boolean isOneSubdomainOfTheOther(String a, String b) {
try {
URL first = new URL(a);
String firstHost = first.getHost();
firstHost = firstHost.startsWith("www.") ? firstHost.substring(4) : firstHost;
URL second = new URL(b);
String secondHost = second.getHost();
secondHost = secondHost.startsWith("www.") ? secondHost.substring(4) : secondHost;
/*
Test if one is a substring of the other
*/
if (firstHost.contains(secondHost) || secondHost.contains(firstHost)) {
String[] firstPieces = firstHost.split("\\.");
String[] secondPieces = secondHost.split("\\.");
String[] longerHost = {""};
String[] shorterHost = {""};
if (firstPieces.length >= secondPieces.length) {
longerHost = firstPieces;
shorterHost = secondPieces;
} else {
longerHost = secondPieces;
shorterHost = firstPieces;
}
//int longLength = longURL.length;
int minLength = shorterHost.length;
int i = 1;
/*
Compare from the tail of both host and work backwards
*/
while (minLength > 0) {
String tail1 = longerHost[longerHost.length - i];
String tail2 = shorterHost[shorterHost.length - i];
if (tail1.equalsIgnoreCase(tail2)) {
//move up one place to the left
minLength--;
} else {
//domains do not match
return false;
}
i++;
}
if (minLength == 0) //shorter host exhausted. Is a sub domain
return true;
}
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
return false;
}
Figure I'd leave it here for future reference of a similar problem.
I understand you are probably looking for a fancy solution using URL class or something but it is not required. Simply think of a way to extract "example.com" from each of the urls.
Note: example.com is essentially a different domain than say example.net. Thus extracting just "example" is technically the wrong thing to do.
We can divide a sample url say:
http://sub.example.com/page1.html
Step 1: Split the url with delimiter " / " to extract the part containing the domain.
Each such part may be looked at in form of the following blocks (which may be empty)
[www][subdomain][basedomain]
Step 2: Discard "www" (if present). We are left with [subdomain][basedomain]
Step 3: Split the string with delimiter " . "
Step 4: Find the total number of strings generated from the split. If there are 2 strings, both of them are the target domain (example and com). If there are >=3 strings, get the last 3 strings. If the length of last string is 3, then the last 2 strings comprise the domain (example and com). If the length of last string is 2, then the last 3 strings comprise the domain (example and co and uk)
I think this should do the trick (I do hope this wasn't a homework :D )
//You may clean this method to make it more optimum / better
private String getRootDomain(String url){
String[] domainKeys = url.split("/")[2].split("\\.");
int length = domainKeys.length;
int dummy = domainKeys[0].equals("www")?1:0;
if(length-dummy == 2)
return domainKeys[length-2] + "." + domainKeys[length-1];
else{
if(domainKeys[length-1].length == 2) {
return domainKeys[length-3] + "." + domainKeys[length-2] + "." + domainKeys[length-1];
}
else{
return domainKeys[length-2] + "." + domainKeys[length-1];
}
}
}