I've been searching for hours and I can't seem to find my error, in fact it doesn't make any sense at all.
I have a android app which has a class that is responsible for parsing an XML document using the XmlPullParser. The result is a list of school days that holds some information about the day and also a list of lessons for each day. I am able to extract all those information correctly but for some reason I end up having the same list of lessons for every day in the end (the other 'header' information of that element remain correct though).
Complete method can be found here(too long to paste it here): http://pastebin.com/AwxqwxQb
I just post the outline here:
List<SubstitutionDay> parseReturnSubstitution() {
SubstitutionDay currentSubstitutionDay = new SubstitutionDay();
List<SubstitutionDay> results = new ArrayList<>();
Substitution currentSubstitution = new Substitution();
List<Substitution> currentSubstitutionList = new ArrayList<>();
int multipleClasses = 0, multiplePeriods = 0;
String text = "";
try {
// all the XmlPullParser set up
int eventType = xmlPullParser.getEventType();
while (eventType != END_DOCUMENT) {
String tag;
String[] tempStringArray;
tag = xmlPullParser.getName();
switch (eventType) {
case TEXT:
text = xmlPullParser.getText();
break;
case START_TAG:
switch (tag) {
case "kopf":
// reset the school day
currentSubstitutionDay = new SubstitutionDay();
break;
case "haupt":
// empty the list before new elements are added
currentSubstitutionList.clear();
break;
case "aktion":
// reset values for each new substitution
currentSubstitution = new Substitution();
multipleClasses = 0;
multiplePeriods = 0;
break;
}
break;
case END_TAG:
switch (tag) {
// iterate over xml elements that contain header information about the day
// iterate over the individual lessons
case "klasse":
break;
case "stunde":
break;
case "lehrer":
break;
case "raum":
break;
case "info":
break;
case "aktion":
break;
case "haupt":
// add the list of lessons to the day
// the number of lessons is correct here
currentSubstitutionDay.setSubstitutionList(currentSubstitutionList);
// add the whole day to the result set
// number of lessons is still correct
results.add(currentSubstitutionDay);
break;
}
break;
}
eventType = xmlPullParser.next();
}
} // catch statements follow here
// when I check for the size of results.get(i).getSubstitutionList().size() the size is the same for all elements
return results;
}
I'm really helpless right here as this shouldn't happening at all. Does anyone have an explanation for that? Any help is appreciated. If there's further information needed, just let me know!
Edit: The XML that is parsed can be found here: http://vplankl.gymnasium-beetzendorf.de/Vertretungsplan_Klassen.xml
The results I'm expecting are basically the number of rows from each table. However if one row is for let's say two periods (column name: Stunde) (4-5 for example) the result is increased by one because I store each Substitution/Lesson in one element of the list.
The lesson count for the last day is 5 - which is the number that is copied to all the other days.
The problem is that you only create one List<Substitution> object and keep clearing it out. Instead you should create a brand new list for each day.
Additionally, you should make a defensive copy when you add the list to a SubstitutionDay.
Related
Using the following Code:
int index3 = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
But it only gets one number and i also don't know which type of number it is. I want to get phone of type mobile , work , home etc. What would be the code for that ?
Figured it out you can simply use a switch statement on the TYPE and get different numbers and their type
CODE:
while (phoneCursor.moveToNext()) {
int index3 = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int type = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
int type1 = phoneCursor.getInt(type);
switch (type1) {
case ContactsContract.CommonDataKinds.Phone.TYPE_WORK:
numberWork = phoneCursor.getString(index3);
break;
case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE:
numberMobile = phoneCursor.getString(index3);
break;
case ContactsContract.CommonDataKinds.Phone.TYPE_HOME:
numberHome = phoneCursor.getString(index3);
break;
}
}
When I'm comparing an EditText's value with a String var in java, it is going to else part.
case 2 is not executing, every time default case is executing
sem=(EditText)findViewById(R.id.csem);
sem1=sem.getText().toString();
switch (sem1) {
case "2":
c = 1;
i.putExtra("fees", c);
startActivity(i);
break;
default:
Toast.makeText(details.this, "Enter valid current sem", Toast.LENGTH_SHORT).show();
break;
Since it is a String, you should compare it using equals method
// Gets the value of the EditText
String value = ((EditText) findViewById(R.id.csem)).getText().toString();
if(value.equals("2")) {
// Do your things
}
switch statement on String objects is a new feature introduced in Java 1.7. Unfortunatelly Android requires version 1.6 or 1.5. This is why you have to forget for some time about such constructions.
You can avoid using if-statements-chain by storing the map of methods which will be executed for certain String: Map. You can always encapsulate Method it with some Handler object. Look here for more info: How to remove large if-else-if chain
and
why-cant-i-switch-on-a-string
I can't help noticing that I'm using quite a lot of string comparisons while parsing a well defined XML file in Android (with a XmlPullParser).
As of now it typically looks something like this (somewhat simplified):
...
tag = parser.getName().toLowerCase();
if ("tag1".equals(tag)) {
// Do something with the state machine
}
else if ("tag2".equals(tag)) {
// Do something else with the state machine
}
...
else if ("tag23".equals(tag)) {
// Do something more with the state machine
}
What I would like to have instead is something like this (where StringMatcher would be the hypothetical happy-maker for me):
private static final StringMatcher tagMatcher = new StringMatcher(StringMatcher.NO_MATCH);
static {
tagMatcher.addString("tag1", 1);
tagMatcher.addString("tag2", 2);
....
tagMatcher.addString("tag23", 23);
}
...
tag = parser.getName().toLowerCase();
switch (tagMatcher.match(tag)) {
case 1:
// Do something with the state machine
break;
case 2:
// Do something else with the state machine
break;
...
case 23:
// Do something more with the state machine
break;
default:
Log.e("PARSER", "Unexpected tag: " + tag);
break;
}
As you see I would like a UriMatcher pattern applied to my XML file tags. Do any of you know of such a class I can use in Android? Any other fast filtering on strings would do as well (it would be neat, though, if the UriMatcher-pattern could be reused).
So far I've been looking at regular expressions but I'm not really sure I can fit it to my needs (I would like a switch - case style test) and, of course, the regular string comparison as shown in above example.
Cheers,
-- dbm
You can either use a HashMap since that does not need to iterate over the whole array to find the match value
private static final HashMap<String, Integer> tagMatcher =
new HashMap<String, Integer>();
static {
tagMatcher.put("tag1", 1);
tagMatcher.put("tag2", 2);
tagMatcher.put("tag23", 23);
}
private void parse (String node) {
Integer value = tagMatcher.get(node);
int match = value != null ? value.intValue() : 0;
switch (match) {
case 1:
// etc
break;
case 0: // no match
break;
}
}
or you can use a SparseIntArray using the same hash approach. Advantage here is that you don't need to box int into Integer which should result in a slight speed / memory advantage.
private static final SparseIntArray tagMatcher2 = new SparseIntArray();
private static void put(String key, int value) {
tagMatcher2.put(key.hashCode(), value);
}
private static int get(String key) {
return tagMatcher2.get(key.hashCode());
}
static {
put("tag1", 1);
put("tag2", 2);
put("tag23", 23);
}
private void parse2 (String node) {
switch (get(node)) {
case 1:
// etc
break;
case 0: // no match
break;
}
}
This is doing binary search instead of iterating over the whole thing like SparseArray#indexOfValue(t) does. Note that there is a chance of hash collisions in this approach.
I think using an approach like that is faster than a long chain of if (equals) else if (equals) for larger amounts of comparisons. The if .. else if approach needs to check String.equals() every time which boils down to comparing all characters of the strings while a hash based approach needs to calculate the hash value just once and can do a binary search over all known hash values then.
Use a SparseArray
static{
tagmatcher.append(0, "tag1");
tagmatcher.append(1, "tag2");
}
switch(tagmatcher.keyAt(tagmatcher.indexOfValue(tag))){
case 0:
break;
case 1:
break
}
But if you are going to add consecutive indexes you can always use an ArrayList
I'm still pretty new to this, but I'm trying to make a calculator for myself to use at work; similar to a resistor calculator.
I am hope to change a textview and imageview according to the last two digits entered into a edittext box. For example, if I were to type in "9,000,011", I would want to display a certain color of image and text that corresponds with "11" and the same color and text for say 1,000,011. Also different for 12, 13, and so on. this way No matter what number I type it only looks at the last two digits. Does anyone know the way to do this or maybe can just point me in the right direction?
here is how I'm
private void calculate() {
number = Double.parseDouble(inputnumber.getText().toString());
ImageView iv = (ImageView) this.findViewById(R.id.pairimage);
if (number == 6000001) {
iv.setImageResource(R.drawable.white);
txtnumber.setText("White");
} else if (number == 6000002) {
iv.setImageResource(R.drawable.red);
txtnumber.setText("Red");
}
//*and so on, all the way up to 99*
}
If you want just the last two, then I would use
String wholeNumber = inputnumber.getText().toString();
int n = Integer.valueOf(wholeNumber.subString(wholeNumber.length()-2, wholeNumber.length()-1);
and then a switch block:
switch(n) {
case 1:
iv.setImageResource(R.drawable.white);
txtnumber.setText("White");
break;
case 2:
iv.setImageResource(R.drawable.red);
txtnumber.setText("Red");
break;
}
etc.
Maybe convert the number to a String and then look at that e.g.
String theNumber = String.valueOf(number);
String lastTwoDigits =
theNumber.substring(theNumber.length() -2, theNumber.length());
Then you can have your if statements to read the lastTwoDigits. Or convert back to int (Integer.valueOf(lastTwoDigits); and use a switch statement. Or put the values 0-99 into a Map and have a Command object or something as the value which gets executed.
Obviously you'll need some validation here on the user input.
You have a couple of options. The easiest one is to use the modulus operator like:
number = number % 100;
switch (number) {
case 1:
// do stuff;
break;
case 2:
// do stuff;
break;
}
For this to work, though, "number" will have to be an integer. It's an integer in your examples, so that may work for you; otherwise you can try some math tricks like:
int number2 = (int)(number * 100) % 100;
Go with the sub-string approach. I've found that some number combinations don't multiply/divide well in java.
How can I display back to the user a basic popup with a saying based on a random number generated. I wanted to use a switch statement, but that just displays all the sayings, ie:
int random = (int) Math.ceil(Math.random() * 5);
switch(random){
case 1:
showToast(this, "Saying 1.");
case 2:
showToast(this, "Saying 2.");
}
etc....
Like I said, this displays all 5 case statements, is there a better way to random generate and display based on the number, or am I doing it all wrong?
Thanks!
The case statements inside a switch "fall through" if you don't break out of them.
It should be like this:
switch(random) {
case 1:
statement;
break;
case 2:
statement;
break;
...
}
The break statement jumps to the next line after the switch statement.
You can also try some thing like
String[] sayings = {"Saying 1.", "Saying 2.", "Saying 3.", "Saying 4.", "Saying 5."};
int random = (int) Math.ceil(Math.random() * 5);
showToast(this, sayings[random]);
and if you have more items then you can prepare the string array dynamically before use.
If there are many sayings... you can also put a .txt file in your assets folder with numerous sayings (one per line), read it and display the saying from a randomly generated line number..
Activity.getAssets().open("sayingsfile.txt");