diff --git a/examples/boot-api/src/main/java/org/springside/examples/bootapi/api/BookEndpoint.java b/examples/boot-api/src/main/java/org/springside/examples/bootapi/api/BookEndpoint.java index 7416f584..cfb6bf97 100644 --- a/examples/boot-api/src/main/java/org/springside/examples/bootapi/api/BookEndpoint.java +++ b/examples/boot-api/src/main/java/org/springside/examples/bootapi/api/BookEndpoint.java @@ -42,7 +42,7 @@ public class BookEndpoint { public List listAllBook(Pageable pageable) { Iterable books = adminService.findAll(pageable); - return BeanMapper.mapList(books, BookDto.class); + return BeanMapper.mapList(books, Book.class,BookDto.class); } @RequestMapping(value = "/api/books/{id}", produces = MediaTypes.JSON_UTF_8) @@ -127,7 +127,7 @@ public class BookEndpoint { checkToken(token); Account currentUser = accountService.getLoginUser(token); List books = adminService.listMyBook(currentUser.id, pageable); - return BeanMapper.mapList(books, BookDto.class); + return BeanMapper.mapList(books, Book.class, BookDto.class); } @RequestMapping(value = "/api/myborrowedbook", produces = MediaTypes.JSON_UTF_8) @@ -136,7 +136,7 @@ public class BookEndpoint { checkToken(token); Account currentUser = accountService.getLoginUser(token); List books = borrowService.listMyBorrowedBook(currentUser.id, pageable); - return BeanMapper.mapList(books, BookDto.class); + return BeanMapper.mapList(books, Book.class, BookDto.class); } private void checkToken(String token) { diff --git a/modules/utils/src/main/java/org/springside/modules/utils/collection/CollectionUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/collection/CollectionUtil.java index 3bf7315b..edb0898c 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/collection/CollectionUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/collection/CollectionUtil.java @@ -6,9 +6,13 @@ package org.springside.modules.utils.collection; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; +import org.springside.modules.utils.collection.type.NaturalOrderComparator; + import com.google.common.annotations.Beta; /** @@ -71,4 +75,28 @@ public abstract class CollectionUtil { } } } + + ///// 排序 //// + + /** + * 按Comparable默认顺序排序的Comparetor + */ + @SuppressWarnings("unchecked") + public static Comparator naturalOrderComparator() { + return NaturalOrderComparator.INSTANCE; + } + + /** + * 按Comparable默认顺序倒序的Comparator + */ + public static Comparator reverseNaturalOrderComparator() { + return Collections.reverseOrder(null); + } + + /** + * 将Comparator倒序的Comparator + */ + public static Comparator reverseComparator(Comparator cmp) { + return Collections.reverseOrder(cmp); + } } diff --git a/modules/utils/src/main/java/org/springside/modules/utils/collection/ListUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/collection/ListUtil.java index 02d9d5ac..fa1ecc74 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/collection/ListUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/collection/ListUtil.java @@ -22,9 +22,9 @@ import com.google.common.primitives.Longs; * * 1. 常用函数(如是否为空,sort/binarySearch/shuffle/reverse(via JDK Collection) * - * 2. 各种 构造函数(from guava and JDK Collection) + * 2. 各种构造函数(from guava and JDK Collection) * - * 3. Array 转ArrayList的特色函数 (from Guava) + * 3. Array 转 ArrayList的特色函数 (from Guava) * * 5. 集合运算,集合是否一致,交集,并集, 差集, 补集,from Commons Collection,但对其不合理的地方做了修正 * @@ -101,8 +101,8 @@ public abstract class ListUtil { * * @see com.google.common.collect.Lists#newArrayListWithCapacity(int) */ - public static ArrayList newArrayListWithCapacity(int initialArraySize) { - return new ArrayList(initialArraySize); + public static ArrayList newArrayListWithCapacity(int initSize) { + return new ArrayList(initSize); } /** @@ -207,7 +207,7 @@ public abstract class ListUtil { ///////////////// from JDK Collections的常用函数 /////////////////// /** - * 排序, 采用JDK认为最优的排序算法. + * 升序排序, 采用JDK认为最优的排序算法, 使用元素自身的compareTo()方法 * * @see java.util.Collections#sort(List) */ @@ -216,7 +216,16 @@ public abstract class ListUtil { } /** - * 排序, 采用JDK认为最优的排序算法. + * 倒序排序, 采用JDK认为最优的排序算法,使用元素自身的compareTo()方法 + * + * @see java.util.Collections#sort(List) + */ + public static > void sortReverse(List list) { + Collections.sort(list, Collections.reverseOrder()); + } + + /** + * 升序排序, 采用JDK认为最优的排序算法, 使用Comparetor. * * @see java.util.Collections#sort(List, Comparator) */ @@ -224,6 +233,15 @@ public abstract class ListUtil { Collections.sort(list, c); } + /** + * 倒序排序, 采用JDK认为最优的排序算法, 使用Comparetor + * + * @see java.util.Collections#sort(List, Comparator) + */ + public static void sortReverse(List list, Comparator c) { + Collections.sort(list, Collections.reverseOrder(c)); + } + /** * 二分法快速查找对象, 使用Comparable对象自身的比较. * diff --git a/modules/utils/src/main/java/org/springside/modules/utils/collection/MapUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/collection/MapUtil.java index d745a3ae..23489751 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/collection/MapUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/collection/MapUtil.java @@ -182,7 +182,7 @@ public abstract class MapUtil { * @see com.google.common.collect.Maps#newTreeMap() */ @SuppressWarnings("rawtypes") - public static TreeMap newNavigableMap() { + public static TreeMap newSortedMap() { return new TreeMap(); } @@ -191,7 +191,7 @@ public abstract class MapUtil { * * @see com.google.common.collect.Maps#newTreeMap(Comparator) */ - public static TreeMap newNavigableMap(@Nullable Comparator comparator) { + public static TreeMap newSortedMap(@Nullable Comparator comparator) { return Maps.newTreeMap(comparator); } @@ -219,7 +219,7 @@ public abstract class MapUtil { /** * 根据等号左边的类型,构造类型正确的ConcurrentSkipListMap. */ - public static ConcurrentSkipListMap newConcurrentNavigableMap() { + public static ConcurrentSkipListMap newConcurrentSortedMap() { return new ConcurrentSkipListMap(); } @@ -274,7 +274,7 @@ public abstract class MapUtil { * * @see java.util.Collections#unmodifiableSortedMap(SortedMap) */ - public static SortedMap unmodifiableNavigableMap(final SortedMap m) { + public static SortedMap unmodifiableSortedMap(final SortedMap m) { return Collections.unmodifiableSortedMap(m); } diff --git a/modules/utils/src/main/java/org/springside/modules/utils/collection/QueueUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/collection/QueueUtil.java index 59dcf34f..2bfaaf30 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/collection/QueueUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/collection/QueueUtil.java @@ -1,49 +1,121 @@ package org.springside.modules.utils.collection; import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; import java.util.LinkedList; +import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingQueue; +import org.springside.modules.utils.base.Platforms; + +/** + * Queue工具集. + * + * 1.各种Queue,Dequeue的创建 + * + * 2.LIFO的Stack的创建 + */ public abstract class QueueUtil { - public static ArrayDeque newArrayQueue() { - return new ArrayDeque(); + /** + * 创建ArrayDeque (JDK无ArrayQueue) + * + * 需设置初始长度,默认为16,数组满时成倍扩容 + */ + public static ArrayDeque newArrayDeque(int initSize) { + return new ArrayDeque(initSize); } - public static LinkedList newLinkedQueue() { + /** + * 创建LinkedDeque (LinkedList实现了Deque接口) + */ + public static LinkedList newLinkedDeque() { return new LinkedList(); } + /** + * 创建无阻塞情况下,性能最优的并发队列 + */ public static ConcurrentLinkedQueue newConcurrentNonBlockingQueue() { return new ConcurrentLinkedQueue(); } - public static ArrayBlockingQueue newArrayBlockingQueue(int capacity) { - return new ArrayBlockingQueue(capacity); + /** + * 创建无阻塞情况下,性能最优的并发双端队列 (兼容JDK6的情况) + */ + public static Deque newConcurrentNonBlockingDeque() { + if (Platforms.IS_ATLEASET_JAVA7) { + return new java.util.concurrent.ConcurrentLinkedDeque(); + } else { + return new org.springside.modules.utils.concurrent.jsr166e.ConcurrentLinkedDeque(); + } } - public static LinkedBlockingQueue newLinkedBlockingQeque(int capacity) { - return new LinkedBlockingQueue(capacity); - } - - public static LinkedBlockingQueue newLinkedBlockingQueueWithoutLimit() { + /** + * 创建并发阻塞情况下,长度不受限的队列. + * + * 长度不受限,即队列不会因为满而阻塞,但会因为空而阻塞. + */ + public static LinkedBlockingQueue newBlockingUnlimitQueue() { return new LinkedBlockingQueue(); } /** - * 支持后进先出的栈,用ArrayDeque实现. + * 创建并发阻塞情况下,长度不受限的双端队列. + * + * 长度不受限,即队列不会因为满而阻塞,但会因为空而阻塞. */ - public static ArrayDeque newStack() { - return new ArrayDeque(); + public static LinkedBlockingDeque newBlockingUnlimitDeque() { + return new LinkedBlockingDeque(); } /** - * 支持后进先出的并发栈,用LinkedBlockingDeque实现. + * 创建并发阻塞情况下,长度受限,更节约内存,但共用一把锁的队列(无双端队列实现). */ - public static LinkedBlockingDeque newConcurrentStack() { - return new LinkedBlockingDeque(); + public static ArrayBlockingQueue newArrayBlockingQueue(int capacity) { + return new ArrayBlockingQueue(capacity); } + + /** + * 创建并发阻塞情况下,长度受限,头队尾两把锁, 但使用更多内存的队列. + */ + public static LinkedBlockingQueue newLinkedBlockingQeque(int capacity) { + return new LinkedBlockingQueue(capacity); + } + + /** + * 创建并发阻塞情况下,长度受限,头队尾两把锁, 但使用更多内存的双端队列. + */ + public static LinkedBlockingDeque newBlockingDeque(int capacity) { + return new LinkedBlockingDeque(capacity); + } + + //////////////// Stack /////////// + + /** + * 支持后进先出的栈,用ArrayDeque实现, 经过Collections#asLifoQueue()转换顺序 + * + * 需设置初始长度,默认为16,数组满时成倍扩容 + * + * @see Collections#asLifoQueue() + */ + public static Queue newStack(int initSize) { + return Collections.asLifoQueue(new ArrayDeque(initSize)); + } + + /** + * 支持后进先出的并发栈,用ConcurrentLinkedDeque实现,经过Collections#asLifoQueue()转换顺序 + * + * 兼容了JDK6 + * + * 另对于BlockingQueue接口, JDK暂无Lifo倒转实现,因此只能直接使用未调转顺序的LinkedBlockingDeque + */ + public static Queue newConcurrentStack() { + return (Queue) Collections.asLifoQueue(newConcurrentNonBlockingDeque()); + } + } diff --git a/modules/utils/src/main/java/org/springside/modules/utils/collection/SetUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/collection/SetUtil.java index ae431749..7f9bfa08 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/collection/SetUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/collection/SetUtil.java @@ -64,7 +64,7 @@ public abstract class SetUtil { * @see com.google.common.collect.Sets#newTreeSet() */ @SuppressWarnings("rawtypes") - public static TreeSet newNavigableSet() { + public static TreeSet newSortedSet() { return new TreeSet(); } @@ -73,7 +73,7 @@ public abstract class SetUtil { * * @see com.google.common.collect.Sets#newTreeSet(Comparator) */ - public static TreeSet newNavigableSet(@Nullable Comparator comparator) { + public static TreeSet newSortedSet(@Nullable Comparator comparator) { return Sets.newTreeSet(comparator); } diff --git a/modules/utils/src/main/java/org/springside/modules/utils/collection/type/ComparableComparator.java b/modules/utils/src/main/java/org/springside/modules/utils/collection/type/ComparableComparator.java deleted file mode 100644 index 2c8c6867..00000000 --- a/modules/utils/src/main/java/org/springside/modules/utils/collection/type/ComparableComparator.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.springside.modules.utils.collection.type; - -import java.util.Comparator; - -/** - * 又一个莫名奇妙缺失的类,如果一定要使用Comparator,不能使用Comparable对象自身的compareTo()函数时使用本类 - * - * 直接使用其INSTANCE即可. - * - * @author calvin - */ -public class ComparableComparator> implements Comparator { - - @SuppressWarnings("rawtypes") - public static final ComparableComparator INSTANCE = new ComparableComparator(); - - @Override - public int compare(T o1, T o2) { - return o1.compareTo(o2); - } - -} diff --git a/modules/utils/src/main/java/org/springside/modules/utils/collection/type/ConcurrentHashSet.java b/modules/utils/src/main/java/org/springside/modules/utils/collection/type/ConcurrentHashSet.java index a004d19d..08c9f6f4 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/collection/type/ConcurrentHashSet.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/collection/type/ConcurrentHashSet.java @@ -5,7 +5,8 @@ import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; + +import org.springside.modules.utils.collection.MapUtil; /** * JDK并没有提供ConcurrenHashSet,考虑到JDK的HashSet也是基于HashMap实现的,因此ConcurrenHashSet也由ConcurrenHashMap完成。 @@ -23,7 +24,7 @@ public class ConcurrentHashSet extends AbstractSet implements Set, java private transient Set s; // Its keySet public ConcurrentHashSet() { - m = new ConcurrentHashMap(); + m = MapUtil.newConcurrentHashMap(); s = m.keySet(); } diff --git a/modules/utils/src/main/java/org/springside/modules/utils/collection/type/NaturalOrderComparator.java b/modules/utils/src/main/java/org/springside/modules/utils/collection/type/NaturalOrderComparator.java new file mode 100644 index 00000000..9234543e --- /dev/null +++ b/modules/utils/src/main/java/org/springside/modules/utils/collection/type/NaturalOrderComparator.java @@ -0,0 +1,24 @@ +package org.springside.modules.utils.collection.type; + +import java.util.Comparator; + +/** + * 又一个莫名奇妙缺失的类, 直到JDK8才补回来的类 + * + * 如果一定要使用Comparator,不能使用Comparable对象自身的compareTo()函数时使用本类 + * + * 直接使用其INSTANCE即可. + * + * @author calvin + */ +public class NaturalOrderComparator> implements Comparator { + + @SuppressWarnings("rawtypes") + public static final NaturalOrderComparator INSTANCE = new NaturalOrderComparator(); + + @Override + public int compare(T o1, T o2) { + return o1.compareTo(o2); + } + +} diff --git a/modules/utils/src/main/java/org/springside/modules/utils/concurrent/jsr166e/ConcurrentLinkedDeque.java b/modules/utils/src/main/java/org/springside/modules/utils/concurrent/jsr166e/ConcurrentLinkedDeque.java new file mode 100644 index 00000000..90222dd3 --- /dev/null +++ b/modules/utils/src/main/java/org/springside/modules/utils/concurrent/jsr166e/ConcurrentLinkedDeque.java @@ -0,0 +1,1470 @@ +/* + * Written by Doug Lea and Martin Buchholz with assistance from members of + * JCP JSR-166 Expert Group and released to the public domain, as explained + * at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package org.springside.modules.utils.concurrent.jsr166e; + +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; + +/** + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/ConcurrentLinkedDeque.java 1.6 + * + * An unbounded concurrent {@linkplain Deque deque} based on linked nodes. + * Concurrent insertion, removal, and access operations execute safely + * across multiple threads. + * A {@code ConcurrentLinkedDeque} is an appropriate choice when + * many threads will share access to a common collection. + * Like most other concurrent collection implementations, this class + * does not permit the use of {@code null} elements. + * + *

Iterators are weakly consistent, returning elements + * reflecting the state of the deque at some point at or since the + * creation of the iterator. They do not throw {@link + * java.util.ConcurrentModificationException + * ConcurrentModificationException}, and may proceed concurrently with + * other operations. + * + *

Beware that, unlike in most collections, the {@code size} method + * is NOT a constant-time operation. Because of the + * asynchronous nature of these deques, determining the current number + * of elements requires a traversal of the elements, and so may report + * inaccurate results if this collection is modified during traversal. + * Additionally, the bulk operations {@code addAll}, + * {@code removeAll}, {@code retainAll}, {@code containsAll}, + * {@code equals}, and {@code toArray} are not guaranteed + * to be performed atomically. For example, an iterator operating + * concurrently with an {@code addAll} operation might view only some + * of the added elements. + * + *

This class and its iterator implement all of the optional + * methods of the {@link Deque} and {@link Iterator} interfaces. + * + *

Memory consistency effects: As with other concurrent collections, + * actions in a thread prior to placing an object into a + * {@code ConcurrentLinkedDeque} + * happen-before + * actions subsequent to the access or removal of that element from + * the {@code ConcurrentLinkedDeque} in another thread. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @since 1.7 + * @author Doug Lea + * @author Martin Buchholz + * @param the type of elements held in this collection + */ +public class ConcurrentLinkedDeque + extends AbstractCollection + implements Deque, java.io.Serializable { + + /* + * This is an implementation of a concurrent lock-free deque + * supporting interior removes but not interior insertions, as + * required to support the entire Deque interface. + * + * We extend the techniques developed for ConcurrentLinkedQueue and + * LinkedTransferQueue (see the internal docs for those classes). + * Understanding the ConcurrentLinkedQueue implementation is a + * prerequisite for understanding the implementation of this class. + * + * The data structure is a symmetrical doubly-linked "GC-robust" + * linked list of nodes. We minimize the number of volatile writes + * using two techniques: advancing multiple hops with a single CAS + * and mixing volatile and non-volatile writes of the same memory + * locations. + * + * A node contains the expected E ("item") and links to predecessor + * ("prev") and successor ("next") nodes: + * + * class Node { volatile Node prev, next; volatile E item; } + * + * A node p is considered "live" if it contains a non-null item + * (p.item != null). When an item is CASed to null, the item is + * atomically logically deleted from the collection. + * + * At any time, there is precisely one "first" node with a null + * prev reference that terminates any chain of prev references + * starting at a live node. Similarly there is precisely one + * "last" node terminating any chain of next references starting at + * a live node. The "first" and "last" nodes may or may not be live. + * The "first" and "last" nodes are always mutually reachable. + * + * A new element is added atomically by CASing the null prev or + * next reference in the first or last node to a fresh node + * containing the element. The element's node atomically becomes + * "live" at that point. + * + * A node is considered "active" if it is a live node, or the + * first or last node. Active nodes cannot be unlinked. + * + * A "self-link" is a next or prev reference that is the same node: + * p.prev == p or p.next == p + * Self-links are used in the node unlinking process. Active nodes + * never have self-links. + * + * A node p is active if and only if: + * + * p.item != null || + * (p.prev == null && p.next != p) || + * (p.next == null && p.prev != p) + * + * The deque object has two node references, "head" and "tail". + * The head and tail are only approximations to the first and last + * nodes of the deque. The first node can always be found by + * following prev pointers from head; likewise for tail. However, + * it is permissible for head and tail to be referring to deleted + * nodes that have been unlinked and so may not be reachable from + * any live node. + * + * There are 3 stages of node deletion; + * "logical deletion", "unlinking", and "gc-unlinking". + * + * 1. "logical deletion" by CASing item to null atomically removes + * the element from the collection, and makes the containing node + * eligible for unlinking. + * + * 2. "unlinking" makes a deleted node unreachable from active + * nodes, and thus eventually reclaimable by GC. Unlinked nodes + * may remain reachable indefinitely from an iterator. + * + * Physical node unlinking is merely an optimization (albeit a + * critical one), and so can be performed at our convenience. At + * any time, the set of live nodes maintained by prev and next + * links are identical, that is, the live nodes found via next + * links from the first node is equal to the elements found via + * prev links from the last node. However, this is not true for + * nodes that have already been logically deleted - such nodes may + * be reachable in one direction only. + * + * 3. "gc-unlinking" takes unlinking further by making active + * nodes unreachable from deleted nodes, making it easier for the + * GC to reclaim future deleted nodes. This step makes the data + * structure "gc-robust", as first described in detail by Boehm + * (http://portal.acm.org/citation.cfm?doid=503272.503282). + * + * GC-unlinked nodes may remain reachable indefinitely from an + * iterator, but unlike unlinked nodes, are never reachable from + * head or tail. + * + * Making the data structure GC-robust will eliminate the risk of + * unbounded memory retention with conservative GCs and is likely + * to improve performance with generational GCs. + * + * When a node is dequeued at either end, e.g. via poll(), we would + * like to break any references from the node to active nodes. We + * develop further the use of self-links that was very effective in + * other concurrent collection classes. The idea is to replace + * prev and next pointers with special values that are interpreted + * to mean off-the-list-at-one-end. These are approximations, but + * good enough to preserve the properties we want in our + * traversals, e.g. we guarantee that a traversal will never visit + * the same element twice, but we don't guarantee whether a + * traversal that runs out of elements will be able to see more + * elements later after enqueues at that end. Doing gc-unlinking + * safely is particularly tricky, since any node can be in use + * indefinitely (for example by an iterator). We must ensure that + * the nodes pointed at by head/tail never get gc-unlinked, since + * head/tail are needed to get "back on track" by other nodes that + * are gc-unlinked. gc-unlinking accounts for much of the + * implementation complexity. + * + * Since neither unlinking nor gc-unlinking are necessary for + * correctness, there are many implementation choices regarding + * frequency (eagerness) of these operations. Since volatile + * reads are likely to be much cheaper than CASes, saving CASes by + * unlinking multiple adjacent nodes at a time may be a win. + * gc-unlinking can be performed rarely and still be effective, + * since it is most important that long chains of deleted nodes + * are occasionally broken. + * + * The actual representation we use is that p.next == p means to + * goto the first node (which in turn is reached by following prev + * pointers from head), and p.next == null && p.prev == p means + * that the iteration is at an end and that p is a (static final) + * dummy node, NEXT_TERMINATOR, and not the last active node. + * Finishing the iteration when encountering such a TERMINATOR is + * good enough for read-only traversals, so such traversals can use + * p.next == null as the termination condition. When we need to + * find the last (active) node, for enqueueing a new node, we need + * to check whether we have reached a TERMINATOR node; if so, + * restart traversal from tail. + * + * The implementation is completely directionally symmetrical, + * except that most public methods that iterate through the list + * follow next pointers ("forward" direction). + * + * We believe (without full proof) that all single-element deque + * operations (e.g., addFirst, peekLast, pollLast) are linearizable + * (see Herlihy and Shavit's book). However, some combinations of + * operations are known not to be linearizable. In particular, + * when an addFirst(A) is racing with pollFirst() removing B, it is + * possible for an observer iterating over the elements to observe + * A B C and subsequently observe A C, even though no interior + * removes are ever performed. Nevertheless, iterators behave + * reasonably, providing the "weakly consistent" guarantees. + * + * Empirically, microbenchmarks suggest that this class adds about + * 40% overhead relative to ConcurrentLinkedQueue, which feels as + * good as we can hope for. + */ + + private static final long serialVersionUID = 876323262645176354L; + + /** + * A node from which the first node on list (that is, the unique node p + * with p.prev == null && p.next != p) can be reached in O(1) time. + * Invariants: + * - the first node is always O(1) reachable from head via prev links + * - all live nodes are reachable from the first node via succ() + * - head != null + * - (tmp = head).next != tmp || tmp != head + * - head is never gc-unlinked (but may be unlinked) + * Non-invariants: + * - head.item may or may not be null + * - head may not be reachable from the first or last node, or from tail + */ + private transient volatile Node head; + + /** + * A node from which the last node on list (that is, the unique node p + * with p.next == null && p.prev != p) can be reached in O(1) time. + * Invariants: + * - the last node is always O(1) reachable from tail via next links + * - all live nodes are reachable from the last node via pred() + * - tail != null + * - tail is never gc-unlinked (but may be unlinked) + * Non-invariants: + * - tail.item may or may not be null + * - tail may not be reachable from the first or last node, or from head + */ + private transient volatile Node tail; + + private static final Node PREV_TERMINATOR, NEXT_TERMINATOR; + + @SuppressWarnings("unchecked") + Node prevTerminator() { + return (Node) PREV_TERMINATOR; + } + + @SuppressWarnings("unchecked") + Node nextTerminator() { + return (Node) NEXT_TERMINATOR; + } + + static final class Node { + volatile Node prev; + volatile E item; + volatile Node next; + + Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR + } + + /** + * Constructs a new node. Uses relaxed write because item can + * only be seen after publication via casNext or casPrev. + */ + Node(E item) { + UNSAFE.putObject(this, itemOffset, item); + } + + boolean casItem(E cmp, E val) { + return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); + } + + void lazySetNext(Node val) { + UNSAFE.putOrderedObject(this, nextOffset, val); + } + + boolean casNext(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); + } + + void lazySetPrev(Node val) { + UNSAFE.putOrderedObject(this, prevOffset, val); + } + + boolean casPrev(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val); + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE; + private static final long prevOffset; + private static final long itemOffset; + private static final long nextOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class k = Node.class; + prevOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("prev")); + itemOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("item")); + nextOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("next")); + } catch (Exception e) { + throw new Error(e); + } + } + } + + /** + * Links e as first element. + */ + private void linkFirst(E e) { + checkNotNull(e); + final Node newNode = new Node(e); + + restartFromHead: + for (;;) + for (Node h = head, p = h, q;;) { + if ((q = p.prev) != null && + (q = (p = q).prev) != null) + // Check for head updates every other hop. + // If p == q, we are sure to follow head instead. + p = (h != (h = head)) ? h : q; + else if (p.next == p) // PREV_TERMINATOR + continue restartFromHead; + else { + // p is first node + newNode.lazySetNext(p); // CAS piggyback + if (p.casPrev(null, newNode)) { + // Successful CAS is the linearization point + // for e to become an element of this deque, + // and for newNode to become "live". + if (p != h) // hop two nodes at a time + casHead(h, newNode); // Failure is OK. + return; + } + // Lost CAS race to another thread; re-read prev + } + } + } + + /** + * Links e as last element. + */ + private void linkLast(E e) { + checkNotNull(e); + final Node newNode = new Node(e); + + restartFromTail: + for (;;) + for (Node t = tail, p = t, q;;) { + if ((q = p.next) != null && + (q = (p = q).next) != null) + // Check for tail updates every other hop. + // If p == q, we are sure to follow tail instead. + p = (t != (t = tail)) ? t : q; + else if (p.prev == p) // NEXT_TERMINATOR + continue restartFromTail; + else { + // p is last node + newNode.lazySetPrev(p); // CAS piggyback + if (p.casNext(null, newNode)) { + // Successful CAS is the linearization point + // for e to become an element of this deque, + // and for newNode to become "live". + if (p != t) // hop two nodes at a time + casTail(t, newNode); // Failure is OK. + return; + } + // Lost CAS race to another thread; re-read next + } + } + } + + private static final int HOPS = 2; + + /** + * Unlinks non-null node x. + */ + void unlink(Node x) { + // assert x != null; + // assert x.item == null; + // assert x != PREV_TERMINATOR; + // assert x != NEXT_TERMINATOR; + + final Node prev = x.prev; + final Node next = x.next; + if (prev == null) { + unlinkFirst(x, next); + } else if (next == null) { + unlinkLast(x, prev); + } else { + // Unlink interior node. + // + // This is the common case, since a series of polls at the + // same end will be "interior" removes, except perhaps for + // the first one, since end nodes cannot be unlinked. + // + // At any time, all active nodes are mutually reachable by + // following a sequence of either next or prev pointers. + // + // Our strategy is to find the unique active predecessor + // and successor of x. Try to fix up their links so that + // they point to each other, leaving x unreachable from + // active nodes. If successful, and if x has no live + // predecessor/successor, we additionally try to gc-unlink, + // leaving active nodes unreachable from x, by rechecking + // that the status of predecessor and successor are + // unchanged and ensuring that x is not reachable from + // tail/head, before setting x's prev/next links to their + // logical approximate replacements, self/TERMINATOR. + Node activePred, activeSucc; + boolean isFirst, isLast; + int hops = 1; + + // Find active predecessor + for (Node p = prev; ; ++hops) { + if (p.item != null) { + activePred = p; + isFirst = false; + break; + } + Node q = p.prev; + if (q == null) { + if (p.next == p) + return; + activePred = p; + isFirst = true; + break; + } + else if (p == q) + return; + else + p = q; + } + + // Find active successor + for (Node p = next; ; ++hops) { + if (p.item != null) { + activeSucc = p; + isLast = false; + break; + } + Node q = p.next; + if (q == null) { + if (p.prev == p) + return; + activeSucc = p; + isLast = true; + break; + } + else if (p == q) + return; + else + p = q; + } + + // TODO: better HOP heuristics + if (hops < HOPS + // always squeeze out interior deleted nodes + && (isFirst | isLast)) + return; + + // Squeeze out deleted nodes between activePred and + // activeSucc, including x. + skipDeletedSuccessors(activePred); + skipDeletedPredecessors(activeSucc); + + // Try to gc-unlink, if possible + if ((isFirst | isLast) && + + // Recheck expected state of predecessor and successor + (activePred.next == activeSucc) && + (activeSucc.prev == activePred) && + (isFirst ? activePred.prev == null : activePred.item != null) && + (isLast ? activeSucc.next == null : activeSucc.item != null)) { + + updateHead(); // Ensure x is not reachable from head + updateTail(); // Ensure x is not reachable from tail + + // Finally, actually gc-unlink + x.lazySetPrev(isFirst ? prevTerminator() : x); + x.lazySetNext(isLast ? nextTerminator() : x); + } + } + } + + /** + * Unlinks non-null first node. + */ + private void unlinkFirst(Node first, Node next) { + // assert first != null; + // assert next != null; + // assert first.item == null; + for (Node o = null, p = next, q;;) { + if (p.item != null || (q = p.next) == null) { + if (o != null && p.prev != p && first.casNext(next, p)) { + skipDeletedPredecessors(p); + if (first.prev == null && + (p.next == null || p.item != null) && + p.prev == first) { + + updateHead(); // Ensure o is not reachable from head + updateTail(); // Ensure o is not reachable from tail + + // Finally, actually gc-unlink + o.lazySetNext(o); + o.lazySetPrev(prevTerminator()); + } + } + return; + } + else if (p == q) + return; + else { + o = p; + p = q; + } + } + } + + /** + * Unlinks non-null last node. + */ + private void unlinkLast(Node last, Node prev) { + // assert last != null; + // assert prev != null; + // assert last.item == null; + for (Node o = null, p = prev, q;;) { + if (p.item != null || (q = p.prev) == null) { + if (o != null && p.next != p && last.casPrev(prev, p)) { + skipDeletedSuccessors(p); + if (last.next == null && + (p.prev == null || p.item != null) && + p.next == last) { + + updateHead(); // Ensure o is not reachable from head + updateTail(); // Ensure o is not reachable from tail + + // Finally, actually gc-unlink + o.lazySetPrev(o); + o.lazySetNext(nextTerminator()); + } + } + return; + } + else if (p == q) + return; + else { + o = p; + p = q; + } + } + } + + /** + * Guarantees that any node which was unlinked before a call to + * this method will be unreachable from head after it returns. + * Does not guarantee to eliminate slack, only that head will + * point to a node that was active while this method was running. + */ + private final void updateHead() { + // Either head already points to an active node, or we keep + // trying to cas it to the first node until it does. + Node h, p, q; + restartFromHead: + while ((h = head).item == null && (p = h.prev) != null) { + for (;;) { + if ((q = p.prev) == null || + (q = (p = q).prev) == null) { + // It is possible that p is PREV_TERMINATOR, + // but if so, the CAS is guaranteed to fail. + if (casHead(h, p)) + return; + else + continue restartFromHead; + } + else if (h != head) + continue restartFromHead; + else + p = q; + } + } + } + + /** + * Guarantees that any node which was unlinked before a call to + * this method will be unreachable from tail after it returns. + * Does not guarantee to eliminate slack, only that tail will + * point to a node that was active while this method was running. + */ + private final void updateTail() { + // Either tail already points to an active node, or we keep + // trying to cas it to the last node until it does. + Node t, p, q; + restartFromTail: + while ((t = tail).item == null && (p = t.next) != null) { + for (;;) { + if ((q = p.next) == null || + (q = (p = q).next) == null) { + // It is possible that p is NEXT_TERMINATOR, + // but if so, the CAS is guaranteed to fail. + if (casTail(t, p)) + return; + else + continue restartFromTail; + } + else if (t != tail) + continue restartFromTail; + else + p = q; + } + } + } + + private void skipDeletedPredecessors(Node x) { + whileActive: + do { + Node prev = x.prev; + // assert prev != null; + // assert x != NEXT_TERMINATOR; + // assert x != PREV_TERMINATOR; + Node p = prev; + findActive: + for (;;) { + if (p.item != null) + break findActive; + Node q = p.prev; + if (q == null) { + if (p.next == p) + continue whileActive; + break findActive; + } + else if (p == q) + continue whileActive; + else + p = q; + } + + // found active CAS target + if (prev == p || x.casPrev(prev, p)) + return; + + } while (x.item != null || x.next == null); + } + + private void skipDeletedSuccessors(Node x) { + whileActive: + do { + Node next = x.next; + // assert next != null; + // assert x != NEXT_TERMINATOR; + // assert x != PREV_TERMINATOR; + Node p = next; + findActive: + for (;;) { + if (p.item != null) + break findActive; + Node q = p.next; + if (q == null) { + if (p.prev == p) + continue whileActive; + break findActive; + } + else if (p == q) + continue whileActive; + else + p = q; + } + + // found active CAS target + if (next == p || x.casNext(next, p)) + return; + + } while (x.item != null || x.prev == null); + } + + /** + * Returns the successor of p, or the first node if p.next has been + * linked to self, which will only be true if traversing with a + * stale pointer that is now off the list. + */ + final Node succ(Node p) { + // TODO: should we skip deleted nodes here? + Node q = p.next; + return (p == q) ? first() : q; + } + + /** + * Returns the predecessor of p, or the last node if p.prev has been + * linked to self, which will only be true if traversing with a + * stale pointer that is now off the list. + */ + final Node pred(Node p) { + Node q = p.prev; + return (p == q) ? last() : q; + } + + /** + * Returns the first node, the unique node p for which: + * p.prev == null && p.next != p + * The returned node may or may not be logically deleted. + * Guarantees that head is set to the returned node. + */ + Node first() { + restartFromHead: + for (;;) + for (Node h = head, p = h, q;;) { + if ((q = p.prev) != null && + (q = (p = q).prev) != null) + // Check for head updates every other hop. + // If p == q, we are sure to follow head instead. + p = (h != (h = head)) ? h : q; + else if (p == h + // It is possible that p is PREV_TERMINATOR, + // but if so, the CAS is guaranteed to fail. + || casHead(h, p)) + return p; + else + continue restartFromHead; + } + } + + /** + * Returns the last node, the unique node p for which: + * p.next == null && p.prev != p + * The returned node may or may not be logically deleted. + * Guarantees that tail is set to the returned node. + */ + Node last() { + restartFromTail: + for (;;) + for (Node t = tail, p = t, q;;) { + if ((q = p.next) != null && + (q = (p = q).next) != null) + // Check for tail updates every other hop. + // If p == q, we are sure to follow tail instead. + p = (t != (t = tail)) ? t : q; + else if (p == t + // It is possible that p is NEXT_TERMINATOR, + // but if so, the CAS is guaranteed to fail. + || casTail(t, p)) + return p; + else + continue restartFromTail; + } + } + + // Minor convenience utilities + + /** + * Throws NullPointerException if argument is null. + * + * @param v the element + */ + private static void checkNotNull(Object v) { + if (v == null) + throw new NullPointerException(); + } + + /** + * Returns element unless it is null, in which case throws + * NoSuchElementException. + * + * @param v the element + * @return the element + */ + private E screenNullResult(E v) { + if (v == null) + throw new NoSuchElementException(); + return v; + } + + /** + * Creates an array list and fills it with elements of this list. + * Used by toArray. + * + * @return the array list + */ + private ArrayList toArrayList() { + ArrayList list = new ArrayList(); + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null) + list.add(item); + } + return list; + } + + /** + * Constructs an empty deque. + */ + public ConcurrentLinkedDeque() { + head = tail = new Node(null); + } + + /** + * Constructs a deque initially containing the elements of + * the given collection, added in traversal order of the + * collection's iterator. + * + * @param c the collection of elements to initially contain + * @throws NullPointerException if the specified collection or any + * of its elements are null + */ + public ConcurrentLinkedDeque(Collection c) { + // Copy c into a private chain of Nodes + Node h = null, t = null; + for (E e : c) { + checkNotNull(e); + Node newNode = new Node(e); + if (h == null) + h = t = newNode; + else { + t.lazySetNext(newNode); + newNode.lazySetPrev(t); + t = newNode; + } + } + initHeadTail(h, t); + } + + /** + * Initializes head and tail, ensuring invariants hold. + */ + private void initHeadTail(Node h, Node t) { + if (h == t) { + if (h == null) + h = t = new Node(null); + else { + // Avoid edge case of a single Node with non-null item. + Node newNode = new Node(null); + t.lazySetNext(newNode); + newNode.lazySetPrev(t); + t = newNode; + } + } + head = h; + tail = t; + } + + /** + * Inserts the specified element at the front of this deque. + * As the deque is unbounded, this method will never throw + * {@link IllegalStateException}. + * + * @throws NullPointerException if the specified element is null + */ + public void addFirst(E e) { + linkFirst(e); + } + + /** + * Inserts the specified element at the end of this deque. + * As the deque is unbounded, this method will never throw + * {@link IllegalStateException}. + * + *

This method is equivalent to {@link #add}. + * + * @throws NullPointerException if the specified element is null + */ + public void addLast(E e) { + linkLast(e); + } + + /** + * Inserts the specified element at the front of this deque. + * As the deque is unbounded, this method will never return {@code false}. + * + * @return {@code true} (as specified by {@link Deque#offerFirst}) + * @throws NullPointerException if the specified element is null + */ + public boolean offerFirst(E e) { + linkFirst(e); + return true; + } + + /** + * Inserts the specified element at the end of this deque. + * As the deque is unbounded, this method will never return {@code false}. + * + *

This method is equivalent to {@link #add}. + * + * @return {@code true} (as specified by {@link Deque#offerLast}) + * @throws NullPointerException if the specified element is null + */ + public boolean offerLast(E e) { + linkLast(e); + return true; + } + + public E peekFirst() { + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null) + return item; + } + return null; + } + + public E peekLast() { + for (Node p = last(); p != null; p = pred(p)) { + E item = p.item; + if (item != null) + return item; + } + return null; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E getFirst() { + return screenNullResult(peekFirst()); + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E getLast() { + return screenNullResult(peekLast()); + } + + public E pollFirst() { + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && p.casItem(item, null)) { + unlink(p); + return item; + } + } + return null; + } + + public E pollLast() { + for (Node p = last(); p != null; p = pred(p)) { + E item = p.item; + if (item != null && p.casItem(item, null)) { + unlink(p); + return item; + } + } + return null; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E removeFirst() { + return screenNullResult(pollFirst()); + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E removeLast() { + return screenNullResult(pollLast()); + } + + // *** Queue and stack methods *** + + /** + * Inserts the specified element at the tail of this deque. + * As the deque is unbounded, this method will never return {@code false}. + * + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + return offerLast(e); + } + + /** + * Inserts the specified element at the tail of this deque. + * As the deque is unbounded, this method will never throw + * {@link IllegalStateException} or return {@code false}. + * + * @return {@code true} (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return offerLast(e); + } + + public E poll() { return pollFirst(); } + public E remove() { return removeFirst(); } + public E peek() { return peekFirst(); } + public E element() { return getFirst(); } + public void push(E e) { addFirst(e); } + public E pop() { return removeFirst(); } + + /** + * Removes the first element {@code e} such that + * {@code o.equals(e)}, if such an element exists in this deque. + * If the deque does not contain the element, it is unchanged. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if the deque contained the specified element + * @throws NullPointerException if the specified element is null + */ + public boolean removeFirstOccurrence(Object o) { + checkNotNull(o); + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && o.equals(item) && p.casItem(item, null)) { + unlink(p); + return true; + } + } + return false; + } + + /** + * Removes the last element {@code e} such that + * {@code o.equals(e)}, if such an element exists in this deque. + * If the deque does not contain the element, it is unchanged. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if the deque contained the specified element + * @throws NullPointerException if the specified element is null + */ + public boolean removeLastOccurrence(Object o) { + checkNotNull(o); + for (Node p = last(); p != null; p = pred(p)) { + E item = p.item; + if (item != null && o.equals(item) && p.casItem(item, null)) { + unlink(p); + return true; + } + } + return false; + } + + /** + * Returns {@code true} if this deque contains at least one + * element {@code e} such that {@code o.equals(e)}. + * + * @param o element whose presence in this deque is to be tested + * @return {@code true} if this deque contains the specified element + */ + public boolean contains(Object o) { + if (o == null) return false; + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && o.equals(item)) + return true; + } + return false; + } + + /** + * Returns {@code true} if this collection contains no elements. + * + * @return {@code true} if this collection contains no elements + */ + public boolean isEmpty() { + return peekFirst() == null; + } + + /** + * Returns the number of elements in this deque. If this deque + * contains more than {@code Integer.MAX_VALUE} elements, it + * returns {@code Integer.MAX_VALUE}. + * + *

Beware that, unlike in most collections, this method is + * NOT a constant-time operation. Because of the + * asynchronous nature of these deques, determining the current + * number of elements requires traversing them all to count them. + * Additionally, it is possible for the size to change during + * execution of this method, in which case the returned result + * will be inaccurate. Thus, this method is typically not very + * useful in concurrent applications. + * + * @return the number of elements in this deque + */ + public int size() { + int count = 0; + for (Node p = first(); p != null; p = succ(p)) + if (p.item != null) + // Collection.size() spec says to max out + if (++count == Integer.MAX_VALUE) + break; + return count; + } + + /** + * Removes the first element {@code e} such that + * {@code o.equals(e)}, if such an element exists in this deque. + * If the deque does not contain the element, it is unchanged. + * + * @param o element to be removed from this deque, if present + * @return {@code true} if the deque contained the specified element + * @throws NullPointerException if the specified element is null + */ + public boolean remove(Object o) { + return removeFirstOccurrence(o); + } + + /** + * Appends all of the elements in the specified collection to the end of + * this deque, in the order that they are returned by the specified + * collection's iterator. Attempts to {@code addAll} of a deque to + * itself result in {@code IllegalArgumentException}. + * + * @param c the elements to be inserted into this deque + * @return {@code true} if this deque changed as a result of the call + * @throws NullPointerException if the specified collection or any + * of its elements are null + * @throws IllegalArgumentException if the collection is this deque + */ + public boolean addAll(Collection c) { + if (c == this) + // As historically specified in AbstractQueue#addAll + throw new IllegalArgumentException(); + + // Copy c into a private chain of Nodes + Node beginningOfTheEnd = null, last = null; + for (E e : c) { + checkNotNull(e); + Node newNode = new Node(e); + if (beginningOfTheEnd == null) + beginningOfTheEnd = last = newNode; + else { + last.lazySetNext(newNode); + newNode.lazySetPrev(last); + last = newNode; + } + } + if (beginningOfTheEnd == null) + return false; + + // Atomically append the chain at the tail of this collection + restartFromTail: + for (;;) + for (Node t = tail, p = t, q;;) { + if ((q = p.next) != null && + (q = (p = q).next) != null) + // Check for tail updates every other hop. + // If p == q, we are sure to follow tail instead. + p = (t != (t = tail)) ? t : q; + else if (p.prev == p) // NEXT_TERMINATOR + continue restartFromTail; + else { + // p is last node + beginningOfTheEnd.lazySetPrev(p); // CAS piggyback + if (p.casNext(null, beginningOfTheEnd)) { + // Successful CAS is the linearization point + // for all elements to be added to this deque. + if (!casTail(t, last)) { + // Try a little harder to update tail, + // since we may be adding many elements. + t = tail; + if (last.next == null) + casTail(t, last); + } + return true; + } + // Lost CAS race to another thread; re-read next + } + } + } + + /** + * Removes all of the elements from this deque. + */ + public void clear() { + while (pollFirst() != null) + ; + } + + /** + * Returns an array containing all of the elements in this deque, in + * proper sequence (from first to last element). + * + *

The returned array will be "safe" in that no references to it are + * maintained by this deque. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this deque + */ + public Object[] toArray() { + return toArrayList().toArray(); + } + + /** + * Returns an array containing all of the elements in this deque, + * in proper sequence (from first to last element); the runtime + * type of the returned array is that of the specified array. If + * the deque fits in the specified array, it is returned therein. + * Otherwise, a new array is allocated with the runtime type of + * the specified array and the size of this deque. + * + *

If this deque fits in the specified array with room to spare + * (i.e., the array has more elements than this deque), the element in + * the array immediately following the end of the deque is set to + * {@code null}. + * + *

Like the {@link #toArray()} method, this method acts as + * bridge between array-based and collection-based APIs. Further, + * this method allows precise control over the runtime type of the + * output array, and may, under certain circumstances, be used to + * save allocation costs. + * + *

Suppose {@code x} is a deque known to contain only strings. + * The following code can be used to dump the deque into a newly + * allocated array of {@code String}: + * + *

 {@code String[] y = x.toArray(new String[0]);}
+ * + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. + * + * @param a the array into which the elements of the deque are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this deque + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this deque + * @throws NullPointerException if the specified array is null + */ + public T[] toArray(T[] a) { + return toArrayList().toArray(a); + } + + /** + * Returns an iterator over the elements in this deque in proper sequence. + * The elements will be returned in order from first (head) to last (tail). + * + *

The returned iterator is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * ConcurrentModificationException}, and guarantees to traverse + * elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications + * subsequent to construction. + * + * @return an iterator over the elements in this deque in proper sequence + */ + public Iterator iterator() { + return new Itr(); + } + + /** + * Returns an iterator over the elements in this deque in reverse + * sequential order. The elements will be returned in order from + * last (tail) to first (head). + * + *

The returned iterator is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * ConcurrentModificationException}, and guarantees to traverse + * elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications + * subsequent to construction. + * + * @return an iterator over the elements in this deque in reverse order + */ + public Iterator descendingIterator() { + return new DescendingItr(); + } + + private abstract class AbstractItr implements Iterator { + /** + * Next node to return item for. + */ + private Node nextNode; + + /** + * nextItem holds on to item fields because once we claim + * that an element exists in hasNext(), we must return it in + * the following next() call even if it was in the process of + * being removed when hasNext() was called. + */ + private E nextItem; + + /** + * Node returned by most recent call to next. Needed by remove. + * Reset to null if this element is deleted by a call to remove. + */ + private Node lastRet; + + abstract Node startNode(); + abstract Node nextNode(Node p); + + AbstractItr() { + advance(); + } + + /** + * Sets nextNode and nextItem to next valid node, or to null + * if no such. + */ + private void advance() { + lastRet = nextNode; + + Node p = (nextNode == null) ? startNode() : nextNode(nextNode); + for (;; p = nextNode(p)) { + if (p == null) { + // p might be active end or TERMINATOR node; both are OK + nextNode = null; + nextItem = null; + break; + } + E item = p.item; + if (item != null) { + nextNode = p; + nextItem = item; + break; + } + } + } + + public boolean hasNext() { + return nextItem != null; + } + + public E next() { + E item = nextItem; + if (item == null) throw new NoSuchElementException(); + advance(); + return item; + } + + public void remove() { + Node l = lastRet; + if (l == null) throw new IllegalStateException(); + l.item = null; + unlink(l); + lastRet = null; + } + } + + /** Forward iterator */ + private class Itr extends AbstractItr { + Node startNode() { return first(); } + Node nextNode(Node p) { return succ(p); } + } + + /** Descending iterator */ + private class DescendingItr extends AbstractItr { + Node startNode() { return last(); } + Node nextNode(Node p) { return pred(p); } + } + + /** + * Saves the state to a stream (that is, serializes it). + * + * @serialData All of the elements (each an {@code E}) in + * the proper order, followed by a null + * @param s the stream + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + + // Write out any hidden stuff + s.defaultWriteObject(); + + // Write out all elements in the proper order. + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null) + s.writeObject(item); + } + + // Use trailing null as sentinel + s.writeObject(null); + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + // Read in elements until trailing null sentinel found + Node h = null, t = null; + Object item; + while ((item = s.readObject()) != null) { + @SuppressWarnings("unchecked") + Node newNode = new Node((E) item); + if (h == null) + h = t = newNode; + else { + t.lazySetNext(newNode); + newNode.lazySetPrev(t); + t = newNode; + } + } + initHeadTail(h, t); + } + + + private boolean casHead(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + } + + private boolean casTail(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + private static final long tailOffset; + static { + PREV_TERMINATOR = new Node(); + PREV_TERMINATOR.next = PREV_TERMINATOR; + NEXT_TERMINATOR = new Node(); + NEXT_TERMINATOR.prev = NEXT_TERMINATOR; + try { + UNSAFE = getUnsafe(); + Class k = ConcurrentLinkedDeque.class; + headOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("head")); + tailOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("tail")); + } catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController.doPrivileged + (new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + }}); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/modules/utils/src/main/java/org/springside/modules/utils/io/FilePathUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/io/FilePathUtil.java index 6225a6f7..2ca44775 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/io/FilePathUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/io/FilePathUtil.java @@ -31,7 +31,7 @@ public abstract class FilePathUtil { public static String getFileExtension(File file) { return Files.getFileExtension(file.getName()); } - + /** * 获取文件名的扩展名部分(不包含.) */ @@ -49,11 +49,24 @@ public abstract class FilePathUtil { /** * 以拼接路径名 */ - public static String contact(String baseName, String appendName) { - if (MoreStringUtil.endWith(baseName, Platforms.FILE_PATH_SEPARATOR_CHAR)) { - return baseName + appendName; - } else { - return baseName + Platforms.FILE_PATH_SEPARATOR_CHAR + appendName; + public static String contact(String baseName, String... appendName) { + if (appendName.length == 0) { + return baseName; } + + String contactName; + if (MoreStringUtil.endWith(baseName, Platforms.FILE_PATH_SEPARATOR_CHAR)) { + contactName = baseName + appendName[0]; + } else { + contactName = baseName + Platforms.FILE_PATH_SEPARATOR_CHAR + appendName[0]; + } + + if (appendName.length > 1) { + for (int i = 1; i < appendName.length; i++) { + contactName += Platforms.FILE_PATH_SEPARATOR_CHAR + appendName[i]; + } + } + + return contactName; } } diff --git a/modules/utils/src/main/java/org/springside/modules/utils/reflect/BeanMapper.java b/modules/utils/src/main/java/org/springside/modules/utils/reflect/BeanMapper.java index 5014ec47..8a7c6f95 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/reflect/BeanMapper.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/reflect/BeanMapper.java @@ -10,9 +10,11 @@ import java.util.List; import ma.glasnost.orika.MapperFacade; import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.impl.DefaultMapperFactory; +import ma.glasnost.orika.metadata.Type; +import ma.glasnost.orika.metadata.TypeFactory; /** - * 简单封装orika, 实现深度转换Bean<->Bean的Mapper. + * 简单封装orika, 实现深度的BeanOfClasssA<->BeanOfClassB复制 * * 不要是用Apache Common BeanUtils进行类复制,每次就行反射查询对象的属性列表, 非常缓慢. */ @@ -26,16 +28,64 @@ public abstract class BeanMapper { } /** - * 基于传入的对象类型复制出新对象. + * 简单的复制出新类型对象. + * + * 通过source.getClass() 获得源Class */ public static D map(S source, Class destinationClass) { return mapper.map(source, destinationClass); } /** - * 基于传入的对象类型复制出新对象列表到List. + * 极致性能的复制出新类型对象. + * + * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入 */ - public static List mapList(Iterable sourceList, Class destinationClass) { - return mapper.mapAsList(sourceList, destinationClass); + public static D map(S source, Type sourceType, Type destinationType) { + return mapper.map(source, sourceType, destinationType); } + + /** + * 简单的复制出新对象列表到ArrayList + * + * 不建议使用mapper.mapAsList(Iterable,Class)接口, sourceClass需要反射,实在有点慢 + */ + public static List mapList(Iterable sourceList, Class sourceClass, Class destinationClass) { + return mapper.mapAsList(sourceList, TypeFactory.valueOf(sourceClass), TypeFactory.valueOf(destinationClass)); + } + + /** + * 极致性能的复制出新类型对象到ArrayList. + * + * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入 + */ + public static List mapList(Iterable sourceList, Type sourceType, Type destinationType) { + return mapper.mapAsList(sourceList, sourceType, destinationType); + } + + /** + * 简单复制出新对象列表到数组 + * + * 通过source.getComponentType() 获得源Class + */ + public static D[] mapArray(final D[] destination, final S[] source, final Class destinationClass) { + return mapper.mapAsArray(destination, source, destinationClass); + } + + /** + * 极致性能的复制出新类型对象到数组 + * + * 预先通过BeanMapper.getType() 静态获取并缓存Type类型,在此处传入 + */ + public static D[] mapArray(D[] destination, S[] source, Type sourceType, Type destinationType) { + return mapper.mapAsArray(destination, source, sourceType, destinationType); + } + + /** + * 预先获取orika转换所需要的Type,避免每次转换. + */ + public static Type getType(final Class rawType) { + return TypeFactory.valueOf(rawType); + } + } \ No newline at end of file diff --git a/modules/utils/src/main/java/org/springside/modules/utils/reflect/FastMethodInvoker.java b/modules/utils/src/main/java/org/springside/modules/utils/reflect/FastMethodInvoker.java index 7edd5431..2c45ff5f 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/reflect/FastMethodInvoker.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/reflect/FastMethodInvoker.java @@ -3,6 +3,7 @@ package org.springside.modules.utils.reflect; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentMap; +import org.springside.modules.utils.base.ExceptionUtil; import org.springside.modules.utils.collection.MapUtil; import org.springside.modules.utils.collection.MapUtil.ValueCreator; @@ -10,29 +11,42 @@ import net.sf.cglib.reflect.FastClass; import net.sf.cglib.reflect.FastMethod; /** - * 基于Cglib, 基于字节码生成的快速反射. + * 基于Cglib, 基于字节码生成的快速反射, 比JDK反射的速度快. * * @author calvin */ public class FastMethodInvoker { - private static ConcurrentMap, FastClass> classMap = MapUtil.newConcurrentHashMap(); + // 存放cglib的FastClass + private static ConcurrentMap, FastClass> fastClassMap = MapUtil.newConcurrentHashMap(); /** - * 获取cglib生成的FastMethod + * 获取cglib生成的FastMethod,并保存起来留作后用 */ - public static FastMethod getFastMethod(final Object obj, final String methodName, Class... parameterTypes) { + public static FastMethod getFastMethod(final Class clz, final String methodName, Class... parameterTypes) { - final Class clz = obj.getClass(); - FastClass fastClz = MapUtil.createIfAbsent(classMap, clz, new ValueCreator() { + FastClass fastClz = MapUtil.createIfAbsent(fastClassMap, clz, new ValueCreator() { @Override public FastClass get() { return FastClass.create(clz); } }); - Method method = ReflectionUtil.getAccessibleMethod(obj.getClass(), methodName, parameterTypes); + Method method = ReflectionUtil.getAccessibleMethod(clz, methodName, parameterTypes); + return fastClz.getMethod(method); } + /** + * 调用方法 + */ + @SuppressWarnings("unchecked") + public static T invoke(FastMethod method, Object obj, Object... args) { + try { + return (T) method.invoke(obj, args); + } catch (Exception e) { + throw ExceptionUtil.unchecked(ExceptionUtil.unwrap(e)); + } + } + } diff --git a/modules/utils/src/main/java/org/springside/modules/utils/reflect/ReflectionUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/reflect/ReflectionUtil.java index 643d03bc..35b81337 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/reflect/ReflectionUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/reflect/ReflectionUtil.java @@ -42,9 +42,9 @@ public class ReflectionUtil { private static Logger logger = LoggerFactory.getLogger(ReflectionUtil.class); - /////////// 属性相关函数/////////// + /////////// 属性相关函数, 用于一次性调用的情况 /////////// /** - * 调用Getter方法,无视private/protected修饰符. + * 调用Getter方法, 无视private/protected修饰符. */ public static T invokeGetter(Object obj, String propertyName) { String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(propertyName); @@ -173,7 +173,7 @@ public class ReflectionUtil { * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. */ public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) { - Method method = getAccessibleMethodByName(obj.getClass(), methodName); + Method method = findAccessibleMethodByName(obj.getClass(), methodName); if (method == null) { throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + ']'); } @@ -195,8 +195,7 @@ public class ReflectionUtil { public static Field getAccessibleField(final Class clazz, final String fieldName) { Validate.notNull(clazz, "clazz can't be null"); Validate.notEmpty(fieldName, "fieldName can't be blank"); - for (Class superClass = clazz; superClass != Object.class; superClass = superClass - .getSuperclass()) { + for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) { try { Field field = superClass.getDeclaredField(fieldName); makeAccessible(field); @@ -224,8 +223,10 @@ public class ReflectionUtil { Validate.notEmpty(methodName, "methodName can't be blank"); Class[] theParameterTypes = ArrayUtils.nullToEmpty(parameterTypes); - for (Class searchType = clazz; searchType != Object.class; searchType = searchType - .getSuperclass()) { + // 处理原子类型与对象类型的兼容 + wrapClassses(theParameterTypes); + + for (Class searchType = clazz; searchType != Object.class; searchType = searchType.getSuperclass()) { try { Method method = searchType.getDeclaredMethod(methodName, theParameterTypes); makeAccessible(method); @@ -248,12 +249,11 @@ public class ReflectionUtil { * * 因为class.getMethods() 不能获取父类的private函数, 因此采用循环向上的getMethods(); */ - public static Method getAccessibleMethodByName(final Class clazz, final String methodName) { + public static Method findAccessibleMethodByName(final Class clazz, final String methodName) { Validate.notNull(clazz, "clazz can't be null"); Validate.notEmpty(methodName, "methodName can't be blank"); - for (Class searchType =clazz; searchType != Object.class; searchType = searchType - .getSuperclass()) { + for (Class searchType = clazz; searchType != Object.class; searchType = searchType.getSuperclass()) { Method[] methods = searchType.getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals(methodName)) { @@ -309,4 +309,34 @@ public class ReflectionUtil { } return new RuntimeException("Unexpected Checked Exception.", e); } + + /** + * 兼容原子类型与非原子类型的转换,不考虑依赖两者不同来区分不同函数的场景 + */ + private static void wrapClassses(Class[] source) { + for (int i = 0; i < source.length; i++) { + source[i] = wrapClass(source[i]); + } + } + + /** + * 兼容原子类型与非原子类型的转换,不考虑依赖两者不同来区分不同函数的场景 + */ + private static Class wrapClass(Class clazz) { + if (clazz.equals(Integer.class)) { + return Integer.TYPE; + } else if (clazz.equals(Long.class)) { + return Long.TYPE; + } else if (clazz.equals(Double.class)) { + return Double.TYPE; + } else if (clazz.equals(Float.class)) { + return Float.TYPE; + } else if (clazz.equals(Boolean.class)) { + return Boolean.TYPE; + } else if (clazz.equals(Byte.class)) { + return Byte.TYPE; + } + + return clazz; + } } diff --git a/modules/utils/src/main/java/org/springside/modules/utils/text/TextValidator.java b/modules/utils/src/main/java/org/springside/modules/utils/text/TextValidator.java index ad73c205..c9e6721f 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/text/TextValidator.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/text/TextValidator.java @@ -8,7 +8,7 @@ import org.springside.modules.utils.base.annotation.Nullable; /** * 通过正则表达判断是否正确的手机号,固定电话,身份证,邮箱等. * - * 从AndroidUtilCode的RegexUtils移植, 优化正则表达式为预编译, 并修改了TEL的正则表达式. + * 从AndroidUtilCode的RegexUtils移植, 性能优化将正则表达式为预编译, 并修改了TEL的正则表达式. * * @author calvin */ diff --git a/modules/utils/src/main/java/org/springside/modules/utils/time/DateUtil.java b/modules/utils/src/main/java/org/springside/modules/utils/time/DateUtil.java index 26be633a..5ec481a2 100644 --- a/modules/utils/src/main/java/org/springside/modules/utils/time/DateUtil.java +++ b/modules/utils/src/main/java/org/springside/modules/utils/time/DateUtil.java @@ -54,7 +54,6 @@ public abstract class DateUtil { return !date.before(start) && !date.after(end); } - /////////// 日期设置处理 ///////// /** diff --git a/modules/utils/src/test/java/org/springside/modules/utils/base/ExceptionUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/base/ExceptionUtilTest.java index 2ae4386c..f3256ea1 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/base/ExceptionUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/base/ExceptionUtilTest.java @@ -11,7 +11,6 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.ExecutionException; -import org.junit.Assert; import org.junit.Test; import org.springside.modules.utils.base.ExceptionUtil.CloneableException; import org.springside.modules.utils.base.ExceptionUtil.CloneableRuntimeException; @@ -33,7 +32,7 @@ public class ExceptionUtilTest { Exception exception = new Exception("my exception"); try { ExceptionUtil.unchecked(exception); - Assert.fail("should fail"); + fail("should fail before"); } catch (Throwable t) { assertThat(t.getCause()).isSameAs(exception); } @@ -42,7 +41,7 @@ public class ExceptionUtilTest { RuntimeException runtimeException = new RuntimeException("haha"); try { ExceptionUtil.unchecked(runtimeException); - Assert.fail("should fail"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isSameAs(runtimeException); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/base/PropertiesUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/base/PropertiesUtilTest.java index d5f1a9fe..bb96ce8d 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/base/PropertiesUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/base/PropertiesUtilTest.java @@ -4,7 +4,6 @@ import static org.assertj.core.api.Assertions.*; import java.util.Properties; -import org.junit.Assert; import org.junit.Test; import org.springside.modules.utils.base.PropertiesUtil.PropertiesListener; import org.springside.modules.utils.number.RandomUtil; @@ -106,7 +105,7 @@ public class PropertiesUtilTest { try { // 非法字符 String result4 = PropertiesUtil.stringSystemProperty(name, name, "123"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/collection/CollectionUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/collection/CollectionUtilTest.java index 7e25016c..2c2ccee1 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/collection/CollectionUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/collection/CollectionUtilTest.java @@ -16,12 +16,12 @@ public class CollectionUtilTest { List list2 = ListUtil.newArrayList("a", "b", "c"); List list3 = ListUtil.newArrayList("a"); - Set set1 = SetUtil.newNavigableSet(); + Set set1 = SetUtil.newSortedSet(); set1.add("a"); set1.add("b"); set1.add("c"); - Set set2 = SetUtil.newNavigableSet(); + Set set2 = SetUtil.newSortedSet(); set2.add("a"); assertThat(CollectionUtil.isEmpty(list1)).isTrue(); diff --git a/modules/utils/src/test/java/org/springside/modules/utils/collection/ListUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/collection/ListUtilTest.java index 88706bc4..6520a659 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/collection/ListUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/collection/ListUtilTest.java @@ -5,9 +5,8 @@ import static org.assertj.core.api.Assertions.*; import java.util.List; import java.util.Random; -import org.junit.Assert; import org.junit.Test; -import org.springside.modules.utils.collection.type.ComparableComparator; +import org.springside.modules.utils.collection.type.NaturalOrderComparator; public class ListUtilTest { @Test @@ -44,7 +43,7 @@ public class ListUtilTest { try { list1.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } @@ -53,7 +52,7 @@ public class ListUtilTest { assertThat(list4).hasSize(1).contains("1"); try { list4.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } @@ -63,7 +62,7 @@ public class ListUtilTest { try { list6.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } @@ -108,15 +107,29 @@ public class ListUtilTest { ListUtil.shuffle(list, new Random()); System.out.println("shuffle list:" + list); - ListUtil.sort(list, ComparableComparator.INSTANCE); + + ListUtil.sort(list, NaturalOrderComparator.INSTANCE); assertThat(list).hasSize(7).containsExactly("a", "b", "c", "d", "e", "g", "i"); + assertThat(ListUtil.binarySearch(list, "b")).isEqualTo(1); - assertThat(ListUtil.binarySearch(list, "b", ComparableComparator.INSTANCE)).isEqualTo(1); + assertThat(ListUtil.binarySearch(list, "b", NaturalOrderComparator.INSTANCE)).isEqualTo(1); assertThat(ListUtil.binarySearch(list, "x")).isEqualTo(-8); + + //reverse List list8 = ListUtil.reverse(list); - + assertThat(list8).hasSize(7).containsExactly("i", "g", "e", "d", "c", "b", "a"); + + + + //sortReverse + ListUtil.shuffle(list8); + ListUtil.sortReverse(list8); + assertThat(list8).hasSize(7).containsExactly("i", "g", "e", "d", "c", "b", "a"); + + ListUtil.shuffle(list8); + ListUtil.sortReverse(list8,NaturalOrderComparator.INSTANCE); assertThat(list8).hasSize(7).containsExactly("i", "g", "e", "d", "c", "b", "a"); } @@ -127,7 +140,7 @@ public class ListUtilTest { try { list.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } @@ -137,7 +150,7 @@ public class ListUtilTest { try { list2.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } @@ -163,7 +176,7 @@ public class ListUtilTest { assertThat(ListUtil.isEqual(list1, list1)).isTrue(); assertThat(ListUtil.isEqual(list1, list2)).isTrue(); - + assertThat(ListUtil.isEqual(list1, list3)).isFalse(); assertThat(ListUtil.isEqual(list1, list4)).isFalse(); assertThat(ListUtil.isEqual(list1, list5)).isFalse(); diff --git a/modules/utils/src/test/java/org/springside/modules/utils/collection/MapUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/collection/MapUtilTest.java index c4e3b033..e7303fca 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/collection/MapUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/collection/MapUtilTest.java @@ -2,18 +2,58 @@ package org.springside.modules.utils.collection; import static org.assertj.core.api.Assertions.*; +import java.util.EnumMap; import java.util.HashMap; +import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import org.junit.Test; -import org.springside.modules.utils.collection.type.ComparableComparator; +import org.springside.modules.utils.collection.MapUtil.ValueCreator; +import org.springside.modules.utils.collection.type.NaturalOrderComparator; public class MapUtilTest { @Test - public void guavaBuildSet() { + public void generalMethod() { + HashMap map = MapUtil.newHashMap(); + assertThat(MapUtil.isEmpty(map)).isTrue(); + assertThat(MapUtil.isEmpty(null)).isTrue(); + assertThat(MapUtil.isNotEmpty(map)).isFalse(); + assertThat(MapUtil.isNotEmpty(null)).isFalse(); + + map.put("haha", 1); + assertThat(MapUtil.isEmpty(map)).isFalse(); + assertThat(MapUtil.isNotEmpty(map)).isTrue(); + + ////////// + ConcurrentMap map2 = MapUtil.newConcurrentHashMap(); + assertThat(MapUtil.putIfAbsentWithFinalValue(map2, "haha", 3)).isEqualTo(3); + assertThat(MapUtil.putIfAbsentWithFinalValue(map2, "haha", 4)).isEqualTo(3); + + MapUtil.createIfAbsent(map2, "haha", new ValueCreator() { + @Override + public Integer get() { + return 5; + } + }); + + assertThat(map2).hasSize(1).containsEntry("haha", 3); + + MapUtil.createIfAbsent(map2, "haha2", new ValueCreator() { + @Override + public Integer get() { + return 5; + } + }); + + assertThat(map2).hasSize(2).containsEntry("haha2", 5); + + } + + @Test + public void guavaBuildMap() { HashMap map1 = MapUtil.newHashMap(); HashMap map2 = MapUtil.newHashMapWithCapacity(10); @@ -28,12 +68,49 @@ public class MapUtilTest { HashMap map5 = MapUtil.newHashMap(ListUtil.asList("1", "2", "3"), ListUtil.asList(1, 2, 3)); assertThat(map5).hasSize(3).containsEntry("1", 1).containsEntry("2", 2).containsEntry("3", 3); - TreeMap map6 = MapUtil.newNavigableMap(); + TreeMap map6 = MapUtil.newSortedMap(); - TreeMap map7= MapUtil.newNavigableMap(ComparableComparator.INSTANCE); + TreeMap map7 = MapUtil.newSortedMap(NaturalOrderComparator.INSTANCE); ConcurrentMap map8 = MapUtil.newConcurrentHashMap(); - ConcurrentSkipListMap map9 = MapUtil.newConcurrentNavigableMap(); + ConcurrentSkipListMap map9 = MapUtil.newConcurrentSortedMap(); + + EnumMap map10 = MapUtil.newEnumMap(EnumA.class); } + @Test + public void jdkBuildMap() { + Map map1 = MapUtil.emptyMap(); + assertThat(map1).hasSize(0); + + Map map2 = MapUtil.emptyMapIfNull(null); + assertThat(map2).isNotNull().hasSize(0); + + Map map3 = MapUtil.emptyMapIfNull(map1); + assertThat(map3).isSameAs(map1); + + Map map4 = MapUtil.singletonMap("haha", 1); + assertThat(map4).hasSize(1).containsEntry("haha", 1); + try { + map4.put("dada", 2); + fail("should fail before"); + } catch (Throwable t) { + assertThat(t).isInstanceOf(UnsupportedOperationException.class); + } + + Map map5 = MapUtil.newHashMap(); + Map map6 = MapUtil.unmodifiableMap(map5); + + try { + map6.put("a", 2); + fail("should fail before"); + } catch (Throwable t) { + assertThat(t).isInstanceOf(UnsupportedOperationException.class); + } + + } + + public enum EnumA { + A, B, C + } } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/collection/QueueUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/collection/QueueUtilTest.java index b17b6256..21d70e14 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/collection/QueueUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/collection/QueueUtilTest.java @@ -1,5 +1,53 @@ package org.springside.modules.utils.collection; +import static org.assertj.core.api.Assertions.*; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.LinkedBlockingQueue; + +import org.junit.Test; + public class QueueUtilTest { + @Test + public void guavaBuildSet() { + ArrayDeque queue1 = QueueUtil.newArrayDeque(16); + LinkedList queue2 = QueueUtil.newLinkedDeque(); + + ConcurrentLinkedQueue queue3 = QueueUtil.newConcurrentNonBlockingQueue(); + Deque queue7 = QueueUtil.newConcurrentNonBlockingDeque(); + + LinkedBlockingQueue queue4 = QueueUtil.newBlockingUnlimitQueue(); + LinkedBlockingDeque queue8 = QueueUtil.newBlockingUnlimitDeque(); + + LinkedBlockingQueue queue5 = QueueUtil.newLinkedBlockingQeque(100); + ArrayBlockingQueue queue6 = QueueUtil.newArrayBlockingQueue(100); + LinkedBlockingDeque queue9 = QueueUtil.newBlockingDeque(100); + } + + @Test + public void stack() { + + Queue stack = QueueUtil.newStack(10); + Queue stack2 = QueueUtil.newConcurrentStack(); + + stack.offer("1"); + stack.offer("2"); + + assertThat(stack.poll()).isEqualTo("2"); + assertThat(stack.poll()).isEqualTo("1"); + + stack2.offer("1"); + stack2.offer("2"); + + assertThat(stack2.poll()).isEqualTo("2"); + assertThat(stack2.poll()).isEqualTo("1"); + } + } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/collection/SetUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/collection/SetUtilTest.java index 78066d33..315932d7 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/collection/SetUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/collection/SetUtilTest.java @@ -6,10 +6,9 @@ import java.util.HashSet; import java.util.Set; import java.util.TreeSet; -import org.junit.Assert; import org.junit.Test; -import org.springside.modules.utils.collection.type.ComparableComparator; import org.springside.modules.utils.collection.type.ConcurrentHashSet; +import org.springside.modules.utils.collection.type.NaturalOrderComparator; public class SetUtilTest { @@ -26,9 +25,9 @@ public class SetUtilTest { HashSet set4 = SetUtil.newHashSet(ListUtil.newArrayList("1", "2", "2")); assertThat(set4).hasSize(2).contains("1", "2"); - TreeSet set5 = SetUtil.newNavigableSet(); + TreeSet set5 = SetUtil.newSortedSet(); - TreeSet set6 = SetUtil.newNavigableSet(ComparableComparator.INSTANCE); + TreeSet set6 = SetUtil.newSortedSet(NaturalOrderComparator.INSTANCE); ConcurrentHashSet set7 = SetUtil.newConcurrentHashSet(); } @@ -46,7 +45,7 @@ public class SetUtilTest { try { set1.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } @@ -55,7 +54,7 @@ public class SetUtilTest { assertThat(set4).hasSize(1).contains("1"); try { set4.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } @@ -65,12 +64,12 @@ public class SetUtilTest { try { set6.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } - Set set7 = SetUtil.newSetFromMap(MapUtil. newConcurrentNavigableMap()); + Set set7 = SetUtil.newSetFromMap(MapUtil. newConcurrentSortedMap()); } @Test @@ -92,7 +91,7 @@ public class SetUtilTest { try { set6.add("a"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/collection/type/ComparableComparatorTest.java b/modules/utils/src/test/java/org/springside/modules/utils/collection/type/ComparableComparatorTest.java index 8330ef0c..130628e5 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/collection/type/ComparableComparatorTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/collection/type/ComparableComparatorTest.java @@ -11,7 +11,7 @@ public class ComparableComparatorTest { @Test public void test() { - TreeSet set = SetUtil.newNavigableSet(ComparableComparator.INSTANCE); + TreeSet set = SetUtil.newSortedSet(NaturalOrderComparator.INSTANCE); set.add("3"); set.add("1"); diff --git a/modules/utils/src/test/java/org/springside/modules/utils/collection/type/ConcurrentHashSetTest.java b/modules/utils/src/test/java/org/springside/modules/utils/collection/type/ConcurrentHashSetTest.java index 9ad44257..70cf5e7c 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/collection/type/ConcurrentHashSetTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/collection/type/ConcurrentHashSetTest.java @@ -23,34 +23,32 @@ public class ConcurrentHashSetTest { for (String key : conrrentHashSet) { System.out.print(key + ","); } - + conrrentHashSet.remove("c"); assertThat(conrrentHashSet).hasSize(2); - + Object[] strings = conrrentHashSet.toArray(); - assertThat(strings).hasSize(2).contains("a","b"); - + assertThat(strings).hasSize(2).contains("a", "b"); + conrrentHashSet.toArray(new String[conrrentHashSet.size()]); conrrentHashSet.hashCode(); conrrentHashSet.toString(); - - + ConcurrentHashSet conrrentHashSet2 = SetUtil.newConcurrentHashSet(); conrrentHashSet2.add("a"); - + assertThat(conrrentHashSet.equals(conrrentHashSet)).isTrue(); assertThat(conrrentHashSet.equals(conrrentHashSet2)).isFalse(); - - + assertThat(conrrentHashSet.containsAll(conrrentHashSet2)).isTrue(); - + conrrentHashSet.retainAll(conrrentHashSet2); assertThat(conrrentHashSet).hasSize(1).contains("a"); assertThat(conrrentHashSet.equals(conrrentHashSet2)).isTrue(); - + conrrentHashSet.removeAll(conrrentHashSet2); assertThat(conrrentHashSet.isEmpty()).isTrue(); - + conrrentHashSet2.clear(); assertThat(conrrentHashSet2.isEmpty()).isTrue(); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/collection/type/SortedArrayListTest.java b/modules/utils/src/test/java/org/springside/modules/utils/collection/type/SortedArrayListTest.java index 46000f67..65cb1ce1 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/collection/type/SortedArrayListTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/collection/type/SortedArrayListTest.java @@ -2,7 +2,6 @@ package org.springside.modules.utils.collection.type; import static org.assertj.core.api.Assertions.*; -import org.junit.Assert; import org.junit.Test; import org.springside.modules.utils.collection.ListUtil; @@ -27,19 +26,19 @@ public class SortedArrayListTest { try { list.add(1, "2"); - Assert.fail("should fail before"); - } catch (Throwable t) { - assertThat(t).isInstanceOf(UnsupportedOperationException.class); - } - - try { - list.set(1, "2"); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(UnsupportedOperationException.class); } - SortedArrayList list2 = ListUtil.newSortedArrayList(ComparableComparator.INSTANCE); + try { + list.set(1, "2"); + fail("should fail before"); + } catch (Throwable t) { + assertThat(t).isInstanceOf(UnsupportedOperationException.class); + } + + SortedArrayList list2 = ListUtil.newSortedArrayList(NaturalOrderComparator.INSTANCE); list2.addAll(ListUtil.newArrayList("3", "1", "2")); assertThat(list2).containsExactly("1", "2", "3"); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/BasicFutureTest.java b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/BasicFutureTest.java index a5026794..8f243239 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/BasicFutureTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/BasicFutureTest.java @@ -6,7 +6,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.junit.Assert; import org.junit.Test; import org.springside.modules.utils.base.ExceptionUtil; @@ -56,7 +55,7 @@ public class BasicFutureTest { try { MyFuture future2 = new MyFuture(); future2.get(10, TimeUnit.MILLISECONDS); - Assert.fail("should fail before"); + fail("should fail before"); } catch (TimeoutException e) { assertThat(e).isInstanceOf(TimeoutException.class); } @@ -66,7 +65,7 @@ public class BasicFutureTest { MyFuture future3 = new MyFuture(); Tasks.fail(future3); future3.get(); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(ExceptionUtil.unwrap(t)).hasMessage("wuwu"); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/ConcurrentToolsTest.java b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/ConcurrentToolsTest.java index e82939f0..d62c8b45 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/ConcurrentToolsTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/ConcurrentToolsTest.java @@ -6,9 +6,9 @@ import org.junit.Test; import org.springside.modules.utils.concurrent.jsr166e.LongAdder; public class ConcurrentToolsTest { - + @Test - public void longAdder(){ + public void longAdder() { LongAdder counter = ConcurrentTools.newLongAdder(); counter.increment(); counter.add(2); diff --git a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/ThreadLocalContextTest.java b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/ThreadLocalContextTest.java index a43d518c..79fdb9d1 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/ThreadLocalContextTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/ThreadLocalContextTest.java @@ -27,7 +27,8 @@ public class ThreadLocalContextTest { ThreadUtil.sleep(RandomUtil.nextLong(100, 300)); System.out.println(ThreadLocalContext.get("myname")); ThreadLocalContext.reset(); - System.out.println("shoud null for " +Thread.currentThread().getName() + ":"+ThreadLocalContext.get("myname")); + System.out.println( + "shoud null for " + Thread.currentThread().getName() + ":" + ThreadLocalContext.get("myname")); countdown.countDown(); } }; diff --git a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/threadpool/QueuableCachedThreadPoolTest.java b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/threadpool/QueuableCachedThreadPoolTest.java index 8183e45b..581adbf8 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/threadpool/QueuableCachedThreadPoolTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/threadpool/QueuableCachedThreadPoolTest.java @@ -5,7 +5,6 @@ import static org.assertj.core.api.Assertions.*; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; -import org.junit.Assert; import org.junit.Test; import org.springside.modules.utils.concurrent.ThreadUtil; @@ -41,7 +40,7 @@ public class QueuableCachedThreadPoolTest { // 爆 try { threadPool.submit(new LongRunTask()); - Assert.fail("should fail before"); + fail("should fail before"); } catch (Throwable t) { assertThat(t).isInstanceOf(RejectedExecutionException.class); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/threadpool/ThreadPoolBuildersTest.java b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/threadpool/ThreadPoolBuildersTest.java index cad5d922..79a2bcdd 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/concurrent/threadpool/ThreadPoolBuildersTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/concurrent/threadpool/ThreadPoolBuildersTest.java @@ -51,7 +51,7 @@ public class ThreadPoolBuildersTest { assertThat(thread2.getName()).startsWith("fixPool"); assertThat(thread2.isDaemon()).isFalse(); fixPoolWithNamePrefix.shutdown(); - + ThreadPoolExecutor fixPoolWithNamePrefixAndDaemon = ThreadPoolBuilders.fixedPool().setPoolSize(10) .setThreadNamePrefix("fixPoolDaemon").setDaemon(true).build(); Thread thread3 = fixPoolWithNamePrefixAndDaemon.getThreadFactory().newThread(new Runnable() { @@ -115,7 +115,6 @@ public class ThreadPoolBuildersTest { fixPoolWithNamePrefix.shutdown(); } - @Test public void quequablePool() { ThreadPoolExecutor singlePool = ThreadPoolBuilders.queuableCachedPool().build(); @@ -132,8 +131,8 @@ public class ThreadPoolBuildersTest { assertThat(sizeablePool.getKeepAliveTime(TimeUnit.SECONDS)).isEqualTo(20); sizeablePool.shutdown(); - ThreadPoolExecutor fixPoolWithNamePrefix = ThreadPoolBuilders.queuableCachedPool().setThreadNamePrefix("queuableCachedPool") - .build(); + ThreadPoolExecutor fixPoolWithNamePrefix = ThreadPoolBuilders.queuableCachedPool() + .setThreadNamePrefix("queuableCachedPool").build(); Thread thread = fixPoolWithNamePrefix.getThreadFactory().newThread(new Runnable() { @Override diff --git a/modules/utils/src/test/java/org/springside/modules/utils/io/FilePathUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/io/FilePathUtilTest.java index 61a32db0..fa0eb5c8 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/io/FilePathUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/io/FilePathUtilTest.java @@ -16,19 +16,18 @@ public class FilePathUtilTest { assertThat(FilePathUtil.getFileExtension("/a/d/b/abc.")).isEqualTo(""); } - + @Test - public void pathName(){ + public void pathName() { String filePath = FilePathUtil.contact("/abc/", "ef"); assertThat(filePath).isEqualTo("/abc/ef"); - + String filePath2 = FilePathUtil.contact("/stuv", "xy"); assertThat(filePath2).isEqualTo("/stuv/xy"); - + assertThat(FilePathUtil.simplifyPath("../dd/../abc")).isEqualTo("../abc"); assertThat(FilePathUtil.simplifyPath("../../dd/../abc")).isEqualTo("../../abc"); assertThat(FilePathUtil.simplifyPath("./abc")).isEqualTo("abc"); - - + } } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/io/IOUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/io/IOUtilTest.java index 0571e493..a6b8400e 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/io/IOUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/io/IOUtilTest.java @@ -17,19 +17,19 @@ public class IOUtilTest { assertThat(IOUtil.toString(ResourceUtil.asStream("test.txt"))).isEqualTo("ABCDEFG\nABC"); assertThat(IOUtil.toLines(ResourceUtil.asStream("test.txt"))).hasSize(2).containsExactly("ABCDEFG", "ABC"); } - + @Test - public void write() throws IOException{ + public void write() throws IOException { StringBuilderWriter sw = new StringBuilderWriter(); IOUtil.write("hahahaha", sw); assertThat(sw.toString()).isEqualTo("hahahaha"); - + ByteArrayOutputStream out = new ByteArrayOutputStream(); IOUtil.write("hahahaha", out); - assertThat(new String(out.toByteArray(),Charsets.UTF_8)).isEqualTo("hahahaha"); - + assertThat(new String(out.toByteArray(), Charsets.UTF_8)).isEqualTo("hahahaha"); + IOUtil.closeQuietly(out); - IOUtil.closeQuietly((Closeable)null); + IOUtil.closeQuietly((Closeable) null); } } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/io/ResouceUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/io/ResouceUtilTest.java index f7c5e1f9..d3e4619d 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/io/ResouceUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/io/ResouceUtilTest.java @@ -7,13 +7,13 @@ import java.io.IOException; import org.junit.Test; public class ResouceUtilTest { - + @Test - public void test() throws IOException{ - assertThat(ResourceUtil.toString("test.txt")).contains("ABCDEFG"); - assertThat(ResourceUtil.toString(ResouceUtilTest.class,"/test.txt")).contains("ABCDEFG"); - assertThat(ResourceUtil.toLines("test.txt")).containsExactly("ABCDEFG","ABC"); - assertThat(ResourceUtil.toLines(ResouceUtilTest.class,"/test.txt")).containsExactly("ABCDEFG","ABC"); + public void test() throws IOException { + assertThat(ResourceUtil.toString("test.txt")).contains("ABCDEFG"); + assertThat(ResourceUtil.toString(ResouceUtilTest.class, "/test.txt")).contains("ABCDEFG"); + assertThat(ResourceUtil.toLines("test.txt")).containsExactly("ABCDEFG", "ABC"); + assertThat(ResourceUtil.toLines(ResouceUtilTest.class, "/test.txt")).containsExactly("ABCDEFG", "ABC"); } } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/net/NetUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/net/NetUtilTest.java index 48526e82..f4da5929 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/net/NetUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/net/NetUtilTest.java @@ -9,7 +9,6 @@ import java.net.UnknownHostException; import javax.net.ServerSocketFactory; -import org.junit.Assert; import org.junit.Test; import org.mockito.internal.util.io.IOUtil; @@ -36,25 +35,25 @@ public class NetUtilTest { assertThat(port3).isBetween(NetUtil.PORT_RANGE_MIN, NetUtil.PORT_RANGE_MAX); System.out.println("random port:" + port3); - - //尝试占住一个端口 + + // 尝试占住一个端口 ServerSocket serverSocket = null; try { serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost")); assertThat(NetUtil.isPortAvailable(port)).isFalse(); - + int port4 = NetUtil.findAvailablePortFrom(port); - assertThat(port4).isEqualTo(port+1); - - try{ + assertThat(port4).isEqualTo(port + 1); + + try { int port5 = NetUtil.findRandomAvailablePort(port, port); - Assert.fail("should fail before"); - }catch(Throwable t){ + fail("should fail before"); + } catch (Throwable t) { assertThat(t).isInstanceOf(IllegalStateException.class); } - + } finally { IOUtil.close(serverSocket); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/number/NumberUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/number/NumberUtilTest.java index f8b34276..74b64b14 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/number/NumberUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/number/NumberUtilTest.java @@ -38,14 +38,14 @@ public class NumberUtilTest { public void toNumber() { assertThat(NumberUtil.toInt("122")).isEqualTo(122); assertThat(NumberUtil.toInt("12A")).isEqualTo(0); - assertThat(NumberUtil.toInt((String)null)).isEqualTo(0); + assertThat(NumberUtil.toInt((String) null)).isEqualTo(0); assertThat(NumberUtil.toInt("12A", 123)).isEqualTo(123); - + assertThat(NumberUtil.toLong("122")).isEqualTo(122L); assertThat(NumberUtil.toLong("12A")).isEqualTo(0L); - assertThat(NumberUtil.toLong((String)null)).isEqualTo(0); + assertThat(NumberUtil.toLong((String) null)).isEqualTo(0); assertThat(NumberUtil.toLong("12A", 123)).isEqualTo(123L); - + assertThat(NumberUtil.toDouble("122.1")).isEqualTo(122.1); assertThat(NumberUtil.toDouble("12A")).isEqualTo(0L); assertThat(NumberUtil.toDouble("12A", 123.1)).isEqualTo(123.1); @@ -60,25 +60,24 @@ public class NumberUtilTest { assertThat(NumberUtil.toLongObject("12A")).isEqualTo(null); assertThat(NumberUtil.toLongObject("12A", 123L)).isEqualTo(123L); assertThat(NumberUtil.toLongObject(null, 123L)).isEqualTo(123L); - + assertThat(NumberUtil.toDoubleObject("122.1")).isEqualTo(122.1); assertThat(NumberUtil.toDoubleObject("12A")).isEqualTo(null); assertThat(NumberUtil.toDoubleObject("12A", 123.1)).isEqualTo(123.1); - - + assertThat(NumberUtil.hexToIntObject("0x10")).isEqualTo(16); assertThat(NumberUtil.hexToIntObject("0X100")).isEqualTo(256); assertThat(NumberUtil.hexToIntObject("-0x100")).isEqualTo(-256); assertThat(NumberUtil.hexToIntObject("0xHI")).isEqualTo(null); assertThat(NumberUtil.hexToIntObject(null)).isEqualTo(null); - assertThat(NumberUtil.hexToIntObject("0xHI",123)).isEqualTo(123); - + assertThat(NumberUtil.hexToIntObject("0xHI", 123)).isEqualTo(123); + assertThat(NumberUtil.hexToLongObject("0x10")).isEqualTo(16L); assertThat(NumberUtil.hexToLongObject("0X100")).isEqualTo(256L); assertThat(NumberUtil.hexToLongObject("-0x100")).isEqualTo(-256L); assertThat(NumberUtil.hexToLongObject("0xHI")).isEqualTo(null); assertThat(NumberUtil.hexToLongObject(null)).isEqualTo(null); - assertThat(NumberUtil.hexToLongObject("0xHI",123L)).isEqualTo(123L); + assertThat(NumberUtil.hexToLongObject("0xHI", 123L)).isEqualTo(123L); } } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/number/RandomUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/number/RandomUtilTest.java index 133ef7d0..b954a05e 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/number/RandomUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/number/RandomUtilTest.java @@ -72,7 +72,7 @@ public class RandomUtilTest { assertThat(RandomUtil.randomStringFixLength(5)).hasSize(5); assertThat(RandomUtil.randomStringFixLength(RandomUtil.threadLocalRandom(), 5)).hasSize(5); - + System.out.println(RandomUtil.randomLetterFixLength(5)); System.out.println(RandomUtil.randomLetterRandomLength(5, 10)); @@ -81,7 +81,7 @@ public class RandomUtilTest { assertThat(RandomUtil.randomLetterFixLength(5)).hasSize(5); assertThat(RandomUtil.randomLetterFixLength(RandomUtil.threadLocalRandom(), 5)).hasSize(5); - + System.out.println(RandomUtil.randomAsciiFixLength(5)); System.out.println(RandomUtil.randomAsciiRandomLength(5, 10)); diff --git a/modules/utils/src/test/java/org/springside/modules/utils/reflect/BeanMapperTest.java b/modules/utils/src/test/java/org/springside/modules/utils/reflect/BeanMapperTest.java index e74dffe7..de72f41a 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/reflect/BeanMapperTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/reflect/BeanMapperTest.java @@ -1,5 +1,214 @@ package org.springside.modules.utils.reflect; +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.Test; +import org.springside.modules.utils.collection.ListUtil; + +import ma.glasnost.orika.metadata.Type; + public class BeanMapperTest { + @Test + public void copySingleObject() { + Student student = new Student("zhang3", 20, new Teacher("li4"), ListUtil.newArrayList("chinese", "english")); + + StudentVO studentVo = BeanMapper.map(student, StudentVO.class); + + assertThat(studentVo.name).isEqualTo("zhang3"); + assertThat(studentVo.getAge()).isEqualTo(20); + assertThat(studentVo.getTeacher().getName()).isEqualTo("li4"); + assertThat(studentVo.getCourse()).containsExactly("chinese", "english"); + + ////////// + Type studentType = BeanMapper.getType(Student.class); + Type studentVoType = BeanMapper.getType(StudentVO.class); + studentVo = BeanMapper.map(student, studentType, studentVoType); + + assertThat(studentVo.name).isEqualTo("zhang3"); + assertThat(studentVo.getAge()).isEqualTo(20); + assertThat(studentVo.getTeacher().getName()).isEqualTo("li4"); + assertThat(studentVo.getCourse()).containsExactly("chinese", "english"); + } + + @Test + public void copyListObject() { + Student student1 = new Student("zhang3", 20, new Teacher("li4"), ListUtil.newArrayList("chinese", "english")); + Student student2 = new Student("zhang4", 30, new Teacher("li5"), ListUtil.newArrayList("chinese2", "english4")); + Student student3 = new Student("zhang5", 40, new Teacher("li6"), ListUtil.newArrayList("chinese3", "english5")); + List studentList = ListUtil.newArrayList(student1, student2, student3); + + List studentVoList = BeanMapper.mapList(studentList, Student.class, StudentVO.class); + assertThat(studentVoList).hasSize(3); + StudentVO studentVo = studentVoList.get(0); + + assertThat(studentVo.name).isEqualTo("zhang3"); + assertThat(studentVo.getAge()).isEqualTo(20); + assertThat(studentVo.getTeacher().getName()).isEqualTo("li4"); + assertThat(studentVo.getCourse()).containsExactly("chinese", "english"); + + ///////// + Type studentType = BeanMapper.getType(Student.class); + Type studentVoType = BeanMapper.getType(StudentVO.class); + studentVoList = BeanMapper.mapList(studentList, studentType, studentVoType); + + studentVoList = BeanMapper.mapList(studentList, Student.class, StudentVO.class); + assertThat(studentVoList).hasSize(3); + studentVo = studentVoList.get(0); + + assertThat(studentVo.name).isEqualTo("zhang3"); + assertThat(studentVo.getAge()).isEqualTo(20); + assertThat(studentVo.getTeacher().getName()).isEqualTo("li4"); + assertThat(studentVo.getCourse()).containsExactly("chinese", "english"); + } + + + @Test + public void copyArrayObject() { + Student student1 = new Student("zhang3", 20, new Teacher("li4"), ListUtil.newArrayList("chinese", "english")); + Student student2 = new Student("zhang4", 30, new Teacher("li5"), ListUtil.newArrayList("chinese2", "english4")); + Student student3 = new Student("zhang5", 40, new Teacher("li6"), ListUtil.newArrayList("chinese3", "english5")); + Student[] studentList = new Student[]{student1, student2, student3}; + StudentVO[] studentVoList =new StudentVO[studentList.length]; + BeanMapper.mapArray(studentVoList, studentList, StudentVO.class); + assertThat(studentVoList).hasSize(3); + StudentVO studentVo = studentVoList[0]; + + assertThat(studentVo.name).isEqualTo("zhang3"); + assertThat(studentVo.getAge()).isEqualTo(20); + assertThat(studentVo.getTeacher().getName()).isEqualTo("li4"); + assertThat(studentVo.getCourse()).containsExactly("chinese", "english"); + + ///////// + Type studentType = BeanMapper.getType(Student.class); + Type studentVoType = BeanMapper.getType(StudentVO.class); + + StudentVO[] studentVoList2 =new StudentVO[studentList.length]; + BeanMapper.mapArray(studentVoList2, studentList,studentType, studentVoType); + assertThat(studentVoList).hasSize(3); + studentVo = studentVoList2[0]; + + assertThat(studentVo.name).isEqualTo("zhang3"); + assertThat(studentVo.getAge()).isEqualTo(20); + assertThat(studentVo.getTeacher().getName()).isEqualTo("li4"); + assertThat(studentVo.getCourse()).containsExactly("chinese", "english"); + } + + + public static class Student { + public String name; + private int age; + private Teacher teacher; + private List course = ListUtil.newArrayList(); + + public Student(String name, int age, Teacher teacher, List course) { + this.name = name; + this.age = age; + this.teacher = teacher; + this.course = course; + } + + public List getCourse() { + return course; + } + + public void setCourse(List course) { + this.course = course; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Teacher getTeacher() { + return teacher; + } + + public void setTeacher(Teacher teacher) { + this.teacher = teacher; + } + + } + + public static class Teacher { + private String name; + + public Teacher(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } + + public static class StudentVO { + public String name; + private int age; + private TeacherVO teacher; + private List course = ListUtil.newArrayList(); + + public StudentVO(String name, int age, TeacherVO teacher, List course) { + this.name = name; + this.age = age; + this.teacher = teacher; + this.course = course; + } + + public List getCourse() { + return course; + } + + public void setCourse(List course) { + this.course = course; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public TeacherVO getTeacher() { + return teacher; + } + + public void setTeacher(TeacherVO teacher) { + this.teacher = teacher; + } + + } + + public static class TeacherVO { + private String name; + + public TeacherVO(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } + } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/reflect/ClassUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/reflect/ClassUtilTest.java index 437adb0b..308993bc 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/reflect/ClassUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/reflect/ClassUtilTest.java @@ -9,8 +9,6 @@ import org.junit.Test; public class ClassUtilTest { - - @Test public void getMessage() { assertThat(ClassUtil.getShortClassName(ClassUtilTest.class)).isEqualTo("ClassUtilTest"); @@ -32,24 +30,24 @@ public class ClassUtilTest { assertThat(ClassUtil.getAllInterfaces(BClass.class)).hasSize(4).contains(AInterface.class, BInterface.class, CInterface.class, DInterface.class); - + assertThat(ClassUtil.getAllSuperclasses(BClass.class)).hasSize(2).contains(AClass.class, Object.class); - + assertThat(ClassUtil.getAllAnnotations(BClass.class)).hasSize(4); - + assertThat(ClassUtil.getMethodsAnnotatedWith(BClass.class, EAnnotation.class)).hasSize(1) - .contains(ReflectionUtil.getAccessibleMethodByName(BClass.class, "hello")); + .contains(ReflectionUtil.findAccessibleMethodByName(BClass.class, "hello")); assertThat(ClassUtil.getMethodsAnnotatedWith(BClass.class, FAnnotation.class)).hasSize(1) - .contains(ReflectionUtil.getAccessibleMethodByName(BClass.class, "hello")); + .contains(ReflectionUtil.findAccessibleMethodByName(BClass.class, "hello")); assertThat(ClassUtil.getMethodsAnnotatedWith(BClass.class, AAnnotation.class)).hasSize(0); - + assertThat(ClassUtil.getFieldsAnnotatedWith(BClass.class, EAnnotation.class)).hasSize(3).contains( ReflectionUtil.getAccessibleField(BClass.class, "bfield"), ReflectionUtil.getAccessibleField(BClass.class, "efield"), ReflectionUtil.getAccessibleField(AClass.class, "afield")); - - assertThat(ClassUtil.getFieldsAnnotatedWith(BClass.class, FAnnotation.class)).hasSize(1).contains( - ReflectionUtil.getAccessibleField(AClass.class, "dfield")); + + assertThat(ClassUtil.getFieldsAnnotatedWith(BClass.class, FAnnotation.class)).hasSize(1) + .contains(ReflectionUtil.getAccessibleField(AClass.class, "dfield")); } @Test @@ -78,9 +76,7 @@ public class ClassUtilTest { public static class TestBean3 { } - - - + public interface AInterface { } @@ -123,13 +119,12 @@ public class ClassUtilTest { @DAnnotation public static class AClass implements DInterface { - + @EAnnotation private int afield; - - + private int cfield; - + @FAnnotation private int dfield; } @@ -137,13 +132,12 @@ public class ClassUtilTest { @BAnnotation public static class BClass extends AClass implements CInterface, BInterface { - @EAnnotation private int bfield; - + @EAnnotation private int efield; - + @Override @EAnnotation public void hello() { diff --git a/modules/utils/src/test/java/org/springside/modules/utils/reflect/FastMethodInvokerTest.java b/modules/utils/src/test/java/org/springside/modules/utils/reflect/FastMethodInvokerTest.java index f7eaffb9..77228b30 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/reflect/FastMethodInvokerTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/reflect/FastMethodInvokerTest.java @@ -1,5 +1,89 @@ package org.springside.modules.utils.reflect; +import static org.assertj.core.api.Assertions.*; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +import org.junit.Test; +import org.springside.modules.utils.collection.ListUtil; + +import net.sf.cglib.reflect.FastMethod; + public class FastMethodInvokerTest { + @Test + public void test() throws InvocationTargetException { + FastMethod fastGetAge = FastMethodInvoker.getFastMethod(Student.class, "getAge"); + Student student1 = new Student("zhang3", 20, new Teacher("li4"), ListUtil.newArrayList("chinese", "english")); + Student student2 = new Student("zhang4", 30, new Teacher("li5"), ListUtil.newArrayList("chinese2", "english4")); + + int age = FastMethodInvoker.invoke(fastGetAge, student1); + assertThat(age).isEqualTo(20); + + age = FastMethodInvoker.invoke(fastGetAge, student2); + assertThat(age).isEqualTo(30); + + FastMethod fastSetAge = FastMethodInvoker.getFastMethod(Student.class, "setAge", int.class); + FastMethodInvoker.invoke(fastSetAge, student1, 1); + + assertThat(student1.getAge()).isEqualTo(1); + } + + public static class Student { + public String name; + private int age; + private Teacher teacher; + private List course = ListUtil.newArrayList(); + + public Student(String name, int age, Teacher teacher, List course) { + this.name = name; + this.age = age; + this.teacher = teacher; + this.course = course; + } + + public List getCourse() { + return course; + } + + public void setCourse(List course) { + this.course = course; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Teacher getTeacher() { + return teacher; + } + + public void setTeacher(Teacher teacher) { + this.teacher = teacher; + } + + } + + public static class Teacher { + private String name; + + public Teacher(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/reflect/ReflectionUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/reflect/ReflectionUtilTest.java index bf07e282..f9f2cb56 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/reflect/ReflectionUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/reflect/ReflectionUtilTest.java @@ -38,6 +38,7 @@ public class ReflectionUtilTest { ReflectionUtil.setFieldValue(bean, "publicField", 2); assertThat(bean.inspectPublicField()).isEqualTo(2); + // 没有绕过将publicField+1的setter函数 ReflectionUtil.setProperty(bean, "publicField", 3); assertThat(bean.inspectPublicField()).isEqualTo(4); @@ -151,7 +152,7 @@ public class ReflectionUtilTest { } // 通過setter函數會被比輸入值加1 - public void setPublicField(Integer publicField) { + public void setPublicField(int publicField) { this.publicField = publicField + 1; } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/text/EncodeUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/text/EncodeUtilTest.java index d76e361d..e8d001b4 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/text/EncodeUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/text/EncodeUtilTest.java @@ -7,7 +7,6 @@ package org.springside.modules.utils.text; import static org.assertj.core.api.Assertions.*; -import org.junit.Assert; import org.junit.Test; public class EncodeUtilTest { @@ -28,7 +27,7 @@ public class EncodeUtilTest { try { input = "01020G11"; EncodeUtil.decodeHex(input); - Assert.fail("should throw exception before"); + fail("should throw exception before"); } catch (Throwable t) { assertThat(t).isInstanceOf(IllegalArgumentException.class); } @@ -58,7 +57,7 @@ public class EncodeUtilTest { try { assertThat(result).isEqualTo(EncodeUtil.decodeBase64UrlSafe("AQIPE+8=")); - Assert.fail("should throw exception before"); + fail("should throw exception before"); } catch (Throwable t) { assertThat(t).isInstanceOf(IllegalArgumentException.class); } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/text/HashUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/text/HashUtilTest.java index 275fc32a..b32eb81e 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/text/HashUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/text/HashUtilTest.java @@ -30,18 +30,16 @@ public class HashUtilTest { result = EncodeUtil.encodeBase64(HashUtil.sha1("hhahah", HashUtil.generateSalt(5))); System.out.println("sha1 with salt:" + result); - // 带盐,固定的盐 result = EncodeUtil.encodeBase64(HashUtil.sha1("hhahah", new byte[] { 1, 2, 3 })); assertThat(result).isEqualTo("U/7wy5R1sVrjEf3dOTAPz383g2k="); result2 = EncodeUtil.encodeBase64(HashUtil.sha1("hhahah".getBytes(), new byte[] { 1, 2, 3 })); assertThat(result).isEqualTo(result2); - // 带盐迭代, 每次salt值不一样,所以值也不一样。 result = EncodeUtil.encodeBase64(HashUtil.sha1("hhahah", HashUtil.generateSalt(5), 2)); System.out.println("sha1 with salt with iteration:" + result); - + // 带盐迭代, 固定的盐 result = EncodeUtil.encodeBase64(HashUtil.sha1("hhahah", new byte[] { 1, 2, 3 }, 2)); assertThat(result).isEqualTo("n9O7laits+ovoK8X8xde+XrsCtM="); @@ -64,7 +62,7 @@ public class HashUtilTest { assertThat(HashUtil.crc32AsInt("hahhha1")).isEqualTo(-625925593); assertThat(HashUtil.crc32AsInt("hahhha1".getBytes())).isEqualTo(-625925593); assertThat(HashUtil.crc32AsInt("hahhha2")).isEqualTo(1136161693); - + assertThat(HashUtil.crc32AsLong("hahhha1")).isEqualTo(3669041703L); assertThat(HashUtil.crc32AsLong("hahhha1".getBytes())).isEqualTo(3669041703L); assertThat(HashUtil.crc32AsLong("hahhha2")).isEqualTo(1136161693L); @@ -79,10 +77,9 @@ public class HashUtilTest { assertThat(HashUtil.murmur32AsInt("hahhha4")).isEqualTo(-1044105167); assertThat(HashUtil.murmur32AsInt("hahhha5")).isEqualTo(-93397348); assertThat(HashUtil.murmur32AsInt("hahhha6")).isEqualTo(1844168902); - - - assertThat(HashUtil.murmur32AsInt("hahhha1",12345)).isEqualTo(-1980822128); - assertThat(HashUtil.murmur32AsInt("hahhha1".getBytes(),12345)).isEqualTo(-1980822128); + + assertThat(HashUtil.murmur32AsInt("hahhha1", 12345)).isEqualTo(-1980822128); + assertThat(HashUtil.murmur32AsInt("hahhha1".getBytes(), 12345)).isEqualTo(-1980822128); } } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/time/ClockUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/time/ClockUtilTest.java index e4e16a70..3203d2cf 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/time/ClockUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/time/ClockUtilTest.java @@ -10,22 +10,22 @@ import org.springside.modules.utils.time.ClockUtil.DummyClock; public class ClockUtilTest { @Test - public void testDummyClock(){ - DummyClock clock = new DummyClock(); + public void testDummyClock() { + DummyClock clock = new DummyClock(); clock.updateNow(111); assertThat(clock.currentTimeMillis()).isEqualTo(111); assertThat(clock.currentDate().getTime()).isEqualTo(111); - + clock.updateNow(new Date(112)); assertThat(clock.currentTimeMillis()).isEqualTo(112); clock.increaseTime(200); assertThat(clock.currentTimeMillis()).isEqualTo(312); clock.decreaseTime(100); assertThat(clock.currentTimeMillis()).isEqualTo(212); - + clock.setNanoTime(150); assertThat(clock.nanoTime()).isEqualTo(150); - + } } diff --git a/modules/utils/src/test/java/org/springside/modules/utils/time/DateUtilTest.java b/modules/utils/src/test/java/org/springside/modules/utils/time/DateUtilTest.java index 1aa4304b..27932ff7 100644 --- a/modules/utils/src/test/java/org/springside/modules/utils/time/DateUtilTest.java +++ b/modules/utils/src/test/java/org/springside/modules/utils/time/DateUtilTest.java @@ -17,6 +17,10 @@ public class DateUtilTest { Date date3 = new Date(106, 10, 1); assertThat(DateUtil.isSameTime(date1, date3)).isTrue(); + + Date date4 = new Date(106, 10, 1, 12, 23, 43); + assertThat(DateUtil.isBetween(date3, date1, date2)); + assertThat(DateUtil.isBetween(date4, date1, date2)); } @@ -95,7 +99,7 @@ public class DateUtilTest { assertThat(DateUtil.isSameTime(DateUtil.setHours(date, 9), expectedDate4)).isTrue(); assertThat(DateUtil.isSameTime(DateUtil.setMinutes(date, 9), expectedDate5)).isTrue(); assertThat(DateUtil.isSameTime(DateUtil.setSeconds(date, 10), expectedDate6)).isTrue(); - + } @Test