Java tutorial
/* * Copyright 2011 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.googlecode.concurrentlinkedhashmap; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import com.google.common.collect.Sets; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap.Node; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap.WeightedValue; import org.hamcrest.Description; import org.hamcrest.Factory; import org.hamcrest.TypeSafeDiagnosingMatcher; import static com.googlecode.concurrentlinkedhashmap.IsEmptyMap.emptyMap; import static com.googlecode.concurrentlinkedhashmap.IsValidLinkedDeque.validLinkedDeque; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasValue; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; /** * A matcher that evaluates a {@link ConcurrentLinkedHashMap} to determine if it * is in a valid state. * * @author ben.manes@gmail.com (Ben Manes) */ @SuppressWarnings("unchecked") public final class IsValidConcurrentLinkedHashMap<K, V> extends TypeSafeDiagnosingMatcher<ConcurrentLinkedHashMap<? extends K, ? extends V>> { @Override public void describeTo(Description description) { description.appendText("valid"); } @Override protected boolean matchesSafely(ConcurrentLinkedHashMap<? extends K, ? extends V> map, Description description) { DescriptionBuilder builder = new DescriptionBuilder(description); drain(map); checkMap(map, builder); checkEvictionDeque(map, builder); return builder.matches(); } private void drain(ConcurrentLinkedHashMap<? extends K, ? extends V> map) { for (int i = 0; i < map.readBuffers.length; i++) { for (;;) { map.drainBuffers(); boolean fullyDrained = map.writeBuffer.isEmpty(); for (int j = 0; j < map.readBuffers.length; j++) { fullyDrained &= (map.readBuffers[i][j].get() == null); } if (fullyDrained) { break; } map.readBufferReadCount[i]++; } } } private void checkMap(ConcurrentLinkedHashMap<? extends K, ? extends V> map, DescriptionBuilder builder) { builder.expectThat("listenerQueue", map.pendingNotifications.isEmpty(), is(true)); builder.expectThat("Inconsistent size", map.data.size(), is(map.size())); builder.expectThat("weightedSize", map.weightedSize(), is(map.weightedSize.get())); builder.expectThat("capacity", map.capacity(), is(map.capacity.get())); builder.expectThat("overflow", map.capacity.get(), is(greaterThanOrEqualTo(map.weightedSize()))); builder.expectThat(((ReentrantLock) map.evictionLock).isLocked(), is(false)); if (map.isEmpty()) { builder.expectThat(map, emptyMap()); } } private void checkEvictionDeque(ConcurrentLinkedHashMap<? extends K, ? extends V> map, DescriptionBuilder builder) { LinkedDeque<?> deque = map.evictionDeque; checkLinks(map, builder); builder.expectThat(deque, hasSize(map.size())); validLinkedDeque().matchesSafely(map.evictionDeque, builder.getDescription()); } @SuppressWarnings("rawtypes") private void checkLinks(ConcurrentLinkedHashMap<? extends K, ? extends V> map, DescriptionBuilder builder) { long weightedSize = 0; Set<Node> seen = Sets.newIdentityHashSet(); for (Node node : map.evictionDeque) { String errorMsg = String.format("Loop detected: %s, saw %s in %s", node, seen, map); builder.expectThat(errorMsg, seen.add(node), is(true)); weightedSize += ((WeightedValue) node.get()).weight; checkNode(map, node, builder); } builder.expectThat("Size != list length", map.size(), is(seen.size())); builder.expectThat( "WeightedSize != link weights" + " [" + map.weightedSize() + " vs. " + weightedSize + "]" + " {size: " + map.size() + " vs. " + seen.size() + "}", map.weightedSize(), is(weightedSize)); } @SuppressWarnings("rawtypes") private void checkNode(ConcurrentLinkedHashMap<? extends K, ? extends V> map, Node node, DescriptionBuilder builder) { builder.expectThat(node.key, is(not(nullValue()))); builder.expectThat(node.get(), is(not(nullValue()))); builder.expectThat(node.getValue(), is(not(nullValue()))); builder.expectThat("weight", ((WeightedValue) node.get()).weight, is(((EntryWeigher) map.weigher).weightOf(node.key, node.getValue()))); builder.expectThat("inconsistent", map, hasKey(node.key)); builder.expectThat("Could not find value: " + node.getValue(), map, hasValue(node.getValue())); builder.expectThat("found wrong node", map.data, hasEntry(node.key, node)); } @Factory public static <K, V> IsValidConcurrentLinkedHashMap<K, V> valid() { return new IsValidConcurrentLinkedHashMap<K, V>(); } }