diff --git a/src/main/java/com/realtimetech/opack/Opacker.java b/src/main/java/com/realtimetech/opack/Opacker.java index 73a8ee9..0086232 100644 --- a/src/main/java/com/realtimetech/opack/Opacker.java +++ b/src/main/java/com/realtimetech/opack/Opacker.java @@ -596,11 +596,21 @@ private void executeDeserializeStack(int endOfStack) throws DeserializeException } else if (opackValue instanceof OpackObject) { OpackObject opackObject = (OpackObject) opackValue; for (BakedType.Property property : bakedType.getFields()) { + String propertyName = property.getName(); + try { - Object element = opackObject.get(property.getName()); + Object element; Class fieldType = property.getType(); Class actualFieldType = property.getField().getType(); + if (opackObject.containsKey(propertyName)) { + element = opackObject.get(propertyName); + } else if (property.getDefaultValueProvider() != null) { + element = property.getDefaultValueProvider().provide(this, object, property); + } else { + throw new DeserializeException("Missing " + property.getName() + " property value of for " + bakedType.getType().getSimpleName() + " in given opack value."); + } + Object propertyValue = null; if (element != null) { diff --git a/src/main/java/com/realtimetech/opack/annotation/DefaultValue.java b/src/main/java/com/realtimetech/opack/annotation/DefaultValue.java new file mode 100644 index 0000000..fa41b5c --- /dev/null +++ b/src/main/java/com/realtimetech/opack/annotation/DefaultValue.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 REALTIMETECH All Rights Reserved + * + * Licensed either under the Apache License, Version 2.0, or (at your option) + * under the terms of the GNU General Public License as published by + * the Free Software Foundation (subject to the "Classpath" exception), + * either version 2, or any later version (collectively, 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 + * http://www.gnu.org/licenses/ + * http://www.gnu.org/software/classpath/license.html + * + * or as provided in the LICENSE file that accompanied this code. + * 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.realtimetech.opack.annotation; + +import com.realtimetech.opack.provider.DefaultValueProvider; +import org.jetbrains.annotations.NotNull; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Fields annotated with @DefaultValue will not be serialized and deserialized + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ + ElementType.FIELD, +}) +public @interface DefaultValue { + /** + * Return the default value provider for field + * + * @return the default value provider class + */ + @NotNull Class provider(); +} \ No newline at end of file diff --git a/src/main/java/com/realtimetech/opack/bake/BakedType.java b/src/main/java/com/realtimetech/opack/bake/BakedType.java index d550d77..1f1b600 100644 --- a/src/main/java/com/realtimetech/opack/bake/BakedType.java +++ b/src/main/java/com/realtimetech/opack/bake/BakedType.java @@ -22,6 +22,7 @@ package com.realtimetech.opack.bake; +import com.realtimetech.opack.provider.DefaultValueProvider; import com.realtimetech.opack.transformer.Transformer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,14 +37,16 @@ public final static class Property { private final boolean withType; private final @Nullable Transformer transformer; + private final @Nullable DefaultValueProvider defaultValueProvider; - public Property(@NotNull Field field, @Nullable String name, @Nullable Class type, boolean withType, @Nullable Transformer transformer) { + public Property(@NotNull Field field, @Nullable String name, @Nullable Class type, boolean withType, @Nullable Transformer transformer, @Nullable DefaultValueProvider defaultValueProvider) { this.field = field; this.name = name == null ? this.field.getName() : name; this.type = type == null ? this.field.getType() : type; this.withType = withType; this.transformer = transformer; + this.defaultValueProvider = defaultValueProvider; } public @NotNull Field getField() { @@ -66,6 +69,10 @@ public boolean isWithType() { return transformer; } + public @Nullable DefaultValueProvider getDefaultValueProvider() { + return defaultValueProvider; + } + /** * Sets the field of the object to a specified value * diff --git a/src/main/java/com/realtimetech/opack/bake/TypeBaker.java b/src/main/java/com/realtimetech/opack/bake/TypeBaker.java index 85c9bff..07eea73 100644 --- a/src/main/java/com/realtimetech/opack/bake/TypeBaker.java +++ b/src/main/java/com/realtimetech/opack/bake/TypeBaker.java @@ -25,6 +25,8 @@ import com.realtimetech.opack.Opacker; import com.realtimetech.opack.annotation.*; import com.realtimetech.opack.exception.BakeException; +import com.realtimetech.opack.provider.DefaultValueProvider; +import com.realtimetech.opack.provider.DefaultValueProviderFactory; import com.realtimetech.opack.transformer.Transformer; import com.realtimetech.opack.transformer.TransformerFactory; import com.realtimetech.opack.util.ReflectionUtil; @@ -65,6 +67,7 @@ public boolean isInheritable() { private final @NotNull Opacker opacker; private final @NotNull TransformerFactory transformerFactory; + private final @NotNull DefaultValueProviderFactory defaultValueProviderFactory; private final @NotNull HashMap<@NotNull Class, @NotNull BakedType> backedTypeMap; private final @NotNull HashMap<@NotNull Class, @NotNull List<@NotNull PredefinedTransformer>> predefinedTransformerMap; @@ -78,6 +81,7 @@ public TypeBaker(@NotNull Opacker opacker) { this.opacker = opacker; this.transformerFactory = new TransformerFactory(opacker); + this.defaultValueProviderFactory = new DefaultValueProviderFactory(opacker); this.backedTypeMap = new HashMap<>(); this.predefinedTransformerMap = new HashMap<>(); @@ -318,11 +322,25 @@ private void addTransformer(@NotNull List<@NotNull Transformer> transformers, @N } Transformer[] fieldTransformers = this.getTransformer(field); + DefaultValueProvider defaultValueProvider = null; + Class type = this.getAnnotatedType(field); String name = this.getAnnotatedName(field); + boolean withType = field.isAnnotationPresent(WithType.class); - properties.add(new BakedType.Property(field, name, type, withType, fieldTransformers.length > 0 ? fieldTransformers[0] : null)); + if (field.isAnnotationPresent(DefaultValue.class)) { + DefaultValue defaultValue = field.getAnnotation(DefaultValue.class); + Class defaultValueProviderType = (Class) defaultValue.provider(); + + try { + defaultValueProvider = this.defaultValueProviderFactory.get(defaultValueProviderType); + } catch (InstantiationException e) { + throw new BakeException(e); + } + } + + properties.add(new BakedType.Property(field, name, type, withType, fieldTransformers.length > 0 ? fieldTransformers[0] : null, defaultValueProvider)); } transformers = this.getTransformer(bakeType); diff --git a/src/main/java/com/realtimetech/opack/provider/DefaultValueProvider.java b/src/main/java/com/realtimetech/opack/provider/DefaultValueProvider.java new file mode 100644 index 0000000..9f93d12 --- /dev/null +++ b/src/main/java/com/realtimetech/opack/provider/DefaultValueProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 REALTIMETECH All Rights Reserved + * + * Licensed either under the Apache License, Version 2.0, or (at your option) + * under the terms of the GNU General Public License as published by + * the Free Software Foundation (subject to the "Classpath" exception), + * either version 2, or any later version (collectively, 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 + * http://www.gnu.org/licenses/ + * http://www.gnu.org/software/classpath/license.html + * + * or as provided in the LICENSE file that accompanied this code. + * 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.realtimetech.opack.provider; + +import com.realtimetech.opack.Opacker; +import com.realtimetech.opack.bake.BakedType; +import com.realtimetech.opack.exception.DeserializeException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface DefaultValueProvider { + /** + * Deserialize opack value + * + * @param opacker the opacker + * @param object the property owner + * @param property the property to provide default value + * @return the default value + */ + @Nullable Object provide(@NotNull Opacker opacker, @NotNull Object object, @NotNull BakedType.Property property); +} \ No newline at end of file diff --git a/src/main/java/com/realtimetech/opack/provider/DefaultValueProviderFactory.java b/src/main/java/com/realtimetech/opack/provider/DefaultValueProviderFactory.java new file mode 100644 index 0000000..76201f6 --- /dev/null +++ b/src/main/java/com/realtimetech/opack/provider/DefaultValueProviderFactory.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 REALTIMETECH All Rights Reserved + * + * Licensed either under the Apache License, Version 2.0, or (at your option) + * under the terms of the GNU General Public License as published by + * the Free Software Foundation (subject to the "Classpath" exception), + * either version 2, or any later version (collectively, 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 + * http://www.gnu.org/licenses/ + * http://www.gnu.org/software/classpath/license.html + * + * or as provided in the LICENSE file that accompanied this code. + * 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.realtimetech.opack.provider; + +import com.realtimetech.opack.Opacker; +import com.realtimetech.opack.util.ReflectionUtil; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; + +public class DefaultValueProviderFactory { + private final @NotNull Opacker opacker; + + private final @NotNull HashMap<@NotNull Class, @NotNull DefaultValueProvider> defaultValueProviderMap; + + /** + * Constructs a TransformerFactory with the opacker + * + * @param opacker the opacker + */ + public DefaultValueProviderFactory(@NotNull Opacker opacker) { + this.opacker = opacker; + + this.defaultValueProviderMap = new HashMap<>(); + } + + /** + * Returns default value provider instance + * + * @param defaultValueProviderType the default value provider class + * @return the default value provider instance + * @throws InstantiationException if the default value provider class object cannot be instantiated; if the default value provider is not in the default value provider class + */ + public

@NotNull P get(@NotNull Class

defaultValueProviderType) throws InstantiationException { + if (!this.defaultValueProviderMap.containsKey(defaultValueProviderType)) { + synchronized (this.defaultValueProviderMap) { + if (!this.defaultValueProviderMap.containsKey(defaultValueProviderType)) { + P instance = null; + + try { + // Create instance using DefaultValueProvider(Opacker) constructor + try { + instance = ReflectionUtil.createInstance(defaultValueProviderType, this.opacker); + } catch (IllegalArgumentException exception) { + // Ok, let's find no parameter constructor + } + + // Create instance using DefaultValueProvider() constructor + try { + if (instance == null) { + instance = ReflectionUtil.createInstance(defaultValueProviderType); + } + } catch (IllegalArgumentException exception) { + // Ok, let's throw exception + } + } catch (InvocationTargetException | IllegalAccessException exception) { + InstantiationException instantiationException = new InstantiationException(defaultValueProviderType.getSimpleName() + " default value provider can't instantiation."); + instantiationException.initCause(exception); + + throw instantiationException; + } + + if (instance == null) { + throw new InstantiationException(defaultValueProviderType.getSimpleName() + " default value provider must be implemented constructor(Opacker) or constructor()."); + } + + this.defaultValueProviderMap.put(defaultValueProviderType, instance); + } + } + } + + return defaultValueProviderType.cast(this.defaultValueProviderMap.get(defaultValueProviderType)); + } +} \ No newline at end of file diff --git a/src/main/java/com/realtimetech/opack/provider/impl/BooleanFalseProvider.java b/src/main/java/com/realtimetech/opack/provider/impl/BooleanFalseProvider.java new file mode 100644 index 0000000..7de8495 --- /dev/null +++ b/src/main/java/com/realtimetech/opack/provider/impl/BooleanFalseProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 REALTIMETECH All Rights Reserved + * + * Licensed either under the Apache License, Version 2.0, or (at your option) + * under the terms of the GNU General Public License as published by + * the Free Software Foundation (subject to the "Classpath" exception), + * either version 2, or any later version (collectively, 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 + * http://www.gnu.org/licenses/ + * http://www.gnu.org/software/classpath/license.html + * + * or as provided in the LICENSE file that accompanied this code. + * 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.realtimetech.opack.provider.impl; + +import com.realtimetech.opack.Opacker; +import com.realtimetech.opack.bake.BakedType; +import com.realtimetech.opack.provider.DefaultValueProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class BooleanFalseProvider implements DefaultValueProvider { + /** + * Deserialize opack value + * + * @param opacker the opacker + * @param object the property owner + * @param property the property to provide default value + * @return the default value + */ + @Override + public @Nullable Object provide(@NotNull Opacker opacker, @NotNull Object object, BakedType.@NotNull Property property) { + return false; + } +} diff --git a/src/main/java/com/realtimetech/opack/provider/impl/BooleanTrueProvider.java b/src/main/java/com/realtimetech/opack/provider/impl/BooleanTrueProvider.java new file mode 100644 index 0000000..5f6dd36 --- /dev/null +++ b/src/main/java/com/realtimetech/opack/provider/impl/BooleanTrueProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 REALTIMETECH All Rights Reserved + * + * Licensed either under the Apache License, Version 2.0, or (at your option) + * under the terms of the GNU General Public License as published by + * the Free Software Foundation (subject to the "Classpath" exception), + * either version 2, or any later version (collectively, 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 + * http://www.gnu.org/licenses/ + * http://www.gnu.org/software/classpath/license.html + * + * or as provided in the LICENSE file that accompanied this code. + * 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.realtimetech.opack.provider.impl; + +import com.realtimetech.opack.Opacker; +import com.realtimetech.opack.bake.BakedType; +import com.realtimetech.opack.provider.DefaultValueProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class BooleanTrueProvider implements DefaultValueProvider { + /** + * Deserialize opack value + * + * @param opacker the opacker + * @param object the property owner + * @param property the property to provide default value + * @return the default value + */ + @Override + public @Nullable Object provide(@NotNull Opacker opacker, @NotNull Object object, BakedType.@NotNull Property property) { + return true; + } +} diff --git a/src/main/java/com/realtimetech/opack/provider/impl/NumberOneProvider.java b/src/main/java/com/realtimetech/opack/provider/impl/NumberOneProvider.java new file mode 100644 index 0000000..19406ae --- /dev/null +++ b/src/main/java/com/realtimetech/opack/provider/impl/NumberOneProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 REALTIMETECH All Rights Reserved + * + * Licensed either under the Apache License, Version 2.0, or (at your option) + * under the terms of the GNU General Public License as published by + * the Free Software Foundation (subject to the "Classpath" exception), + * either version 2, or any later version (collectively, 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 + * http://www.gnu.org/licenses/ + * http://www.gnu.org/software/classpath/license.html + * + * or as provided in the LICENSE file that accompanied this code. + * 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.realtimetech.opack.provider.impl; + +import com.realtimetech.opack.Opacker; +import com.realtimetech.opack.bake.BakedType; +import com.realtimetech.opack.provider.DefaultValueProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class NumberOneProvider implements DefaultValueProvider { + /** + * Deserialize opack value + * + * @param opacker the opacker + * @param object the property owner + * @param property the property to provide default value + * @return the default value + */ + @Override + public @Nullable Object provide(@NotNull Opacker opacker, @NotNull Object object, BakedType.@NotNull Property property) { + return 1; + } +} diff --git a/src/main/java/com/realtimetech/opack/provider/impl/NumberZeroProvider.java b/src/main/java/com/realtimetech/opack/provider/impl/NumberZeroProvider.java new file mode 100644 index 0000000..f9aee3b --- /dev/null +++ b/src/main/java/com/realtimetech/opack/provider/impl/NumberZeroProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 REALTIMETECH All Rights Reserved + * + * Licensed either under the Apache License, Version 2.0, or (at your option) + * under the terms of the GNU General Public License as published by + * the Free Software Foundation (subject to the "Classpath" exception), + * either version 2, or any later version (collectively, 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 + * http://www.gnu.org/licenses/ + * http://www.gnu.org/software/classpath/license.html + * + * or as provided in the LICENSE file that accompanied this code. + * 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.realtimetech.opack.provider.impl; + +import com.realtimetech.opack.Opacker; +import com.realtimetech.opack.bake.BakedType; +import com.realtimetech.opack.provider.DefaultValueProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class NumberZeroProvider implements DefaultValueProvider { + /** + * Deserialize opack value + * + * @param opacker the opacker + * @param object the property owner + * @param property the property to provide default value + * @return the default value + */ + @Override + public @Nullable Object provide(@NotNull Opacker opacker, @NotNull Object object, BakedType.@NotNull Property property) { + return 0; + } +} diff --git a/src/main/java/com/realtimetech/opack/provider/impl/StringEmptyProvider.java b/src/main/java/com/realtimetech/opack/provider/impl/StringEmptyProvider.java new file mode 100644 index 0000000..e9d30d2 --- /dev/null +++ b/src/main/java/com/realtimetech/opack/provider/impl/StringEmptyProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 REALTIMETECH All Rights Reserved + * + * Licensed either under the Apache License, Version 2.0, or (at your option) + * under the terms of the GNU General Public License as published by + * the Free Software Foundation (subject to the "Classpath" exception), + * either version 2, or any later version (collectively, 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 + * http://www.gnu.org/licenses/ + * http://www.gnu.org/software/classpath/license.html + * + * or as provided in the LICENSE file that accompanied this code. + * 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.realtimetech.opack.provider.impl; + +import com.realtimetech.opack.Opacker; +import com.realtimetech.opack.bake.BakedType; +import com.realtimetech.opack.provider.DefaultValueProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class StringEmptyProvider implements DefaultValueProvider { + /** + * Deserialize opack value + * + * @param opacker the opacker + * @param object the property owner + * @param property the property to provide default value + * @return the default value + */ + @Override + public @Nullable Object provide(@NotNull Opacker opacker, @NotNull Object object, BakedType.@NotNull Property property) { + return ""; + } +}