All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.lecousin.framework.serialization.AbstractSerializationSpecWriter Maven / Gradle / Ivy

The newest version!
package net.lecousin.framework.serialization;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import net.lecousin.framework.concurrent.async.Async;
import net.lecousin.framework.concurrent.async.IAsync;
import net.lecousin.framework.concurrent.threads.Task;
import net.lecousin.framework.concurrent.threads.Task.Priority;
import net.lecousin.framework.exception.NoException;
import net.lecousin.framework.io.IO;
import net.lecousin.framework.serialization.SerializationClass.Attribute;
import net.lecousin.framework.serialization.SerializationContext.AttributeContext;
import net.lecousin.framework.serialization.SerializationContext.CollectionContext;
import net.lecousin.framework.serialization.SerializationContext.ObjectContext;
import net.lecousin.framework.serialization.SerializationUtil.MapEntry;
import net.lecousin.framework.serialization.annotations.AttributeAnnotationToRuleOnAttribute;
import net.lecousin.framework.serialization.annotations.AttributeAnnotationToRuleOnType;
import net.lecousin.framework.serialization.annotations.TypeAnnotationToRule;
import net.lecousin.framework.serialization.rules.SerializationRule;

/** Generate serialization specification. */
public abstract class AbstractSerializationSpecWriter implements SerializationSpecWriter {

	protected Priority priority;
	protected final String taskDescription = "Write specification using " + getClass().getName();
	
	protected abstract IAsync initializeSpecWriter(IO.Writable output);
	
	protected abstract IAsync finalizeSpecWriter();
	
	@Override
	public IAsync writeSpecification(Class type, IO.Writable output, List rules) {
		priority = output.getPriority();
		IAsync init = initializeSpecWriter(output);
		Async result = new Async<>();
		init.thenStart(taskDescription, priority, (Task t) -> {
			IAsync sp;
			if (type != null)
				sp = specifyValue(null, new TypeDefinition(type), rules);
			else
				sp = specifyAnyValue(null);
			sp.onDone(() -> finalizeSpecWriter().onDone(result), result);
			return null;
		}, result);
		return result;
	}
	
	protected List addRulesForType(SerializationClass type, List currentList) {
		currentList = TypeAnnotationToRule.addRules(type.getType().getBase(), currentList);
		currentList = AttributeAnnotationToRuleOnType.addRules(type, true, currentList);
		return currentList;
	}
	
	protected List addRulesForAttribute(Attribute a, List currentList) {
		return AttributeAnnotationToRuleOnAttribute.addRules(a, true, currentList);
	}
	
	protected abstract IAsync specifyAnyValue(SerializationContext context);
	
	/** Can be used when already initialized. */
	@SuppressWarnings("squid:S3776") // complexity
	public IAsync specifyValue(
		SerializationContext context, TypeDefinition typeDef, List rules
	) {
		for (SerializationRule rule : rules)
			typeDef = rule.getDeserializationType(typeDef, context);
		
		Class type = typeDef.getBase();
		
		if (type.isArray()) {
			if (byte[].class.equals(type))
				return specifyByteArrayValue(context, rules);
			Class elementType = type.getComponentType();
			CollectionContext ctx = new CollectionContext(context, null, typeDef, new TypeDefinition(elementType));
			return specifyCollectionValue(ctx, rules);
		}
		
		if (boolean.class.equals(type))
			return specifyBooleanValue(context, false);
		if (Boolean.class.equals(type))
			return specifyBooleanValue(context, true);
		
		if (byte.class.equals(type))
			return specifyNumericValue(context, Byte.class, false, Byte.valueOf(Byte.MIN_VALUE), Byte.valueOf(Byte.MAX_VALUE));
		if (Byte.class.equals(type))
			return specifyNumericValue(context, Byte.class, true, Byte.valueOf(Byte.MIN_VALUE), Byte.valueOf(Byte.MAX_VALUE));
		if (short.class.equals(type))
			return specifyNumericValue(context, Short.class, false, Short.valueOf(Short.MIN_VALUE), Short.valueOf(Short.MAX_VALUE));
		if (Short.class.equals(type))
			return specifyNumericValue(context, Short.class, true, Short.valueOf(Short.MIN_VALUE), Short.valueOf(Short.MAX_VALUE));
		if (int.class.equals(type))
			return specifyNumericValue(context, Integer.class, false,
				Integer.valueOf(Integer.MIN_VALUE), Integer.valueOf(Integer.MAX_VALUE));
		if (Integer.class.equals(type))
			return specifyNumericValue(context, Integer.class, true,
				Integer.valueOf(Integer.MIN_VALUE), Integer.valueOf(Integer.MAX_VALUE));
		if (long.class.equals(type))
			return specifyNumericValue(context, Long.class, false, Long.valueOf(Long.MIN_VALUE), Long.valueOf(Long.MAX_VALUE));
		if (Long.class.equals(type))
			return specifyNumericValue(context, Long.class, true, Long.valueOf(Long.MIN_VALUE), Long.valueOf(Long.MAX_VALUE));
		if (float.class.equals(type) ||
			double.class.equals(type) ||
			Number.class.isAssignableFrom(type))
			return specifyNumericValue(context, type, !type.isPrimitive(), null, null);

		if (char.class.equals(type))
			return specifyCharacterValue(context, false);
		if (Character.class.equals(type))
			return specifyCharacterValue(context, true);
		
		if (CharSequence.class.isAssignableFrom(type))
			return specifyStringValue(context, typeDef);
		
		if (type.isEnum())
			return specifyEnumValue(context, typeDef);

		if (Collection.class.isAssignableFrom(type)) {
			TypeDefinition elementType;
			if (typeDef.getParameters().isEmpty())
				elementType = null;
			else
				elementType = typeDef.getParameters().get(0);
			CollectionContext ctx = new CollectionContext(context, null, typeDef, elementType);
			return specifyCollectionValue(ctx, rules);
		}

		if (Map.class.isAssignableFrom(type))
			return specifyMapValue(context, typeDef, rules);
		
		if (InputStream.class.isAssignableFrom(type))
			return specifyInputStreamValue(context, rules);
		
		if (IO.Readable.class.isAssignableFrom(type))
			return specifyIOReadableValue(context, rules);
		
		return specifyObjectValue(context, typeDef, rules);
	}
	
