convert优化features

This commit is contained in:
redkale
2023-08-31 22:30:55 +08:00
parent 3979af71cb
commit 707ad2da70
28 changed files with 976 additions and 198 deletions

View File

@@ -0,0 +1,39 @@
/*
*
*/
package org.redkale.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 定时任务标记只能作用于Service的方法上, 功能类似Spring里的Scheduled注解
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.8.0
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Scheduled {
String cron() default "";
String zone() default "";
long fixedDelay() default -1;
String fixedDelayString() default "";
long fixedRate() default -1;
String fixedRateString() default "";
long initialDelay() default -1;
String initialDelayString() default "";
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

View File

@@ -338,7 +338,7 @@ public final class ApiDocCommand {
swaggerOperatMap.put("deprecated", true);
}
Map<String, Object> respSchemaMap = new LinkedHashMap<>();
JsonFactory returnFactory = Rest.createJsonFactory(false, method.getAnnotationsByType(RestConvert.class), method.getAnnotationsByType(RestConvertCoder.class));
JsonFactory returnFactory = Rest.createJsonFactory(0, method.getAnnotationsByType(RestConvert.class), method.getAnnotationsByType(RestConvertCoder.class));
simpleSchemaType(returnFactory, node.getLogger(), swaggerComponentsMap, action.result(), resultType, respSchemaMap, true);
Map<String, Object> respMap = new LinkedHashMap<>();
@@ -762,6 +762,6 @@ public final class ApiDocCommand {
return example;
}
private static final JsonFactory exampleFactory = JsonFactory.create().tiny(false).nullable(false);
private static final JsonFactory exampleFactory = JsonFactory.create().features(0);
}

View File

