// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed 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 com.google.collide.shared.util; import com.google.collide.json.server.JsonArrayListAdapter; import com.google.collide.json.server.JsonIntegerMapAdapter; import com.google.collide.json.server.JsonStringMapAdapter; import com.google.collide.json.server.JsonStringSetAdapter; import com.google.collide.json.shared.JsonArray; import com.google.collide.json.shared.JsonIntegerMap; import com.google.collide.json.shared.JsonStringMap; import com.google.collide.json.shared.JsonStringSet; import com.google.common.base.Equivalence; import com.google.common.base.Objects; import com.google.gwt.core.shared.GWT; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import javax.annotation.Nullable; /** * A set of static factory methods for lightweight collections. * */ public final class JsonCollections { public interface Implementation { <T> JsonArray<T> createArray(); <T> JsonStringMap<T> createMap(); <T> JsonIntegerMap<T> createIntegerMap(); JsonStringSet createStringSet(); } private static class PureJavaImplementation implements Implementation { @Override public <T> JsonArray<T> createArray() { return new JsonArrayListAdapter<T>(new ArrayList<T>()); } @Override public <T> JsonStringMap<T> createMap() { return new JsonStringMapAdapter<T>(new HashMap<String, T>()); } @Override public JsonStringSet createStringSet() { return new JsonStringSetAdapter(new HashSet<String>()); } @Override public <T> JsonIntegerMap<T> createIntegerMap() { return new JsonIntegerMapAdapter<T>(new HashMap<Integer, T>()); } } // If running in pure java (server code or tests) or in dev mode, use the pure java impl private static Implementation implementation = !GWT.isClient() || !GWT.isScript() ? new PureJavaImplementation() : null; public static void setImplementation(Implementation implementation) { JsonCollections.implementation = implementation; } public static <T> JsonArray<T> createArray() { return implementation.createArray(); } public static <T> JsonStringMap<T> createMap() { return implementation.createMap(); } public static <T> JsonIntegerMap<T> createIntegerMap() { return implementation.createIntegerMap(); } public static <T> JsonArray<T> createArray(T... items) { JsonArray<T> array = createArray(); for (int i = 0, n = items.length; i < n; i++) { array.add(items[i]); } return array; } public static <T> JsonArray<T> createArray(Iterable<T> items) { JsonArray<T> array = createArray(); for (Iterator<T> it = items.iterator(); it.hasNext(); ) { array.add(it.next()); } return array; } public static JsonStringSet createStringSet() { return implementation.createStringSet(); } public static JsonStringSet createStringSet(String... items) { JsonStringSet set = createStringSet(); for (int i = 0, n = items.length; i < n; i++) { set.add(items[i]); } return set; } public static JsonStringSet createStringSet(Iterator<String> iterator) { JsonStringSet set = createStringSet(); while (iterator.hasNext()) { set.add(iterator.next()); } return set; } // TODO: Is it used? public static <T> void addAllMissing(JsonArray<T> self, JsonArray<T> b) { if (b == null || self == b) { return; } JsonArray<T> addList = createArray(); for (int i = 0, n = b.size(); i < n; i++) { T addCandidate = b.get(i); if (!self.contains(addCandidate)) { addList.add(addCandidate); } } self.addAll(addList); } /** * Check if two lists are equal. The lists are equal if they are both the same * size, and the items at every index are equal. Returns true if both lists * are null. * * @param <T> the data type of the arrays */ public static <T> boolean equals(JsonArray<T> a, JsonArray<T> b) { return equals(a, b, null); } /** * Check if two lists are equal. The lists are equal if they are both the same * size, and the items at every index are equal according to the provided * equator. Returns true if both lists are null. * * @param equivalence if null the {@link Object#equals(Object)} is used to * determine item equality. * * @param <T> the data type of the arrays */ public static <T> boolean equals( JsonArray<T> a, JsonArray<T> b, @Nullable Equivalence<T> equivalence) { if (a == b) { // Same list or both null. return true; } else if (a == null || b == null) { // One list is null, the other is not. return false; } else if (a.size() != b.size()) { // Different sizes. return false; } else { // Check the elements in the array. for (int i = 0; i < a.size(); i++) { T itemA = a.get(i); T itemB = b.get(i); // if the equator is null we just the equals method and some null checking if (equivalence == null && !Objects.equal(itemA, itemB)) { return false; } else if (equivalence != null && !equivalence.equivalent(itemA, itemB)) { return false; } } return true; } } /** * Check if two maps are equal. The maps are equal if they have exactly the * same set of keys value pairs. * * @param <T> the data type of the arrays */ public static <T> boolean equals(final JsonStringMap<T> a, final JsonStringMap<T> b) { return equals(a, b, null); } /** * Check if two maps are equal. The maps are equal if they have exactly the * same set of keys value pairs. Checks the values using a custom * {@link Equivalence} check. * * @param equivalence if null {@link Objects#equal(Object, Object)} is used to * verify equivalence. * * @param <T> the data type of the arrays */ public static <T> boolean equals( final JsonStringMap<T> a, final JsonStringMap<T> b, @Nullable Equivalence<T> equivalence) { if (a == b) { // Same map or both null. return true; } else if (a == null || b == null) { // One map is null, the other is not. return false; } else { JsonArray<String> keys = a.getKeys(); if (!equals(keys, b.getKeys())) { return false; } for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); T valueA = a.get(key); T valueB = b.get(key); boolean isNotEquivalent = (equivalence == null && !Objects.equal(valueA, valueB)) || (equivalence != null && !equivalence.equivalent(valueA, valueB)); if (isNotEquivalent) { return false; } } return true; } } }