SimpleStreamWriteContext.java

package tools.jackson.core.util;

import tools.jackson.core.*;
import tools.jackson.core.exc.StreamWriteException;
import tools.jackson.core.json.DupDetector;

/**
 * Basic implementation of {@link TokenStreamContext} useful for most
 * format backend {@link JsonGenerator} implementations
 * (with notable exception of JSON that needs bit more advanced state).
 *
 * @since 3.0
 */
public final class SimpleStreamWriteContext extends TokenStreamContext
{
    /**
     * Parent context for this context; null for root context.
     */
    protected final SimpleStreamWriteContext _parent;

    // // // Optional duplicate detection

    protected DupDetector _dups;

    /*
    /**********************************************************************
    /* Simple instance reuse slots; speed up things a bit (10-15%)
    /* for docs with lots of small arrays/objects
    /**********************************************************************
     */

    protected SimpleStreamWriteContext _childToRecycle;

    /*
    /**********************************************************************
    /* Location/state information (minus source reference)
    /**********************************************************************
     */

    /**
     * Name of the property of which value is to be written; only
     * used for OBJECT contexts.
     */
    protected String _currentName;

    protected Object _currentValue;

    /**
     * Marker used to indicate that we just wrote a property name (or possibly
     * property id for some backends) and now expect a value to write.
     */
    protected boolean _gotPropertyId;

    /*
    /**********************************************************************
    /* Life-cycle
    /**********************************************************************
     */

    protected SimpleStreamWriteContext(int type, SimpleStreamWriteContext parent, int nestingDepth,
            DupDetector dups, Object currentValue) {
        super();
        _type = type;
        _parent = parent;
        _nestingDepth = nestingDepth;
        _dups = dups;
        _index = -1;
        _currentValue = currentValue;
    }

    // REMOVE as soon as nothing uses this
    /*
    @Deprecated
    protected SimpleStreamWriteContext(int type, SimpleStreamWriteContext parent,
            DupDetector dups, Object currentValue) {
        super();
        _type = type;
        _parent = parent;
        _dups = dups;
        _index = -1;
        _currentValue = currentValue;
    }
    */

    private SimpleStreamWriteContext reset(int type, Object currentValue) {
        _type = type;
        // Due to way reuse works, "_parent" and "_nestingDepth" are fine already
        _index = -1;
        _currentName = null;
        _gotPropertyId = false;
        _currentValue = currentValue;
        if (_dups != null) { _dups.reset(); }
        return this;
    }

    public SimpleStreamWriteContext withDupDetector(DupDetector dups) {
        _dups = dups;
        return this;
    }

    @Override
    public Object currentValue() {
        return _currentValue;
    }

    @Override
    public void assignCurrentValue(Object v) {
        _currentValue = v;
    }

    /*
    /**********************************************************************
    /* Factory methods
    /**********************************************************************
     */

    public static SimpleStreamWriteContext createRootContext(DupDetector dd) {
        return new SimpleStreamWriteContext(TYPE_ROOT, null, 0, dd, null);
    }

    public SimpleStreamWriteContext createChildArrayContext(Object currentValue) {
        SimpleStreamWriteContext ctxt = _childToRecycle;
        if (ctxt == null) {
            _childToRecycle = ctxt = new SimpleStreamWriteContext(TYPE_ARRAY, this,
                    _nestingDepth + 1,
                    (_dups == null) ? null : _dups.child(), currentValue);
            return ctxt;
        }
        return ctxt.reset(TYPE_ARRAY, currentValue);
    }

    public SimpleStreamWriteContext createChildObjectContext(Object currentValue) {
        SimpleStreamWriteContext ctxt = _childToRecycle;
        if (ctxt == null) {
            _childToRecycle = ctxt = new SimpleStreamWriteContext(TYPE_OBJECT, this,
                    _nestingDepth + 1,
                    (_dups == null) ? null : _dups.child(), currentValue);
            return ctxt;
        }
        return ctxt.reset(TYPE_OBJECT, currentValue);
    }

    /*
    /**********************************************************************
    /* Accessors
    /**********************************************************************
     */

    @Override public final SimpleStreamWriteContext getParent() { return _parent; }
    @Override public final String currentName() {
        // 15-Aug-2019, tatu: Should NOT check this status because otherwise name
        //    in parent context is not accessible after new structured scope started
//        if (_gotPropertyId) { ... }
        return _currentName;
    }

    @Override public boolean hasCurrentName() { return _gotPropertyId; }

    /**
     * Method that can be used to both clear the accumulated references
     * (specifically value set with {@link #assignCurrentValue(Object)})
     * that should not be retained, and returns parent (as would
     * {@link #getParent()} do). Typically called when closing the active
     * context when encountering {@link JsonToken#END_ARRAY} or
     * {@link JsonToken#END_OBJECT}.
     *
     * @return Parent context of this context node, if any; {@code null} for root context
     */
    public SimpleStreamWriteContext clearAndGetParent() {
        _currentValue = null;
        // could also clear the current name, but seems cheap enough to leave?
        return _parent;
    }

    public DupDetector getDupDetector() {
        return _dups;
    }

    /*
    /**********************************************************************
    /* State changing
    /**********************************************************************
     */

    /**
     * Method that writer is to call before it writes an Object Property name.
     *
     * @param name Name of Object property name being written
     *
     * @return {@code True} if name writing should proceed; {@code false} if not
     *
     * @throws StreamWriteException If write fails due to duplicate check
     */
    public boolean writeName(String name) throws StreamWriteException {
        if ((_type != TYPE_OBJECT) || _gotPropertyId) {
            return false;
        }
        _gotPropertyId = true;
        _currentName = name;
        if (_dups != null) { _checkDup(_dups, name); }
        return true;
    }

    private final void _checkDup(DupDetector dd, String name) throws StreamWriteException {
        if (dd.isDup(name)) {
            Object src = dd.getSource();
            throw new StreamWriteException(((src instanceof JsonGenerator) ? ((JsonGenerator) src) : null),
                    "Duplicate Object property \""+name+"\"");
        }
    }

    public boolean writeValue() {
        // Only limitation is with OBJECTs:
        if (_type == TYPE_OBJECT) {
            if (!_gotPropertyId) {
                return false;
            }
            _gotPropertyId = false;
        }
        ++_index;
        return true;
    }
}