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<>();
   }
}