Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/main/java/org/htmlunit/html/HtmlPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ public class HtmlPage extends SgmlPage {

private Map<String, MappedElementIndexEntry> idMap_ = new ConcurrentHashMap<>();
private Map<String, MappedElementIndexEntry> nameMap_ = new ConcurrentHashMap<>();
// The id/name lookup index is built lazily on first use. Until then,
// notifyNodeAdded / fireAttributeChange skip the per-element index updates.
// Reads must call ensureMappedElementsBuilt() before consulting idMap_/nameMap_.
private boolean mappedElementsBuilt_;

private List<BaseFrameElement> frameElements_ = new ArrayList<>();
private int parserCount_;
Expand Down Expand Up @@ -631,6 +635,7 @@ public ProcessingInstruction createProcessingInstruction(final String namespaceU
@Override
public DomElement getElementById(final String elementId) {
if (elementId != null) {
ensureMappedElementsBuilt();
final MappedElementIndexEntry elements = idMap_.get(elementId);
if (elements != null) {
return elements.first();
Expand Down Expand Up @@ -1708,6 +1713,7 @@ public <E extends HtmlElement> E getHtmlElementById(final String elementId) thro
*/
public List<DomElement> getElementsById(final String elementId) {
if (elementId != null) {
ensureMappedElementsBuilt();
final MappedElementIndexEntry elements = idMap_.get(elementId);
if (elements != null) {
return new ArrayList<>(elements.elements());
Expand All @@ -1728,6 +1734,7 @@ public List<DomElement> getElementsById(final String elementId) {
@SuppressWarnings("unchecked")
public <E extends DomElement> E getElementByName(final String name) throws ElementNotFoundException {
if (name != null) {
ensureMappedElementsBuilt();
final MappedElementIndexEntry elements = nameMap_.get(name);
if (elements != null) {
return (E) elements.first();
Expand All @@ -1746,6 +1753,7 @@ public <E extends DomElement> E getElementByName(final String name) throws Eleme
*/
public List<DomElement> getElementsByName(final String name) {
if (name != null) {
ensureMappedElementsBuilt();
final MappedElementIndexEntry elements = nameMap_.get(name);
if (elements != null) {
return new ArrayList<>(elements.elements());
Expand All @@ -1765,6 +1773,7 @@ public List<DomElement> getElementsByIdAndOrName(final String idAndOrName) {
if (idAndOrName == null) {
return Collections.emptyList();
}
ensureMappedElementsBuilt();
final MappedElementIndexEntry list1 = idMap_.get(idAndOrName);
final MappedElementIndexEntry list2 = nameMap_.get(idAndOrName);
final List<DomElement> list = new ArrayList<>();
Expand Down Expand Up @@ -1841,12 +1850,32 @@ void notifyNodeRemoved(final DomNode node) {
* @param recurse indicates if children must be added too
*/
void addMappedElement(final DomElement element, final boolean recurse) {
// Index is built lazily; skip while not built. ensureMappedElementsBuilt()
// walks the tree once and populates everything on first read.
if (!mappedElementsBuilt_) {
return;
}
if (isAncestorOf(element)) {
addElement(idMap_, element, DomElement.ID_ATTRIBUTE, recurse);
addElement(nameMap_, element, DomElement.NAME_ATTRIBUTE, recurse);
}
}

private void ensureMappedElementsBuilt() {
if (mappedElementsBuilt_) {
return;
}
final DomElement root = getDocumentElement();
if (root != null) {
addElement(idMap_, root, DomElement.ID_ATTRIBUTE, true);
addElement(nameMap_, root, DomElement.NAME_ATTRIBUTE, true);
}
// Flip the flag only after the maps are populated, so a partial
// failure mid-walk leaves us with built_=false and the next read
// tries again rather than seeing a half-populated index.
mappedElementsBuilt_ = true;
}

private void addElement(final Map<String, MappedElementIndexEntry> map, final DomElement element,
final String attribute, final boolean recurse) {
final String value = element.getAttribute(attribute);
Expand Down Expand Up @@ -1882,6 +1911,10 @@ private void addElement(final Map<String, MappedElementIndexEntry> map, final Do
* @param descendant indicates of the element was descendant of this HtmlPage, but now its parent might be null
*/
void removeMappedElement(final DomElement element, final boolean recurse, final boolean descendant) {
// see addMappedElement: while the index is unbuilt, removals are also no-ops.
if (!mappedElementsBuilt_) {
return;
}
if (descendant || isAncestorOf(element)) {
removeElement(idMap_, element, DomElement.ID_ATTRIBUTE, recurse);
removeElement(nameMap_, element, DomElement.NAME_ATTRIBUTE, recurse);
Expand Down Expand Up @@ -1998,6 +2031,7 @@ protected HtmlPage clone() {

result.idMap_ = new ConcurrentHashMap<>();
result.nameMap_ = new ConcurrentHashMap<>();
result.mappedElementsBuilt_ = false;

return result;
}
Expand Down
Loading