/*
* Copyright 2015 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 org.inferred.freebuilder.processor.util;
import java.util.ArrayList;
import java.util.List;
/**
* Represents an instance that compares in {@link Object#hashCode()} and
* {@link Object#equals(Object)} using a sequence of fields. If two instances have the same runtime
* class and the same sequence of fields, then they are considered equal.
*
* <p>This class implements the two {@link Object} methods mentioned above as well as
* {@link Object#toString()}.
*/
public abstract class ValueType {
/**
* An object that receives fields (names and current values) for processing. It is assumed that
* for a given class, the order of fields is always the same, although individual fields may be
* omitted.
*/
public interface FieldReceiver {
void add(String name, Object value);
}
/** A receiver that adds all received field names and values into a list in order. */
private static final class ReceiverIntoList implements FieldReceiver {
private final List<Object> list = new ArrayList<Object>();
@Override
public void add(String name, Object value) {
list.add(name);
list.add(value);
}
public List<Object> get() {
return list;
}
}
/** A receiver that uses each received name and value to calculate a hash code. */
private static final class ReceiverIntoHashCode implements FieldReceiver {
private int hashCode = 1;
private void add(Object value) {
hashCode *= 31;
hashCode += (value == null) ? 0 : value.hashCode();
}
@Override
public void add(String name, Object value) {
add(name);
add(value);
}
public int get() {
return hashCode;
}
}
/**
* A receiver that puts each name and value into a {@link StringBuilder} that generates a human-
* readable representation of the value.
*/
private static final class ReceiverIntoStringBuilder implements FieldReceiver {
private final StringBuilder builder;
private String delimiter;
ReceiverIntoStringBuilder(StringBuilder builder) {
this.builder = builder;
this.delimiter = "";
}
@Override
public void add(String name, Object value) {
builder.append(delimiter);
delimiter = ", ";
builder.append(name).append("=").append(value);
}
}
/** Implement this method to report the name and value of each field. */
protected abstract void addFields(FieldReceiver fields);
@Override
public final boolean equals(Object obj) {
if ((obj == null) || (obj.getClass() != this.getClass())) {
return false;
}
ReceiverIntoList a = new ReceiverIntoList();
ReceiverIntoList b = new ReceiverIntoList();
this.addFields(a);
((ValueType) obj).addFields(b);
return a.get().equals(b.get());
}
@Override
public final int hashCode() {
ReceiverIntoHashCode receiver = new ReceiverIntoHashCode();
addFields(receiver);
return receiver.get();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(getClass().getSimpleName())
.append("{");
addFields(new ReceiverIntoStringBuilder(builder));
return builder.append("}")
.toString();
}
}