-
-
Notifications
You must be signed in to change notification settings - Fork 180
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5048 from evolvedbinary/5.x.x/hotfix/nodepath-all…
…ocation-strategy [5.x.x] Fix a bug in Node Path equality
- Loading branch information
Showing
6 changed files
with
364 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,12 +33,22 @@ | |
|
||
|
||
/** | ||
* Represents a Node Path. | ||
* | ||
* Internally the node path is held as an array of {@link QName} components. | ||
* Upon construction the array of `components` is sized such that it will be exactly | ||
* what is required to represent the Node Path. | ||
* Mutation operations however will over-allocate the array as an optmisation, so that | ||
* we need not allocate/free memory on every mutation operation, but only | ||
* every {@link #DEFAULT_NODE_PATH_SIZE} operations. | ||
* | ||
* @author <a href="mailto:[email protected]">Adam Retter</a> | ||
* @author wolf | ||
* @author Adam Retter | ||
*/ | ||
public class NodePath implements Comparable<NodePath> { | ||
|
||
private static final int DEFAULT_NODE_PATH_SIZE = 5; | ||
static final int DEFAULT_NODE_PATH_SIZE = 4; | ||
static final int MAX_OVER_ALLOCATION_FACTOR = 2; | ||
|
||
private static final Logger LOG = LogManager.getLogger(NodePath.class); | ||
|
||
|
@@ -89,21 +99,33 @@ public boolean includeDescendants() { | |
} | ||
|
||
public void append(final NodePath other) { | ||
// expand the array | ||
final int newLength = pos + other.length(); | ||
this.components = Arrays.copyOf(components, newLength); | ||
System.arraycopy(other.components, 0, components, pos, other.length()); | ||
this.pos = newLength; | ||
// do we have enough space to accommodate the components from `other` | ||
final int numOtherComponentsToAppend = other.length(); | ||
allocateIfNeeded(numOtherComponentsToAppend); | ||
|
||
// at this point we have enough space, append the components from `other` | ||
System.arraycopy(other.components, 0, components, pos, numOtherComponentsToAppend); | ||
this.pos += numOtherComponentsToAppend; | ||
} | ||
|
||
public void addComponent(final QName component) { | ||
if (pos == components.length) { | ||
// extend the array | ||
this.components = Arrays.copyOf(components, pos + 1); | ||
} | ||
// do we have enough space to add the component | ||
allocateIfNeeded(1); | ||
|
||
// at this point we have enough space, add the component | ||
components[pos++] = component; | ||
} | ||
|
||
private void allocateIfNeeded(final int numOtherComponentsToAppend) { | ||
final int available = components.length - pos; | ||
if (available < numOtherComponentsToAppend) { | ||
// we need more space, allocate a multiple of DEFAULT_NODE_PATH_SIZE | ||
final int allocationFactor = (int) Math.ceil((numOtherComponentsToAppend - available + components.length) / ((float) DEFAULT_NODE_PATH_SIZE)); | ||
final int newSize = allocationFactor * DEFAULT_NODE_PATH_SIZE; | ||
this.components = Arrays.copyOf(components, newSize); | ||
} | ||
} | ||
|
||
/** | ||
* Remove the Last Component from the NodePath. | ||
* | ||
|
@@ -117,8 +139,8 @@ public void removeLastComponent() { | |
} | ||
|
||
public void reset() { | ||
// when resetting if this object has twice the capacity of a new object, then set it back to the default capacity | ||
if (pos > DEFAULT_NODE_PATH_SIZE * 2) { | ||
// when resetting if this object has more than twice the capacity of a new object, then set it back to the default capacity | ||
if (pos > DEFAULT_NODE_PATH_SIZE * MAX_OVER_ALLOCATION_FACTOR) { | ||
components = new QName[DEFAULT_NODE_PATH_SIZE]; | ||
} else { | ||
Arrays.fill(components, null); | ||
|
@@ -130,6 +152,18 @@ public int length() { | |
return pos; | ||
} | ||
|
||
/** | ||
* Return the size of the components array. | ||
* | ||
* This function is intentionally marked as package-private | ||
* so that it may only be called for testing purposes! | ||
* | ||
* @return the size of the components array. | ||
*/ | ||
int componentsSize() { | ||
return components.length; | ||
} | ||
|
||
protected void reverseComponents() { | ||
for (int i = 0; i < pos / 2; ++i) { | ||
QName tmp = components[i]; | ||
|
@@ -290,10 +324,24 @@ private void init(@Nullable final Map<String, String> namespaces, final String p | |
|
||
@Override | ||
public boolean equals(final Object obj) { | ||
if (obj != null && obj instanceof NodePath) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
|
||
if (obj instanceof NodePath) { | ||
final NodePath otherNodePath = (NodePath) obj; | ||
return Arrays.equals(components, otherNodePath.components); | ||
|
||
// NOTE(AR) we cannot use Array.equals on the components of the NodePaths as they may be over-allocated! | ||
if (pos == otherNodePath.pos) { | ||
for (int i = 0; i < pos; i++) { | ||
if (!components[i].equals(otherNodePath.components[i])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.