/*
* Copyright (C) 2015, 2016 higherfrequencytrading.com
* Copyright (C) 2016 Roman Leventov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.chronicle.values;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import net.openhft.chronicle.core.Jvm;
import sun.misc.Unsafe;
import static java.lang.String.format;
import static javax.lang.model.element.Modifier.*;
import static net.openhft.chronicle.values.Utils.capitalize;
abstract class HeapMemberGenerator extends MemberGenerator {
FieldSpec field;
private FieldSpec fieldOffset;
HeapMemberGenerator(FieldModel fieldModel) {
super(fieldModel);
}
abstract String putVolatile();
abstract String putOrdered();
abstract String compareAndSwap();
abstract String arrayBase();
abstract String arrayScale();
FieldSpec fieldOffset(ValueBuilder valueBuilder) {
if (fieldOffset == null) {
fieldOffset = FieldSpec.builder(long.class, fieldModel.name + "Offset")
.addModifiers(PRIVATE, STATIC, FINAL)
.build();
valueBuilder.staticBlockBuilder().addStatement(
"$N = $N.objectFieldOffset($T.getField($N.class, $S))",
fieldOffset, valueBuilder.unsafe(), Jvm.class, valueBuilder.className,
field.name);
valueBuilder.typeBuilder.addField(fieldOffset);
}
return fieldOffset;
}
Class fieldType() {
return fieldModel.type;
}
abstract String wrap(
ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder, String rawStoredValue);
abstract String unwrap(MethodSpec.Builder methodBuilder, String inputValue);
@Override
void generateFields(ValueBuilder valueBuilder) {
field = FieldSpec.builder(fieldType(), fieldModel.fieldName(), PRIVATE).build();
valueBuilder.typeBuilder.addField(field);
}
@Override
void generateArrayElementFields(ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder) {
field = FieldSpec.builder(ArrayTypeName.of(fieldType()), fieldModel.fieldName())
.addModifiers(PRIVATE, FINAL)
.initializer("new $T[$L]", fieldType(), arrayFieldModel.array.length())
.build();
valueBuilder.typeBuilder.addField(field);
}
@Override
public void generateGet(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
String rawValue = "raw" + capitalize(field.name) + "Value";
methodBuilder.addStatement("$T $N = $N", fieldType(), rawValue, field);
methodBuilder.addStatement("return $N", wrap(valueBuilder, methodBuilder, rawValue));
}
@Override
public void generateArrayElementGet(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
String rawValue = "raw" + capitalize(field.name) + "Value";
methodBuilder.addStatement("$T $N = $N[index]", fieldType(), rawValue, field);
methodBuilder.addStatement("return $N", wrap(valueBuilder, methodBuilder, rawValue));
}
@Override
public void generateSet(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
methodBuilder.addStatement("this.$N = $N",
field, unwrap(methodBuilder, fieldModel.varName()));
}
@Override
public void generateArrayElementSet(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
methodBuilder.addStatement("this.$N[index] = $N",
field, unwrap(methodBuilder, fieldModel.varName()));
}
@Override
public void generateSetVolatile(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
methodBuilder.addStatement("$N.$N(this, $N, $N)",
valueBuilder.unsafe(), putVolatile(), fieldOffset(valueBuilder),
unwrap(methodBuilder, fieldModel.varName()));
}
@Override
public void generateArrayElementSetVolatile(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
arrayFieldModel.checkBounds(methodBuilder);
methodBuilder.addStatement(
format("$N.$N($N, (long) $T.$N + (index * (long) $T.$N), %s)",
unwrap(methodBuilder, fieldModel.varName())),
valueBuilder.unsafe(), putVolatile(), field, Unsafe.class, arrayBase(),
Unsafe.class, arrayScale());
}
@Override
public void generateSetOrdered(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
methodBuilder.addStatement("$N.$N(this, $N, $N)",
valueBuilder.unsafe(), putOrdered(), fieldOffset(valueBuilder),
unwrap(methodBuilder, fieldModel.varName()));
}
@Override
public void generateArrayElementSetOrdered(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
arrayFieldModel.checkBounds(methodBuilder);
methodBuilder.addStatement(
format("$N.$N($N, (long) $T.$N + (index * (long) $T.$N), %s)",
unwrap(methodBuilder, fieldModel.varName())),
valueBuilder.unsafe(), putOrdered(), field, Unsafe.class, arrayBase(), Unsafe.class,
arrayScale());
}
@Override
public void generateCompareAndSwap(
ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
String unwrappedOld = unwrap(methodBuilder, fieldModel.oldName());
String unwrappedNew = unwrap(methodBuilder, fieldModel.newName());
methodBuilder.addStatement("return $N.$N(this, $N, $N, $N)",
valueBuilder.unsafe(), compareAndSwap(), fieldOffset(valueBuilder),
unwrappedOld, unwrappedNew);
}
@Override
public void generateArrayElementCompareAndSwap(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
arrayFieldModel.checkBounds(methodBuilder);
String unwrappedOld = unwrap(methodBuilder, fieldModel.oldName());
String unwrappedNew = unwrap(methodBuilder, fieldModel.newName());
methodBuilder.addStatement(
"return $N.$N($N, (long) $T.$N + (index * (long) $T.$N), $N, $N)",
valueBuilder.unsafe(), compareAndSwap(), field, Unsafe.class, arrayBase(),
Unsafe.class, arrayScale(), unwrappedOld, unwrappedNew);
}
@Override
public void generateCopyFrom(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
String copy = fieldModel.name + "Copy";
methodBuilder.addStatement("$T $N = from.$N()",
fieldModel.type, copy, fieldModel.getOrGetVolatile().getName());
methodBuilder.addStatement("this.$N = $N", field, unwrap(methodBuilder, copy));
}
@Override
public void generateArrayElementCopyFrom(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
String copy = arrayFieldModel.name + "Copy";
methodBuilder.addStatement("$T $N = from.$N(index)",
arrayFieldModel.type, copy, arrayFieldModel.getOrGetVolatile().getName());
methodBuilder.addStatement("this.$N[index] = $N", field, unwrap(methodBuilder, copy));
}
@Override
void generateToString(ValueBuilder valueBuilder, MethodSpec.Builder methodBuilder) {
String rawValue = "raw" + capitalize(field.name) + "Value";
methodBuilder.addStatement("$T $N = $N", fieldType(), rawValue, field);
genToString(methodBuilder, wrap(valueBuilder, methodBuilder, rawValue));
}
@Override
void generateArrayElementToString(
ArrayFieldModel arrayFieldModel, ValueBuilder valueBuilder,
MethodSpec.Builder methodBuilder) {
String rawValue = "raw" + capitalize(field.name) + "Value";
methodBuilder.addStatement("$T $N = $N[index]", fieldType(), rawValue, field);
genArrayElementToString(methodBuilder, wrap(valueBuilder, methodBuilder, rawValue));
}
}