# HandleChain.java

    /*
    ** Module   : HandleChain.java
    ** Abstract : An abstract class which provides an  implementation for the most
    **            common methods from CommonHandleChain.
    **
    ** Copyright (c) 2013-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 CS  20130207 Created initial version.
    ** 002 CS  20130215 TYPE attribute must be saved as a string, not character.
    ** 003 CA  20130221 Extracted TYPE, unknown and readOnlyError into a separate superclass.
    ** 004 CA  20130529 Implemented proper resource chaining. Hold the first/last resource as context
    **                  local info, per each chainable resource type.
    ** 005 CA  20130919 Resource cleanup must be done after it was deleted.
    ** 006 CA  20130927 FIRST-/LAST-resource maps are kept per resource type, not class (as a resource
    **                  can have multiple implementations).
    ** 007 CA  20131024 Fixed getName(), to return a new character instance.
    ** 008 SVL 20131203 Changes caused by renaming of Nameable.get/setName to Nameable.name.
    ** 009 CA  20131115 Changes related to properly instantiate dynamic resources.
    ** 010 CA  20131220 Fixed H006 - lastResource and firstResource must always use the resource type
    **                  as key.  Changed c'tors so that it is possible to avoid updating the first and
    **                  last references for a resource.
    ** 011 VIG 20140314 Added possibility to add an element as a first element of the chain.
    ** 012 CA  20140513 Added a weight for the context-local var, to ensure predetermined order during
    **                  context reset.
    ** 013 CA  20150312 When the resource gets deleted, remove the references for the NEXT-SIBLING,
    **                  PREV-SIBLING and INSTANTIATING-PROCEDURE, to help the garbage collector.
    ** 014 OM  20150303 Fixed (swap back) PREV/NEXT-SIBLING check for invalid attribute. Added support
    **                  for sorted BUFFER list (TEMP-TABLES before permanent ones).
    ** 015 CA  20150416 Fixed runtime support for FIRST/LAST-CHILD and NEXT/PREV-SIBLING for tree
    **                  widget structures.
    ** 016 CA  20150503 Remove the resource from the creator procedure (it needs to be tracked,
    **                  because dynamic resources don't get deleted when the creator proc gets
    **                  deleted, but their INSTANTIATING-PROCEDURE attr needs to be unknown).
    ** 017 CA  20151030 When a resource or widget get deleted, all triggers associated with them must
    **                  be destroyed.
    ** 018 EVL 20160224 Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
    ** 019 GES 20170831 Added setPrivateData(BDT) to work with _POLY inputs.
    ** 020 CA  20170914 Added API to allow resources to be moved to the tail or head, in the chain.
    ** 021 CA  20171109 DIALOG-BOX and FRAME are linked in the same chain.
    ** 022 ECF 20180420 Do not invoke LogicalTerminal.cleanupTriggers unnecessarily.
    ** 023 HC  20181128 Moved the relevant portion of delete() in HandleResource.
    ** 024 OM  20190511 Added name attribute validator. Javadoc update.
    ** 025 ECF 20190807 Fixed NPE regression by allowing unknown to be set as a valid value for NAME
    **                  attribute.
    ** 026 OM  20190721 The name validator is optional. Each subclass can decide if names are checked.
    ** 027 OM  20201030 Invalid attribute API support for getters/setters.
    **     OM  20201203 Fixed handling of READ/ONLY attributes.
    **     OM  20210309 Removed deprecation warnings.
    **     OM  20210930 The name of an entity can always start with underscore character.
    **     CA  20221006 PRIVATE-DATA is kept as Java type instead of BDT.  Refs #6827
    **     SVL 20230106 Improved performance of delete().
    **     CA  20230110 Cache the WorkArea at the resource, to avoid context-local lookup.
    **     CA  20230116 Avoid using handle.unwrap, handle.getReference or other BDT usage from within FWD runtime.
    ** 027 CA  20230724 Moved the 'resolveChainType' logic related to DIALOG-BOX to FrameWidget, for performance
    **                  improvement.
    ** 028 OM  20240618 On delete update the associated chain only if the object was added to it.
    ** 029 ICP 20250131 Used character constant to leverage cached instances.
    * */

    /*
    ** 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 com.goldencode.p2j.security.*;

    /**
     * This acts as a superclass which implements the most common methods of the
     * CommonHandleChain interface:
     * <table summary="">
     * <tr>
     *    <th>attribute or base method</th>
     *    <th>API</th>
     * </tr>
     * <tr>
     *    <td>NAME</td>
     *    <td>{@link #name()}</td>
     * </tr>
     * <tr>
     *    <td>PREV-SIBLING</td>
     *    <td>{@link #getPrevSibling()}</td>
     * </tr>
     * <tr>
     *    <td>NEXT-SIBLING</td>
     *    <td>{@link #getNextSibling()}</td>
     * </tr>
     * <tr>
     *    <td>PRIVATE-DATA</td>
     *    <td>{@link #getPrivateData()}, {@link #setPrivateData(character)} and
     *        {@link #setPrivateData(String)}
     *    </td>
     * </tr>
     * </table>
     * <p>
     * The abstract class also provides setters for the implemented attributes
     * which are not defined inside the interfaces to be used for initializing of
     * those attributes
     */
    public abstract class HandleChain
    extends HandleResource
    implements CommonHandleChain

    {
       /** This context's chain-related data. */
       private static final ContextLocal<WorkArea> local =
       new ContextLocal<WorkArea>()
       {
          @Override
          public WeightFactor getWeight()
          {
             return WeightFactor.LEVEL_5;
          }

          protected WorkArea initialValue()
          {
             return new WorkArea();
          }
       };

       /** Variable for holding the name attribute. */
       protected String name = null;

       /** Variable for holding the privateData attribute. */
       private String privateData = null;

       /** Variable for holding the PREV-SIBLING attribute. */
       protected HandleChain prevSibling = null;

       /** Variable for holding the NEXT-SIBLING attribute. */
       protected HandleChain nextSibling = null;

       /** Cache the context-local {@link WorkArea} instance. */
       protected final WorkArea wa = local.get();

       /**
        * Default c'tor.
        */
       public HandleChain()
       {
          this(false, true);
       }

       /**
        * Create this dynamic or static resource.
        *
        * @param    dynamic
        *           Flag indicating if this is a static or dynamic resource.
        */
       public HandleChain(boolean dynamic)
       {
          this(dynamic, true);
       }

       /**
        * Create this dynamic or static resource.
        *
        * @param    dynamic
        *           Flag indicating if this is a static or dynamic resource.
        * @param    saveFirstLast
        *           Flag indicating if the first/last resource should be updated.
        */
       public HandleChain(boolean dynamic, boolean saveFirstLast)
       {
          this(dynamic, saveFirstLast, null, false);
       }

       /**
        * Create this dynamic or static resource.
        *
        * @param    dynamic
        *           Flag indicating if this is a static or dynamic resource.
        * @param    saveFirstLast
        *           Flag indicating if the first/last resource should be updated.
        * @param    before
        *           The right neighbor of the BaseEntity been created or null if new BaseEntity
        *           will be the last in the chain. If <b>before</b> is the first element in the chain
        *           the new BaseEntity will become the new first element in the chain.
        * @param    firstAsLast
        *           The flag indicating that this new entity must be inserted as last element
        *           in the chain in the case when <b>before</b> - is the first element of the chain.
        *           When <b>before</b> is the first element of the chain, default behavior (expected
        *           from the name of this parameter is to insert new element as the first.
        *           This flag allows to change the expected behavior of <b>before</b> parameter when
        *           it needs and makes sense only if <b>before</b> is the first element.
        */
       public HandleChain(boolean     dynamic,
                          boolean     saveFirstLast,
                          HandleChain before,
                          boolean     firstAsLast)
       {
          super(dynamic);

          if (saveFirstLast)
          {
             if (before == null) // denotes to make created component as last elem. in the chain
             {
                interlink();
             }
             else
             {
                if (before.getPrevSibling() == null ||
                    before.getPrevSibling().isUnknown() && firstAsLast)
                {
                   interlink();
                }
                else
                {
                   ((HandleChain) before.getPrevSibling().getResource()).setNextSibling(this);
                   this.setPrevSibling(before.getPrevSibling());
                   before.setPrevSibling(this);
                   this.setNextSibling(before);
                }
             }
          }
       }

       /**
        * Get the first resource in the chain, for the given class.
        *
        * @param    type
        *           The resource type.
        *
        * @return   The first resource in the chain.
        */
       protected static handle firstResource(String type)
       {
          return firstResource(null, type);
       }

       /**
        * Get the first resource in the chain, for the given class.
        *
        * @param    parent
        *           The parent for which the first child is needed.  If <code>null</code>, the first
        *           resource with no parent set is returned.
        * @param    type
        *           The resource type.
        *
        * @return   The first resource in the chain.
        */
       protected static handle firstResource(HandleChain parent, String type)
       {
          HandleChain head = getHead(type);

          while (head != null)
          {
             if (head.hasParent(parent) && head.isChained())
             {
                return new handle(head);
             }

             head = head.nextSibling;
          }

          return new handle();
       }

       /**
        * Get the first unwrapped resource in the chain, for the given class.
        *
        * @param    type
        *           The resource type.
        *
        * @return   The first resource in the chain.
        */
       protected static HandleChain getHead(String type)
       {
          return local.get().firstResource.get(type);
       }

       /**
        * Set the first unwrapped resource in the chain, for the given class.
        *
        * @param    type
        *           The resource type.
        * @param    resource
        *           The resource to be set the head of the chain (the first in chain).
        *
        * @return   The first resource in the chain.
        */
       protected static HandleChain setHead(String type, HandleChain resource)
       {
          return local.get().firstResource.put(type, resource);
       }

       /**
        * Get the last resource in the chain, for the given class.
        *
        * @param    type
        *           The resource type.
        *
        * @return   The last resource in the chain.
        */
       protected static handle lastResource(String type)
       {
          return lastResource(null, type);
       }

       /**
        * Get the last resource in the chain, for the given class.
        *
        * @param    parent
        *           The parent for which the last child is needed.  If <code>null</code>, the last
        *           resource with no parent set is returned.
        * @param    type
        *           The resource type.
        *
        * @return   The last resource in the chain.
        */
       protected static handle lastResource(HandleChain parent, String type)
       {
          HandleChain tail = getTail(type);

          while (tail != null)
          {
             if (tail.hasParent(parent) && tail.isChained())
             {
                return new handle(tail);
             }

             tail = tail.prevSibling;
          }

          return new handle();
       }

       /**
        * Get the last unwrapped resource in the chain, for the given class.
        *
        * @param    type
        *           The resource type.
        *
        * @return   The last resource in the chain.
        */
       protected static HandleChain getTail(String type)
       {
          return local.get().lastResource.get(type);
       }

       /**
        * Show a specific error, as this read-only {@code attribute} was used on the right-side of an assignment.
        *
        * @param    attribute
        *           The read-only attribute.
        *
        * @deprecated Use the overloaded method with multiple arguments.<p>
        *             This API is deprecated because of two reasons: first, it looses the possible side effect of
        *             the assigned expression (consider the case when it is a function or method, but not
        *             exclusively). Secondly, some (if not all) are unknown value sensitive, meaning that, prior
        *             testing whether this is a writable attribute, P4GL checks first if the expression is
        *             unknown and if this value is supported. In this case a different error condition is raised.
        */
       @Override
       @Deprecated
       public void readOnlyError(String attribute)
       {
          handle.readOnlyError(new handle(this), attribute, handle.UNKNOWN_ARGUMENT);
       }

       /**
        * Shows a specific error, as this read-only {@code attribute} was used on the right-side of an assignment.
        * If the {@code expr} is evaluated to {@code ?} (unknown value) and this attribute does not support it,
        * a specific error is raised.
        *
        * @param    attribute
        *           The read-only attribute.
        * @param    expr
        *           The expression whose value was attempted to be assigned to READ-ONLY attribute.
        */
       @Override
       public void readOnlyError(String attribute, Object expr)
       {
          handle.readOnlyError(new handle(this), attribute, expr);
       }

       /**
        * Interlinks the newly created HandleChain in the both chains. The back and forth links are
        * created with respective elements.
        * The heads of the chains are updated: if this is the first of its kind, it becomes the first
        * element of the chain. It is always stored as the last resource in his chain.
        * Even if this kind of resource does not support NEXT or PREV sibling, both heads of the
        * chain are kept for a fast access in future maintenance.
        * <p>
        * This method is protected since it needs to be called on BUFFERs at a later moment than their
        * construction, when they are configured as dynamic.
        */
       protected void interlink()
       {
          if (!hasNextSibling() && !hasPrevSibling())
          {
             // quick out this resource is not chain-able
             return;
          }

          String type = resolveChainType();

          HandleChain last = wa.lastResource.get(type);
          if (last != null)
          {
             last.nextSibling = this;
             this.prevSibling = last;
          }
          else
          {
             // this is the first resource in the chain
             wa.firstResource.put(type, this);
          }

          // always mark it as the last resource in the chain
          wa.lastResource.put(type, this);
       }

       /**
        * Perform actual delete of an resource. At the time of this call, it is assumed the resource
        * is valid for deletion (the handle and the resource are both valid).
        * <p>
        * The method first calls {@link #resourceDelete()}, if the method returns {@code false}, the deletion
        * is aborted.
        */
       @Override
       public void delete()
       {
          // call resource-specific delete
          if (!resourceDelete())
          {
             // if the resource-specific delete was not possible, exit
             return;
          }

          super.doDelete();

          // disconnect from both directions of the list (if they are available).
          // remember that, if the resource is chain-able (at least in one direction)) we are keeping
          // both links (to next and prev element) just to ease management of the lists.
          // Only navigation for P4GL application developer is restricted by NEXT/PREV-SIBLING methods

          String type = resolveChainType();
          boolean first = false;
          boolean last = false;

          if (prevSibling != null)
          {
             prevSibling.nextSibling = nextSibling; // at least one object before of me, so I am not the first
          }
          else if (wa.firstResource.get(type) == this)
          {
             first = true; // I am the first, the head must be updated
          }
          // else not backward chained

          if (nextSibling != null)
          {
             nextSibling.prevSibling = prevSibling; // at least one object after of me, so I am not the last
          }
          else if (wa.lastResource.get(type) == this)
          {
             last = true; // I am the last
          }
          // else not forward chained

          if (first && last)
          {
             wa.firstResource.remove(type);
             wa.lastResource.remove(type);
          }
          else if (first)
          {
             wa.firstResource.put(type, this.nextSibling);
          }
          else if (last)
          {
             wa.lastResource.put(type, this.prevSibling);
          }

          this.nextSibling = null;
          this.prevSibling = null;
       }

       /**
        * Get the {@code name} attribute of handle.
        *
        * @return   See above.
        */
       public character name()
       {
          if (!hasName())
          {
             invalidAttribute("NAME", false);

             return character.UNKNOWN;
          }

          return new character(name);
       }

       /**
        * Get the value of the {@link #name} field.
        * <p>
        * <b>FOR INTERNAL USAGE ONLY!</b>
        *
        * @return   See above.
        */
       public String _name()
       {
          return name;
       }

       /**
        * Sets the {@code name} attribute of the object referred by this handle.
        *
        * @param   name
        *          The new name to be set to target object.
        */
       public void name(character name)
       {
          if (hasNameReadOnly())
          {
             readOnlyError("NAME", handle.UNKNOWN_ARGUMENT);

             return;
          }

          if (hasNameConstraints() && !validateName(name.getValue()))
          {
             ErrorManager.recordOrThrowError(3131, "NAME", name.getValue(),  type());
             // Unable to set attribute <attribute name> in widget <widget name> of type <widget-type>.
          }
          else
          {
             this.name = name.getValue();
          }
       }

       /**
        * Sets the {@code name} attribute of the object referred by this handle.
        *
        * @param   name
        *          The new name to be set to target object.
        */
       public void name(String name)
       {
          name(new character(name));
       }

       /**
        * Checks whether a string is a valid name for an object. If an issue is detected with the
        * candidate errors #257 or #274 are shown or recorded.
        *
        * @param   name
        *          The candidate to be checked.
        *
        * @return  {@code true} if the candidate is a valid name and false otherwise.
        */
       public static boolean validateName(String name)
       {
          for (int k = 0; k < name.length(); k++)
          {
             char c = name.charAt(k);
             switch (c)
             {
                case '#':
                case '$':
                case '%':
                case '&':
                case '-':
                   if (k > 0)
                   {
                      continue; // for
                   }
                   break;

                case '_': // this character is valid in any position of the name string
                   continue;

                default:
                   if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
                   {
                      continue; // for
                   }
                   if (c >= '0' && c <= '9' && k > 0)
                   {
                      continue; // for
                   }
                   break;
             }

             // now, that's invalid:
             if (k == 0)
             {
                ErrorManager.recordOrShowError(257, name);
                // ** The first character of <name> must be alphabetic.
             }
             else
             {
                ErrorManager.recordOrShowError(274, Character.toString(c), name);
                // ** The character <character> is not permitted in name <name>.
             }
             return false;
          }

          return true; // all chars are fine
       }

       /**
        * Get the <code>next-sibling</code> attribute of this handle.
        *
        * @return   See above.
        */
       public handle getNextSibling()
       {
          return getNextSibling(null);
       }

       /**
        * Get the <code>next-sibling</code> attribute of this handle.
        *
        * @param    parent
        *           The parent for which the first child is needed.  If <code>null</code>, the first
        *           resource with no parent set is returned.
        *
        * @return   See above.
        */
       public handle getNextSibling(HandleChain parent)
       {
          if (!hasNextSibling())
          {
             invalidAttribute("NEXT-SIBLING", false);

             return new handle();
          }

          // get the next chained sibling (with the specified parent)
          HandleChain next = nextSibling;
          while (next != null)
          {
             if (next.hasParent(parent) && next.isChained())
             {
                return new handle(next);
             }

             next = next.nextSibling;
          }

          return new handle();
       }

       /**
        * Sets the NEXT-SIBLING attribute of this handle. This method is outside
        * interfaces and only meant for initializing this within the implementation.
        * If the NEXT-SIBLING attribute is not supported, this method does nothing.
        *
        * @param    nextSibling
        *           The next sibling handle in the chain.
        */
       public void setNextSibling(handle nextSibling)
       {
          if (!hasNextSibling())
          {
             return;
          }

          setNextSibling((HandleChain) nextSibling.getResource());
       }

       /**
        * Sets the NEXT-SIBLING attribute of this handle. This method is outside
        * interfaces and only meant for initializing this within the implementation.
        * If the NEXT-SIBLING attribute is not supported, this method does nothing.
        *
        * @param    nextSibling
        *           The next sibling resource in the chain.
        */
       public void setNextSibling(HandleChain nextSibling)
       {
          if (!hasNextSibling())
          {
             return;
          }

          this.nextSibling = nextSibling;
       }

       /**
        * Get the <code>prev-sibling</code> attribute of this handle.
        *
        * @return   See above.
        */
       public handle getPrevSibling()
       {
          return getPrevSibling(null);
       }

       /**
        * Get the <code>prev-sibling</code> attribute of this handle.
        *
        * @param    parent
        *           The parent for which the first child is needed.  If <code>null</code>, the first
        *           resource with no parent set is returned.
        *
        * @return   See above.
        */
       public handle getPrevSibling(HandleChain parent)
       {
          if (!hasPrevSibling())
          {
             invalidAttribute("PREV-SIBLING", false);

             return new handle();
          }

          // get the prev chained sibling (with the specified parent)
          HandleChain prev = prevSibling;
          while (prev != null)
          {
             if (prev.hasParent(parent) && prev.isChained())
             {
                return new handle(prev);
             }

             prev = prev.prevSibling;
          }

          return new handle();
       }

       /**
        * Sets the PREV-SIBLING attribute of this handle. This method is outside
        * interfaces and only meant for initializing this within the implementation.
        * If the PREV-SIBLING attribute is not supported, this method does nothing.
        *
        * @param    prevSibling
        *           The previous sibling handle in the chain.
        */
       public void setPrevSibling(handle prevSibling)
       {
          if (!hasPrevSibling())
          {
             return;
          }

          setPrevSibling((HandleChain) prevSibling.getResource());
       }

       /**
        * Sets the PREV-SIBLING attribute of this handle. This method is outside interfaces and only
        * meant for initializing this within the implementation.
        * If the PREV-SIBLING attribute is not supported, this method does nothing.
        *
        * @param    prevSibling
        *           The previous sibling resource in the chain.
        */
       public void setPrevSibling(HandleChain prevSibling)
       {
          if (!hasPrevSibling())
          {
             return;
          }

          this.prevSibling = prevSibling;
       }

       /**
        * Get the <code>private-data</code> attribute of this handle.
        *
        * @return   See above.
        */
       public character getPrivateData()
       {
          if (!hasPrivateData())
          {
             invalidAttribute("PRIVATE-DATA", false);

             return new character();
          }

          return new character(privateData);
       }

       /**
        * Set the <code>private-data</code> attribute of this handle.
        *
        * @param    data
        *           The new private-data value.
        */
       public void setPrivateData(character data)
       {
          if (!hasPrivateData())
          {
             readOnlyError("PRIVATE-DATA", handle.UNKNOWN_ARGUMENT);

             return;
          }

          this.privateData = data.toJavaType();
       }

       /**
        * Set the <code>private-data</code> attribute of this handle.
        *
        * @param    data
        *           The new private-data value.
        */
       public void setPrivateData(String data)
       {
          setPrivateData(new character(data));
       }

       /**
        * Set the <code>private-data</code> attribute of this handle.
        *
        * @param    data
        *           The new private-data value.
        */
       public void setPrivateData(BaseDataType data)
       {
          setPrivateData(new character(data));
       }

       /**
        * Move the this resource to the head or tail of the chain
        *
        * @param   head
        *          If <code>true</code>, move to head otherwise tail.
        */
       public void moveInChain(boolean head)
       {
          if (!hasNextSibling() && !hasPrevSibling())
          {
             // quick out this resource is not chain-able
             return;
          }

          // the chaining is from top-to-bottom (not the same as is kept on client side, where
          // z-order starts with bottommost widget on 0 index

          String type = resolveChainType();
          HandleChain sibling = (head ? getHead(type) : getTail(type));

          if (sibling == null || this == sibling)
          {
             // nothing is changed, so quick exit
             return;
          }

          // disconnect from both directions of the list (if they are available).
          // remember that, if the resource is chain-able (at least in one direction)) we are keeping
          // both links (to next and prev element) just to ease management of the lists.
          // Only navigation for P4GL application developer is restricted by NEXT/PREV-SIBLING methods
          if (prevSibling != null)
          {
             prevSibling.nextSibling = nextSibling;
          }
          if (nextSibling != null)
          {
             nextSibling.prevSibling = prevSibling;
          }

          HandleChain first = wa.firstResource.get(type);
          HandleChain last = wa.lastResource.get(type);

          if (this == first)
          {
             wa.firstResource.put(type, this.nextSibling);
          }
          if (this == last)
          {
             wa.lastResource.put(type, this.prevSibling);
          }

          if (head)
          {
             // old top has a new prev sibling
             sibling.prevSibling = this;

             // move to top
             this.nextSibling = sibling;
             this.prevSibling = null;

             wa.firstResource.put(type, this);
          }
          else
          {
             // old bottom has a new next sibling
             sibling.nextSibling = this;

             // move to bottom
             this.prevSibling = sibling;
             this.nextSibling = null;

             wa.lastResource.put(type, this);
          }
       }

       /**
        * Check if this resource supports the NEXT-SIBLING attribute.
        *
        * @return   <code>true</code> if the resource has the attribute.
        */
       protected boolean hasNextSibling()
       {
          return true;
       }

       /**
        * Check if this resource supports the PREV-SIBLING attribute.
        *
        * @return   <code>true</code> if the resource has the attribute.
        */
       protected boolean hasPrevSibling()
       {
          return true;
       }

       /**
        * Check if this resource supports the NAME attribute in read-only mode.
        *
        * @return   <code>true</code> if the resource has the attribute in r/o mode.
        */
       protected boolean hasNameReadOnly()
       {
          return true;
       }

       /**
        * Check if this resource supports the NAME attribute.
        *
        * @return   <code>true</code> if the resource has the attribute.
        */
       protected boolean hasName()
       {
          return true;
       }

       /**
        * Check if this resource supports the PRIVATE-DATA attribute.
        *
        * @return   <code>true</code> if the resource has the attribute.
        */
       protected boolean hasPrivateData()
       {
          return true;
       }

       /**
        * Check if this resource can be reported as chained.
        *
        * @return   Always <code>true</code>. Subclasses can override this as needed.
        */
       protected boolean isChained()
       {
          return true;
       }

       /**
        * Check if this resource has as parent the specified resource.
        *
        * @param    parent
        *           The parent for which the first child is needed.  If <code>null</code>, the first
        *           resource with no parent set is returned.
        *
        * @return   Always <code>true</code>. Subclasses can override this as needed.
        */
       protected boolean hasParent(HandleChain parent)
       {
          return true;
       }

       /**
        * Allows each resource to declare whether it has some naming constraints.
        * <p>
        * NOTE: The current name validation checks if the name candidate consists of alphabetic
        *       characters, numerals, and '#', '$', '%', '&amp;', '-', and '_' characters. Also the
        *       first character must be an alphabetic character ('a' to 'z' and 'A' to 'Z').
        *       If we find that there are resources with other constraints, new different validators
        *       will have to be implemented.
        *
        * @return  The base definition of this method always returns {@code false}, each implementing
        *          dynamic resource can return different value depending whether its name should be
        *          validated before assignment.
        *
        * @see  #validateName
        */
       protected boolean hasNameConstraints()
       {
          return false;
       }

       /**
        * Resolve the resource's type, for internal management purposes.
        *
        * @return   the type.
        */
       protected String resolveChainType()
       {
          return type();
       }

       /**
        * Stores global data relating to the state of the current context.
        */
       private static class WorkArea
       {
          /** The first connected resource in the chain, per resource type. */
          private final Map<String, HandleChain> firstResource = new HashMap<>();

          /** The last connected resource in the chain, per resource type. */
          private final Map<String, HandleChain> lastResource = new HashMap<>();
       }
    }