/*
* Copyright 2016 higherfrequencytrading.com
*
* 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.lang.model;
import net.openhft.lang.io.serialization.BytesMarshallable;
import net.openhft.lang.model.constraints.Digits;
import net.openhft.lang.model.constraints.Group;
import net.openhft.lang.model.constraints.MaxSize;
import net.openhft.lang.model.constraints.Range;
import java.io.Externalizable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import static net.openhft.lang.MemoryUnit.BITS;
import static net.openhft.lang.MemoryUnit.BYTES;
/**
* User: peter.lawrey Date: 06/10/13private static final int VALUE Time: 17:23
*/
public class DataValueModelImpl<T> implements DataValueModel<T> {
private static final Map<Class, Integer> HEAP_SIZE_MAP = new HashMap<Class, Integer>();
private static final String VOLATILE_GETTER_PREFIX = "volatile";
private static final java.lang.String ORDERED_SETTER_PREFIX = "ordered";
static {
HEAP_SIZE_MAP.put(boolean.class, 1);
HEAP_SIZE_MAP.put(byte.class, 8);
HEAP_SIZE_MAP.put(char.class, 16);
HEAP_SIZE_MAP.put(short.class, 16);
HEAP_SIZE_MAP.put(int.class, 32);
HEAP_SIZE_MAP.put(float.class, 32);
HEAP_SIZE_MAP.put(long.class, 64);
HEAP_SIZE_MAP.put(double.class, 64);
HEAP_SIZE_MAP.put(Date.class, 64);
}
private final Map<String, FieldModelImpl> fieldModelMap = new TreeMap<String, FieldModelImpl>();
private final Class<T> type;
private final Map<Class, DataValueModel> nestedMap = new HashMap<Class, DataValueModel>();
public DataValueModelImpl(Class<T> type) {
this.type = type;
if (!type.isInterface())
throw new IllegalArgumentException("type must be an interface, was " + type);
Method[] methods = type.getMethods();
for (Method method : methods) {
Class<?> declaringClass = method.getDeclaringClass();
if (declaringClass == Object.class
|| declaringClass == Externalizable.class
|| declaringClass == BytesMarshallable.class
|| declaringClass == Copyable.class
|| declaringClass == Byteable.class)
continue;
// ignore the default or static methods
if(isMethodDefaultOrStatic(method))
continue;
String name = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
final Class<?> returnType = method.getReturnType();
switch (parameterTypes.length) {
case 0: {
String name5 = getUnlock(name);
if (name5 != null && returnType == void.class) {
FieldModelImpl fm = acquireField(name5);
fm.unlock(method);
break;
}
String name4 = getBusyLock(name);
if (name4 != null && returnType == void.class) {
FieldModelImpl fm = acquireField(name4);
fm.busyLock(method);
break;
}
String name3 = getTryLock(name);
if (name3 != null && returnType == boolean.class) {
FieldModelImpl fm = acquireField(name3);
fm.tryLock(method);
break;
}
String name6 = getSizeOf(name);
if (name6 != null && returnType == int.class) {
FieldModelImpl fm = acquireField(name6);
fm.sizeOf(method);
break;
}
if (returnType == void.class)
throw new IllegalArgumentException("void () not supported " + method);
String name2 = getGetter(name, returnType);
if (isVolatileGetter(name2)) {
FieldModelImpl fm = acquireField(volatileGetterFieldName(name2));
fm.volatileGetter(method);
fm.setVolatile(true);
} else {
FieldModelImpl fm = acquireField(name2);
fm.getter(method);
}
break;
}
case 1: {
String name7 = getUsing(name, method);
if (name7 != null) {
FieldModelImpl fm = acquireField(name7);
fm.getUsing(method);
break;
}
String name5 = getTryLockNanos(name);
if (name5 != null && returnType == boolean.class) {
FieldModelImpl fm = acquireField(name5);
fm.tryLockNanos(method);
break;
}
String name4 = getAtomicAdder(name);
if (name4 != null) {
FieldModelImpl fm = acquireField(name4);
fm.atomicAdder(method);
break;
}
String name3 = getAdder(name);
if (name3 != null) {
FieldModelImpl fm = acquireField(name3);
fm.adder(method);
break;
}
String name6 = getGetterAt(name, returnType);
if (name6 != null && parameterTypes[0] == int.class && returnType != void.class) {
if (isVolatileGetter(name6)) {
FieldModelImpl fm = acquireField(volatileGetterFieldName(name6));
fm.volatileIndexedGetter(method);
fm.setVolatile(true);
} else {
FieldModelImpl fm = acquireField(name6);
fm.indexedGetter(method);
}
break;
}
if (returnType != void.class)
throw new IllegalArgumentException("setter must be void " + method);
String name2 = getSetter(name);
if (isOrderedSetter(name2)) {
FieldModelImpl fm = acquireField(orderedSetterFieldName(name2));
fm.orderedSetter(method);
} else {
FieldModelImpl fm = acquireField(name2);
fm.setter(method);
}
break;
}
case 2: {
String name2 = getCAS(name);
if (name2 != null && returnType == boolean.class) {
FieldModelImpl fm = acquireField(name2);
fm.cas(method);
break;
}
String name3 = getSetterAt(name);
if (name3 != null && parameterTypes[0] == int.class && returnType == void.class) {
if (isOrderedSetter(name3)) {
FieldModelImpl fm = acquireField(orderedSetterFieldName(name3));
fm.orderedIndexedSetter(method);
} else {
FieldModelImpl fm = acquireField(name3);
fm.indexedSetter(method);
}
break;
}
}
default: {
throw new IllegalArgumentException("method not supported " + method);
}
}
}
for (Map.Entry<String, FieldModelImpl> entry : fieldModelMap.entrySet()) {
FieldModelImpl model = entry.getValue();
if ((model.getter() == null && model.getUsing() == null) || (model.setter() == null && model
.getter()
.getReturnType()
.isPrimitive()))
if (model.volatileGetter() == null || (model.orderedSetter() == null && model.volatileGetter().getReturnType().isPrimitive()))
if (model.indexedGetter() == null || (model.indexedSetter() == null && model.indexedGetter().getReturnType().isPrimitive()))
if (model.volatileIndexedGetter() == null || (model.orderedIndexedSetter() == null && model.volatileIndexedGetter().getReturnType().isPrimitive()))
if (model.busyLock() == null || model.unlock() == null)
throw new IllegalArgumentException("Field " + entry.getKey() + " must have a getter & setter, or getAt & setAt, or busyLock & unlock.");
if (model.indexedGetter() != null || model.indexedSetter() != null)
if (model.indexSize() == null)
throw new IllegalStateException("You must set a MaxSize for the range of the index for the getter or setter");
Class ftype = model.type();
if (!isScalar(ftype) && !nestedMap.containsKey(ftype))
nestedMap.put(ftype, new DataValueModelImpl(ftype));
}
}
public static int heapSize(Class primitiveType) {
if (!primitiveType.isPrimitive())
throw new IllegalArgumentException();
return (int) BYTES.alignAndConvert(HEAP_SIZE_MAP.get(primitiveType), BITS);
}
private static String getCAS(String name) {
final int len = 14;
if (name.length() > len && name.startsWith("compareAndSwap") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return null;
}
private static String getSizeOf(String name) {
final int len = 6;
if (name.length() > len && name.startsWith("sizeOf") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return null;
}
private static String getAtomicAdder(String name) {
final int len = 9;
if (name.length() > len && name.startsWith("addAtomic") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return null;
}
private static String getAdder(String name) {
final int len = 3;
if (name.length() > len && name.startsWith("add") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return null;
}
private static String getSetter(String name) {
final int len = 3;
if (name.length() > len && name.startsWith("set") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return name;
}
private static String getSetterAt(String name) {
final int len = 3;
final int len2 = 2;
if (name.length() > len + len2 && name.startsWith("set") && Character.isUpperCase(
name.charAt(len)) && name.endsWith("At"))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1, name.length() - len2);
return name;
}
private static String getGetter(String name, Class returnType) {
if (name.length() > 3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3)))
return Character.toLowerCase(name.charAt(3)) + name.substring(4);
if ((returnType == boolean.class || returnType == Boolean.class)
&& name.length() > 2 && name.startsWith("is") && Character.isUpperCase(name.charAt(2)))
return Character.toLowerCase(name.charAt(2)) + name.substring(3);
return name;
}
private static String getUsing(String name, Method method) {
Class<?> returnType = method.getReturnType();
if (method.getParameterTypes().length != 1)
return null;
Class<?> parameter = method.getParameterTypes()[0];
if ((returnType == StringBuilder.class || returnType == void.class) && parameter ==
StringBuilder.class &&
name.length() > "getUsing".length() && name.startsWith
("getUsing") && Character.isUpperCase(name.charAt("getUsing".length())))
return Character.toLowerCase(name.charAt("getUsing".length())) + name.substring("getUsing"
.length() + 1);
return null;
}
private static String getGetterAt(String name, Class returnType) {
final int len = 3;
final int len2 = 2;
if (name.length() > len + len2 && name.startsWith("get") && Character.isUpperCase(
name.charAt(len)) && name.endsWith("At"))
return Character.toLowerCase(name.charAt(len)) + name.substring(4, name.length() - len2);
return name;
}
private static String getBusyLock(String name) {
final int len = 8;
if (name.length() > len && name.startsWith("busyLock") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return null;
}
private static String getUnlock(String name) {
final int len = 6;
if (name.length() > len && name.startsWith("unlock") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return null;
}
private static String getTryLockNanos(String name) {
final int len = 12;
if (name.length() > len && name.startsWith("tryLockNanos") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return null;
}
private static String getTryLock(String name) {
final int len = 7;
if (name.length() > len && name.startsWith("tryLock") && Character.isUpperCase(name.charAt(len)))
return Character.toLowerCase(name.charAt(len)) + name.substring(len + 1);
return null;
}
public boolean isMethodDefaultOrStatic(Method method) {
return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC)) ==
Modifier.PUBLIC) && method.getDeclaringClass().isInterface();
}
private boolean isOrderedSetter(String name2) {
return name2.startsWith(ORDERED_SETTER_PREFIX) ? true : false;
}
private boolean isVolatileGetter(String name2) {
return name2.startsWith(VOLATILE_GETTER_PREFIX) ? true : false;
}
private String volatileGetterFieldName(String name) {
name = name.substring(VOLATILE_GETTER_PREFIX.length());
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
}
private String orderedSetterFieldName(String name) {
name = name.substring(ORDERED_SETTER_PREFIX.length());
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
}
private FieldModelImpl acquireField(String name) {
FieldModelImpl fieldModelImpl = fieldModelMap.get(name);
if (fieldModelImpl == null)
fieldModelMap.put(name, fieldModelImpl = new FieldModelImpl(name));
return fieldModelImpl;
}
@Override
public Map<String, ? extends FieldModel> fieldMap() {
return fieldModelMap;
}
public boolean isScalar(Class type) {
return type.isPrimitive() || CharSequence.class.isAssignableFrom(type) || Enum.class.isAssignableFrom(type) || Date.class == type;
}
@Override
public Set<Class> nestedModels() {
return nestedMap.keySet();
}
@Override
public <N> DataValueModel<N> nestedModel(Class<N> nClass) {
@SuppressWarnings("unchecked")
DataValueModel<N> model = (DataValueModel<N>) (nClass == type ? this : nestedMap.get(nClass));
return model;
}
@Override
public Class<T> type() {
return type;
}
static class FieldModelImpl<T> implements FieldModel<T> {
private final String name;
private Method getter, setter;
private Method volatileGetter;
private Method orderedSetter;
private Digits digits;
private Range range;
private MaxSize maxSize;
private Group group;
private MaxSize indexSize;
private Method adder;
private Method atomicAdder;
private Method getUsing;
private Method cas;
private Method tryLockNanos;
private Method tryLock;
private Method busyLock;
private Method unlock;
private Method getterAt;
private Method setterAt;
private Method volatileGetterAt;
private Method orderedSetterAt;
private Method sizeOf;
private boolean isArray = false;
private boolean isVolatile = false;
public FieldModelImpl(String name) {
this.name = name;
}
public String name() {
return name;
}
public boolean isArray() {
return isArray;
}
@Override
public boolean isVolatile() {
return isVolatile;
}
@Override
public void setVolatile(boolean isVolatile) {
this.isVolatile = isVolatile;
}
public void getter(Method getter) {
this.getter = getter;
}
public Method getter() {
return getter;
}
public void setter(Method setter) {
this.setter = setter;
for (Annotation a : setter.getParameterAnnotations()[0]) {
if (a instanceof Digits)
digits = (Digits) a;
if (a instanceof Range)
range = (Range) a;
if (a instanceof MaxSize)
maxSize = (MaxSize) a;
}
for (Annotation a : setter.getAnnotations()) {
if (a instanceof Group)
group = (Group) a;
}
}
public Method setter() {
return setter;
}
public void volatileGetter(Method volatileGetter) {
this.volatileGetter = volatileGetter;
}
public Method volatileGetter() {
return volatileGetter;
}
public void orderedSetter(Method orderedSetter) {
this.orderedSetter = orderedSetter;
for (Annotation a : orderedSetter.getParameterAnnotations()[0]) {
if (a instanceof Digits)
digits = (Digits) a;
if (a instanceof Range)
range = (Range) a;
if (a instanceof MaxSize)
maxSize = (MaxSize) a;
}
for (Annotation a : orderedSetter.getAnnotations()) {
if (a instanceof Group)
group = (Group) a;
}
}
public Method orderedSetter() {
return orderedSetter;
}
@Override
public Class<T> type() {
return (Class<T>) (getter != null ? getter.getReturnType() :
volatileGetter != null ? volatileGetter.getReturnType() :
getterAt != null ? getterAt.getReturnType() :
volatileGetterAt != null ? volatileGetterAt.getReturnType() :
unlock != null ? int.class :
setter != null && setter.getParameterTypes().length == 1 ?
setter.getParameterTypes()[0] : null);
}
public void adder(Method method) {
adder = method;
}
public Method adder() {
return adder;
}
@Override
public int heapSize() {
Integer size = HEAP_SIZE_MAP.get(type());
if (size == null) return -1;
return size;
}
// maxSize in bits.
@Override
public int nativeSize() {
Integer size = HEAP_SIZE_MAP.get(type());
if (size != null)
return size;
return size().value() << 3;
}
@Override
public Digits digits() {
return digits;
}
@Override
public Range range() {
return range;
}
@Override
public MaxSize size() {
if (maxSize == null)
throw new IllegalStateException("Field " + name + " is missing @MaxSize on the setter");
return maxSize;
}
@Override
public Group group() {
return group;
}
@Override
public String toString() {
return "FieldModel{" +
"name='" + name + '\'' +
", getter=" + (getterAt != null ? getterAt : getter) +
", setter=" + (setterAt != null ? setterAt : setter) +
(unlock == null ? "" : ", busyLock=" + busyLock + ", tryLock=" + tryLock + ", unlock=" + unlock) +
(digits == null ? "" : ", digits= " + digits) +
(range == null ? "" : ", range= " + range) +
(maxSize == null ? "" : ", size= " + maxSize) +
((getterAt == null && setterAt == null) ? "" : ", indexSize= " + indexSize.toString().replace("@net.openhft.lang.model.constraints.", "")) +
'}';
}
public void atomicAdder(Method method) {
atomicAdder = method;
}
public void getUsing(Method method) {
getUsing = method;
}
public Method atomicAdder() {
return atomicAdder;
}
public void cas(Method method) {
cas = method;
}
public Method cas() {
return cas;
}
public void sizeOf(Method method) {
sizeOf = method;
}
public Method sizeOf() {
return sizeOf;
}
public void tryLockNanos(Method method) {
tryLockNanos = method;
}
public Method tryLockNanos() {
return tryLockNanos;
}
public void tryLock(Method tryLock) {
this.tryLock = tryLock;
}
public Method tryLock() {
return tryLock;
}
public void busyLock(Method busyLock) {
this.busyLock = busyLock;
}
public Method busyLock() {
return busyLock;
}
public void unlock(Method unlock) {
this.unlock = unlock;
}
public Method unlock() {
return unlock;
}
public void indexSize(MaxSize indexSize) {
if (indexSize != null)
this.indexSize = indexSize;
}
public MaxSize indexSize() {
return indexSize;
}
public void indexedGetter(Method indexedGetter) {
isArray = true;
this.getterAt = indexedGetter;
indexAnnotations(indexedGetter);
}
public Method indexedGetter() {
return getterAt;
}
public void indexedSetter(Method indexedSetter) {
isArray = true;
this.setterAt = indexedSetter;
indexAnnotations(indexedSetter);
}
public Method indexedSetter() {
return setterAt;
}
public void indexAnnotations(Method method) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (Annotation a : parameterAnnotations[0]) {
// if (a instanceof Digits)
// digits = (Digits) a;
// if (a instanceof Range)
// range = (Range) a;
if (a instanceof MaxSize)
indexSize = (MaxSize) a;
}
if( parameterAnnotations.length > 1 ) {
for (Annotation a : parameterAnnotations[1]) {
if (a instanceof Digits)
digits = (Digits) a;
if (a instanceof Range)
range = (Range) a;
if (a instanceof MaxSize)
maxSize = (MaxSize) a;
}
}
}
public void volatileIndexedGetter(Method volatileIndexedGetter) {
isArray = true;
this.volatileGetterAt = volatileIndexedGetter;
indexAnnotations(volatileIndexedGetter);
}
public Method volatileIndexedGetter() {
return volatileGetterAt;
}
public void orderedIndexedSetter(Method orderedIndexedSetter) {
isArray = true;
this.orderedSetterAt = orderedIndexedSetter;
indexAnnotations(orderedIndexedSetter);
}
public Method orderedIndexedSetter() {
return orderedSetterAt;
}
@Override
public Method getUsing() {
return getUsing;
}
}
}