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

com.fitbur.jackson.databind.cfg.ContextAttributes Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package com.fitbur.jackson.databind.cfg;

import java.util.*;

/**
 * Helper class used for storing and accessing per-call attributes.
 * Storage is two-layered: at higher precedence, we have actual per-call
 * attributes; and at lower precedence, default attributes that may be
 * defined for Object readers and writers.
 *

* Note that the way mutability is implemented differs between kinds * of attributes, to account for thread-safety: per-call attributes * are handled assuming that instances are never shared, whereas * changes to per-reader/per-writer attributes are made assuming * sharing, by creating new copies instead of modifying state. * This allows sharing of default values without per-call copying, but * requires two-level lookup on access. * * @since 2.3 */ public abstract class ContextAttributes { public static ContextAttributes getEmpty() { return Impl.getEmpty(); } /* /********************************************************** /* Per-reader/writer access /********************************************************** */ public abstract ContextAttributes withSharedAttribute(Object key, Object value); public abstract ContextAttributes withSharedAttributes(Map attributes); public abstract ContextAttributes withoutSharedAttribute(Object key); /* /********************************************************** /* Per-operation (serialize/deserialize) access /********************************************************** */ /** * Accessor for value of specified attribute */ public abstract Object getAttribute(Object key); /** * Mutator used during call (via context) to set value of "non-shared" * part of attribute set. */ public abstract ContextAttributes withPerCallAttribute(Object key, Object value); /* /********************************************************** /* Default implementation /********************************************************** */ public static class Impl extends ContextAttributes implements java.io.Serializable // just so ObjectReader/ObjectWriter can retain configs { private static final long serialVersionUID = 1L; protected final static Impl EMPTY = new Impl(Collections.emptyMap()); protected final static Object NULL_SURROGATE = new Object(); /** * Shared attributes that we can not modify in-place. */ protected final Map _shared; /** * Per-call attributes that we can directly modify, since they are not * shared between threads. *

* NOTE: typed as Object-to-Object, unlike {@link #_shared}, because * we need to be able to modify contents, and wildcard type would * complicate that access. */ protected transient Map _nonShared; /* /********************************************************** /* Construction, factory methods /********************************************************** */ protected Impl(Map shared) { _shared = shared; _nonShared = null; } protected Impl(Map shared, Map nonShared) { _shared = shared; _nonShared = nonShared; } public static ContextAttributes getEmpty() { return EMPTY; } /* /********************************************************** /* Per-reader/writer mutant factories /********************************************************** */ @Override public ContextAttributes withSharedAttribute(Object key, Object value) { Map m; // need to cover one special case, since EMPTY uses Immutable map: if (this == EMPTY) { m = new HashMap(8); } else { m = _copy(_shared); } m.put(key, value); return new Impl(m); } @Override public ContextAttributes withSharedAttributes(Map shared) { return new Impl(shared); } @Override public ContextAttributes withoutSharedAttribute(Object key) { // first couple of trivial optimizations if (_shared.isEmpty()) { return this; } if (_shared.containsKey(key)) { if (_shared.size() == 1) { return EMPTY; } } else { // if we didn't have it anyway, return as-is return this; } // otherwise make copy, modify Map m = _copy(_shared); m.remove(key); return new Impl(m); } /* /********************************************************** /* Per-call access /********************************************************** */ @Override public Object getAttribute(Object key) { if (_nonShared != null) { Object ob = _nonShared.get(key); if (ob != null) { if (ob == NULL_SURROGATE) { return null; } return ob; } } return _shared.get(key); } @Override public ContextAttributes withPerCallAttribute(Object key, Object value) { // First: null value may need masking if (value == null) { // need to mask nulls to ensure default values won't be showing if (_shared.containsKey(key)) { value = NULL_SURROGATE; } else { // except if non-mutable shared list has no entry, we don't care return this; } } // a special case: create non-shared instance if need be if (_nonShared == null) { return nonSharedInstance(key, value); } _nonShared.put(key, value); return this; } /* /********************************************************** /* Internal methods /********************************************************** */ /** * Overridable method that creates initial non-shared instance, * with the first explicit set value. */ protected ContextAttributes nonSharedInstance(Object key, Object value) { Map m = new HashMap(); if (value == null) { value = NULL_SURROGATE; } m.put(key, value); return new Impl(_shared, m); } private Map _copy(Map src) { return new HashMap(src); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy