1
1
package org .jsoup .nodes ;
2
2
3
- import org .jsoup .helper .ChangeNotifyingArrayList ;
4
3
import org .jsoup .helper .Validate ;
5
4
import org .jsoup .internal .Normalizer ;
6
5
import org .jsoup .internal .StringUtil ;
37
36
import java .util .stream .Stream ;
38
37
39
38
import static org .jsoup .internal .Normalizer .normalize ;
40
- import static org .jsoup .nodes .Document .OutputSettings .Syntax .html ;
41
39
import static org .jsoup .nodes .Document .OutputSettings .Syntax .xml ;
42
40
import static org .jsoup .nodes .TextNode .lastCharIsWhitespace ;
43
41
import static org .jsoup .parser .Parser .NamespaceHtml ;
@@ -50,11 +48,11 @@ An HTML Element consists of a tag name, attributes, and child nodes (including t
50
48
*/
51
49
public class Element extends Node implements Iterable <Element > {
52
50
private static final List <Element > EmptyChildren = Collections .emptyList ();
51
+ private static final NodeList EmptyNodeList = new NodeList (0 );
53
52
private static final Pattern ClassSplit = Pattern .compile ("\\ s+" );
54
53
private static final String BaseUriKey = Attributes .internalKey ("baseUri" );
55
54
Tag tag ;
56
- private @ Nullable WeakReference <List <Element >> shadowChildrenRef ; // points to child elements shadowed from node children
57
- List <Node > childNodes ;
55
+ NodeList childNodes ;
58
56
@ Nullable Attributes attributes ; // field is nullable but all methods for attributes are non-null
59
57
60
58
/**
@@ -86,7 +84,7 @@ public Element(String tag) {
86
84
*/
87
85
public Element (Tag tag , @ Nullable String baseUri , @ Nullable Attributes attributes ) {
88
86
Validate .notNull (tag );
89
- childNodes = EmptyNodes ;
87
+ childNodes = EmptyNodeList ;
90
88
this .attributes = attributes ;
91
89
this .tag = tag ;
92
90
if (baseUri != null )
@@ -108,12 +106,12 @@ public Element(Tag tag, @Nullable String baseUri) {
108
106
Internal test to check if a nodelist object has been created.
109
107
*/
110
108
protected boolean hasChildNodes () {
111
- return childNodes != EmptyNodes ;
109
+ return childNodes != EmptyNodeList ;
112
110
}
113
111
114
112
@ Override protected List <Node > ensureChildNodes () {
115
- if (childNodes == EmptyNodes ) {
116
- childNodes = new NodeList (this , 4 );
113
+ if (childNodes == EmptyNodeList ) {
114
+ childNodes = new NodeList (4 );
117
115
}
118
116
return childNodes ;
119
117
}
@@ -393,31 +391,40 @@ public Elements children() {
393
391
* @return a list of child elements
394
392
*/
395
393
List <Element > childElementsList () {
396
- if (childNodeSize () == 0 )
397
- return EmptyChildren ; // short circuit creating empty
398
-
399
- List <Element > children ;
400
- if (shadowChildrenRef == null || (children = shadowChildrenRef .get ()) == null ) {
401
- final int size = childNodes .size ();
402
- children = new ArrayList <>(size );
403
- //noinspection ForLoopReplaceableByForEach (beacause it allocates an Iterator which is wasteful here)
404
- for (int i = 0 ; i < size ; i ++) {
405
- final Node node = childNodes .get (i );
406
- if (node instanceof Element )
407
- children .add ((Element ) node );
408
- }
409
- shadowChildrenRef = new WeakReference <>(children );
394
+ if (childNodeSize () == 0 ) return EmptyChildren ; // short circuit creating empty
395
+ List <Element > children = cachedChildren ();
396
+ if (children == null ) {
397
+ children = filterNodes (Element .class );
398
+ stashChildren (children );
410
399
}
411
400
return children ;
412
401
}
413
402
414
- /**
415
- * Clears the cached shadow child elements.
416
- */
417
- @ Override
418
- void nodelistChanged () {
419
- super .nodelistChanged ();
420
- shadowChildrenRef = null ;
403
+ private static final String childElsKey = "jsoup.childEls" ;
404
+ private static final String childElsMod = "jsoup.childElsMod" ;
405
+
406
+ /** returns the cached child els, if they exist, and the modcount of our childnodes matches the stashed modcount */
407
+ private @ Nullable List <Element > cachedChildren () {
408
+ Map <String , Object > userData = attributes ().userData ();
409
+ //noinspection unchecked
410
+ WeakReference <List <Element >> ref = (WeakReference <List <Element >>) userData .get (childElsKey );
411
+ if (ref != null ) {
412
+ List <Element > els = ref .get ();
413
+ if (els != null ) {
414
+ Integer modCount = (Integer ) userData .get (childElsMod );
415
+ if (modCount != null && modCount == childNodes .modCount ())
416
+ return els ;
417
+ }
418
+ }
419
+ return null ;
420
+ }
421
+
422
+ /** caches the child els into the Attribute user data. */
423
+ private void stashChildren (List <Element > els ) {
424
+ Map <String , Object > userData = attributes ().userData ();
425
+ WeakReference <List <Element >> ref = new WeakReference <>(els );
426
+ userData .put (childElsKey , ref );
427
+ userData .put (childElsMod , childNodes .modCount ());
421
428
}
422
429
423
430
/**
@@ -1898,7 +1905,7 @@ public Element shallowClone() {
1898
1905
protected Element doClone (@ Nullable Node parent ) {
1899
1906
Element clone = (Element ) super .doClone (parent );
1900
1907
clone .attributes = attributes != null ? attributes .clone () : null ;
1901
- clone .childNodes = new NodeList (clone , childNodes .size ());
1908
+ clone .childNodes = new NodeList (childNodes .size ());
1902
1909
clone .childNodes .addAll (childNodes ); // the children then get iterated and cloned in Node.clone
1903
1910
1904
1911
return clone ;
@@ -1961,16 +1968,13 @@ public Element filter(NodeFilter nodeFilter) {
1961
1968
return (Element ) super .filter (nodeFilter );
1962
1969
}
1963
1970
1964
- private static final class NodeList extends ChangeNotifyingArrayList <Node > {
1965
- private final Element owner ;
1966
-
1967
- NodeList (Element owner , int initialCapacity ) {
1968
- super (initialCapacity );
1969
- this .owner = owner ;
1971
+ static final class NodeList extends ArrayList <Node > {
1972
+ public NodeList (int size ) {
1973
+ super (size );
1970
1974
}
1971
1975
1972
- @ Override public void onContentsChanged () {
1973
- owner . nodelistChanged () ;
1976
+ int modCount () {
1977
+ return this . modCount ;
1974
1978
}
1975
1979
}
1976
1980
}
0 commit comments