/* * 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.UNSAFE_ACCESS; import static com.github.benmanes.caffeine.cache.Specifications.offsetName; import static org.apache.commons.lang3.StringUtils.capitalize; import java.lang.ref.Reference; import java.util.function.Consumer; import javax.lang.model.element.Modifier; import com.github.benmanes.caffeine.cache.Feature; import com.google.common.collect.Iterables; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeName; /** * A code generation rule for a node. This class holds the common state and methods for rules to * act upon and mutate. * * @author ben.manes@gmail.com (Ben Manes) */ public abstract class NodeRule implements Consumer<NodeContext> { protected NodeContext context; @Override public final void accept(NodeContext context) { this.context = context; if (applies()) { execute(); } } /** @return if the rule should be executed. */ protected abstract boolean applies(); protected abstract void execute(); protected final boolean isBaseClass() { return context.superClass.equals(TypeName.OBJECT); } protected final Strength keyStrength() { return strengthOf(Iterables.get(context.generateFeatures, 0)); } protected final Strength valueStrength() { return strengthOf(Iterables.get(context.generateFeatures, 1)); } /** Creates an accessor that returns the reference. */ protected final MethodSpec newGetRef(String varName) { MethodSpec.Builder getter = MethodSpec.methodBuilder("get" + capitalize(varName) + "Reference") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(Object.class); getter.addStatement("return $T.UNSAFE.getObject(this, $N)", UNSAFE_ACCESS, offsetName(varName)); return getter.build(); } /** Creates an accessor that returns the unwrapped variable. */ protected final MethodSpec newGetter(Strength strength, TypeName varType, String varName, Visibility visibility) { MethodSpec.Builder getter = MethodSpec.methodBuilder("get" + capitalize(varName)) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(varType); String type; if (varType.isPrimitive()) { type = varType.equals(TypeName.INT) ? "Int" : "Long"; } else { type = "Object"; } if (strength == Strength.STRONG) { if (visibility.isRelaxed) { if (varType.isPrimitive()) { getter.addStatement("return $T.UNSAFE.get$N(this, $N)", UNSAFE_ACCESS, type, offsetName(varName)); } else { getter.addStatement("return ($T) $T.UNSAFE.get$N(this, $N)", varType, UNSAFE_ACCESS, type, offsetName(varName)); } } else { getter.addStatement("return $N", varName); } } else { if (visibility.isRelaxed) { getter.addStatement("return (($T<$T>) $T.UNSAFE.get$N(this, $N)).get()", Reference.class, varType, UNSAFE_ACCESS, type, offsetName(varName)); } else { getter.addStatement("return $N.get()", varName); } } return getter.build(); } /** Creates a mutator to the variable. */ protected final MethodSpec newSetter(TypeName varType, String varName, Visibility visibility) { String methodName = "set" + Character.toUpperCase(varName.charAt(0)) + varName.substring(1); String type; if (varType.isPrimitive()) { type = varType.equals(TypeName.INT) ? "Int" : "Long"; } else { type = "Object"; } MethodSpec.Builder setter = MethodSpec.methodBuilder(methodName) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addParameter(varType, varName); if (visibility.isRelaxed) { setter.addStatement("$T.UNSAFE.put$L(this, $N, $N)", UNSAFE_ACCESS, type, offsetName(varName), varName); } else { setter.addStatement("this.$N = $N", varName, varName); } return setter.build(); } private Strength strengthOf(Feature feature) { for (Strength strength : Strength.values()) { if (feature.name().startsWith(strength.name())) { return strength; } } throw new IllegalStateException("No strength for " + feature); } protected enum Strength { STRONG, WEAK, SOFT, } protected enum Visibility { IMMEDIATE(false), LAZY(true); final boolean isRelaxed; Visibility(boolean mode) { this.isRelaxed = mode; } } }