2828import java .util .Collections ;
2929import java .util .Iterator ;
3030import java .util .List ;
31+ import java .util .ListIterator ;
3132import java .util .Spliterator ;
3233import java .util .Spliterators ;
3334import java .util .function .Consumer ;
4041
4142@ Debug .Renderer (text = "\" ListBinaryTag[type=\" + this.type.toString() + \" ]\" " , childrenArray = "this.tags.toArray()" , hasChildren = "!this.tags.isEmpty()" )
4243final class ListBinaryTagImpl extends AbstractBinaryTag implements ListBinaryTag {
43- static final ListBinaryTag EMPTY = new ListBinaryTagImpl (BinaryTagTypes .END , Collections .emptyList ());
44+ static final ListBinaryTag EMPTY = new ListBinaryTagImpl (BinaryTagTypes .END , false , Collections .emptyList ());
4445 private final List <BinaryTag > tags ;
46+ private final boolean permitsHeterogeneity ;
4547 private final BinaryTagType <? extends BinaryTag > elementType ;
4648 private final int hashCode ;
4749
48- ListBinaryTagImpl (final BinaryTagType <? extends BinaryTag > elementType , final List <BinaryTag > tags ) {
50+ ListBinaryTagImpl (final BinaryTagType <? extends BinaryTag > elementType , final boolean permitsHeterogeneity , final List <BinaryTag > tags ) {
4951 this .tags = Collections .unmodifiableList (tags );
52+ this .permitsHeterogeneity = permitsHeterogeneity ;
5053 this .elementType = elementType ;
5154 this .hashCode = tags .hashCode ();
5255 }
@@ -73,12 +76,13 @@ public boolean isEmpty() {
7376
7477 @ Override
7578 public @ NotNull ListBinaryTag set (final int index , final @ NotNull BinaryTag newTag , final @ Nullable Consumer <? super BinaryTag > removed ) {
79+ final BinaryTagType <?> targetType = ListBinaryTagImpl .validateTagType (newTag , this .elementType , this .permitsHeterogeneity );
7680 return this .edit (tags -> {
7781 final BinaryTag oldTag = tags .set (index , newTag );
7882 if (removed != null ) {
7983 removed .accept (oldTag );
8084 }
81- }, newTag . type () );
85+ }, targetType );
8286 }
8387
8488 @ Override
@@ -93,19 +97,16 @@ public boolean isEmpty() {
9397
9498 @ Override
9599 public @ NotNull ListBinaryTag add (final BinaryTag tag ) {
96- noAddEnd (tag );
97- if (this .elementType != BinaryTagTypes .END ) {
98- mustBeSameType (tag , this .elementType );
99- }
100- return this .edit (tags -> tags .add (tag ), tag .type ());
100+ final BinaryTagType <?> targetType = validateTagType (tag , this .elementType , this .permitsHeterogeneity );
101+ return this .edit (tags -> tags .add (tag ), targetType );
101102 }
102103
103104 @ Override
104105 public @ NotNull ListBinaryTag add (final Iterable <? extends BinaryTag > tagsToAdd ) {
105106 if (tagsToAdd instanceof Collection <?> && ((Collection <?>) tagsToAdd ).isEmpty ()) {
106107 return this ;
107108 }
108- final BinaryTagType <?> type = ListBinaryTagImpl .mustBeSameType (tagsToAdd );
109+ final BinaryTagType <?> type = ListBinaryTagImpl .validateTagType (tagsToAdd , this . permitsHeterogeneity );
109110 return this .edit (tags -> {
110111 for (final BinaryTag tag : tagsToAdd ) {
111112 tags .add (tag );
@@ -120,35 +121,46 @@ static void noAddEnd(final BinaryTag tag) {
120121 }
121122 }
122123
123- // Cannot have different element types in a list tag
124- static BinaryTagType <?> mustBeSameType (final Iterable <? extends BinaryTag > tags ) {
124+ // Cannot have different element types in a list tag unless we're in heterogeneous mode
125+ static BinaryTagType <?> validateTagType (final Iterable <? extends BinaryTag > tags , final boolean permitHeterogeneity ) {
125126 BinaryTagType <?> type = null ;
126127 for (final BinaryTag tag : tags ) {
127128 if (type == null ) {
129+ noAddEnd (tag );
128130 type = tag .type ();
129131 } else {
130- mustBeSameType (tag , type );
132+ validateTagType (tag , type , permitHeterogeneity );
133+ if (type != tag .type ()) {
134+ type = BinaryTagTypes .LIST_WILDCARD ;
135+ }
131136 }
132137 }
133138 return type ;
134139 }
135140
136- // Cannot have different element types in a list tag
137- static void mustBeSameType (final BinaryTag tag , final BinaryTagType <? extends BinaryTag > type ) {
138- if (tag .type () != type ) {
141+ // An end tag cannot be an element in a list tag
142+ // AND Cannot have a different element type in a list tag unless we're in heterogeneous mode
143+ static BinaryTagType <?> validateTagType (final BinaryTag tag , final BinaryTagType <? extends BinaryTag > type , final boolean permitHeterogenity ) {
144+ noAddEnd (tag );
145+ if (type == BinaryTagTypes .END ) {
146+ return tag .type ();
147+ }
148+
149+ if (tag .type () != type && !permitHeterogenity ) {
139150 throw new IllegalArgumentException (String .format ("Trying to add tag of type %s to list of %s" , tag .type (), type ));
140151 }
152+ return tag .type () != type ? BinaryTagTypes .LIST_WILDCARD : type ;
141153 }
142154
143155 private ListBinaryTag edit (final Consumer <List <BinaryTag >> consumer , final @ Nullable BinaryTagType <? extends BinaryTag > maybeElementType ) {
144156 final List <BinaryTag > tags = new ArrayList <>(this .tags );
145157 consumer .accept (tags );
146158 BinaryTagType <? extends BinaryTag > elementType = this .elementType ;
147159 // set the type if it has not yet been set
148- if (maybeElementType != null && elementType == BinaryTagTypes . END ) {
160+ if (maybeElementType != null ) {
149161 elementType = maybeElementType ;
150162 }
151- return new ListBinaryTagImpl (elementType , new ArrayList <>(tags )); // explicitly copy
163+ return new ListBinaryTagImpl (elementType , this . permitsHeterogeneity , new ArrayList <>(tags )); // explicitly copy
152164 }
153165
154166 @ Override
@@ -157,7 +169,53 @@ private ListBinaryTag edit(final Consumer<List<BinaryTag>> consumer, final @Null
157169 }
158170
159171 @ Override
160- public Iterator <BinaryTag > iterator () {
172+ public @ NotNull ListBinaryTag unwrapHeterogeneity () {
173+ // Unlock where it makes sense
174+ if (!this .permitsHeterogeneity ) {
175+ if (this .elementType != BinaryTagTypes .COMPOUND ) {
176+ return new ListBinaryTagImpl (this .elementType , true , this .tags );
177+ } else {
178+ List <BinaryTag > newTags = null ;
179+ BinaryTag current ;
180+ for (final ListIterator <BinaryTag > it = this .tags .listIterator (); it .hasNext ();) {
181+ current = it .next ();
182+ final BinaryTag unboxed = ListBinaryTag0 .unbox ((CompoundBinaryTag ) current );
183+ // only initialize newTags if we need to unbox something
184+ if (unboxed != current && newTags == null ) {
185+ newTags = new ArrayList <>(this .tags .size ());
186+ for (int idx = it .nextIndex () - 1 , ptr = 0 ; ptr < idx ; ptr ++) {
187+ newTags .add (this .tags .get (ptr ));
188+ }
189+ }
190+ // if we're already initialized, unconditionally add
191+ if (newTags != null ) {
192+ newTags .add (unboxed );
193+ }
194+ }
195+ return new ListBinaryTagImpl (newTags == null ? BinaryTagTypes .COMPOUND : BinaryTagTypes .LIST_WILDCARD , true , newTags == null ? this .tags : newTags );
196+ }
197+ }
198+
199+ // heterogeneity-permitted nothing to unbox
200+ return this ;
201+ }
202+
203+ @ Override
204+ public @ NotNull ListBinaryTag wrapHeterogeneity () {
205+ if (this .elementType != BinaryTagTypes .LIST_WILDCARD ) {
206+ return this ;
207+ }
208+
209+ final List <BinaryTag > newTags = new ArrayList <>(this .tags .size ());
210+ for (final BinaryTag tag : this .tags ) {
211+ newTags .add (ListBinaryTag0 .box (tag ));
212+ }
213+
214+ return new ListBinaryTagImpl (BinaryTagTypes .COMPOUND , false , newTags );
215+ }
216+
217+ @ Override
218+ public @ NotNull Iterator <BinaryTag > iterator () {
161219 final Iterator <BinaryTag > iterator = this .tags .iterator ();
162220 return new Iterator <BinaryTag >() {
163221 @ Override
@@ -205,3 +263,27 @@ public int hashCode() {
205263 );
206264 }
207265}
266+
267+ final class ListBinaryTag0 {
268+ private static final String WRAPPER_KEY = "" ;
269+
270+ private ListBinaryTag0 () {
271+ }
272+
273+ static BinaryTag unbox (final CompoundBinaryTag compound ) {
274+ if (compound .size () == 1 ) {
275+ final BinaryTag potentialValue = compound .get (WRAPPER_KEY );
276+ if (potentialValue != null ) return potentialValue ;
277+ }
278+
279+ return compound ;
280+ }
281+
282+ static CompoundBinaryTag box (final BinaryTag tag ) {
283+ if (tag instanceof CompoundBinaryTag ) {
284+ return (CompoundBinaryTag ) tag ;
285+ } else {
286+ return new CompoundBinaryTagImpl (Collections .singletonMap (WRAPPER_KEY , tag ));
287+ }
288+ }
289+ }
0 commit comments