TailScopedDictionary.java
/*
** Module : TailScopedDictionary.java
** Abstract : A ScopedDictionary that contains TailMap collections.
**
** Copyright (c) 2022-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- ----------------------------Description----------------------------
** 001 TW 20220617 Initial implementation.
** 002 TW 20220620 Javadoc fix.
** 003 TW 20220727 Turn EmptyTailMap remove implementations into no-op, to fix ScopedDictionary problem
** identified by CA. Refs #6356-105
** 004 CA 20230223 Avoid the overhead of calculating an empty map when the scope has no dictionary -
** internally, Node.getDictionary(false) will return null if there is no dictionary; but,
** getDictionaryAtScope() will still return an empty map, instead of null.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.util;
import java.util.*;
import java.util.function.*;
/**
* A {@link ScopedDictionary} that contains {@link TailMap} collections.
* Supported scope: context local.
* @param <K>
* Dictionary key type
* @param <V>
* Dictionary value type
*/
public class TailScopedDictionary<K, V>
extends ScopedDictionary<K, V>
{
/**
* <code>EMPTY_TAILMAP</code> is a singleton used for {@link #getDictionaryAtScope} when there is no
* dictionary at that level.
*/
@SuppressWarnings("rawtypes")
private static final TailMap EMPTY_TAILMAP = new EmptyTailMap<>();
/**
* Default constructor.
*/
public TailScopedDictionary()
{
super();
}
/**
* Returns the dictionary from the scope specified, or <code>null</code> if
* the specified scope does not exist.
*
* @param scope
* The depth (in number of scopes from the top of the stack) at
* which the value must be set. Use -1 for the global scope.
* @param create
* <code>true</code> to create a dictionary for this scope if one
* does not already exist; <code>false</code> to have an empty
* (immutable) dictionary returned if one does not already exist.
*
* @return The dictionary, if any, associated with the specified scope.
* Note that this may be an immutable, empty map if no dictionary
* exists at this scope, and <code>create</code> is set to
* <code>false</code>. If the scope itself does not exist, then
* <code>null</code> is returned.
*/
@Override
public TailMap<K, V> getDictionaryAtScope(int scope, boolean create)
{
Node<K, V> node = getNodeAt(scope);
// This cast is safe, since all instances come from our factory methods.
TailMap<K, V> dict = null;
if (node != null)
{
dict = (TailMap<K, V>) node.getDictionary(create);
return dict == null ? EMPTY_TAILMAP : dict;
}
return null;
}
/**
* Factory method for an {@link IdentityHashMap}
*
* @return a new {@link TailIdentityHashMap}
*/
@Override
protected Map<K, Boolean> factoryIdentityHashMap()
{
return new TailIdentityHashMap<>();
}
/**
* Factory method for a {@link Set} as returned by {@link ScopedDictionary#keySet}
* <p>
* Currently, the caller can't yet access {@link TailMap#flagIterating}, since a Set
* is returned. For now, we assume that it's not a realistic scenario in FWD.
* Redesign when needed (symptom would be a concurrency exception while iterating keySet).
*
* @return The Set returned by <code>Collections.newSetFromMap</code> of a new {@link TailHashMap}
*/
@Override
protected Set<K> factoryHashSetFromMap()
{
return Collections.newSetFromMap(new TailHashMap<>());
}
/**
* Factory method for an {@link IdentityHashMap} at the scope items (the {@link
* ScopedDictionary.Node} inner class)
*
* @return a new {@link TailIdentityHashMap}
*/
@Override
protected Map<K, V> factoryNodeIdentityHashMap(int size)
{
return new TailIdentityHashMap<>(size);
}
/**
* Factory method for a {@link HashMap} at the scope items (the {@link ScopedDictionary.Node}
* inner class)
*
* @return a new {@link TailHashMap}
*/
@Override
protected Map<K, V> factoryNodeHashMap(int size)
{
return new TailHashMap<>(size);
}
/**
* An immutable empty static subclass of TailHashMap.
* Attempts to modify this Map result in an <tt>UnsupportedOperationException</tt>.
*
* @param <K>
* Dictionary key type
* @param <V>
* Dictionary value type
*/
private static class EmptyTailMap<K, V>
extends TailHashMap<K, V>
{
/** Generated version UID */
private static final long serialVersionUID = -4775165838523528350L;
// Note: We can't leverage Collections.unmodifiableMap here, since we prefer to return
// a TailMap instance to all client invocations.
/**
* Attempts to <code>put</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public V put(K key, V value)
{
throw new UnsupportedOperationException();
}
/**
* This is a no-op. Attempts to <code>remove</code> will always return <tt>null</tt>.
*/
@Override
public V remove(Object key)
{
return null;
}
/**
* Attempts to <code>putAll</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public void putAll(Map<? extends K, ? extends V> m)
{
throw new UnsupportedOperationException();
}
/**
* Attempts to <code>replaceAll</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
{
throw new UnsupportedOperationException();
}
/**
* Attempts to <code>putIfAbsent</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public V putIfAbsent(K key, V value)
{
throw new UnsupportedOperationException();
}
/**
* This is a no-op. Attempts to <code>remove</code> will always return <tt>false</tt>.
*/
@Override
public boolean remove(Object key, Object value)
{
return false;
}
/**
* Attempts to <code>replace</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public boolean replace(K key, V oldValue, V newValue)
{
throw new UnsupportedOperationException();
}
/**
* Attempts to <code>replace</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public V replace(K key, V value)
{
throw new UnsupportedOperationException();
}
/**
* Attempts to <code>computeIfAbsent</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
{
throw new UnsupportedOperationException();
}
/**
* Attempts to <code>computeIfPresent</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction)
{
throw new UnsupportedOperationException();
}
/**
* Attempts to <code>compute</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
{
throw new UnsupportedOperationException();
}
/**
* Attempts to <code>merge</code> result in an <tt>UnsupportedOperationException</tt>.
*/
@Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction)
{
throw new UnsupportedOperationException();
}
}
}