@@ -19,8 +19,8 @@ import java.lang.reflect.Type;
*/
public abstract class BinaryConvert<R extends Reader, W extends Writer> extends Convert<R, W> {
protected BinaryConvert(ConvertFactory<R, W> factory) {
super(factory);
protected BinaryConvert(ConvertFactory<R, W> factory, int features) {
super(factory, features);
}
@Override

View File

@@ -24,8 +24,11 @@ public abstract class Convert<R extends Reader, W extends Writer> {
protected final ConvertFactory<R, W> factory;
protected Convert(ConvertFactory<R, W> factory) {
protected final int features;
protected Convert(ConvertFactory<R, W> factory, int features) {
this.factory = factory;
this.features = features;
}
public ConvertFactory<R, W> getFactory() {

View File

@@ -36,6 +36,12 @@ import org.redkale.util.*;
@SuppressWarnings("unchecked")
public abstract class ConvertFactory<R extends Reader, W extends Writer> {
//值为true时 String类型值为""Boolean类型值为false时不会输出默认为false
public static final int FEATURE_TINY = 1 << 1;
//值为true时 字段值为null时会输出默认为false
public static final int FEATURE_NULLABLE = 1 << 2;
private static final AtomicBoolean loaderInited = new AtomicBoolean();
private static Convert defProtobufConvert;
@@ -44,9 +50,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
protected Convert<R, W> convert;
protected boolean tiny; //值为true时 String类型值为""Boolean类型值为false时不会输出默认为false
protected boolean nullable; ///值为true时 字段值为null时会输出默认为false
//配置属性集合, 1<<1至1<<10为系统内置
protected int features;
private final Encodeable<W, ?> anyEncoder = new AnyEncoder(this);
@@ -74,9 +79,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
private boolean skipAllIgnore = false;
protected ConvertFactory(ConvertFactory<R, W> parent, boolean tiny, boolean nullable) {
this.tiny = tiny;
this.nullable = nullable;
protected ConvertFactory(ConvertFactory<R, W> parent, int features) {
this.features = features;
this.parent = parent;
if (parent == null) {
//---------------------------------------------------------
@@ -209,6 +213,17 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
}
}
public final int features() {
return this.features;
}
public ConvertFactory features(int features) {
if (features > -1) {
this.features = features;
}
return this;
}
public ConvertFactory parent() {
return this.parent;
}
@@ -242,8 +257,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
return type == ConvertType.PROTOBUF ? defProtobufConvert : null;
}
protected static boolean getSystemPropertyBoolean(String key, String parentkey, boolean defvalue) {
return Boolean.parseBoolean(System.getProperty(key, System.getProperty(parentkey, String.valueOf(defvalue))));
protected static int getSystemPropertyInt(String key, String parentkey, boolean defvalue, int feature) {
return Boolean.parseBoolean(System.getProperty(key, System.getProperty(parentkey, String.valueOf(defvalue)))) ? feature : 0;
}
public abstract ConvertType getConvertType();
@@ -254,7 +269,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
public abstract ConvertFactory createChild();
public abstract ConvertFactory createChild(boolean tiny, boolean nullable);
public abstract ConvertFactory createChild(int features);
protected SimpledCoder createEnumSimpledCoder(Class enumClass) {
return new EnumSimpledCoder(this, enumClass);
@@ -333,13 +348,29 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
return convert;
}
public static boolean tinyFeature(int features) {
return (features & FEATURE_TINY) > 0;
}
public static boolean nullableFeature(int features) {
return (features & FEATURE_NULLABLE) > 0;
}
public ConvertFactory tiny(boolean tiny) {
this.tiny = tiny;
if (tiny) {
this.features |= FEATURE_TINY;
} else {
this.features = this.features & ~FEATURE_TINY;
}
return this;
}
public ConvertFactory nullable(boolean nullable) {
this.nullable = nullable;
if (nullable) {
this.features |= FEATURE_NULLABLE;
} else {
this.features = this.features & ~FEATURE_NULLABLE;
}
return this;
}

View File

@@ -19,8 +19,8 @@ import java.lang.reflect.Type;
*/
public abstract class TextConvert<R extends Reader, W extends Writer> extends Convert<R, W> {
protected TextConvert(ConvertFactory<R, W> factory) {
super(factory);
protected TextConvert(ConvertFactory<R, W> factory, int features) {
super(factory, features);
}
@Override

View File

@@ -35,6 +35,9 @@ public abstract class Writer {
//对某个对象进行动态扩展字段值处理
protected Function<Object, ConvertField[]> objExtFunc;
//配置项
protected int features;
/**
* 设置specificObjectType
*
@@ -69,18 +72,27 @@ public abstract class Writer {
}
/**
* 当tiny=true时 字符串为空、boolean为false的字段值都会被跳过 不会输出。
* 获取配置属性
*
* @return 是否简化
*/
public abstract boolean tiny();
public final int features() {
return features;
}
/**
* 当nullable=true时 字段值为null时会输出该字段
*
* @return 是否简化
*/
public abstract boolean nullable();
public Writer features(int features) {
if (features > -1) {
this.features = features;
}
return this;
}
protected final boolean tiny() {
return ConvertFactory.tinyFeature(features);
}
protected final boolean nullable() {
return ConvertFactory.nullableFeature(features);
}
/**
* 输出null值

View File

@@ -26,13 +26,12 @@ public class BsonByteBufferWriter extends BsonWriter {
private int index;
public BsonByteBufferWriter(Supplier<ByteBuffer> supplier) {
this(false, false, supplier);
this(0, supplier);
}
protected BsonByteBufferWriter(boolean tiny, boolean nullable, Supplier<ByteBuffer> supplier) {
protected BsonByteBufferWriter(int features, Supplier<ByteBuffer> supplier) {
super((byte[]) null);
this.tiny = tiny;
this.nullable = nullable;
this.features = features;
this.supplier = supplier;
}
@@ -72,14 +71,8 @@ public class BsonByteBufferWriter extends BsonWriter {
}
@Override
public BsonByteBufferWriter tiny(boolean tiny) {
this.tiny = tiny;
return this;
}
@Override
public BsonByteBufferWriter nullable(boolean nullable) {
this.nullable = nullable;
public BsonByteBufferWriter features(int features) {
this.features = features;
return this;
}

View File

@@ -46,14 +46,8 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
private final ThreadLocal<BsonReader> readerPool = ThreadLocal.withInitial(BsonReader::new);
private final boolean tiny;
private final boolean nullable;
protected BsonConvert(ConvertFactory<BsonReader, BsonWriter> factory, boolean tiny, boolean nullable) {
super(factory);
this.tiny = tiny;
this.nullable = nullable;
protected BsonConvert(ConvertFactory<BsonReader, BsonWriter> factory, int features) {
super(factory, features);
}
@Override
@@ -82,7 +76,7 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public BsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc, BiFunction<Object, Object, Object> mapFieldFunc, Function<Object, ConvertField[]> objExtFunc) {
return new BsonConvert(getFactory(), tiny, nullable) {
return new BsonConvert(getFactory(), features) {
@Override
protected <S extends BsonWriter> S configWrite(S writer) {
return fieldFunc(writer, fieldFunc, mapFieldFunc, objExtFunc);
@@ -120,11 +114,11 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
//------------------------------ writer -----------------------------------------------------------
public BsonByteBufferWriter pollWriter(final Supplier<ByteBuffer> supplier) {
return configWrite(new BsonByteBufferWriter(tiny, nullable, supplier));
return configWrite(new BsonByteBufferWriter(features, supplier));
}
protected BsonWriter pollWriter(final OutputStream out) {
return configWrite(new BsonStreamWriter(tiny, nullable, out));
return configWrite(new BsonStreamWriter(features, out));
}
@Override
@@ -135,7 +129,7 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
} else {
writerPool.set(null);
}
return configWrite(writer.tiny(tiny).nullable(nullable));
return configWrite(writer.features(features));
}
@Override
@@ -240,7 +234,7 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public void convertToBytes(final ByteArray array, final Type type, final Object value) {
Objects.requireNonNull(array);
final BsonWriter writer = configWrite(new BsonWriter(array).tiny(tiny).nullable(nullable));
final BsonWriter writer = configWrite(new BsonWriter(array).features(features));
if (value == null) {
writer.writeNull();
} else {
@@ -286,7 +280,7 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
if (value == null) {
return null;
}
final BsonWriter writer = writerPool.get().tiny(tiny).nullable(nullable);
final BsonWriter writer = writerPool.get().features(features);
factory.loadEncoder(type == null ? value.getClass() : type).convertTo(writer, value);
return writer;
}

View File

@@ -25,8 +25,9 @@ import org.redkale.util.TypeToken;
public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
private static final BsonFactory instance = new BsonFactory(null,
getSystemPropertyBoolean("redkale.convert.bson.tiny", "redkale.convert.tiny", true),
getSystemPropertyBoolean("redkale.convert.bson.nullable", "redkale.convert.nullable", false));
getSystemPropertyInt("redkale.convert.bson.tiny", "redkale.convert.tiny", true, FEATURE_TINY)
| getSystemPropertyInt("redkale.convert.bson.nullable", "redkale.convert.nullable", false, FEATURE_NULLABLE)
);
static final Decodeable objectDecoder = instance.loadDecoder(Object.class);
@@ -50,28 +51,28 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
//instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class));
}
private BsonFactory(BsonFactory parent, boolean tiny, boolean nullable) {
super(parent, tiny, nullable);
private BsonFactory(BsonFactory parent, int features) {
super(parent, features);
}
@Override
public BsonFactory tiny(boolean tiny) {
this.tiny = tiny;
super.tiny(tiny);
return this;
}
protected boolean tiny() {
return this.tiny;
return (this.features & FEATURE_TINY) > 0;
}
@Override
public BsonFactory nullable(boolean nullable) {
this.nullable = nullable;
super.nullable(nullable);
return this;
}
protected boolean nullable() {
return this.nullable;
return (this.features & FEATURE_NULLABLE) > 0;
}
@Override
@@ -85,26 +86,25 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
}
public static BsonFactory create() {
return new BsonFactory(null, getSystemPropertyBoolean("redkale.convert.bson.tiny", "redkale.convert.tiny", true),
getSystemPropertyBoolean("redkale.convert.bson.nullable", "redkale.convert.nullable", false));
return new BsonFactory(null, instance.features);
}
@Override
public final BsonConvert getConvert() {
if (convert == null) {
convert = new BsonConvert(this, tiny, nullable);
convert = new BsonConvert(this, features);
}
return (BsonConvert) convert;
}
@Override
public BsonFactory createChild() {
return new BsonFactory(this, this.tiny, this.nullable);
return new BsonFactory(this, features);
}
@Override
public BsonFactory createChild(boolean tiny, boolean nullable) {
return new BsonFactory(this, tiny, nullable);
public BsonFactory createChild(int features) {
return new BsonFactory(this, features);
}
@Override

View File

@@ -18,8 +18,8 @@ class BsonStreamWriter extends BsonByteBufferWriter {
private OutputStream out;
protected BsonStreamWriter(boolean tiny, boolean nullable, OutputStream out) {
super(tiny, nullable, null);
protected BsonStreamWriter(int features, OutputStream out) {
super(features, null);
this.out = out;
}

View File

@@ -27,10 +27,6 @@ public class BsonWriter extends Writer implements ByteTuple {
protected int count;
protected boolean tiny = BsonFactory.root().tiny();
protected boolean nullable = BsonFactory.root().nullable();
public static ObjectPool<BsonWriter> createPool(int max) {
return ObjectPool.createSafePool(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
}
@@ -86,6 +82,7 @@ public class BsonWriter extends Writer implements ByteTuple {
public BsonWriter() {
this(defaultSize);
this.features = BsonFactory.root().features();
}
public BsonWriter(int size) {
@@ -97,23 +94,8 @@ public class BsonWriter extends Writer implements ByteTuple {
this.count = array.length();
}
@Override
public final boolean tiny() {
return tiny;
}
public BsonWriter tiny(boolean tiny) {
this.tiny = tiny;
return this;
}
@Override
public final boolean nullable() {
return nullable;
}
public BsonWriter nullable(boolean nullable) {
this.nullable = nullable;
public BsonWriter features(int features) {
super.features(features);
return this;
}

View File

@@ -34,23 +34,16 @@ public class JsonByteBufferWriter extends JsonWriter {
private int index;
public JsonByteBufferWriter(boolean tiny, boolean nullable, Supplier<ByteBuffer> supplier) {
this(tiny, nullable, null, supplier);
public JsonByteBufferWriter(int features, Supplier<ByteBuffer> supplier) {
this(features, null, supplier);
}
public JsonByteBufferWriter(boolean tiny, boolean nullable, Charset charset, Supplier<ByteBuffer> supplier) {
this.tiny = tiny;
this.nullable = nullable;
public JsonByteBufferWriter(int features, Charset charset, Supplier<ByteBuffer> supplier) {
this.features = features;
this.charset = charset;
this.supplier = supplier;
}
@Override
public JsonByteBufferWriter tiny(boolean tiny) {
this.tiny = tiny;
return this;
}
@Override
protected boolean recycle() {
super.recycle();
@@ -575,7 +568,7 @@ public class JsonByteBufferWriter extends JsonWriter {
writeTo('}');
return;
}
if (value == null || (tiny && value.isEmpty())) {
if (value == null || (tiny() && value.isEmpty())) {
expand(1);
this.buffers[index].put((byte) '}');
} else {
@@ -634,7 +627,7 @@ public class JsonByteBufferWriter extends JsonWriter {
writeTo('}');
return;
}
if (value == null || (tiny && value.isEmpty())) {
if (value == null || (tiny() && value.isEmpty())) {
int expandsize = expand(2);
if (expandsize == 0) { // 只需要一个buffer
ByteBuffer bb = this.buffers[index];

View File

@@ -55,9 +55,8 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
this.count = array.length();
}
public JsonBytesWriter(boolean tiny, boolean nullable, ByteArray array) {
this.tiny = tiny;
this.nullable = nullable;
public JsonBytesWriter(int features, ByteArray array) {
this.features = features;
this.content = array.content();
this.count = array.length();
}
@@ -282,7 +281,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
writeTo('}');
return;
}
if (value == null || (tiny && value.isEmpty())) {
if (value == null || (tiny() && value.isEmpty())) {
expand(1);
content[count++] = '}';
} else {
@@ -312,7 +311,7 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
writeTo('}');
return;
}
if (value == null || (tiny && value.isEmpty())) {
if (value == null || (tiny() && value.isEmpty())) {
expand(2);
content[count++] = '{';
content[count++] = '}';

View File

@@ -40,18 +40,13 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
private final ThreadLocal<JsonReader> readerPool = ThreadLocal.withInitial(JsonReader::new);
private final boolean tiny;
private final boolean nullable;
private Encodeable lastConvertEncodeable;
private Decodeable lastConvertDecodeable;
protected JsonConvert(JsonFactory factory, boolean tiny, boolean nullable) {
super(factory);
this.tiny = tiny;
this.nullable = nullable;
protected JsonConvert(JsonFactory factory, int features) {
super(factory, features);
}
@Override
@@ -80,7 +75,7 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override
public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> objFieldFunc, BiFunction<Object, Object, Object> mapFieldFunc, Function<Object, ConvertField[]> objExtFunc) {
return new JsonConvert(getFactory(), tiny, nullable) {
return new JsonConvert(getFactory(), features) {
@Override
protected <S extends JsonWriter> S configWrite(S writer) {
return fieldFunc(writer, objFieldFunc, mapFieldFunc, objExtFunc);
@@ -115,7 +110,7 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
} else {
bytesWriterPool.set(null);
}
return configWrite((JsonBytesWriter) writer.tiny(tiny).nullable(nullable));
return configWrite((JsonBytesWriter) writer.features(features));
}
@Override
@@ -135,7 +130,7 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
} else {
bytesWriterPool.set(null);
}
return configWrite((JsonBytesWriter) writer.tiny(tiny).nullable(nullable));
return configWrite((JsonBytesWriter) writer.features(features));
}
private void offerJsonBytesWriter(final JsonBytesWriter writer) {
@@ -395,7 +390,7 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override
public void convertToBytes(final ByteArray array, final Type type, final Object value) {
Objects.requireNonNull(array);
JsonBytesWriter writer = configWrite(new JsonBytesWriter(tiny, nullable, array));
JsonBytesWriter writer = configWrite(new JsonBytesWriter(features, array));
if (value == null) {
writer.writeNull();
} else {
@@ -415,10 +410,10 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public void convertTo(final OutputStream out, final Type type, final Object value) {
if (value == null) {
configWrite(new JsonStreamWriter(tiny, nullable, out)).writeNull();
configWrite(new JsonStreamWriter(features, out)).writeNull();
} else {
final Type t = type == null ? value.getClass() : type;
JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, nullable, out));
JsonStreamWriter writer = configWrite(new JsonStreamWriter(features, out));
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != t) {
encoder = factory.loadEncoder(t);
@@ -434,7 +429,7 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
Objects.requireNonNull(supplier);
JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, nullable, supplier));
JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(features, supplier));
if (value == null) {
out.writeNull();
} else {

View File

@@ -386,7 +386,7 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
final Map<String, AccessibleObject> mixedNames = mixedNames0;
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
final String newDynName = "org/redkaledyn/json/_Dyn" + JsonDynEncoder.class.getSimpleName()
+ "__" + clazz.getName().replace('.', '_').replace('$', '_') + "_" + factory.tiny() + "_" + factory.nullable() + "_" + Utility.md5Hex(memberb.toString()); //tiny必须要加上, 同一个类会有多个字段定制Convert
+ "__" + clazz.getName().replace('.', '_').replace('$', '_') + "_" + factory.features() + "_" + Utility.md5Hex(memberb.toString()); //tiny必须要加上, 同一个类会有多个字段定制Convert
try {
Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.'));
Class newClazz = clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz;
@@ -511,8 +511,10 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
int maxLocals = 4;
int elementIndex = -1;
final boolean tiny = ConvertFactory.tinyFeature(factory.features());
final boolean nullable = ConvertFactory.nullableFeature(factory.features());
final Class firstType = readGetSetFieldType(members.get(0));
final boolean mustHadComma = firstType.isPrimitive() && (firstType != boolean.class || !factory.tiny() || factory.nullable()); //byte/short/char/int/float/long/double
final boolean mustHadComma = firstType.isPrimitive() && (firstType != boolean.class || !tiny || nullable); //byte/short/char/int/float/long/double
if (onlyOneLatin1FieldObjectFlag) {
//out.writeObjectByOnlyOneLatin1FieldValue(messageFirstFieldBytes, value.getMessage());elementIndex++;
@@ -670,16 +672,16 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
}
}
Label msgnotemptyif = null;
if (!fieldtype.isPrimitive() && !factory.nullable()) { //if (message != null) { start
if (!fieldtype.isPrimitive() && !nullable) { //if (message != null) { start
mv.visitVarInsn(loadid, maxLocals);
msgnotemptyif = new Label();
mv.visitJumpInsn(IFNULL, msgnotemptyif);
if (factory.tiny() && fieldtype == String.class) {
if (tiny && fieldtype == String.class) {
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z", false);
mv.visitJumpInsn(IFNE, msgnotemptyif);
}
} else if (fieldtype == boolean.class && factory.tiny()) {
} else if (fieldtype == boolean.class && tiny) {
mv.visitVarInsn(loadid, maxLocals);
msgnotemptyif = new Label();
mv.visitJumpInsn(IFEQ, msgnotemptyif);
@@ -794,10 +796,10 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEINTERFACE, encodeableName, "convertTo", "(" + writerDesc + "Ljava/lang/Object;)V", true);
}
if (!fieldtype.isPrimitive() && !factory.nullable()) { //if (message != null) } end
if (!fieldtype.isPrimitive() && !nullable) { //if (message != null) } end
mv.visitLabel(msgnotemptyif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
} else if (fieldtype == boolean.class && factory.tiny()) {
} else if (fieldtype == boolean.class && tiny) {
mv.visitLabel(msgnotemptyif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}

View File

@@ -25,8 +25,9 @@ import org.redkale.util.Uint128;
public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
private static final JsonFactory instance = new JsonFactory(null,
getSystemPropertyBoolean("redkale.convert.json.tiny", "redkale.convert.tiny", false),
getSystemPropertyBoolean("redkale.convert.json.nullable", "redkale.convert.nullable", false));
getSystemPropertyInt("redkale.convert.json.tiny", "redkale.convert.tiny", false, FEATURE_TINY)
| getSystemPropertyInt("redkale.convert.json.nullable", "redkale.convert.nullable", false, FEATURE_NULLABLE)
);
static {
instance.register(Serializable.class, instance.loadEncoder(Object.class));
@@ -35,8 +36,8 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
//instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class));
}
private JsonFactory(JsonFactory parent, boolean tiny, boolean nullable) {
super(parent, tiny, nullable);
private JsonFactory(JsonFactory parent, int features) {
super(parent, features);
if (parent == null) {
this.register(InetAddress.class, InetAddressSimpledCoder.InetAddressJsonSimpledCoder.instance);
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressJsonSimpledCoder.instance);
@@ -55,15 +56,8 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
}
}
@Override
public JsonFactory tiny(boolean tiny) {
this.tiny = tiny;
return this;
}
@Override
public JsonFactory nullable(boolean nullable) {
this.nullable = nullable;
public JsonFactory features(int features) {
this.features = features;
return this;
}
@@ -78,9 +72,8 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
}
public static JsonFactory create() {
return new JsonFactory(null, getSystemPropertyBoolean("redkale.convert.json.tiny", "redkale.convert.tiny", false),
getSystemPropertyBoolean("redkale.convert.json.nullable", "redkale.convert.nullable", false));
}
return new JsonFactory(null, instance.features());
}
@Override
protected <E> Encodeable<JsonWriter, E> createDyncEncoder(Type type) {
@@ -97,30 +90,22 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
return new JsonMultiImplDecoder(this, types);
}
protected boolean tiny() {
return this.tiny;
}
protected boolean nullable() {
return this.nullable;
}
@Override
public final JsonConvert getConvert() {
if (convert == null) {
convert = new JsonConvert(this, tiny, nullable);
convert = new JsonConvert(this, features);
}
return (JsonConvert) convert;
}
@Override
public JsonFactory createChild() {
return new JsonFactory(this, this.tiny, this.nullable);
return new JsonFactory(this, this.features);
}
@Override
public JsonFactory createChild(boolean tiny, boolean nullable) {
return new JsonFactory(this, tiny, nullable);
public JsonFactory createChild(int features) {
return new JsonFactory(this, features);
}
@Override

View File

@@ -21,12 +21,12 @@ class JsonStreamWriter extends JsonByteBufferWriter {
private OutputStream out;
protected JsonStreamWriter(boolean tiny, boolean nullable, OutputStream out) {
this(tiny, nullable, null, out);
protected JsonStreamWriter(int features, OutputStream out) {
this(features, null, out);
}
protected JsonStreamWriter(boolean tiny, boolean nullable, Charset charset, OutputStream out) {
super(tiny, nullable, charset, null);
protected JsonStreamWriter(int features, Charset charset, OutputStream out) {
super(features, charset, null);
this.out = out;
}

View File

@@ -21,17 +21,12 @@ public abstract class JsonWriter extends Writer {
protected static final int defaultSize = Integer.getInteger("redkale.convert.json.writer.buffer.defsize", Integer.getInteger("redkale.convert.writer.buffer.defsize", 1024));
protected boolean tiny = JsonFactory.root().tiny();
protected boolean nullable = JsonFactory.root().nullable();
@Override
public boolean tiny() {
return tiny;
protected JsonWriter() {
this.features = JsonFactory.root().features();
}
public JsonWriter tiny(boolean tiny) {
this.tiny = tiny;
public JsonWriter features(int features) {
super.features(features);
return this;
}
@@ -40,16 +35,6 @@ public abstract class JsonWriter extends Writer {
return this.objExtFunc == null && this.objFieldFunc == null;
}
@Override
public boolean nullable() {
return nullable;
}
public JsonWriter nullable(boolean nullable) {
this.nullable = nullable;
return this;
}
//-----------------------------------------------------------------------
public abstract void writeTo(final char ch); //只能是 0 - 127 的字符

View File

@@ -172,14 +172,17 @@ public final class Rest {
}
public static JsonFactory createJsonFactory(RestConvert[] converts, RestConvertCoder[] coders) {
return createJsonFactory(true, converts, coders);
return createJsonFactory(-1, converts, coders);
}
public static JsonFactory createJsonFactory(boolean tiny, RestConvert[] converts, RestConvertCoder[] coders) {
public static JsonFactory createJsonFactory(int features, RestConvert[] converts, RestConvertCoder[] coders) {
if ((converts == null || converts.length < 1) && (coders == null || coders.length < 1)) {
return JsonFactory.root();
}
final JsonFactory childFactory = JsonFactory.create().tiny(tiny);
final JsonFactory childFactory = JsonFactory.create();
if (features > -1) {
childFactory.features(features);
}
List<Class> types = new ArrayList<>();
Set<Class> reloadTypes = new HashSet<>();
if (coders != null) {
@@ -208,8 +211,8 @@ public final class Rest {
childFactory.reloadCoder(rc.type());
}
types.add(rc.type());
if (tiny) {
childFactory.tiny(rc.tiny());
if (rc.features() > -1) {
childFactory.features(rc.features());
}
}
}
@@ -2287,7 +2290,7 @@ public final class Rest {
//设置 RestConvert
for (RestConvert rc : rcs) {
AnnotationVisitor av2 = av1.visitAnnotation(null, restConvertDesc);
av2.visit("tiny", rc.tiny());
av2.visit("features", rc.features());
av2.visit("skipIgnore", rc.skipIgnore());
av2.visit("type", Type.getType(Type.getDescriptor(rc.type())));
AnnotationVisitor av3 = av2.visitArray("onlyColumns");

View File

@@ -5,9 +5,9 @@
*/
package org.redkale.net.http;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。 <br>
@@ -26,11 +26,11 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
public @interface RestConvert {
/**
* 是否输出空字符串0数值
* 配置项
*
* @return boolean
* @return int
*/
boolean tiny() default true;
int features() default -1;
/**
* 是否忽略ConvertColumn.ignore=true的设置 优先级最高

View File

@@ -0,0 +1,18 @@
/*
*
*/
package org.redkale.source;
/**
* CacheSource订阅频道的消费监听器
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.8.0
*/
public interface CacheEventListener<T> {
public void onMessage(String topic, T message);
}

View File

@@ -161,6 +161,26 @@ public final class CacheMemorySource extends AbstractCacheSource {
return CompletableFuture.completedFuture(true);
}
//------------------------ 订阅发布 SUB/PUB ------------------------
@Override
public CompletableFuture<List<String>> pubsubChannelsAsync(@Nullable String pattern){
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public CompletableFuture<Void> subscribeAsync(CacheEventListener<byte[]> consumer, String... topics) {
Objects.requireNonNull(consumer);
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public CompletableFuture<Integer> publishAsync(String topic, byte[] message) {
Objects.requireNonNull(topic);
Objects.requireNonNull(message);
throw new UnsupportedOperationException("Not supported yet.");
}
//------------------------ 字符串 String ------------------------
@Override
public CompletableFuture<Void> msetAsync(Serializable... keyVals) {
return runFuture(() -> {

View File

@@ -10,8 +10,9 @@ import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.annotation.Component;
import org.redkale.annotation.*;
import org.redkale.convert.Convert;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.*;
/**
@@ -36,6 +37,79 @@ public interface CacheSource extends Resourcable {
return isOpenAsync().join();
}
//------------------------ 订阅发布 SUB/PUB ------------------------
default List<String> pubsubChannels(@Nullable String pattern) {
return pubsubChannelsAsync(pattern).join();
}
public CompletableFuture<List<String>> pubsubChannelsAsync(@Nullable String pattern);
//------------------------ 订阅 SUB ------------------------
default <T> void subscribe(Type messageType, CacheEventListener<T> listener, String... topics) {
subscribe(JsonConvert.root(), messageType, listener, topics);
}
default <T> void subscribe(Convert convert, Type messageType, CacheEventListener<T> listener, String... topics) {
final Convert c = convert == null ? JsonConvert.root() : convert;
subscribe((t, bs) -> listener.onMessage(t, bs == null ? null : (T) c.convertFrom(messageType, bs)), topics);
}
default void subscribe(CacheEventListener<byte[]> listener, String... topics) {
subscribeAsync(listener, topics).join();
}
default <T> CompletableFuture<Void> subscribeAsync(Type messageType, CacheEventListener<T> listener, String... topics) {
return subscribeAsync(JsonConvert.root(), messageType, listener, topics);
}
default <T> CompletableFuture<Void> subscribeAsync(Convert convert, Type messageType, CacheEventListener<T> listener, String... topics) {
final Convert c = convert == null ? JsonConvert.root() : convert;
return subscribeAsync((t, bs) -> listener.onMessage(t, bs == null ? null : (T) c.convertFrom(messageType, bs)), topics);
}
public CompletableFuture<Void> subscribeAsync(CacheEventListener<byte[]> listener, String... topics);
//------------------------ 发布 PUB ------------------------
default <T> int publish(String topic, T message) {
return publish(topic, JsonConvert.root(), message.getClass(), message);
}
default <T> int publish(String topic, Convert convert, T message) {
return publish(topic, convert, message.getClass(), message);
}
default <T> int publish(String topic, Type messageType, T message) {
return publish(topic, JsonConvert.root(), messageType, message);
}
default <T> int publish(String topic, Convert convert, Type messageType, T message) {
final Convert c = convert == null ? JsonConvert.root() : convert;
return publish(topic, c.convertToBytes(messageType, message));
}
default int publish(String topic, byte[] message) {
return publishAsync(topic, message).join();
}
default <T> CompletableFuture<Integer> publishAsync(String topic, T message) {
return publishAsync(topic, JsonConvert.root(), message.getClass(), message);
}
default <T> CompletableFuture<Integer> publishAsync(String topic, Convert convert, T message) {
return publishAsync(topic, convert, message.getClass(), message);
}
default <T> CompletableFuture<Integer> publishAsync(String topic, Type messageType, T message) {
return publishAsync(topic, JsonConvert.root(), messageType, message);
}
default <T> CompletableFuture<Integer> publishAsync(String topic, Convert convert, Type messageType, T message) {
final Convert c = convert == null ? JsonConvert.root() : convert;
return publishAsync(topic, c.convertToBytes(messageType, message));
}
public CompletableFuture<Integer> publishAsync(String topic, byte[] message);
//------------------------ 字符串 String ------------------------
default long incr(String key) {
return incrAsync(key).join();

View File

@@ -0,0 +1,643 @@
/*
*
*/
package org.redkale.util;
import java.time.DateTimeException;
import java.time.temporal.*;
import java.util.*;
import org.redkale.annotation.Nullable;
/**
* cron定时表达式解析器 <br> 代码复制于org.springframework.scheduling.support.CronExpression
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.8.0
*/
public class CronExpression {
static final int MAX_ATTEMPTS = 366;
private static final String[] MACROS = new String[]{
"@yearly", "0 0 0 1 1 *",
"@annually", "0 0 0 1 1 *",
"@monthly", "0 0 0 1 * *",
"@weekly", "0 0 0 * * 0",
"@daily", "0 0 0 * * *",
"@midnight", "0 0 0 * * *",
"@hourly", "0 0 * * * *"
};
private final CronField[] fields;
private final String expression;
private CronExpression(CronField seconds, CronField minutes, CronField hours,
CronField daysOfMonth, CronField months, CronField daysOfWeek, String expression) {
this.fields = new CronField[]{daysOfWeek, months, daysOfMonth, hours, minutes, seconds, CronField.zeroNanos()};
this.expression = expression;
}
public static CronExpression parse(String expression) {
if (Utility.isBlank(expression)) {
throw new RedkaleException("Expression string must not be empty");
}
expression = resolveMacros(expression);
String[] fields = expression.split("\\s+");
if (fields.length != 6) {
throw new RedkaleException(String.format("Cron expression must consist of 6 fields (found %d in \"%s\")", fields.length, expression));
}
try {
CronField seconds = CronField.parseSeconds(fields[0]);
CronField minutes = CronField.parseMinutes(fields[1]);
CronField hours = CronField.parseHours(fields[2]);
CronField daysOfMonth = CronField.parseDaysOfMonth(fields[3]);
CronField months = CronField.parseMonth(fields[4]);
CronField daysOfWeek = CronField.parseDaysOfWeek(fields[5]);
return new CronExpression(seconds, minutes, hours, daysOfMonth, months, daysOfWeek, expression);
} catch (IllegalArgumentException ex) {
String msg = ex.getMessage() + " in cron expression \"" + expression + "\"";
throw new RedkaleException(msg, ex);
}
}
private static String resolveMacros(String expression) {
expression = expression.trim();
for (int i = 0; i < MACROS.length; i = i + 2) {
if (MACROS[i].equalsIgnoreCase(expression)) {
return MACROS[i + 1];
}
}
return expression;
}
@Nullable
public <T extends Temporal & Comparable<? super T>> T next(T temporal) {
return nextOrSame(ChronoUnit.NANOS.addTo(temporal, 1));
}
@Nullable
private <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
for (int i = 0; i < MAX_ATTEMPTS; i++) {
T result = nextOrSameInternal(temporal);
if (result == null || result.equals(temporal)) {
return result;
}
temporal = result;
}
return null;
}
@Nullable
private <T extends Temporal & Comparable<? super T>> T nextOrSameInternal(T temporal) {
for (CronField field : this.fields) {
temporal = field.nextOrSame(temporal);
if (temporal == null) {
return null;
}
}
return temporal;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CronExpression)) {
return false;
}
CronExpression that = (CronExpression) other;
return Arrays.equals(this.fields, that.fields);
}
@Override
public int hashCode() {
return Arrays.hashCode(this.fields);
}
@Override
public String toString() {
return this.expression;
}
abstract static class CronField {
private static final String[] MONTHS = new String[]{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP",
"OCT", "NOV", "DEC"};
private static final String[] DAYS = new String[]{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
private final Type type;
protected CronField(Type type) {
this.type = type;
}
public static CronField zeroNanos() {
return BitsCronField.zeroNanos();
}
public static CronField parseSeconds(String value) {
return BitsCronField.parseSeconds(value);
}
public static CronField parseMinutes(String value) {
return BitsCronField.parseMinutes(value);
}
public static CronField parseHours(String value) {
return BitsCronField.parseHours(value);
}
public static CronField parseDaysOfMonth(String value) {
return BitsCronField.parseDaysOfMonth(value);
}
public static CronField parseMonth(String value) {
value = replaceOrdinals(value, MONTHS);
return BitsCronField.parseMonth(value);
}
public static CronField parseDaysOfWeek(String value) {
value = replaceOrdinals(value, DAYS);
return BitsCronField.parseDaysOfWeek(value);
}
private static String replaceOrdinals(String value, String[] list) {
value = value.toUpperCase();
for (int i = 0; i < list.length; i++) {
String replacement = Integer.toString(i + 1);
value = replace(value, list[i], replacement);
}
return value;
}
private static String replace(String inString, String oldPattern, @Nullable String newPattern) {
if (Utility.isEmpty(inString) || Utility.isEmpty(oldPattern) || newPattern == null) {
return inString;
}
int index = inString.indexOf(oldPattern);
if (index == -1) {
// no occurrence -> can return input as-is
return inString;
}
int capacity = inString.length();
if (newPattern.length() > oldPattern.length()) {
capacity += 16;
}
StringBuilder sb = new StringBuilder(capacity);
int pos = 0; // our position in the old string
int patLen = oldPattern.length();
while (index >= 0) {
sb.append(inString, pos, index);
sb.append(newPattern);
pos = index + patLen;
index = inString.indexOf(oldPattern, pos);
}
// append any characters to the right of a match
sb.append(inString, pos, inString.length());
return sb.toString();
}
private static String[] delimitedListToStringArray(@Nullable String str, @Nullable String delimiter) {
return delimitedListToStringArray(str, delimiter, null);
}
private static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {
if (str == null) {
return new String[0];
}
if (delimiter == null) {
return new String[]{str};
}
List<String> result = new ArrayList<>();
if (delimiter.isEmpty()) {
for (int i = 0; i < str.length(); i++) {
result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
}
} else {
int pos = 0;
int delPos;
while ((delPos = str.indexOf(delimiter, pos)) != -1) {
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
pos = delPos + delimiter.length();
}
if (str.length() > 0 && pos <= str.length()) {
// Add rest of String, but not in case of empty input.
result.add(deleteAny(str.substring(pos), charsToDelete));
}
}
return result.toArray(new String[result.size()]);
}
private static String deleteAny(String inString, @Nullable String charsToDelete) {
if (Utility.isEmpty(inString) || Utility.isEmpty(charsToDelete)) {
return inString;
}
int lastCharIndex = 0;
char[] result = new char[inString.length()];
for (int i = 0; i < inString.length(); i++) {
char c = inString.charAt(i);
if (charsToDelete.indexOf(c) == -1) {
result[lastCharIndex++] = c;
}
}
if (lastCharIndex == inString.length()) {
return inString;
}
return new String(result, 0, lastCharIndex);
}
@Nullable
public abstract <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal);
protected Type type() {
return this.type;
}
@SuppressWarnings("unchecked")
protected static <T extends Temporal & Comparable<? super T>> T cast(Temporal temporal) {
return (T) temporal;
}
protected enum Type {
NANO(ChronoField.NANO_OF_SECOND, ChronoUnit.SECONDS),
SECOND(ChronoField.SECOND_OF_MINUTE, ChronoUnit.MINUTES, ChronoField.NANO_OF_SECOND),
MINUTE(ChronoField.MINUTE_OF_HOUR, ChronoUnit.HOURS, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
HOUR(ChronoField.HOUR_OF_DAY, ChronoUnit.DAYS, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
DAY_OF_MONTH(ChronoField.DAY_OF_MONTH, ChronoUnit.MONTHS, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
MONTH(ChronoField.MONTH_OF_YEAR, ChronoUnit.YEARS, ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
DAY_OF_WEEK(ChronoField.DAY_OF_WEEK, ChronoUnit.WEEKS, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND);
private final ChronoField field;
private final ChronoUnit higherOrder;
private final ChronoField[] lowerOrders;
Type(ChronoField field, ChronoUnit higherOrder, ChronoField... lowerOrders) {
this.field = field;
this.higherOrder = higherOrder;
this.lowerOrders = lowerOrders;
}
public int get(Temporal date) {
return date.get(this.field);
}
public ValueRange range() {
return this.field.range();
}
public int checkValidValue(int value) {
if (this == DAY_OF_WEEK && value == 0) {
return value;
} else {
try {
return this.field.checkValidIntValue(value);
} catch (DateTimeException ex) {
throw new IllegalArgumentException(ex.getMessage(), ex);
}
}
}
public <T extends Temporal & Comparable<? super T>> T elapseUntil(T temporal, int goal) {
int current = get(temporal);
ValueRange range = temporal.range(this.field);
if (current < goal) {
if (range.isValidIntValue(goal)) {
return cast(temporal.with(this.field, goal));
} else {
// goal is invalid, eg. 29th Feb, so roll forward
long amount = range.getMaximum() - current + 1;
return this.field.getBaseUnit().addTo(temporal, amount);
}
} else {
long amount = goal + range.getMaximum() - current + 1 - range.getMinimum();
return this.field.getBaseUnit().addTo(temporal, amount);
}
}
public <T extends Temporal & Comparable<? super T>> T rollForward(T temporal) {
T result = this.higherOrder.addTo(temporal, 1);
ValueRange range = result.range(this.field);
return this.field.adjustInto(result, range.getMinimum());
}
public <T extends Temporal> T reset(T temporal) {
for (ChronoField lowerOrder : this.lowerOrders) {
if (temporal.isSupported(lowerOrder)) {
temporal = lowerOrder.adjustInto(temporal, temporal.range(lowerOrder).getMinimum());
}
}
return temporal;
}
@Override
public String toString() {
return this.field.toString();
}
}
}
static class BitsCronField extends CronField {
private static final long MASK = 0xFFFFFFFFFFFFFFFFL;
@Nullable
private static BitsCronField zeroNanos = null;
// we store at most 60 bits, for seconds and minutes, so a 64-bit long suffices
private long bits;
private BitsCronField(Type type) {
super(type);
}
public static BitsCronField zeroNanos() {
if (zeroNanos == null) {
BitsCronField field = new BitsCronField(Type.NANO);
field.setBit(0);
zeroNanos = field;
}
return zeroNanos;
}
public static BitsCronField parseSeconds(String value) {
return parseField(value, Type.SECOND);
}
public static BitsCronField parseMinutes(String value) {
return BitsCronField.parseField(value, Type.MINUTE);
}
public static BitsCronField parseHours(String value) {
return BitsCronField.parseField(value, Type.HOUR);
}
public static BitsCronField parseDaysOfMonth(String value) {
return parseDate(value, Type.DAY_OF_MONTH);
}
public static BitsCronField parseMonth(String value) {
return BitsCronField.parseField(value, Type.MONTH);
}
public static BitsCronField parseDaysOfWeek(String value) {
BitsCronField result = parseDate(value, Type.DAY_OF_WEEK);
if (result.getBit(0)) {
// cron supports 0 for Sunday; we use 7 like java.time
result.setBit(7);
result.clearBit(0);
}
return result;
}
private static BitsCronField parseDate(String value, BitsCronField.Type type) {
if (value.equals("?")) {
value = "*";
}
return BitsCronField.parseField(value, type);
}
private static BitsCronField parseField(String value, Type type) {
if (Utility.isBlank(value)) {
throw new RedkaleException("Value must not be empty");
}
if (type == null) {
throw new RedkaleException("Type must not be null");
}
try {
BitsCronField result = new BitsCronField(type);
String[] fields = CronField.delimitedListToStringArray(value, ",");
for (String field : fields) {
int slashPos = field.indexOf('/');
if (slashPos == -1) {
ValueRange range = parseRange(field, type);
result.setBits(range);
} else {
String rangeStr = field.substring(0, slashPos);
String deltaStr = field.substring(slashPos + 1);
ValueRange range = parseRange(rangeStr, type);
if (rangeStr.indexOf('-') == -1) {
range = ValueRange.of(range.getMinimum(), type.range().getMaximum());
}
int delta = Integer.parseInt(deltaStr);
if (delta <= 0) {
throw new IllegalArgumentException("Incrementer delta must be 1 or higher");
}
result.setBits(range, delta);
}
}
return result;
} catch (DateTimeException | IllegalArgumentException ex) {
String msg = ex.getMessage() + " '" + value + "'";
throw new IllegalArgumentException(msg, ex);
}
}
private static ValueRange parseRange(String value, Type type) {
if (value.equals("*")) {
return type.range();
} else {
int hyphenPos = value.indexOf('-');
if (hyphenPos == -1) {
int result = type.checkValidValue(Integer.parseInt(value));
return ValueRange.of(result, result);
} else {
int min = Integer.parseInt(value, 0, hyphenPos, 10);
int max = Integer.parseInt(value, hyphenPos + 1, value.length(), 10);
min = type.checkValidValue(min);
max = type.checkValidValue(max);
if (type == Type.DAY_OF_WEEK && min == 7) {
// If used as a minimum in a range, Sunday means 0 (not 7)
min = 0;
}
return ValueRange.of(min, max);
}
}
}
@Nullable
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
int current = type().get(temporal);
int next = nextSetBit(current);
if (next == -1) {
temporal = type().rollForward(temporal);
next = nextSetBit(0);
}
if (next == current) {
return temporal;
} else {
int count = 0;
current = type().get(temporal);
while (current != next && count++ < CronExpression.MAX_ATTEMPTS) {
temporal = type().elapseUntil(temporal, next);
current = type().get(temporal);
next = nextSetBit(current);
if (next == -1) {
temporal = type().rollForward(temporal);
next = nextSetBit(0);
}
}
if (count >= CronExpression.MAX_ATTEMPTS) {
return null;
}
return type().reset(temporal);
}
}
boolean getBit(int index) {
return (this.bits & (1L << index)) != 0;
}
private int nextSetBit(int fromIndex) {
long result = this.bits & (MASK << fromIndex);
if (result != 0) {
return Long.numberOfTrailingZeros(result);
} else {
return -1;
}
}
private void setBits(ValueRange range) {
if (range.getMinimum() == range.getMaximum()) {
setBit((int) range.getMinimum());
} else {
long minMask = MASK << range.getMinimum();
long maxMask = MASK >>> -(range.getMaximum() + 1);
this.bits |= (minMask & maxMask);
}
}
private void setBits(ValueRange range, int delta) {
if (delta == 1) {
setBits(range);
} else {
for (int i = (int) range.getMinimum(); i <= range.getMaximum(); i += delta) {
setBit(i);
}
}
}
private void setBit(int index) {
this.bits |= (1L << index);
}
private void clearBit(int index) {
this.bits &= ~(1L << index);
}
@Override
public int hashCode() {
return Long.hashCode(this.bits);
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BitsCronField)) {
return false;
}
BitsCronField other = (BitsCronField) o;
return type() == other.type() && this.bits == other.bits;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(type().toString());
builder.append(" {");
int i = nextSetBit(0);
if (i != -1) {
builder.append(i);
i = nextSetBit(i + 1);
while (i != -1) {
builder.append(", ");
builder.append(i);
i = nextSetBit(i + 1);
}
}
builder.append('}');
return builder.toString();
}
}
static class CompositeCronField extends CronField {
private final CronField[] fields;
private final String value;
private CompositeCronField(Type type, CronField[] fields, String value) {
super(type);
this.fields = fields;
this.value = value;
}
public static CronField compose(CronField[] fields, Type type, String value) {
if (fields == null || fields.length < 1) {
throw new RedkaleException("Fields must not be empty");
}
if (Utility.isBlank(value)) {
throw new RedkaleException("Value must not be empty");
}
if (fields.length == 1) {
return fields[0];
} else {
return new CompositeCronField(type, fields, value);
}
}
@Nullable
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
T result = null;
for (CronField field : this.fields) {
T candidate = field.nextOrSame(temporal);
if (result == null
|| candidate != null && candidate.compareTo(result) < 0) {
result = candidate;
}
}
return result;
}
@Override
public int hashCode() {
return this.value.hashCode();
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CompositeCronField)) {
return false;
}
CompositeCronField other = (CompositeCronField) o;
return type() == other.type() && this.value.equals(other.value);
}
@Override
public String toString() {
return type() + " '" + this.value + "'";
}
}
}

View File

@@ -4,6 +4,7 @@ package org.redkale.test.convert;
import java.util.*;
import org.junit.jupiter.api.*;
import org.redkale.convert.ConvertFactory;
import org.redkale.convert.json.*;
/**
@@ -23,7 +24,7 @@ public class Json5Test {
@Test
public void run1() throws Exception {
JsonFactory factory = JsonFactory.root().tiny(true).nullable(true);
JsonFactory factory = JsonFactory.root().features(ConvertFactory.FEATURE_TINY | ConvertFactory.FEATURE_NULLABLE);
final JsonConvert convert = factory.getConvert();
Json5Bean bean = new Json5Bean();
bean.id = 60;

View File

@@ -9,6 +9,7 @@ import java.io.*;
import java.nio.ByteBuffer;
import java.util.Map;
import org.junit.jupiter.api.*;
import org.redkale.convert.ConvertFactory;
import org.redkale.convert.json.*;
/**
@@ -32,7 +33,7 @@ public class JsonMainTest {
@Test
public void run1() throws Throwable {
JsonFactory factory = JsonFactory.root().tiny(true);
JsonFactory factory = JsonFactory.root().features(ConvertFactory.FEATURE_TINY);
final JsonConvert convert = JsonConvert.root();
String json = "{\"access_token\":\"null\",\"priv\":null, vvv:nulla,\"priv2\":\"nulla\",\"expires_in\":7200, \"aa\":\"\"}";
Map<String, String> map = convert.convertFrom(JsonConvert.TYPE_MAP_STRING_STRING, json);
@@ -53,7 +54,7 @@ public class JsonMainTest {
SimpleChildEntity entry = SimpleChildEntity.create();
String json = convert.convertTo(SimpleEntity.class, entry);
System.out.println("长度: " + json.length());
JsonByteBufferWriter writer = new JsonByteBufferWriter(false, false, () -> ByteBuffer.allocate(1)) {
JsonByteBufferWriter writer = new JsonByteBufferWriter(0, () -> ByteBuffer.allocate(1)) {
};
convert.convertTo(writer, SimpleEntity.class, entry);
ByteBuffer[] buffers = writer.toBuffers();

View File

@@ -3,6 +3,7 @@
package org.redkale.test.convert;
import org.junit.jupiter.api.*;
import org.redkale.convert.ConvertFactory;
import org.redkale.convert.json.*;
/**
@@ -24,17 +25,21 @@ public class TinyTest {
TinyRecord record = new TinyRecord();
record.id = 5;
{
JsonFactory factory = JsonFactory.create().tiny(true);
JsonFactory factory = JsonFactory.create().features(ConvertFactory.FEATURE_TINY);
JsonConvert convert = factory.getConvert();
String json = "{\"id\":5}";
if (!main) Assertions.assertEquals(json, convert.convertTo(record));
if (!main) {
Assertions.assertEquals(json, convert.convertTo(record));
}
System.out.println(convert.convertTo(record));
}
{
JsonFactory factory = JsonFactory.create().tiny(false);
JsonFactory factory = JsonFactory.create().features(0);
JsonConvert convert = factory.getConvert();
String json = "{\"id\":5,\"name\":\"\"}";
if (!main) Assertions.assertEquals(json, convert.convertTo(record));
if (!main) {
Assertions.assertEquals(json, convert.convertTo(record));
}
System.out.println(convert.convertTo(record));
}
}