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

com.venky.swf.db.model.io.AbstractModelWriter Maven / Gradle / Ivy

The newest version!
package com.venky.swf.db.model.io;

import com.venky.cache.Cache;
import com.venky.core.collections.SequenceSet;
import com.venky.core.log.SWFLogger;
import com.venky.core.log.TimerUtils;
import com.venky.core.string.StringUtil;
import com.venky.core.util.ObjectUtil;
import com.venky.swf.db.Database;
import com.venky.swf.db.annotations.column.ATTRIBUTE_GROUP;
import com.venky.swf.db.annotations.column.ui.mimes.MimeType;
import com.venky.swf.db.model.Model;
import com.venky.swf.db.model.reflection.ModelReflector;
import com.venky.swf.integration.FormatHelper;
import com.venky.swf.integration.FormatHelper.KeyCase;
import com.venky.swf.routing.Config;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class AbstractModelWriter extends ModelIO implements ModelWriter{
	boolean parentIdExposed = true;
	public void setParentIdExposed(boolean parentIdExposed){
		this.parentIdExposed = parentIdExposed;
	}
	public boolean isParentIdExposed() {
		return this.parentIdExposed;
	}
	protected AbstractModelWriter(Class beanClass) {
		super(beanClass);
	}
	@SuppressWarnings("unchecked")
	public Class getFormatClass(){
		ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass();
		return (Class) pt.getActualTypeArguments()[1];
	}
	
	public MimeType getMimeType(){
		return FormatHelper.getMimeType(getFormatClass());
	}
	private List getFields(List includeFields){
		return getFields(getReflector(),includeFields);
	}
	private static  List getFields(ModelReflector reflector, List includeFields) {
		List fields = includeFields;
		if (fields == null){
			fields = reflector.getVisibleFields();
		}
		return fields;
	}
	

	public void write(List records, OutputStream os,List fields) throws IOException {
		FormatHelper helper  = FormatHelper.instance(getMimeType(),StringUtil.pluralize(getBeanClass().getSimpleName()),true);
		write (records,helper.getRoot(),fields);
		os.write(helper.toString().getBytes());
	}

	public void write(List records, T into,List fields) throws IOException {
		Map , List> mapFields = new HashMap<>();
		Set> parentsWritten = new HashSet<>();
		write (records,into,fields,parentsWritten,mapFields);
	}

	public void write (List records,OutputStream os, List fields, Set> parentsAlreadyConsidered,
					   Map,List> templateFields) throws IOException {
		FormatHelper helper  = FormatHelper.instance(getMimeType(),StringUtil.pluralize(getBeanClass().getSimpleName()),true);
		write(records,helper.getRoot(),fields,parentsAlreadyConsidered,templateFields);
		os.write(helper.toString().getBytes());

	}
	public void write (List records,T into, List fields, Set> parentsAlreadyConsidered,
					   Map,List> templateFields) throws IOException {
		write(records,into,fields,parentsAlreadyConsidered,getChildrenToConsider(templateFields),templateFields);
	}

	public void write (List records,OutputStream os, List fields, Set> parentsAlreadyConsidered ,
					   Map,List>> childrenToBeConsidered,
					   Map,List> templateFields) throws IOException {
		FormatHelper helper  = FormatHelper.instance(getMimeType(),StringUtil.pluralize(getBeanClass().getSimpleName()),true);
		write(records,helper.getRoot(),fields,parentsAlreadyConsidered,childrenToBeConsidered,templateFields);
		os.write(helper.toString().getBytes());
	}
	public void write (List records,T into, List fields, Set> parentsAlreadyConsidered ,
					   Map,List>> childrenToBeConsidered,
					   Map,List> templateFields) throws IOException {
		FormatHelper helper  = FormatHelper.instance(into);
		for (M record: records){
			T childElement = helper.createArrayElement(getBeanClass().getSimpleName());
			write(record,childElement,fields,parentsAlreadyConsidered, childrenToBeConsidered, templateFields);
		}
	}

	public void write(M record,T into, List fields){
		write(record,into,fields,new HashSet<>(), new HashMap<>());
	}
	private boolean isParentIgnored(Class parent, Set ignoredParents) {
		return ignoredParents.contains(parent.getSimpleName());
	}
	private final SWFLogger cat = Config.instance().getLogger(getClass().getName());
	public Map , List>> getChildrenToConsider(Map, List> templateFields){
		return getReflector().getChildrenToBeConsidered(templateFields);
	}
	public void write(M record,T into, List fields, Set> parentsAlreadyConsidered , Map,List> templateFields) {
		//Consider first level children.
		write(record,into,fields,parentsAlreadyConsidered,getChildrenToConsider(templateFields),templateFields);
	}
	public void write(M record,T into, List fields, Set> parentsAlreadyConsidered ,
					  Map, List>> considerChildren,
					  Map, List> templateFields) {

		Set simplifiedParentsConsidered = new HashSet<>();
		parentsAlreadyConsidered.forEach(c->simplifiedParentsConsidered.add(c.getSimpleName()));

		Map> simplifiedConsiderChildren = new Cache>() {
			@Override
			protected List getValue(String s) {
				return new SequenceSet<>();
			}
		};
		considerChildren.forEach((m,l)->{
			for (Class child: l){
				simplifiedConsiderChildren.get(m.getSimpleName()).add(child.getSimpleName());
			}
		});
		Map> simplifiedTemplateFields = new Cache>() {
			@Override
			protected List getValue(String s) {
				return new SequenceSet<>();
			}
		};

		templateFields.forEach((m,fl)->{
			for (String f: fl != null ? fl : ModelReflector.instance(m).getVisibleFields()){
				simplifiedTemplateFields.get(m.getSimpleName()).add(f);
			}
		});
		writeSimplified(record,into,fields,simplifiedParentsConsidered,simplifiedConsiderChildren,simplifiedTemplateFields);

	}
	public void writeSimplified(M record,T into, List fields,
					  Set parentsAlreadyConsidered ,
					  Map> considerChildren,
					  Map> templateFields) {


		FormatHelper _formatHelper = FormatHelper.instance(into);
		ModelReflector ref = getReflector();
		Map> groupedFields = ref.getGroupedFields();

		for (String field: getFields(fields)){
			FormatHelper formatHelper = _formatHelper;
			Object value = TimerUtils.time(cat,"ref.get", ()-> ref.get(record, field));
			if (value == null){
				continue;
			}
			Method fieldGetter = TimerUtils.time(cat,"getFieldGetter" , () ->ref.getFieldGetter(field));
			Method referredModelGetter = TimerUtils.time(cat,"getReferredModelGetterFor" , ()->ref.getReferredModelGetterFor(fieldGetter));

			if (referredModelGetter != null){
				Class aParent = ref.getReferredModelClass(referredModelGetter);
				if (!isParentIgnored(aParent,parentsAlreadyConsidered) || fields != null) {
					String refElementName = referredModelGetter.getName().substring("get".length());
					T refElement = formatHelper.createElementAttribute(refElementName);
					parentsAlreadyConsidered.add(aParent.getSimpleName());
					try {
						write(aParent, ((Number) value).longValue(), refElement, parentsAlreadyConsidered, considerChildren,templateFields);
					}finally {
						parentsAlreadyConsidered.remove(aParent.getSimpleName());
					}
				}
			}else {
				String attributeName = TimerUtils.time(cat,"getAttributeName()" , ()->getAttributeName(field));



				if (!groupedFields.isEmpty()){
					ATTRIBUTE_GROUP group = ref.getAnnotation(fieldGetter, ATTRIBUTE_GROUP.class);
					if (group != null && !ObjectUtil.isVoid(group.value())){
						T groupElement  = formatHelper.getElementAttribute(group.value());
						if (groupElement == null){
							groupElement = formatHelper.createElementAttribute(group.value());
						}

						formatHelper = FormatHelper.instance(groupElement);
					}
				}

				if (String[].class.isAssignableFrom(value.getClass())) {
					formatHelper.setAttribute(attributeName, (String[])value);
				}else {
					String sValue = Database.getJdbcTypeHelper(getReflector().getPool()).getTypeRef(fieldGetter.getReturnType()).getTypeConverter().toStringISO(value);
					if (InputStream.class.isAssignableFrom(fieldGetter.getReturnType())) {
						formatHelper.setElementAttribute(attributeName, sValue);
					} else {
						formatHelper.setAttribute(attributeName, sValue);
					}
				}
			}
		}

		TimerUtils.time(cat,"Writing all Children Objects",()->{
			if (!templateFields.isEmpty()){
				parentsAlreadyConsidered.add(ref.getModelClass().getSimpleName());
				try {
					List childGetters = ref.getChildGetters();
					for (Method childGetter : childGetters) {
						write(_formatHelper,record,childGetter,parentsAlreadyConsidered,considerChildren,templateFields);
					}
				}finally {
					parentsAlreadyConsidered.remove(ref.getModelClass().getSimpleName());
				}
			}
			return  true;
		});
	}
	private  boolean containsChild(Class childModelClass, Collection considerChildren){
		return considerChildren != null && considerChildren.contains(childModelClass.getSimpleName());
	}
	private  void write(FormatHelper formatHelper, M record, Method childGetter, Set parentsWritten ,
										 Map> considerChildren,
										 Map> templateFields){
		@SuppressWarnings("unchecked")
		Class childModelClass = (Class) getReflector().getChildModelClass(childGetter);
		if (!containsChild(childModelClass,considerChildren.get(getBeanClass().getSimpleName()))){
			return;
		}

		if (!containsChild(childModelClass,templateFields.keySet())){
			return;
		}
		
		ModelWriter childWriter = ModelIOFactory.getWriter(childModelClass, getFormatClass());
		try {
			@SuppressWarnings("unchecked")
			List children = (List)childGetter.invoke(record);
			List fields = new ArrayList<>();
			List f = templateFields.get(childModelClass.getSimpleName());
			if (f != null) {
				fields.addAll(f);
			}
			if (fields.isEmpty()){
				fields = null;
			}
			Map> newConsiderChilden = new HashMap<>(considerChildren);
			newConsiderChilden.remove(getBeanClass().getSimpleName()); //Don't print children of parent via children. !! Duplication.
			for (R child : children) {
				T childElement = formatHelper.createArrayElement(childModelClass.getSimpleName());
				childWriter.writeSimplified(child, childElement, fields, parentsWritten, newConsiderChilden, templateFields);
			}
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			throw new RuntimeException(e);
		}
		
	}
	
	private  void write(Class referredModelClass, long id, T referredModelElement,  Set parentsAlreadyConsidered,
										 Map> considerChildren,
										 Map> templateFields){
		Class formatClass = getFormatClass();
		ModelWriter writer = ModelIOFactory.getWriter(referredModelClass,formatClass);
		R referredModel = Database.getTable(referredModelClass).get(id);
		if (referredModel == null){
			return;
		}
		ModelReflector referredModelReflector = ModelReflector.instance(referredModelClass);

		List parentFieldsToAdd = referredModelReflector.getUniqueFields();
		parentFieldsToAdd.removeIf(referredModelReflector::isFieldHidden);

		if (isParentIdExposed() || parentFieldsToAdd.isEmpty()) {
			parentFieldsToAdd.add("ID");
		}

		if (templateFields != null){
			List parentFieldsToAddBasedOnTemplate = new SequenceSet<>();
			if (templateFields.get(referredModelClass.getSimpleName()) != null) {
				//Never add parent class with null template.
				parentFieldsToAddBasedOnTemplate.addAll(getFields(referredModelReflector,templateFields.get(referredModelClass.getSimpleName())));
			}
			if (!parentFieldsToAddBasedOnTemplate.isEmpty()){
				parentFieldsToAdd.clear();
				parentFieldsToAdd.addAll(parentFieldsToAddBasedOnTemplate);
			}
        }
		//* Which parent's children to include */
        List newChildModelsToConsider = new SequenceSet<>();
		if (considerChildren != null){
			newChildModelsToConsider = considerChildren.getOrDefault(referredModelClass.getSimpleName(),newChildModelsToConsider);
		}

		List> childModels = referredModelReflector.getChildModels();
		Map> newTemplateFields = null;
		if (templateFields != null){
			newTemplateFields = new HashMap<>(templateFields);
			for (Class childModelClass : childModels){
				boolean keepChildModelClassInTemplate = newChildModelsToConsider.contains(childModelClass.getSimpleName());
				if (!keepChildModelClassInTemplate){
					newTemplateFields.remove(childModelClass.getSimpleName());
				}
			}
		}

		writer.writeSimplified(referredModel , referredModelElement, parentFieldsToAdd, parentsAlreadyConsidered, considerChildren, newTemplateFields);
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy