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

com.javanut.json.encode.JSONBuilder Maven / Gradle / Ivy

Go to download

Ring buffer based queuing utility for applications that require high performance and/or a small footprint. Well suited for embedded and stream based processing.

There is a newer version: 1.1.27
Show newest version
package com.javanut.json.encode;

import com.javanut.json.JSONType;
import com.javanut.json.encode.function.IterBoolFunction;
import com.javanut.json.encode.function.IterDoubleFunction;
import com.javanut.json.encode.function.IterEnumFunction;
import com.javanut.json.encode.function.IterLongFunction;
import com.javanut.json.encode.function.IterMemberFunction;
import com.javanut.json.encode.function.IterStringFunction;
import com.javanut.json.encode.function.IteratorFunction;
import com.javanut.json.encode.function.ToBoolFunction;
import com.javanut.json.encode.function.ToDoubleFunction;
import com.javanut.json.encode.function.ToEnumFunction;
import com.javanut.json.encode.function.ToLongFunction;
import com.javanut.json.encode.function.ToMemberFunction;
import com.javanut.json.encode.function.ToStringFunction;
import com.javanut.pronghorn.pipe.PipeWriter;
import com.javanut.pronghorn.util.AppendableByteWriter;
import com.javanut.pronghorn.util.Appendables;
import com.javanut.pronghorn.util.ByteWriter;
import com.javanut.pronghorn.util.template.StringTemplateBranching;
import com.javanut.pronghorn.util.template.StringTemplateBuilder;
import com.javanut.pronghorn.util.template.StringTemplateIterScript;
import com.javanut.pronghorn.util.template.StringTemplateScript;

// TODO: support rational, decimal
// TODO: implement the primitive type converters, or refactor to use lambdas, or delete methods
// TODO: refactor for duplicate code
// TODO: determine use case for recursive array and either uncomment/test or delete methods
// TODO: implement select as an array element

// Maintain no dependencies to the public API classes (i.e. JSONObject)

class JSONBuilder extends StringTemplateScript {
    // Do not store mutable state used during render.
    // Use ThreadLocal if required.
    private final StringTemplateBuilder scripts;
    private final JSONKeywords kw;
    private final int depth;
    private JSONBuilder root;

    // Stored between declaration calls and consumed on use in declaration
    private byte[] declaredMemberName;

    // In order to support tryCase, we need a render state for objects.
    private ObjectRenderState ors;

    JSONBuilder() {
        this(new StringTemplateBuilder(), JSONKeywords.instance, 0, null);
    }

    JSONBuilder(JSONKeywords kw) {
        this(new StringTemplateBuilder(), kw, 0, null);
    }

    private JSONBuilder(StringTemplateBuilder scripts, JSONKeywords kw, int depth, JSONBuilder root) {
        this.scripts = scripts;
        this.kw = kw;
        this.depth = depth;
        this.root = root;

        if (root == null) {
            this.root = (JSONBuilder)this;
        }
    }

    void start() {
        kw.Start(scripts, depth);
    }

    void complete() {
        kw.Complete(scripts, depth);
    }

    public void render(AppendableByteWriter writer, T source) {
    	StringTemplateBuilder.render(scripts,writer, source);
    }

    // Object Helpers

    JSONBuilder addFieldPrefix(String name) {
        this.declaredMemberName = name.getBytes();
        return this;
    }
    
    JSONBuilder addFieldPrefix(byte[] utf8EncodedName) {
        this.declaredMemberName = utf8EncodedName;
        return this;
    }

    private static class ObjectRenderState {
        private final JSONKeywords kw;
        private int objectElementIndex = -1;

        ObjectRenderState(JSONKeywords kw) {
            this.kw = kw;
        }

        private void beginObjectRender() {
            objectElementIndex = -1;
        }

        void prefixObjectMemberName(byte[] declaredMemberName, int depth, ByteWriter writer) {
            objectElementIndex++;
            if (objectElementIndex == 0) {
                kw.FirstObjectElement(writer, depth);
            }
            else {
                kw.NextObjectElement(writer, depth);
            }
            writer.write(declaredMemberName);
            kw.ObjectValue(writer, depth);
        }
    }

