Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2016-2021 chronicle.software
*
* https://chronicle.software
*
* 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 net.openhft.chronicle.values;
import net.openhft.chronicle.bytes.Byteable;
import net.openhft.chronicle.bytes.BytesMarshallable;
import java.io.Externalizable;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static net.openhft.chronicle.values.MethodTemplate.Type.ARRAY;
import static net.openhft.chronicle.values.MethodTemplate.Type.SCALAR;
import static net.openhft.chronicle.values.Primitives.isPrimitiveIntegerType;
enum CodeTemplate {
; // none
public static final Function NO_ANNOTATED_PARAM = m -> null;
static final List> NON_MODEL_TYPES = asList(
Object.class, Serializable.class, Externalizable.class, BytesMarshallable.class,
Copyable.class, Byteable.class);
private static final SortedSet METHOD_TEMPLATES =
new TreeSet<>(
comparing((MethodTemplate t) -> t.parameters)
.thenComparing(k -> -k.regex.length())
.thenComparing(k -> k.regex));
private static final String FIELD_NAME = "([a-zA-Z_$][a-zA-Z\\d_$]*)";
static {
addReadPatterns("get", 0, FieldModel::setGet);
addReadPatterns("", 0, FieldModel::setGet);
addReadPatterns("is", 0, FieldModel::setGet);
addReadPatterns("getVolatile", 0, FieldModel::setGetVolatile);
addReadPatterns("getUsing", 1, FieldModel::setGetUsing);
addWritePattern("set", 1, FieldModel::setSet);
addWritePattern("", 1, FieldModel::setSet);
addWritePattern("setVolatile", 1, FieldModel::setSetVolatile);
addWritePattern("setOrdered", 1, FieldModel::setSetOrdered);
addWritePattern("add", 1, FieldModel::setAdd);
addWritePattern("addAtomic", 1, FieldModel::setAddAtomic);
addWritePattern("compareAndSwap", 2, FieldModel::setCompareAndSwap);
}
private static void addReadPatterns(
String regex, int arguments, BiConsumer addMethodToModel) {
regex += FIELD_NAME;
add(regex, arguments, SCALAR, Method::getReturnType, NO_ANNOTATED_PARAM, addMethodToModel);
add(regex + "At", arguments + 1, ARRAY, Method::getReturnType, NO_ANNOTATED_PARAM,
addMethodToModel);
}
public static void addWritePattern(
String regex, int arguments, BiConsumer addMethodToModel) {
regex += FIELD_NAME;
add(regex, arguments, SCALAR,
m -> m.getParameterTypes()[arguments - 1],
m -> m.getParameters()[arguments - 1],
addMethodToModel);
add(regex + "At", arguments + 1, ARRAY,
m -> m.getParameterTypes()[arguments],
m -> m.getParameters()[arguments],
addMethodToModel);
}
private static void add(
String regex, int parameters, MethodTemplate.Type type,
Function fieldType, Function annotatedParameter,
BiConsumer addMethodToModel) {
METHOD_TEMPLATES.add(new MethodTemplate(regex, parameters, type, fieldType,
annotatedParameter, addMethodToModel));
}
static ValueModel createValueModel(Class> valueType) {
List fields = methodsAndTemplatesByField(valueType).entrySet().stream()
.map(e -> createAndConfigureModel(e.getKey(), e.getValue())).collect(toList());
if (fields.isEmpty())
throw new IllegalArgumentException(valueType + " is not a value interface");
fields.forEach(FieldModel::checkAnyWriteMethodPresent);
fields.forEach(FieldModel::postProcess);
fields.forEach(FieldModel::checkState);
return new ValueModel(valueType, fields.stream());
}
private static FieldModel createAndConfigureModel(
String fieldName, List methodsAndTemplates) {
if (methodsAndTemplates.stream().map(mt -> mt.template.type).distinct().count() > 1) {
throw new IllegalArgumentException("All or none accessors of the " + fieldName +
" field should end with -At (what means this is an array field)");
}
ScalarFieldModel scalarModel =
createAndConfigureScalarModel(fieldName, methodsAndTemplates);
if (methodsAndTemplates.get(0).template.type == SCALAR) {
return scalarModel;
} else {
// If the field turns out to be an array field, @Align applied to the element model belongs to the array
// model, and the element model is configured with Array.elementOffsetAlignment() and
// Array.elementDontCrossAlignment().
scalarModel.resetAlignment();
ArrayFieldModel arrayModel = new ArrayFieldModel(scalarModel);
configureModel(arrayModel, methodsAndTemplates);
return arrayModel;
}
}
private static ScalarFieldModel createAndConfigureScalarModel(
String fieldName, List methodsAndTemplates) {
ScalarFieldModel nonPointerModel =
createNonPointerScalarModel(fieldName, methodsAndTemplates);
configureModel(nonPointerModel, methodsAndTemplates);
boolean hasPointerAnnotation = methodsAndTemplates.stream().map(mt -> mt.method)
.flatMap(m -> Arrays.stream(m.getParameterAnnotations()).flatMap(Arrays::stream))
.anyMatch(a -> a.annotationType() == Pointer.class);
if (hasPointerAnnotation) {
if (!(nonPointerModel instanceof ValueFieldModel)) {
throw new IllegalStateException(fieldName + " annotated with @Pointer but has " +
nonPointerModel.type.getName() + " type which is not a value interface");
}
PointerFieldModel pointerModel =
new PointerFieldModel((ValueFieldModel) nonPointerModel);
configureModel(pointerModel, methodsAndTemplates);
return pointerModel;
} else {
return nonPointerModel;
}
}
private static void configureModel(
FieldModel model, List methodsAndTemplates) {
methodsAndTemplates.forEach(mt -> {
model.name = mt.fieldName;
model.addInfo(mt.method, mt.template);
mt.template.addMethodToModel.accept(model, mt.method);
});
}
private static ScalarFieldModel createNonPointerScalarModel(
String fieldName, List methodsAndTemplates) {
// CharSequence fields could have a method void getUsing() which doesn't contain actual
// field type info (String or CharSequence).
MethodAndTemplate nonGetUsingMethodAndTemplate = methodsAndTemplates.stream()
.filter(mt -> !mt.template.regex.startsWith("getUsing"))
.findAny().orElseThrow(() -> new IllegalStateException(fieldName +
" field should have some accessor methods except " +
methodsAndTemplates.get(0).method.getName()));
MethodTemplate nonGetUsingMethodTemplate = nonGetUsingMethodAndTemplate.template;
Method nonGetUsingMethod = nonGetUsingMethodAndTemplate.method;
Class fieldType = nonGetUsingMethodTemplate.fieldType.apply(nonGetUsingMethod);
if (isPrimitiveIntegerType(fieldType))
return new IntegerFieldModel();
if (fieldType == float.class || fieldType == double.class)
return new FloatingFieldModel();
if (fieldType == boolean.class)
return new BooleanFieldModel();
if (Enum.class.isAssignableFrom(fieldType))
return new EnumFieldModel();
if (fieldType == Date.class)
return new DateFieldModel();
if (CharSequence.class.isAssignableFrom(fieldType))
return new CharSequenceFieldModel();
if (fieldType.isInterface())
return new ValueFieldModel();
throw new IllegalStateException(fieldName + " field type " + fieldType +
" is not supported: not a primitive, enum, CharSequence " +
"or another value interface");
}
private static Map> methodsAndTemplatesByField(
Class> valueType) {
return Stream.of(valueType.getMethods())
.filter(m -> (m.getModifiers() & Modifier.ABSTRACT) != 0)
.filter(m -> NON_MODEL_TYPES.stream().noneMatch(t -> hasMethod(t, m)))
.map(m -> {
MethodTemplate methodTemplate = METHOD_TEMPLATES.stream()
.filter(t -> t.parameters == m.getParameterCount())
.filter(t -> m.getName().matches(t.regex))
.findFirst().orElseThrow(IllegalStateException::new);
Matcher matcher = Pattern.compile(methodTemplate.regex)
.matcher(m.getName());
if (!matcher.find())
throw new AssertionError();
String fieldName = convertFieldName(matcher.group(1));
return new MethodAndTemplate(m, methodTemplate, fieldName);
}).collect(groupingBy(mt -> mt.fieldName));
}
private static boolean hasMethod(Class> type, Method m) {
return Stream.of(type.getMethods())
.anyMatch(m2 -> m2.getName().equals(m.getName()) &&
Arrays.equals(m2.getParameterTypes(), m.getParameterTypes()));
}
static String convertFieldName(String name) {
if (name.length() > 1 && Character.isUpperCase(name.charAt(1))) return name;
if (Character.isLowerCase(name.charAt(0))) return name;
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
}
static class MethodAndTemplate {
final Method method;
final MethodTemplate template;
final String fieldName;
MethodAndTemplate(Method method, MethodTemplate template, String fieldName) {
this.method = method;
this.template = template;
this.fieldName = fieldName;
}
}
}