001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import java.lang.reflect.*; 016import java.util.*; 017 018import org.apache.juneau.*; 019import org.apache.juneau.json.*; 020 021/** 022 * Utility methods for collections. 023 */ 024public final class CollectionUtils { 025 026 /** 027 * Reverses the order of a {@link LinkedHashMap}. 028 * 029 * @param in The map to reverse the order on. 030 * @return A new {@link LinkedHashMap} with keys in reverse order. 031 */ 032 public static <K,V> LinkedHashMap<K,V> reverse(Map<K,V> in) { 033 if (in == null) 034 return null; 035 LinkedHashMap<K,V> m = new LinkedHashMap<>(); 036 037 // Note: Entry objects are reusable in an entry set, so we simply can't 038 // create a reversed iteration of that set. 039 List<K> keys = new ArrayList<>(in.keySet()); 040 List<V> values = new ArrayList<>(in.values()); 041 for (int i = in.size()-1; i >= 0; i--) 042 m.put(keys.get(i), values.get(i)); 043 044 return m; 045 } 046 047 /** 048 * Add a value to a list if the value is not null. 049 * 050 * @param l The list to add to. 051 * @param o The element to add. 052 * @return The same list. 053 */ 054 public static <T> List<T> addIfNotNull(List<T> l, T o) { 055 if (o != null) 056 l.add(o); 057 return l; 058 } 059 060 /** 061 * Adds the contents of one list to the other in reverse order. 062 * 063 * <p> 064 * i.e. add values from 2nd list from end-to-start order to the end of the 1st list. 065 * 066 * @param list The list to append to. 067 * @param append Contains the values to append to the list. 068 * @return The same list. 069 */ 070 @SuppressWarnings({ "rawtypes", "unchecked" }) 071 public static List<?> addReverse(List list, List append) { 072 for (ListIterator i = append.listIterator(append.size()); i.hasPrevious();) 073 list.add(i.previous()); 074 return list; 075 } 076 077 /** 078 * Adds the contents of the array to the list in reverse order. 079 * 080 * <p> 081 * i.e. add values from the array from end-to-start order to the end of the list. 082 * 083 * @param list The list to append to. 084 * @param append Contains the values to append to the list. 085 * @return The same list. 086 */ 087 @SuppressWarnings({ "rawtypes", "unchecked" }) 088 public static List<?> addReverse(List list, Object[] append) { 089 for (int i = append.length - 1; i >= 0; i--) 090 list.add(append[i]); 091 return list; 092 } 093 094 /** 095 * Returns a reverse iterable of the specified collection. 096 * 097 * @param c The collection to iterate over. 098 * @return An iterable over the collection in reverse order. 099 */ 100 public static <T> Iterable<T> reverseIterable(final Collection<T> c) { 101 return new Iterable<T>() { 102 @Override 103 public Iterator<T> iterator() { 104 if (c == null) 105 return Collections.EMPTY_LIST.iterator(); 106 ArrayList<T> l = new ArrayList<>(c); 107 Collections.reverse(l); 108 return l.iterator(); 109 } 110 }; 111 } 112 113 /** 114 * Same as {@link Collections#reverse(List)}, but returns the list. 115 * 116 * @param l The list being reversed 117 * @return The same list. 118 */ 119 public static <T> List<T> reverse(List<T> l) { 120 Collections.reverse(l); 121 return l; 122 } 123 124 /** 125 * Creates a new copy of a list in reverse order. 126 * 127 * @param l The old list. 128 * @return 129 * A new list with reversed entries. 130 * <br>Returns <jk>null</jk> if the list was <jk>null</jk>. 131 * <br>Returns the same list if the list is empty. 132 */ 133 public static <T> List<T> reverseCopy(List<T> l) { 134 if (l == null || l.isEmpty()) 135 return l; 136 List<T> l2 = new ArrayList<>(l); 137 Collections.reverse(l2); 138 return l2; 139 } 140 141 /** 142 * Collapses a collection of individual objects, arrays, and collections into a single list of objects. 143 * 144 * @param o The collection of objects to collapse. 145 * @return A new linked-list of objects. 146 */ 147 public static List<Object> collapse(Object...o) { 148 return collapse(new LinkedList<>(), o); 149 } 150 151 /** 152 * Same as {@link #collapse(Object...)} but allows you to supply your own list to append to. 153 * 154 * @param l The list to append to. 155 * @param o The collection of objects to collapse. 156 * @return The same list passed in. 157 */ 158 @SuppressWarnings("unchecked") 159 public static List<Object> collapse(List<Object> l, Object...o) { 160 for (Object o2 : o) { 161 if (o2 != null) { 162 if (o2.getClass().isArray()) { 163 for (int i = 0; i < Array.getLength(o2); i++) 164 collapse(l, Array.get(o2, i)); 165 } else if (o2 instanceof Collection) { 166 for (Object o3 : (Collection<Object>)o2) 167 collapse(l, o3); 168 } else { 169 l.add(o2); 170 } 171 } 172 } 173 return l; 174 } 175 176 /** 177 * Creates an immutable list from the specified collection. 178 * 179 * @param l The collection to copy from. 180 * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()} 181 * if the collection was empty or <jk>null</jk>. 182 */ 183 public static <T> List<T> immutableList(Collection<T> l) { 184 if (l == null || l.isEmpty()) 185 return Collections.emptyList(); 186 return Collections.unmodifiableList(new ArrayList<>(l)); 187 } 188 189 /** 190 * Creates an unmodifiable list from the specified collection. 191 * 192 * @param l The collection to copy from. 193 * @return An unmodifiable view of the list, or a {@link Collections#emptyList()} 194 * if the list was empty or <jk>null</jk>. 195 */ 196 public static <T> List<T> unmodifiableList(List<T> l) { 197 if (l == null || l.isEmpty()) 198 return Collections.emptyList(); 199 return Collections.unmodifiableList(l); 200 } 201 202 /** 203 * Creates an immutable list from the specified array. 204 * 205 * @param l The array to copy from. 206 * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()} 207 * if the collection was empty or <jk>null</jk>. 208 */ 209 public static <T> List<T> immutableList(T[] l) { 210 if (l == null || l.length == 0) 211 return Collections.emptyList(); 212 return Collections.unmodifiableList(new ArrayList<>(Arrays.asList(l))); 213 } 214 215 /** 216 * Creates an immutable map from the specified map. 217 * 218 * @param m The map to copy from. 219 * @return An unmodifiable {@link LinkedHashMap} copy of the collection, or a {@link Collections#emptyMap()} 220 * if the collection was empty or <jk>null</jk>. 221 */ 222 public static <K,V> Map<K,V> immutableMap(Map<K,V> m) { 223 if (m == null || m.isEmpty()) 224 return Collections.emptyMap(); 225 return Collections.unmodifiableMap(new LinkedHashMap<>(m)); 226 } 227 228 /** 229 * Creates an unmodifiable map from the specified map. 230 * 231 * @param m The map to copy from. 232 * @return An unmodifiable view of the collection, or a {@link Collections#emptyMap()} 233 * if the collection was empty or <jk>null</jk>. 234 */ 235 public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> m) { 236 if (m == null || m.isEmpty()) 237 return Collections.emptyMap(); 238 return Collections.unmodifiableMap(m); 239 } 240 241 /** 242 * Asserts that all entries in the list are either instances or subclasses of at least one of the specified classes. 243 * 244 * @param l The list to check. 245 * @param c The valid classes. 246 */ 247 public static void assertTypes(List<Object> l, Class<?>...c) { 248 for (Object o : l) { 249 boolean matches = false; 250 if (o.getClass() == Class.class) { 251 Class<?> o2 = (Class<?>)o; 252 for (int i = 0; i < c.length && ! matches; i++) 253 matches = c[i].isAssignableFrom(o2); 254 } else { 255 for (int i = 0; i < c.length && ! matches; i++) 256 matches = c[i].isInstance(o); 257 } 258 if (! matches) 259 throw new FormattedRuntimeException("Invalid list entry ''{0}'' ({1}). Not one of the following types: {2}", string(o), className(o), c); 260 } 261 } 262 263 static String string(Object value) { 264 return JsonSerializer.DEFAULT_LAX.toString(value); 265 } 266 267 static String className(Object value) { 268 return value.getClass().getSimpleName(); 269 } 270 271}