    private void prefixObjectMemberName(byte[] declaredMemberName, int depth, ByteWriter writer) {
        if (declaredMemberName != null && this.ors != null) {
            ObjectRenderState ors = this.ors;
            if (ors != null) {
                ors.prefixObjectMemberName(declaredMemberName, depth, writer);
            }
        }
    }

    private StringTemplateScript createNullObjectScript(final byte[] declaredMemberName) {
        return new StringTemplateScript() {
            @Override
            public void render(AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                kw.Null(writer);
            }
        };
    }

    private byte[] consumeDeclaredMemberName() {
        byte[] declaredMemberName = this.declaredMemberName;
        this.declaredMemberName = null;
        return declaredMemberName;
    }

    // Array Helpers

    interface RenderIteration {
        void render(AppendableByteWriter appendable, M member, int i);
    }

    private  void iterate(
            final IteratorFunction iterator,
            final boolean checkNull,
            final IterMemberFunction accessor,
            final RenderIteration func) {
        scripts.add(new StringTemplateIterScript() {
            @Override
            public boolean render(AppendableByteWriter writer, T source, int i) {
                N node = iterator.get(source, i);
                if (node != null) {
                    if (i > 0) {
                        kw.NextArrayElement(writer, depth);
                    }
                    M member = accessor.get(source, i);
                    if (checkNull && member == null) {
                        kw.Null(writer);
                    } else {
                        func.render(writer, member, i);
                    }
                    return true;
                }
                return false;
            }
        });
    }

    private  void iterate(
            final IteratorFunction iterator,
            final IterBoolFunction isNull,
            final RenderIteration func) {
        scripts.add(new StringTemplateIterScript() {
            @Override
            public boolean render(final AppendableByteWriter writer, T source, int i) {
                N node = iterator.get(source, i);
                if (node != null) {
                    if (i > 0) {
                        kw.NextArrayElement(writer, depth);
                    }
                    if (isNull != null && isNull.applyAsBool(source, i)) {
                        kw.Null(writer);
                    } else {
                        func.render(writer, source, i);
                    }
                    return true;
                }
                return false;
            }
        });
    }

    // Sub Builders

     void addBuilder(final JSONBuilder builder, final ToMemberFunction accessor) {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        scripts.add(new StringTemplateScript() {
            @Override
            public void render(AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                M member = accessor.get(source);
                if (member == null) {
                    kw.Null(writer);
                }
                else {
                    builder.render(writer, member);
                }
            }
        });
    }

    void recurseRoot(final ToMemberFunction accessor) {
        if (root != this) {
            addBuilder(root, accessor);
        }
    }

     void addBuilder(final IteratorFunction iterator, final JSONBuilder builder, final IterMemberFunction accessor) {
        iterate(iterator, true, accessor, new RenderIteration() {
            @Override
            public void render(AppendableByteWriter writer, M m, int i) {
                builder.render(writer, m);
            }
        });
    }

/*
     void recurseRoot(final IteratorFunction iterator, final IterMemberFunction accessor) {
        addBuilder(iterator, root, accessor);
    }
*/
    // Select

    JSONBuilder beginSelect() {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        JSONBuilder builder = new JSONBuilder(new StringTemplateBuilder(), kw, depth, root);
        builder.declaredMemberName = declaredMemberName;
        builder.ors = ors;
        this.scripts.add(builder);
        return builder;
    }

    JSONBuilder tryCase() {
        final byte[] declaredMemberName = this.declaredMemberName; // Do not consume for other try
        JSONBuilder builder = new JSONBuilder(new StringTemplateBuilder(), kw, depth, root);
        builder.declaredMemberName = declaredMemberName;
        builder.ors = ors;
        return builder;
    }

    void endSelect(final int count, final ToBoolFunction[] branches, final JSONBuilder[] cases) {
        consumeDeclaredMemberName();
        final StringTemplateScript[] caseScripts = new StringTemplateScript[count];
        for (int i = 0; i < count; i++) {
            final JSONBuilder builder = cases[i];
            caseScripts[i] = builder;
        }

        scripts.add(caseScripts, new StringTemplateBranching() {
            @Override
            public int branch(T source) {
                for (int i = 0; i < count; i++) {
                    if (branches[i].applyAsBool(source)) {
                        return i;
                    }
                }
                return -1;
            }
        });
    }