	// *** boolean ***
	
	protected abstract IAsync specifyBooleanValue(SerializationContext context, boolean nullable);

	// *** number ***

	protected abstract IAsync specifyNumericValue(
		SerializationContext context, Class type, boolean nullable, Number min, Number max);

	// *** character ***
	
	protected abstract IAsync specifyCharacterValue(SerializationContext context, boolean nullable);
	
	// *** string ***
	
	protected abstract IAsync specifyStringValue(SerializationContext context, TypeDefinition type);

	// *** enum ***
	
	protected abstract IAsync specifyEnumValue(SerializationContext context, TypeDefinition type);

	// *** collection ***
	
	protected abstract IAsync specifyCollectionValue(
		CollectionContext context, List rules);
	
	// *** map ***

	protected IAsync specifyMapValue(
		SerializationContext context, TypeDefinition type, List rules
	) {
		TypeDefinition elementType = new TypeDefinition(MapEntry.class, type.getParameters());
		TypeDefinition colType = new TypeDefinition(ArrayList.class, elementType);
		CollectionContext ctx = new CollectionContext(context, null, colType, elementType);
		return specifyCollectionValue(ctx, rules);
	}
	
	// *** InputStream ***

	protected IAsync specifyInputStreamValue(
		SerializationContext context, List rules
	) {
		return specifyIOReadableValue(context, rules);
	}
	
	// *** IO.Readable ***

	protected abstract IAsync specifyIOReadableValue(
		SerializationContext context, List rules);
	
	// *** byte[] ***
	
	protected IAsync specifyByteArrayValue(
		SerializationContext context, List rules
	) {
		return specifyIOReadableValue(context, rules);
	}
	
	// *** object ***
	
	protected IAsync specifyObjectValue(
		SerializationContext context, TypeDefinition typeDef, List rules
	) {
		ObjectContext ctx;
		try {
			Class type = typeDef.getBase();
			SerializationClass sc = new SerializationClass(TypeDefinition.from(type, typeDef));
			ctx = new ObjectContext(context, null, sc, typeDef);
			rules = addRulesForType(sc, rules);
			sc.apply(rules, ctx, true);
		} catch (SerializationException e) {
			return new Async<>(e);
		}
		return specifyTypedValue(ctx, rules);
	}

	protected abstract IAsync specifyTypedValue(ObjectContext context, List rules);
	
	protected IAsync specifyTypeContent(ObjectContext context, List rules) {
		List attributes = sortAttributes(context.getSerializationClass().getAttributes());
		Async sp = new Async<>();
		specifyTypeAttribute(context, attributes, 0, rules, sp);
		return sp;
	}
	
	protected List sortAttributes(List attributes) {
		return attributes;
	}
	
	protected void specifyTypeAttribute(
		ObjectContext context, List attributes, int index, List rules,
		Async sp
	) {
		if (index == attributes.size()) {
			sp.unblock();
			return;
		}
		Attribute attr = attributes.get(index);
		AttributeContext ctx = new AttributeContext(context, attr);
		IAsync s = specifyTypeAttribute(ctx, rules);
		if (s.isDone()) {
			if (s.hasError()) sp.error(s.getError());
			else specifyTypeAttribute(context, attributes, index + 1, rules, sp);
			return;
		}
		s.thenStart(taskDescription, priority, () -> specifyTypeAttribute(context, attributes, index + 1, rules, sp), sp);
	}
	
	protected abstract IAsync specifyTypeAttribute(
		AttributeContext context, List rules);
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy