/*
* Copyright 2015 Ben Manes. 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.github.benmanes.caffeine.cache.node;
import static com.github.benmanes.caffeine.cache.Specifications.PACKAGE_NAME;
import static com.github.benmanes.caffeine.cache.Specifications.UNSAFE_ACCESS;
import static com.github.benmanes.caffeine.cache.Specifications.newFieldOffset;
import static com.github.benmanes.caffeine.cache.Specifications.offsetName;
import static com.github.benmanes.caffeine.cache.Specifications.vRefQueueType;
import static com.github.benmanes.caffeine.cache.Specifications.vTypeVar;
import static com.google.common.base.Preconditions.checkState;
import java.lang.ref.Reference;
import java.util.Objects;
import javax.lang.model.element.Modifier;
import com.github.benmanes.caffeine.cache.Feature;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
/**
* Adds the value to the node.
*
* @author ben.manes@gmail.com (Ben Manes)
*/
public final class AddValue extends NodeRule {
@Override
protected boolean applies() {
return isBaseClass();
}
@Override
protected void execute() {
context.nodeSubtype
.addField(newFieldOffset(context.className, "value"))
.addField(newValueField())
.addMethod(newGetter(valueStrength(), vTypeVar, "value", Visibility.LAZY))
.addMethod(newGetRef("value"))
.addMethod(makeSetValue())
.addMethod(makeContainsValue());
addValueConstructorAssignment(context.constructorByKey, "key");
addValueConstructorAssignment(context.constructorByKeyRef, "keyReference");
}
private FieldSpec newValueField() {
FieldSpec.Builder fieldSpec = isStrongValues()
? FieldSpec.builder(vTypeVar, "value", Modifier.VOLATILE)
: FieldSpec.builder(valueReferenceType(), "value", Modifier.VOLATILE);
return fieldSpec.build();
}
/** Creates the setValue method. */
private MethodSpec makeSetValue() {
MethodSpec.Builder setter = MethodSpec.methodBuilder("setValue")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addParameter(vTypeVar, "value")
.addParameter(vRefQueueType, "referenceQueue");
if (isStrongValues()) {
setter.addStatement("$T.UNSAFE.putObject(this, $N, $N)",
UNSAFE_ACCESS, offsetName("value"), "value");
} else {
setter.addStatement("(($T<V>) getValueReference()).clear()", Reference.class);
setter.addStatement("$T.UNSAFE.putObject(this, $N, new $T($L, $N, referenceQueue))",
UNSAFE_ACCESS, offsetName("value"), valueReferenceType(), "key", "value");
}
return setter.build();
}
private MethodSpec makeContainsValue() {
MethodSpec.Builder containsValue = MethodSpec.methodBuilder("containsValue")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addParameter(Object.class, "value")
.returns(boolean.class);
if (isStrongValues()) {
containsValue.addStatement("return $T.equals(value, getValue())", Objects.class);
} else {
containsValue.addStatement("return getValue() == value");
}
return containsValue.build();
}
private TypeName valueReferenceType() {
checkState(!context.generateFeatures.contains(Feature.STRONG_VALUES));
String clazz = context.generateFeatures.contains(Feature.WEAK_VALUES)
? "WeakValueReference"
: "SoftValueReference";
return ParameterizedTypeName.get(ClassName.get(PACKAGE_NAME + ".References", clazz), vTypeVar);
}
/** Adds a constructor assignment. */
private void addValueConstructorAssignment(MethodSpec.Builder constructor, String keyName) {
if (isStrongValues()) {
constructor.addStatement("$T.UNSAFE.putObject(this, $N, $N)",
UNSAFE_ACCESS, offsetName("value"), "value");
} else {
constructor.addStatement("$T.UNSAFE.putObject(this, $N, new $T($N, $N, $N))",
UNSAFE_ACCESS, offsetName("value"), valueReferenceType(),
keyName, "value", "valueReferenceQueue");
}
}
private boolean isStrongValues() {
return context.parentFeatures.contains(Feature.STRONG_VALUES)
|| context.generateFeatures.contains(Feature.STRONG_VALUES);
}
}