String.contains not returning what I'd expect. Solved.

Started by Exempt, February 27, 2015, 04:13:26

Previous topic - Next topic

Exempt

I'm working on a parser for collada and I'm just trying to do a simple check thru my list of Joints and compare that name with the animation data in collada..

This is only checking the first part of animationData.name which is Bone. The first result is Bone_012 only because it is the first element in the animationData list. How can I force the check to be the entire name?
NodeList animations = (NodeList)result;
		for(int i = 0; i < animations.getLength(); i++)
		{
			if(animations.item(i).getNodeType() == Node.ELEMENT_NODE)
			{
				Element float_array = (Element)animations.item(i);
				String name = float_array.getAttribute("id").trim();
				//System.out.println(name);
				if(name.contains("-input"))
				{
					for(int j = 0; j < animationData.size(); j++)
					{
						//System.out.println(animationData.get(j).name);
						if(name.contains(animationData.get(j).name.trim()))
						{
							System.out.println("Found: " + animationData.get(j).name);
							break;
						}
					}
					//System.out.println("-input");
				}
			}
		}


The results from this..
Found: Bone_012
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone
Found: Bone


These are all the names stored within List<AnimationData> animationData
Bone_012
Bone
Bone_003
Bone_001
Bone_002
Bone_004
Bone_006
Bone_008
Bone_010
Bone_005
Bone_007
Bone_009
Bone_011
Bone_013
Bone_015
Bone_017
Bone_019
Bone_014
Bone_016
Bone_018
Bone_020


This is a list of float_array node names which is what I store in the name variable.
Armature_Bone_012_pose_matrix-input-array
Armature_Bone_012_pose_matrix-output-array
Armature_Bone_pose_matrix-input-array
Armature_Bone_pose_matrix-output-array
Armature_Bone_003_pose_matrix-input-array
Armature_Bone_003_pose_matrix-output-array
Armature_Bone_001_pose_matrix-input-array
Armature_Bone_001_pose_matrix-output-array
Armature_Bone_002_pose_matrix-input-array
Armature_Bone_002_pose_matrix-output-array
Armature_Bone_004_pose_matrix-input-array
Armature_Bone_004_pose_matrix-output-array
Armature_Bone_006_pose_matrix-input-array
Armature_Bone_006_pose_matrix-output-array
Armature_Bone_008_pose_matrix-input-array
Armature_Bone_008_pose_matrix-output-array
Armature_Bone_010_pose_matrix-input-array
Armature_Bone_010_pose_matrix-output-array
Armature_Bone_005_pose_matrix-input-array
Armature_Bone_005_pose_matrix-output-array
Armature_Bone_007_pose_matrix-input-array
Armature_Bone_007_pose_matrix-output-array
Armature_Bone_009_pose_matrix-input-array
Armature_Bone_009_pose_matrix-output-array
Armature_Bone_011_pose_matrix-input-array
Armature_Bone_011_pose_matrix-output-array
Armature_Bone_013_pose_matrix-input-array
Armature_Bone_013_pose_matrix-output-array
Armature_Bone_015_pose_matrix-input-array
Armature_Bone_015_pose_matrix-output-array
Armature_Bone_017_pose_matrix-input-array
Armature_Bone_017_pose_matrix-output-array
Armature_Bone_019_pose_matrix-input-array
Armature_Bone_019_pose_matrix-output-array
Armature_Bone_014_pose_matrix-input-array
Armature_Bone_014_pose_matrix-output-array
Armature_Bone_016_pose_matrix-input-array
Armature_Bone_016_pose_matrix-output-array
Armature_Bone_018_pose_matrix-input-array
Armature_Bone_018_pose_matrix-output-array
Armature_Bone_020_pose_matrix-input-array
Armature_Bone_020_pose_matrix-output-array

quew8

Quote from: Exempt on February 27, 2015, 04:13:26
How can I force the check to be the entire name?

Using String.equals() with another String will return true if both Strings contain the same text.

If you were doing it with regular expressions, you can stick "\A" and "\Z" which match the start and end of the input respectively.

Exempt

.equals doesn't work because "name" will equal something like "Armature_Bone_012_pose_matrix-input-array" and "animationData.name" will be "Bone_012". Using .contains for some reason only checks the first part of animationData.name so it's returning "Bone" instead of the actual match of Bone_XXX.

quew8

Ah I see. I'm sorry, I didn't read through your post properly this morning.

Well the simplest solution would be to get rid of the joint called just Bone without the numerical suffix. But that's engineering mentality and since we're programmers I'm going to assume you can't change the problem, I would just do a check on the full name. So:

if(("Armature_" + name + "_pose_matrix-input-array").equals(animationData.get(j).name))


(Written on tablet so apologies for any typos in code but toy get the idea)

Exempt

The problem is the data in both name and animationData.name change based on the collada file I'm parsing. I'd rather not put assumptions like some names cannot be used. Any other idea how I may be able to check the full animationData.name string?

Edit: Actually I could use something similar to that since I believe _pose_matrix-... is always like that at least when collada is exported from blender. This is not ideal at all since there could be a case where value[0] and value[1] could be the same for multiple bones but it's far less likely...I wish I had a better solutions for this.

This probably isn't much help to anyone since it's pretty much made for this one task but...
private static boolean exactMatch(String source, String value)
	{
		String[] sources = source.split("_");
		String[] values = value.split("_");
		for(int i = 0; i < sources.length; i++)
		{
			if(sources[i].contains(values[0]))
			{
				if(values.length > 1)
					if(sources[i+1].contains(values[1]))
						return true;
				if(sources[i+1].contains("pose") && values.length == 1)
					return true;
			}
		}
		return false;
	}

quew8

Well in that case, the only one-size solution is to keep an "active" list of names which haven't been matched yet and then for each search, iterate through the entire active list and only match if it finds a unique match with exactly one entry in the active list.

public void uniqueContainsMatch(String[] set1, String[] set2) {
    List<String> searching = Arrays.asList(set1);
    List<String> active = Arrays.asList(set2);
    int i = -1;
    int itersSinceLastChange = 0;
    while(!searching.isEmpty()) {
        if(itersSinceLastChange > searching.size()) {
            //Then you have gone through the whole list and not matched anything.
            //Hence a unique match isn't possible.
            //If you can be bothered you'd handle this properly
            throw new RuntimeException("Unique Match Not Possible");
	    }
        if(i >= searching.size()) {
            i = 0;
        }
        int match = -1;
        for(int j = 0; j < active.size(); j++) {
            if(active.get(j).contains(searching.get(i))) {
                if(match == -1) {
                    match = j;
                } else {
                    match = -1; //Since not unique match
                    break;
                }
            }
        }
        if(match != -1) {
            itersSinceLastChange = 0;
            String s1 = searching.remove(i);
            String s2 = active.remove(match);
            //Then you've got a bingo. Handle here.
        } else {
            itersSinceLastChange++;
            i++;
        }
    }
}


Disclaimer: this code is not tested and it is the kind of sort of complicated code I normally make mistakes in but it should get the idea across. It is also not the prettiest code I've ever written but it should get the idea across. It is also not the most efficient code I've ever written but it should get the idea across.

Exempt

After sleeping on it I came to a similar solution but instead of taking in the full list I could just iterate though the "value" pattern after I split it at the underscores, at least it would check the full names. Thanks for the help, it's crazy how a seemingly simple issue can become such a pain. lol ;D

Edit:
Ok, this is what I ended up with.

private static boolean exactMatch(String source, String value, String splitter)
	{
		String[] sources = source.split(splitter);
		String[] values = value.split(splitter);
		for(int i = 0; i < sources.length; i++)
		{
			if(sources[i].equals(values[0]))
			{
				if(sources[i+1].equals("pose") && values.length == 1)
					return true;
				else if(values.length > 1)
				{
					for(int j = 1; j < values.length; j++)
					{
						if(!sources[i+j].equals(values[j]))
							return false;
					}
					return true;
				}
			}
		}
		return false;
	}