You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2022/08/12 21:35:15 UTC
[juneau] branch jbFixRestNpe updated: PartList should be an ArrayList.
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch jbFixRestNpe
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/jbFixRestNpe by this push:
new c00f8fe89 PartList should be an ArrayList.
c00f8fe89 is described below
commit c00f8fe897dc13c40d57b49a5afc8a9f2d9f7f6d
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Fri Aug 12 17:34:49 2022 -0400
PartList should be an ArrayList.
---
.../juneau/collections/ControlledArrayList.java | 391 +++++++++++++++++++++
.../org/apache/juneau/rest/client/RestClient.java | 2 +-
.../java/org/apache/juneau/http/part/PartList.java | 267 ++++----------
.../ArgsTest.java => collections/Args_Test.java} | 11 +-
.../collections/ControlledArrayList_Test.java | 191 ++++++++++
.../org/apache/juneau/http/part/PartList_Test.java | 12 +-
6 files changed, 656 insertions(+), 218 deletions(-)
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/collections/ControlledArrayList.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/collections/ControlledArrayList.java
new file mode 100644
index 000000000..0b38c3613
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/collections/ControlledArrayList.java
@@ -0,0 +1,391 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.collections;
+
+import java.util.*;
+import java.util.function.*;
+
+/**
+ * An array list that allows you to control whether it's read-only via a constructor parameter.
+ *
+ * <p>
+ * Override methods such as {@link #overrideAdd(int, Object)} are provided that bypass the unmodifiable restriction
+ * on the list. They allow you to manipulate the list while not exposing the ability to manipulate the list through
+ * any of the methods provided by the {@link List} interface (meaning you can pass the object around as an unmodifiable List).
+ *
+ * @param <E> The element type.
+ */
+public class ControlledArrayList<E> extends ArrayList<E> {
+
+ private static final long serialVersionUID = -1L;
+
+ private final boolean modifiable;
+
+ /**
+ * Constructor.
+ *
+ * @param modifiable If <jk>true</jk>, this list can be modified through normal list operation methods on the {@link List} interface.
+ */
+ public ControlledArrayList(boolean modifiable) {
+ this.modifiable = modifiable;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param modifiable If <jk>true</jk>, this list can be modified through normal list operation methods on the {@link List} interface.
+ * @param list The initial contents of this list.
+ */
+ public ControlledArrayList(boolean modifiable, List<? extends E> list) {
+ super(list);
+ this.modifiable = modifiable;
+ }
+
+ void checkModifiable() {
+ if (! modifiable)
+ throw new UnsupportedOperationException("List is read-only.");
+ }
+
+ /**
+ * Returns <jk>true</jk> if this list is modifiable.
+ *
+ * @return <jk>true</jk> if this list is modifiable.
+ */
+ public boolean isModifiable() {
+ return modifiable;
+ }
+
+ @Override
+ public E set(int index, E element) {
+ checkModifiable();
+ return overrideSet(index, element);
+ }
+
+ /**
+ * Same as {@link #set(int, Object)} but bypasses the modifiable flag.
+ *
+ * @param index Index of the element to replace.
+ * @param element Element to be stored at the specified position.
+ * @return The element previously at the specified position.
+ */
+ public E overrideSet(int index, E element) {
+ return super.set(index, element);
+ }
+
+ @Override
+ public void add(int index, E element) {
+ checkModifiable();
+ overrideAdd(index, element);
+ }
+
+ /**
+ * Same as {@link #add(int, Object)} but bypasses the modifiable flag.
+ *
+ * @param index Index of the element to replace.
+ * @param element Element to be stored at the specified position.
+ */
+ public void overrideAdd(int index, E element) {
+ super.add(index, element);
+ }
+
+ @Override
+ public E remove(int index) {
+ checkModifiable();
+ return overrideRemove(index);
+ }
+
+ /**
+ * Same as {@link #remove(int)} but bypasses the modifiable flag.
+ *
+ * @param index Index of the element to remove.
+ * @return The element that was removed from the list.
+ */
+ public E overrideRemove(int index) {
+ return super.remove(index);
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends E> c) {
+ checkModifiable();
+ return overrideAddAll(index, c);
+ }
+
+ /**
+ * Same as {@link #addAll(int,Collection)} but bypasses the modifiable flag.
+ *
+ * @param index Index at which to insert the first element from the specified collection.
+ * @param c Collection containing elements to be added to this list.
+ * @return <jk>true</jk> if this list changed as a result of the call.
+ */
+ public boolean overrideAddAll(int index, Collection<? extends E> c) {
+ return super.addAll(index, c);
+ }
+
+ @Override
+ public void replaceAll(UnaryOperator<E> operator) {
+ checkModifiable();
+ overrideReplaceAll(operator);
+ }
+
+ /**
+ * Same as {@link #replaceAll(UnaryOperator)} but bypasses the modifiable flag.
+ *
+ * @param operator The operator to apply to each element.
+ */
+ public void overrideReplaceAll(UnaryOperator<E> operator) {
+ super.replaceAll(operator);
+ }
+
+ @Override
+ public void sort(Comparator<? super E> c) {
+ checkModifiable();
+ overrideSort(c);
+ }
+
+ /**
+ * Same as {@link #overrideSort(Comparator)} but bypasses the modifiable flag.
+ *
+ * @param c The Comparator used to compare list elements. A null value indicates that the elements' natural ordering should be used.
+ */
+ public void overrideSort(Comparator<? super E> c) {
+ super.sort(c);
+ }
+
+ @Override
+ public boolean add(E element) {
+ checkModifiable();
+ return overrideAdd(element);
+ }
+
+ /**
+ * Same as {@link #add(Object)} but bypasses the modifiable flag.
+ *
+ * @param element Element to be stored at the specified position.
+ * @return <jk>true</jk>.
+ */
+ public boolean overrideAdd(E element) {
+ return super.add(element);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ checkModifiable();
+ return overrideRemove(o);
+ }
+
+ /**
+ * Same as {@link #remove(Object)} but bypasses the modifiable flag.
+ *
+ * @param o Element to be removed from this list, if present.
+ * @return <jk>true</jk> if this list contained the specified element.
+ */
+ public boolean overrideRemove(Object o) {
+ return super.remove(o);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> c) {
+ checkModifiable();
+ return overrideAddAll(c);
+ }
+
+ /**
+ * Same as {@link #addAll(Collection)} but bypasses the modifiable flag.
+ *
+ * @param c Collection containing elements to be added to this list.
+ * @return <jk>true</jk> if this list changed as a result of the call.
+ */
+ public boolean overrideAddAll(Collection<? extends E> c) {
+ return super.addAll(c);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> coll) {
+ checkModifiable();
+ return overrideRemoveAll(coll);
+ }
+
+ /**
+ * Same as {@link #removeAll(Collection)} but bypasses the modifiable flag.
+ *
+ * @param c Collection containing elements to be removed from this list.
+ * @return <jk>true</jk> if this list changed as a result of the call.
+ */
+ public boolean overrideRemoveAll(Collection<?> c) {
+ return super.removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ checkModifiable();
+ return overrideRetainAll(c);
+ }
+
+ /**
+ * Same as {@link #retainAll(Collection)} but bypasses the modifiable flag.
+ *
+ * @param c Collection containing elements to be retained in this list.
+ * @return <jk>true</jk> if this list changed as a result of the call.
+ */
+ public boolean overrideRetainAll(Collection<?> c) {
+ return super.retainAll(c);
+ }
+
+ @Override
+ public void clear() {
+ checkModifiable();
+ overrideClear();
+ }
+
+ /**
+ * Same as {@link #clear()} but bypasses the modifiable flag.
+ */
+ public void overrideClear() {
+ super.clear();
+ }
+
+ @Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ checkModifiable();
+ return overrideRemoveIf(filter);
+ }
+
+ /**
+ * Same as {@link #removeIf(Predicate)} but bypasses the modifiable flag.
+ *
+ * @param filter A predicate which returns true for elements to be removed.
+ * @return <jk>true</jk> if any elements were removed.
+ */
+ public boolean overrideRemoveIf(Predicate<? super E> filter) {
+ return super.removeIf(filter);
+ }
+
+ @Override
+ public List<E> subList(int fromIndex, int toIndex) {
+ return new ControlledArrayList<>(modifiable, super.subList(fromIndex, toIndex));
+ }
+
+ @Override
+ public ListIterator<E> listIterator() {
+ return listIterator(0);
+ }
+
+ @Override
+ public ListIterator<E> listIterator(final int index) {
+ if (modifiable)
+ return overrideListIterator(index);
+
+ return new ListIterator<E>() {
+ private final ListIterator<? extends E> i = overrideListIterator(index);
+
+ @Override
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ @Override
+ public E next() {
+ return i.next();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return i.hasPrevious();
+ }
+
+ @Override
+ public E previous() {
+ return i.previous();
+ }
+
+ @Override
+ public int nextIndex() {
+ return i.nextIndex();
+ }
+
+ @Override
+ public int previousIndex() {
+ return i.previousIndex();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(E e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void add(E e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super E> action) {
+ i.forEachRemaining(action);
+ }
+ };
+ }
+
+ /**
+ * Same as {@link #listIterator()} but bypasses the modifiable flag.
+ *
+ * @param index Index of the first element to be returned from the list iterator.
+ * @return A list iterator over the elements in this list (in proper sequence), starting at the specified position in the list.
+ */
+ public ListIterator<E> overrideListIterator(final int index) {
+ return super.listIterator(index);
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ if (modifiable)
+ return overrideIterator();
+
+ return new Iterator<E>() {
+ private final Iterator<? extends E> i = overrideIterator();
+
+ @Override
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ @Override
+ public E next() {
+ return i.next();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super E> action) {
+ i.forEachRemaining(action);
+ }
+ };
+ }
+
+ /**
+ * Same as {@link #iterator()} but bypasses the modifiable flag.
+ *
+ * @return An iterator over the elements in this list in proper sequence.
+ */
+ public Iterator<E> overrideIterator() {
+ return super.iterator();
+ }
+}
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index f26e9a472..b4c1a3493 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -6777,7 +6777,7 @@ public class RestClient extends BeanContextable implements HttpClient, Closeable
if (body instanceof NameValuePair[])
return req.content(new UrlEncodedFormEntity(alist((NameValuePair[])body)));
if (body instanceof PartList)
- return req.content(new UrlEncodedFormEntity(((PartList)body).toNameValuePairs()));
+ return req.content(new UrlEncodedFormEntity(((PartList)body)));
if (body instanceof HttpResource)
((HttpResource)body).getHeaders().forEach(x-> req.header(x));
if (body instanceof HttpEntity) {
diff --git a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartList.java b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartList.java
index f0ca32b4b..e57bc67c3 100644
--- a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartList.java
+++ b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/part/PartList.java
@@ -24,6 +24,7 @@ import java.util.stream.*;
import org.apache.http.*;
import org.apache.http.util.*;
import org.apache.juneau.*;
+import org.apache.juneau.collections.*;
import org.apache.juneau.http.HttpParts;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.internal.*;
@@ -156,21 +157,27 @@ import org.apache.juneau.svl.*;
* <li class='extlink'>{@source}
* </ul>
*/
-public class PartList {
+public class PartList extends ControlledArrayList<NameValuePair> {
//-----------------------------------------------------------------------------------------------------------------
// Static
//-----------------------------------------------------------------------------------------------------------------
+ private static final long serialVersionUID = 1L;
private static final NameValuePair[] EMPTY_ARRAY = new NameValuePair[0];
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Predicate<NameValuePair> NOT_NULL = x -> x != null;
/** Represents no part supplier in annotations. */
- public static final class Null extends PartList {}
+ public static final class Null extends PartList {
+ Null(boolean modifiable) {
+ super(false);
+ }
+ private static final long serialVersionUID = 1L;
+ }
/** Predefined instance. */
- public static final PartList EMPTY = new PartList();
+ public static final PartList EMPTY = new PartList(false);
/**
* Instantiates a new builder for this bean.
@@ -191,7 +198,7 @@ public class PartList {
* @return A new unmodifiable instance, never <jk>null</jk>.
*/
public static PartList of(List<NameValuePair> parts) {
- return parts == null || parts.isEmpty() ? EMPTY : new PartList(parts);
+ return parts == null || parts.isEmpty() ? EMPTY : new PartList(true, parts);
}
/**
@@ -203,7 +210,7 @@ public class PartList {
* @return A new unmodifiable instance, never <jk>null</jk>.
*/
public static PartList of(NameValuePair...parts) {
- return parts == null || parts.length == 0 ? EMPTY : new PartList(parts);
+ return parts == null || parts.length == 0 ? EMPTY : new PartList(true, parts);
}
/**
@@ -228,7 +235,7 @@ public class PartList {
ArrayBuilder<NameValuePair> b = ArrayBuilder.of(NameValuePair.class).filter(NOT_NULL).size(pairs.length / 2);
for (int i = 0; i < pairs.length; i+=2)
b.add(BasicPart.of(stringify(pairs[i]), pairs[i+1]));
- return new PartList(b.orElse(EMPTY_ARRAY));
+ return new PartList(true, b.orElse(EMPTY_ARRAY));
}
//-----------------------------------------------------------------------------------------------------------------
@@ -244,7 +251,7 @@ public class PartList {
final List<NameValuePair> entries;
List<NameValuePair> defaultEntries;
private VarResolver varResolver;
- boolean caseInsensitive = false;
+ boolean caseInsensitive = false, unmodifiable = false;
/**
* Constructor.
@@ -261,8 +268,9 @@ public class PartList {
*/
protected Builder(PartList copyFrom) {
super(copyFrom.getClass());
- entries = list(copyFrom.entries);
+ entries = copyOf(copyFrom);
caseInsensitive = copyFrom.caseInsensitive;
+ unmodifiable = false;
}
/**
@@ -276,6 +284,7 @@ public class PartList {
defaultEntries = copyOf(copyFrom.defaultEntries);
varResolver = copyFrom.varResolver;
caseInsensitive = copyFrom.caseInsensitive;
+ unmodifiable = copyFrom.unmodifiable;
}
@Override /* BeanBuilder */
@@ -359,27 +368,26 @@ public class PartList {
}
/**
- * Removes any parts already in this builder.
+ * Specifies that the resulting list should be unmodifiable.
+ *
+ * <p>
+ * The default behavior is modifiable.
*
* @return This object.
*/
- @FluentSetter
- public Builder clear() {
- entries.clear();
+ public Builder unmodifiable() {
+ unmodifiable = true;
return this;
}
/**
- * Adds the specified parts to the end of the parts in this builder.
+ * Removes any parts already in this builder.
*
- * @param value The parts to add. <jk>null</jk> values are ignored.
* @return This object.
*/
@FluentSetter
- public Builder append(PartList value) {
- if (value != null)
- for (NameValuePair x : value.entries)
- append(x);
+ public Builder clear() {
+ entries.clear();
return this;
}
@@ -450,8 +458,7 @@ public class PartList {
@FluentSetter
public Builder append(List<? extends NameValuePair> values) {
if (values != null)
- for (int i = 0, j = values.size(); i < j; i++)
- append(values.get(i));
+ values.forEach(x -> append(x));
return this;
}
@@ -464,7 +471,7 @@ public class PartList {
@FluentSetter
public Builder prepend(PartList value) {
if (value != null)
- prependAll(entries, value.entries);
+ prependAll(entries, value.getAll());
return this;
}
@@ -538,20 +545,6 @@ public class PartList {
return this;
}
- /**
- * Removes the specified part from this builder.
- *
- * @param value The part to remove. <jk>null</jk> values are ignored.
- * @return This object.
- */
- @FluentSetter
- public Builder remove(PartList value) {
- if (value != null)
- for (int i = 0; i < value.entries.length; i++)
- remove(value.entries[i]);
- return this;
- }
-
/**
* Removes the specified part from this builder.
*
@@ -586,8 +579,8 @@ public class PartList {
*/
@FluentSetter
public Builder remove(List<? extends NameValuePair> values) {
- for (int i = 0, j = values.size(); i < j; i++) /* See HTTPCORE-361 */
- remove(values.get(i));
+ if (values != null)
+ values.forEach(x -> remove(x));
return this;
}
@@ -727,21 +720,6 @@ public class PartList {
return this;
}
- /**
- * Adds or replaces the parts with the specified names.
- *
- * <p>
- * If no part with the same name is found the given part is added to the end of the list.
- *
- * @param values The parts to replace. <jk>null</jk> values are ignored.
- * @return This object.
- */
- public Builder set(PartList values) {
- if (values != null)
- set(values.entries);
- return this;
- }
-
/**
* Sets a default value for a part.
*
@@ -852,21 +830,6 @@ public class PartList {
return this;
}
- /**
- * Replaces the first occurrence of the parts with the same name.
- *
- * <p>
- * If no part with the same name is found the given part is added to the end of the list.
- *
- * @param values The default parts to set. <jk>null</jk> values are ignored.
- * @return This object.
- */
- public Builder setDefault(PartList values) {
- if (values != null)
- setDefault(values.entries);
- return this;
- }
-
/**
* Adds the specify part to this list.
*
@@ -1007,33 +970,6 @@ public class PartList {
throw new BasicRuntimeException("Invalid value specified for flag parameter on add(flag,values) method: {0}", flag);
}
- /**
- * Adds the specified parts to this list.
- *
- * @param flag
- * What to do with the part.
- * <br>Possible values:
- * <ul>
- * <li>{@link ListOperation#APPEND APPEND} - Calls {@link #append(PartList)}.
- * <li>{@link ListOperation#PREPEND PREEND} - Calls {@link #prepend(PartList)}.
- * <li>{@link ListOperation#SET REPLACE} - Calls {@link #set(PartList)}.
- * <li>{@link ListOperation#DEFAULT DEFAULT} - Calls {@link #setDefault(PartList)}.
- * </ul>
- * @param values The parts to add.
- * @return This object.
- */
- public Builder add(ListOperation flag, PartList values) {
- if (flag == ListOperation.APPEND)
- return append(values);
- if (flag == ListOperation.PREPEND)
- return prepend(values);
- if (flag == ListOperation.SET)
- return set(values);
- if (flag == ListOperation.DEFAULT)
- return setDefault(values);
- throw new BasicRuntimeException("Invalid value specified for flag parameter on add(flag,values) method: {0}", flag);
- }
-
/**
* Performs an action on all the parts in this list.
*
@@ -1177,7 +1113,6 @@ public class PartList {
// Instance
//-----------------------------------------------------------------------------------------------------------------
- final NameValuePair[] entries;
final boolean caseInsensitive;
/**
@@ -1186,24 +1121,17 @@ public class PartList {
* @param builder The builder containing the settings for this bean.
*/
public PartList(Builder builder) {
- if (builder.defaultEntries == null) {
- entries = builder.entries.toArray(new NameValuePair[builder.entries.size()]);
- } else {
- ArrayBuilder<NameValuePair> l = ArrayBuilder.of(NameValuePair.class).filter(NOT_NULL).size(builder.entries.size() + builder.defaultEntries.size());
-
- for (int i = 0, j = builder.entries.size(); i < j; i++)
- l.add(builder.entries.get(i));
+ super(! builder.unmodifiable, builder.entries);
+ if (builder.defaultEntries != null) {
for (int i1 = 0, j1 = builder.defaultEntries.size(); i1 < j1; i1++) {
NameValuePair x = builder.defaultEntries.get(i1);
boolean exists = false;
for (int i2 = 0, j2 = builder.entries.size(); i2 < j2 && ! exists; i2++)
exists = eq(builder.entries.get(i2).getName(), x.getName());
if (! exists)
- l.add(x);
+ overrideAdd(x);
}
-
- entries = l.orElse(EMPTY_ARRAY);
}
this.caseInsensitive = builder.caseInsensitive;
}
@@ -1211,39 +1139,37 @@ public class PartList {
/**
* Constructor.
*
+ * @param modifiable Whether this list should be modifiable.
* @param parts
* The parts to add to the list.
* <br>Can be <jk>null</jk>.
* <br><jk>null</jk> entries are ignored.
*/
- protected PartList(List<NameValuePair> parts) {
- ArrayBuilder<NameValuePair> l = ArrayBuilder.of(NameValuePair.class).filter(NOT_NULL).size(parts.size());
- for (int i = 0, j = parts.size(); i < j; i++)
- l.add(parts.get(i));
- entries = l.orElse(EMPTY_ARRAY);
+ protected PartList(boolean modifiable, List<NameValuePair> parts) {
+ super(modifiable, parts);
caseInsensitive = false;
}
/**
* Constructor.
*
+ * @param modifiable Whether this list should be modifiable.
* @param parts
* The parts to add to the list.
* <br><jk>null</jk> entries are ignored.
*/
- protected PartList(NameValuePair...parts) {
- ArrayBuilder<NameValuePair> l = ArrayBuilder.of(NameValuePair.class).filter(NOT_NULL).size(parts.length);
- for (int i = 0; i < parts.length; i++)
- l.add(parts[i]);
- entries = l.orElse(EMPTY_ARRAY);
+ protected PartList(boolean modifiable, NameValuePair...parts) {
+ super(modifiable, Arrays.asList(parts));
caseInsensitive = false;
}
/**
* Default constructor.
+ *
+ * @param modifiable Whether this list should be modifiable.
*/
- protected PartList() {
- entries = EMPTY_ARRAY;
+ protected PartList(boolean modifiable) {
+ super(modifiable);
caseInsensitive = false;
}
@@ -1270,8 +1196,7 @@ public class PartList {
NameValuePair first = null;
List<NameValuePair> rest = null;
- for (int i = 0; i < entries.length; i++) {
- NameValuePair x = entries[i];
+ for (NameValuePair x : this) {
if (eq(x.getName(), name)) {
if (first == null)
first = x;
@@ -1329,8 +1254,7 @@ public class PartList {
NameValuePair first = null;
List<NameValuePair> rest = null;
- for (int i = 0; i < entries.length; i++) {
- NameValuePair x = entries[i];
+ for (NameValuePair x : this) {
if (eq(x.getName(), name)) {
if (first == null)
first = x;
@@ -1393,7 +1317,7 @@ public class PartList {
* An array of all the parts in this list, or an empty array if no parts are present.
*/
public NameValuePair[] getAll() {
- return entries.length == 0 ? EMPTY_ARRAY : Arrays.copyOf(entries, entries.length);
+ return size() == 0 ? EMPTY_ARRAY : toArray(new NameValuePair[size()]);
}
/**
@@ -1410,21 +1334,12 @@ public class PartList {
*/
public NameValuePair[] getAll(String name) {
ArrayBuilder<NameValuePair> b = ArrayBuilder.of(NameValuePair.class).filter(NOT_NULL);
- for (int i = 0; i < entries.length; i++)
- if (eq(entries[i].getName(), name))
- b.add(entries[i]);
+ for (NameValuePair x : this)
+ if (eq(x.getName(), name))
+ b.add(x);
return b.orElse(EMPTY_ARRAY);
}
- /**
- * Returns the number of parts in this list.
- *
- * @return The number of parts in this list.
- */
- public int size() {
- return entries.length;
- }
-
/**
* Gets the first part with the given name.
*
@@ -1435,8 +1350,7 @@ public class PartList {
* @return The first matching part, or <jk>null</jk> if not found.
*/
public Optional<NameValuePair> getFirst(String name) {
- for (int i = 0; i < entries.length; i++) {
- NameValuePair x = entries[i];
+ for (NameValuePair x : this) {
if (eq(x.getName(), name))
return optional(x);
}
@@ -1453,8 +1367,8 @@ public class PartList {
* @return The last matching part, or <jk>null</jk> if not found.
*/
public Optional<NameValuePair> getLast(String name) {
- for (int i = entries.length - 1; i >= 0; i--) {
- NameValuePair x = entries[i];
+ for (int i = size() - 1; i >= 0; i--) {
+ NameValuePair x = get(i);
if (eq(x.getName(), name))
return optional(x);
}
@@ -1505,8 +1419,7 @@ public class PartList {
* @return <jk>true</jk> if at least one part with the name is present.
*/
public boolean contains(String name) {
- for (int i = 0; i < entries.length; i++) {
- NameValuePair x = entries[i];
+ for (NameValuePair x : this) {
if (eq(x.getName(), name))
return true;
}
@@ -1518,8 +1431,9 @@ public class PartList {
*
* @return A new iterator over this list of parts.
*/
+ @Override
public PartIterator iterator() {
- return new BasicPartIterator(entries, null, caseInsensitive);
+ return new BasicPartIterator(getAll(), null, caseInsensitive);
}
/**
@@ -1530,21 +1444,7 @@ public class PartList {
* @return A new iterator over the matching parts in this list.
*/
public PartIterator iterator(String name) {
- return new BasicPartIterator(entries, name, caseInsensitive);
- }
-
- /**
- * Performs an action on all parts in this list.
- *
- * <p>
- * This is the preferred method for iterating over parts as it does not involve
- * creation or copy of lists/arrays.
- *
- * @param action An action to perform on each element.
- * @return This object.
- */
- public PartList forEach(Consumer<NameValuePair> action) {
- return forEach(x -> true, action);
+ return new BasicPartIterator(getAll(), name, caseInsensitive);
}
/**
@@ -1574,23 +1474,10 @@ public class PartList {
* @return This object.
*/
public PartList forEach(Predicate<NameValuePair> filter, Consumer<NameValuePair> action) {
- for (int i = 0; i < entries.length; i++)
- consume(filter, action, entries[i]);
+ forEach(x -> consume(filter, action, x));
return this;
}
- /**
- * Returns a stream of the parts in this list.
- *
- * <p>
- * This does not involve a copy of the underlying array of <c>NameValuePair</c> objects so should perform well.
- *
- * @return This object.
- */
- public Stream<NameValuePair> stream() {
- return Arrays.stream(entries);
- }
-
/**
* Returns a stream of the parts in this list with the specified name.
*
@@ -1601,28 +1488,7 @@ public class PartList {
* @return This object.
*/
public Stream<NameValuePair> stream(String name) {
- return Arrays.stream(entries).filter(x->eq(name, x.getName()));
- }
-
- /**
- * Returns the contents of this list as an unmodifiable list of {@link NameValuePair} objects.
- *
- * @return The contents of this list as an unmodifiable list of {@link NameValuePair} objects.
- */
- public List<NameValuePair> toNameValuePairs() {
- return ulist(entries);
- }
-
- /**
- * Performs an action on the contents of this list.
- *
- * @param action The action to perform.
- * @return This object.
- */
- public PartList forEachNameValuePair(Consumer<NameValuePair> action) {
- for (NameValuePair p : entries)
- action.accept(p);
- return this;
+ return Arrays.stream(getAll(name)).filter(x->eq(name, x.getName()));
}
private boolean eq(String s1, String s2) {
@@ -1635,15 +1501,16 @@ public class PartList {
@Override /* Object */
public String toString() {
StringBuilder sb = new StringBuilder();
- for (int i = 0; i < entries.length; i++) {
- NameValuePair p = entries[i];
- String v = p.getValue();
- if (v != null) {
- if (sb.length() > 0)
- sb.append("&");
- sb.append(urlEncode(p.getName())).append('=').append(urlEncode(p.getValue()));
+ forEach(p -> {
+ if (p != null) {
+ String v = p.getValue();
+ if (v != null) {
+ if (sb.length() > 0)
+ sb.append("&");
+ sb.append(urlEncode(p.getName())).append('=').append(urlEncode(p.getValue()));
+ }
}
- }
+ });
return sb.toString();
}
}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/utils/ArgsTest.java b/juneau-utest/src/test/java/org/apache/juneau/collections/Args_Test.java
similarity index 88%
rename from juneau-utest/src/test/java/org/apache/juneau/utils/ArgsTest.java
rename to juneau-utest/src/test/java/org/apache/juneau/collections/Args_Test.java
index ace209ace..0a9dd9865 100755
--- a/juneau-utest/src/test/java/org/apache/juneau/utils/ArgsTest.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/collections/Args_Test.java
@@ -10,22 +10,21 @@
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
-package org.apache.juneau.utils;
+package org.apache.juneau.collections;
import static org.junit.Assert.*;
import static org.junit.runners.MethodSorters.*;
-import org.apache.juneau.collections.*;
import org.junit.*;
@FixMethodOrder(NAME_ASCENDING)
-public class ArgsTest {
+public class Args_Test {
- //====================================================================================================
+ //-----------------------------------------------------------------------------------------------------------------
// test - Basic tests
- //====================================================================================================
+ //-----------------------------------------------------------------------------------------------------------------
@Test
- public void test() throws Exception {
+ public void basic() throws Exception {
Args a;
// Empty args
diff --git a/juneau-utest/src/test/java/org/apache/juneau/collections/ControlledArrayList_Test.java b/juneau-utest/src/test/java/org/apache/juneau/collections/ControlledArrayList_Test.java
new file mode 100644
index 000000000..d568f5da0
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/collections/ControlledArrayList_Test.java
@@ -0,0 +1,191 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.collections;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import java.util.*;
+
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class ControlledArrayList_Test {
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // test - Basic tests
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void a01_constructors() throws Exception {
+ ControlledArrayList<Integer> x;
+
+ x = new ControlledArrayList<>(true);
+ assertTrue(x.isModifiable());
+
+ x = new ControlledArrayList<>(false);
+ assertFalse(x.isModifiable());
+
+ x = new ControlledArrayList<>(true, Arrays.asList(1));
+ assertTrue(x.isModifiable());
+
+ x = new ControlledArrayList<>(false, Arrays.asList(1));
+ assertFalse(x.isModifiable());
+ }
+
+ @Test
+ public void a02_basicMethods() throws Exception {
+ ControlledArrayList<Integer> x1 = new ControlledArrayList<Integer>(true, Arrays.asList(1));
+ ControlledArrayList<Integer> x2 = new ControlledArrayList<Integer>(false, Arrays.asList(1));
+
+ x1.set(0, 2);
+ assertThrown(() -> x2.set(0, 2)).isType(UnsupportedOperationException.class);
+ x2.overrideSet(0, 2);
+ assertList(x1).is(x2);
+
+ x1.add(0, 2);
+ assertThrown(() -> x2.add(0, 2)).isType(UnsupportedOperationException.class);
+ x2.overrideAdd(0, 2);
+ assertList(x1).is(x2);
+
+ x1.remove(0);
+ assertThrown(() -> x2.remove(0)).isType(UnsupportedOperationException.class);
+ x2.overrideRemove(0);
+ assertList(x1).is(x2);
+
+ x1.addAll(0, Arrays.asList(3));
+ assertThrown(() -> x2.addAll(0, Arrays.asList(3))).isType(UnsupportedOperationException.class);
+ x2.overrideAddAll(0, Arrays.asList(3));
+ assertList(x1).is(x2);
+
+ x1.replaceAll(x -> x);
+ assertThrown(() -> x2.replaceAll(x -> x)).isType(UnsupportedOperationException.class);
+ x2.overrideReplaceAll(x -> x);
+ assertList(x1).is(x2);
+
+ x1.sort(null);
+ assertThrown(() -> x2.sort(null)).isType(UnsupportedOperationException.class);
+ x2.overrideSort(null);
+ assertList(x1).is(x2);
+
+ x1.add(1);
+ assertThrown(() -> x2.add(1)).isType(UnsupportedOperationException.class);
+ x2.overrideAdd(1);
+ assertList(x1).is(x2);
+
+ x1.remove((Integer)1);
+ assertThrown(() -> x2.remove((Integer)1)).isType(UnsupportedOperationException.class);
+ x2.overrideRemove((Integer)1);
+ assertList(x1).is(x2);
+
+ x1.addAll(Arrays.asList(3));
+ assertThrown(() -> x2.addAll(Arrays.asList(3))).isType(UnsupportedOperationException.class);
+ x2.overrideAddAll(Arrays.asList(3));
+ assertList(x1).is(x2);
+
+ x1.removeAll(Arrays.asList(3));
+ assertThrown(() -> x2.removeAll(Arrays.asList(3))).isType(UnsupportedOperationException.class);
+ x2.overrideRemoveAll(Arrays.asList(3));
+ assertList(x1).is(x2);
+
+ x1.retainAll(Arrays.asList(2));
+ assertThrown(() -> x2.retainAll(Arrays.asList(2))).isType(UnsupportedOperationException.class);
+ x2.overrideRetainAll(Arrays.asList(2));
+ assertList(x1).is(x2);
+
+ x1.clear();
+ assertThrown(() -> x2.clear()).isType(UnsupportedOperationException.class);
+ x2.overrideClear();
+ assertList(x1).is(x2);
+
+ x1.add(1);
+ x2.overrideAdd(1);
+
+ x1.removeIf(x -> x == 1);
+ assertThrown(() -> x2.removeIf(x -> x == 1)).isType(UnsupportedOperationException.class);
+ x2.overrideRemoveIf(x -> x == 1);
+ assertList(x1).is(x2);
+
+ x1.add(1);
+ x2.overrideAdd(1);
+
+ ControlledArrayList<Integer> x1a = (ControlledArrayList<Integer>) x1.subList(0, 0);
+ ControlledArrayList<Integer> x2a = (ControlledArrayList<Integer>) x2.subList(0, 0);
+ assertTrue(x1a.isModifiable());
+ assertFalse(x2a.isModifiable());
+ }
+
+ @Test
+ public void a03_iterator() throws Exception {
+ ControlledArrayList<Integer> x1 = new ControlledArrayList<Integer>(true, Arrays.asList(1));
+ ControlledArrayList<Integer> x2 = new ControlledArrayList<Integer>(false, Arrays.asList(1));
+
+ Iterator<Integer> i1 = x1.iterator();
+ Iterator<Integer> i2 = x2.iterator();
+
+ assertTrue(i1.hasNext());
+ assertTrue(i2.hasNext());
+
+ assertEquals(1, i1.next().intValue());
+ assertEquals(1, i2.next().intValue());
+
+ i1.remove();
+ assertThrown(() -> i2.remove()).isType(UnsupportedOperationException.class);
+
+ i1.forEachRemaining(x -> {});
+ i2.forEachRemaining(x -> {});
+ }
+
+ @Test
+ public void a04_listIterator() throws Exception {
+ ControlledArrayList<Integer> x1 = new ControlledArrayList<Integer>(true, Arrays.asList(1));
+ ControlledArrayList<Integer> x2 = new ControlledArrayList<Integer>(false, Arrays.asList(1));
+
+ ListIterator<Integer> i1a = x1.listIterator();
+ ListIterator<Integer> i2a = x2.listIterator();
+
+ assertTrue(i1a.hasNext());
+ assertTrue(i2a.hasNext());
+
+ assertEquals(1, i1a.next().intValue());
+ assertEquals(1, i2a.next().intValue());
+
+ assertTrue(i1a.hasPrevious());
+ assertTrue(i2a.hasPrevious());
+
+ assertEquals(1, i1a.nextIndex());
+ assertEquals(1, i2a.nextIndex());
+
+ assertEquals(0, i1a.previousIndex());
+ assertEquals(0, i2a.previousIndex());
+
+ i1a.previous();
+ i2a.previous();
+
+ i1a.set(1);
+ assertThrown(() -> i2a.set(1)).isType(UnsupportedOperationException.class);
+
+ i1a.add(1);
+ assertThrown(() -> i2a.add(1)).isType(UnsupportedOperationException.class);
+
+ i1a.next();
+ i2a.next();
+
+ i1a.remove();
+ assertThrown(() -> i2a.remove()).isType(UnsupportedOperationException.class);
+
+ i1a.forEachRemaining(x -> {});
+ i2a.forEachRemaining(x -> {});
+ }
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/part/PartList_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/part/PartList_Test.java
index 34b679cbd..bb2fe44f1 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/part/PartList_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/part/PartList_Test.java
@@ -92,7 +92,7 @@ public class PartList_Test {
x.append((List<NameValuePair>)null);
assertObject(x.build()).isString("Foo=1&Foo=2&Foo=3&Foo=4&Foo=5&Foo=6&Foo=7");
- assertObject(new PartList.Null()).isString("");
+ assertObject(new PartList.Null(false)).isString("");
}
@Test
@@ -558,16 +558,6 @@ public class PartList_Test {
assertObject(x14).isString("b=x&b=y&a=y&c=x");
}
- //-----------------------------------------------------------------------------------------------------------------
- // Other tests
- //-----------------------------------------------------------------------------------------------------------------
-
- @Test
- public void e01_asNameValuePairs() {
- PartList x = PartList.of(APart.X);
- assertObject(x.toNameValuePairs()).isString("[a=x]");
- }
-
//-----------------------------------------------------------------------------------------------------------------
// Utility methods
//-----------------------------------------------------------------------------------------------------------------