Java tutorial
/* * Copyright (C) 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.caliper.memory; import com.google.common.base.Preconditions; import java.lang.reflect.Field; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * A chain of references, which starts at a root object and leads to a * particular value (either an object or a primitive). */ public abstract class Chain { private final Object value; private final Chain parent; Chain(Chain parent, Object value) { this.parent = parent; this.value = value; } static Chain root(Object value) { return new Chain(null, Preconditions.checkNotNull(value)) { @Override public Class<?> getValueType() { return getValue().getClass(); } }; } FieldChain appendField(Field field, Object value) { return new FieldChain(this, Preconditions.checkNotNull(field), value); } ArrayIndexChain appendArrayIndex(int arrayIndex, Object value) { return new ArrayIndexChain(this, arrayIndex, value); } /** * Returns whether this chain has a parent. This returns false only when * this chain represents the root object itself. */ public boolean hasParent() { return parent != null; } /** * Returns the parent chain, from which this chain was created. * @throws IllegalStateException if {@code !hasParent()}, then an */ public @Nonnull Chain getParent() { Preconditions.checkState(parent != null, "This is the root value, it has no parent"); return parent; } /** * Returns the value that this chain leads to. If the value is a primitive, * a wrapper object is returned instead. */ public @Nullable Object getValue() { return value; } public abstract @Nonnull Class<?> getValueType(); /** * Returns whether the connection of the parent chain and this chain is * through a field (of the getParent().getValue().getClass() class). */ public boolean isThroughField() { return false; } /** * Returns whether the connection of the parent chain and this chain is * through an array index, i.e. the parent leads to an array, and this * chain leads to an element of that array. */ public boolean isThroughArrayIndex() { return false; } /** * Returns whether the value of this chain represents a primitive. */ public boolean isPrimitive() { return getValueType().isPrimitive(); } /** * Returns the root object of this chain. */ public @Nonnull Object getRoot() { Chain current = this; while (current.hasParent()) { current = current.getParent(); } return current.getValue(); } Deque<Chain> reverse() { Deque<Chain> reverseChain = new ArrayDeque<Chain>(8); Chain current = this; reverseChain.addFirst(current); while (current.hasParent()) { current = current.getParent(); reverseChain.addFirst(current); } return reverseChain; } @Override public final String toString() { StringBuilder sb = new StringBuilder(32); Iterator<Chain> it = reverse().iterator(); sb.append(it.next().getValue()); while (it.hasNext()) { sb.append("->"); Chain current = it.next(); if (current.isThroughField()) { sb.append(((FieldChain) current).getField().getName()); } else if (current.isThroughArrayIndex()) { sb.append("[").append(((ArrayIndexChain) current).getArrayIndex()).append("]"); } } return sb.toString(); } static class FieldChain extends Chain { private final Field field; FieldChain(Chain parent, Field referringField, Object value) { super(parent, value); this.field = referringField; } @Override public boolean isThroughField() { return true; } @Override public boolean isThroughArrayIndex() { return false; } @Override public Class<?> getValueType() { return field.getType(); } public Field getField() { return field; } } static class ArrayIndexChain extends Chain { private final int index; ArrayIndexChain(Chain parent, int index, Object value) { super(parent, value); this.index = index; } @Override public boolean isThroughField() { return false; } @Override public boolean isThroughArrayIndex() { return true; } @Override public Class<?> getValueType() { return getParent().getValue().getClass().getComponentType(); } public int getArrayIndex() { return index; } } }