     void endSelect(IteratorFunction iterator, int count, final IterBoolFunction[] branches, final JSONBuilder[] cases) {
    }

    // Object

    public  JSONBuilder beginObject(final ToMemberFunction accessor) {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        final StringTemplateBuilder accessorScript = new StringTemplateBuilder<>();
        kw.OpenObj(accessorScript, depth);

        final ObjectRenderState newOrs = new ObjectRenderState(kw);

        final StringTemplateScript objNullBranch = createNullObjectScript(declaredMemberName);
        final StringTemplateScript notNullBranch = new StringTemplateScript() {
            @Override
            public void render(AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                newOrs.beginObjectRender();
                accessorScript.render(writer, accessor.get(source));
            }
        };

        final StringTemplateScript[] nullableBranches = new StringTemplateScript[2];
        nullableBranches[0] = objNullBranch;
        nullableBranches[1] = notNullBranch;

        scripts.add(nullableBranches, new StringTemplateBranching() {
            @Override
            public int branch(T o) {
                return accessor.get(o) == null ? 0 : 1;
            }
        });
        JSONBuilder builder = new JSONBuilder<>(accessorScript, kw, depth + 1, root);
        builder.ors = newOrs;
        return builder;
    }

     JSONBuilder beginObject(final IteratorFunction iterator, final IterMemberFunction accessor) {
        final StringTemplateBuilder accessorBranch = new StringTemplateBuilder<>();
        kw.OpenObj(accessorBranch, depth);

        final ObjectRenderState newOrs = new ObjectRenderState(kw);

        iterate(iterator, true, new IterMemberFunction() {
            @Override
            public M get(T o, int i) {
                newOrs.beginObjectRender();
                return accessor.get(o, i);
            }
        }, new RenderIteration() {
            @Override
            public void render(AppendableByteWriter writer, M m, int i) {
                accessorBranch.render(writer, m);
            }
        });

        JSONBuilder builder = new JSONBuilder<>(accessorBranch, kw, depth + 1, root);
        builder.ors = newOrs;
        return builder;
    }

    void endObject() {
        kw.CloseObj(scripts, depth);
    }

    // Array

     JSONBuilder beginArray(final ToMemberFunction func) {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        final StringTemplateBuilder arrayBuilder = new StringTemplateBuilder<>();
        kw.OpenArray(arrayBuilder, depth);

        final StringTemplateScript objNullBranch = createNullObjectScript(declaredMemberName);
        final StringTemplateScript notNullBranch = new StringTemplateScript() {
            @Override
            public void render(AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                arrayBuilder.render(writer, func.get(source));
            }
        };

        final StringTemplateScript[] nullableBranches = new StringTemplateScript[2];
        nullableBranches[0] = objNullBranch;
        nullableBranches[1] = notNullBranch;

        scripts.add(nullableBranches, new StringTemplateBranching() {
            @Override
            public int branch(T o) {
                return func.get(o) == null ? 0 : 1;
            }
        });
        return new JSONBuilder(arrayBuilder, kw, depth + 1, root);
    }

    public  JSONBuilder beginArray(final IteratorFunction iterator, final IterMemberFunction func) {
        final StringTemplateBuilder notNullBranch = new StringTemplateBuilder<>();
        kw.OpenArray(notNullBranch, depth);

        iterate(iterator, true, func, new RenderIteration() {
            @Override
            public void render(AppendableByteWriter writer, M m, int i) {
                notNullBranch.render(writer, m);
            }
        });
        return new JSONBuilder(notNullBranch, kw, depth + 1, root);
    }

    void endArray() {
        kw.CloseArray(scripts, depth);
    }

    // Null

    void addNull() {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        scripts.add(createNullObjectScript(declaredMemberName));
    }

     void addNull(final IteratorFunction iterator) {
        iterate(iterator, new IterBoolFunction() {
            @Override
            public boolean applyAsBool(T o, int i) {
                return true;
            }
        }, null);
    }

    // Bool

    void addBool(final ToBoolFunction isNull, final ToBoolFunction func) {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        scripts.add(new StringTemplateScript() {
            @Override
            public void render(final AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                if (isNull != null && isNull.applyAsBool(source)) {
                    kw.Null(writer);
                }
                else {
                    if (func.applyAsBool(source)) {
                        kw.True(writer);
                    }
                    else {
                        kw.False(writer);
                    }
                }
            }
        });
    }

     void addBool(final IteratorFunction iterator, final IterBoolFunction isNull, final IterBoolFunction func) {
        iterate(iterator, isNull, new RenderIteration() {
            @Override
            public void render(AppendableByteWriter writer, T source, int i) {
                if (func.applyAsBool(source, i)) {
                    kw.True(writer);
                }
                else {
                    kw.False(writer);
                }
            }});
    }

    void addBool(final ToBoolFunction isNull, final ToBoolFunction func, JSONType encode) {
        switch (encode) {
            case TypeString:
                break;
            case TypeInteger:
                break;
            case TypeDecimal:
                break;
            case TypeBoolean:
                addBool(isNull, func);
                break;
        }
    }

     void addBool(IteratorFunction iterator, IterBoolFunction isNull, IterBoolFunction func, JSONType encode) {
        switch (encode) {
            case TypeString:
                break;
            case TypeInteger:
                break;
            case TypeDecimal:
                break;
            case TypeBoolean:
                addBool(iterator, isNull, func);
                break;
        }
    }

    // Integer

    void addInteger(final ToBoolFunction isNull, final ToLongFunction func) {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        scripts.add(new StringTemplateScript() {
            @Override
            public void render(AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                if (isNull != null && isNull.applyAsBool(source)) {
                    kw.Null(writer);
                }
                else {
                    Appendables.appendValue(writer, func.applyAsLong(source), false);
                }
            }
        });
    }

     void addInteger(final IteratorFunction iterator, final IterBoolFunction isNull, final IterLongFunction func) {
        iterate(iterator, isNull, new RenderIteration() {
            @Override
            public void render(AppendableByteWriter writer, T source, int i) {
                Appendables.appendValue(writer, func.applyAsLong(source, i), false);
            }});
    }

    void addInteger(ToBoolFunction isNull, ToLongFunction func, JSONType encode) {
        switch (encode) {
            case TypeString:
                break;
            case TypeInteger:
                addInteger(isNull, func);
                break;
            case TypeDecimal:
                break;
            case TypeBoolean:
                break;
        }
    }

     void addInteger(IteratorFunction iterator, IterBoolFunction isNull, IterLongFunction func, JSONType encode) {
        switch (encode) {
            case TypeString:
                break;
            case TypeInteger:
                addInteger(iterator, isNull, func);
                break;
            case TypeDecimal:
                break;
            case TypeBoolean:
                break;
        }
    }

    // Decimal

    void addDecimal(final int precision, final ToBoolFunction isNull, final ToDoubleFunction func) {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        scripts.add(new StringTemplateScript() {
            @Override
            public void render(final AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                if (isNull != null && isNull.applyAsBool(source)) {
                    kw.Null(writer);
                }
                else {
                    double v = func.applyAsDouble(source);
                    Appendables.appendDecimalValue(writer, (long) (v * PipeWriter.powd[64 + precision]), (byte) (precision * -1));
                }
            }
        });
    }

     void addDecimal(final IteratorFunction iterator, final int precision, final IterBoolFunction isNull, final IterDoubleFunction func) {
        iterate(iterator, isNull, new RenderIteration() {
            @Override
            public void render(AppendableByteWriter writer, T source, int i) {
                double v = func.applyAsDouble(source, i);
                Appendables.appendDecimalValue(writer, (long) (v * PipeWriter.powd[64 + precision]), (byte) (precision * -1));
            }
        });
    }

    void addDecimal(int precision, ToBoolFunction isNull, ToDoubleFunction func, JSONType encode) {
        switch (encode) {
            case TypeString:
                break;
            case TypeInteger:
                break;
            case TypeDecimal:
                addDecimal(precision, isNull, func);
                break;
            case TypeBoolean:
                break;
        }
    }

     void addDecimal(IteratorFunction iterator, int precision, IterBoolFunction isNull, IterDoubleFunction func, JSONType encode) {
        switch (encode) {
            case TypeString:
                break;
            case TypeInteger:
                break;
            case TypeDecimal:
                addDecimal(iterator, precision, isNull, func);
                break;
            case TypeBoolean:
                break;
        }
    }

