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 '#', '$', '%', '&', '-', 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<>();
}
}