    // String

    void addString(final boolean checkNull, final ToStringFunction func) {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        
        scripts.add(new StringTemplateScript() {
        	
        	NullableAppendableByteWriterWrapper nabww = new NullableAppendableByteWriterWrapper();
        	
            @Override
            public void render(AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                
                if (!checkNull) {
                	kw.Quote(writer);
                	func.applyAsString(source, writer);
                	kw.Quote(writer);                	
                } else {
                	nabww.externalWriter = writer;
                	nabww.wasNull = false;
                	nabww.needsQuote = true;
                	func.applyAsString(source, nabww);
                	if (!nabww.wasNull) {
                		kw.Quote(writer);
                	} else {
                		//Note we are already reset to the beginning.
                		kw.Null(writer);
                	}
                }
                
            }
        });
    }

     void addString(final IteratorFunction iterator,final boolean checkNull, final IterStringFunction func) {
        iterate(iterator, checkNull, new IterMemberFunction() {
            @Override
            public T get(T o, int i) {
            	return o;                
            }
        }, new RenderIteration() {
        	
        	NullableAppendableByteWriterWrapper nabww = new NullableAppendableByteWriterWrapper();

            @Override
            public void render(AppendableByteWriter writer, T o, int i) {

                if (!checkNull) {
                	kw.Quote(writer);
                	func.applyAsString(o, i, writer);
                	kw.Quote(writer);                	
                } else {
                	nabww.externalWriter = writer;
                	nabww.wasNull = false;
                	nabww.needsQuote = true;
                	func.applyAsString(o, i, nabww);
                	if (!nabww.wasNull) {
                		kw.Quote(writer);
                	} else {
                		//Note we are already reset to the beginning.
                		kw.Null(writer);
                	}
                }
            }
        });
    }

    void addString(boolean checkNull, ToStringFunction func, JSONType encode) {
        switch (encode) {
            case TypeString:
                addString(checkNull, func);
                break;
            case TypeInteger:
                break;
            case TypeDecimal:
                break;
            case TypeBoolean:
                break;
        }
    }

     void addString(IteratorFunction iterator, boolean checkNull, IterStringFunction func, JSONType encode) {
        switch (encode) {
            case TypeString:
                addString(iterator, checkNull, func);
                break;
            case TypeInteger:
                break;
            case TypeDecimal:
                break;
            case TypeBoolean:
                break;
        }
    }

    // Enum

    > void addEnumName(final ToEnumFunction func) {
        addString(true, new ToStringFunction() {

			@Override
			public void applyAsString(T value, AppendableByteWriter target) {
                E v = func.applyAsEnum(value);
                if (null!=v) {
                	target.append(v.name());
                } else {
                	target.append(null);
                }
			}
        });
    }

    > void addEnumName(final IteratorFunction iterator, final IterEnumFunction func) {
        iterate(iterator, true, new IterMemberFunction() {
            @Override
            public CharSequence get(T o, int i) {
                E v = func.applyAsEnum(o, i);
                return (v != null ? v.name() : null);
            }
        }, new RenderIteration() {
            @Override
            public void render(AppendableByteWriter writer, CharSequence member, int i) {
                kw.Quote(writer);
                writer.append(member);
                kw.Quote(writer);
            }
        });
    }

    > void addEnumOrdinal(final ToEnumFunction func) {
        final byte[] declaredMemberName = consumeDeclaredMemberName();
        scripts.add(new StringTemplateScript() {
            @Override
            public void render(AppendableByteWriter writer, T source) {
                prefixObjectMemberName(declaredMemberName, depth, writer);
                E v = func.applyAsEnum(source);
                if (v == null) {
                    kw.Null(writer);
                }
                else {
                    Appendables.appendValue(writer, v.ordinal());
                }
            }
        });
    }

    > void addEnumOrdinal(final IteratorFunction iterator, final IterEnumFunction func) {
        iterate(iterator, true, new IterMemberFunction() {
            @Override
            public Enum get(T o, int i) {
                return func.applyAsEnum(o, i);
            }
        }, new RenderIteration() {
            @Override
            public void render(AppendableByteWriter writer, Enum member, int i) {
                Appendables.appendValue(writer, member.ordinal());
            }
        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy