Package com.isomorphic.datasource

# Class DataSource

java.lang.Object
com.isomorphic.base.Base
com.isomorphic.datasource.DataSource

All Implemented Interfaces:
  `com.isomorphic.base.IAutoConfigurable`, `com.isomorphic.datasource.Committable`, `com.isomorphic.datasource.FreeResourcesHandler`, `com.isomorphic.datasource.IType`, `IToJSON`, `Serializable`

Direct Known Subclasses:
  `BasicDataSource`

public class DataSource extends com.isomorphic.base.Base implements com.isomorphic.datasource.Committable, com.isomorphic.datasource.IType, IToJSON, com.isomorphic.datasource.FreeResourcesHandler, Serializable
A DataSource is metadata that provides a high-level, implementation independent description of a set of permanently stored objects that will be manipulated within a SmartClient application.

The server-side DataSource object supports declarative validation, XPath-based mapping of DataSource fields to Java Objects and XML structures, and dynamic instantiation from XML. See the client reference for the client-side capabilities of DataSource objects.

See Also:
  - Serialized Form

- ## Field Summary

  Fields
  Modifier and Type
  Field
  Description
  `static final String`
  `OP_ADD`
  Constant specifying the add operation.
  `static final String`
  `OP_CLIENT_EXPORT`
  Constant specifying a clientExport operation.
  `static final String`
  `OP_CUSTOM`
  Constant specifying a custom operation.
  `static final String`
  `OP_DOWNLOAD_FILE`
  Constant specifying the downloadFile operation.
  See the Binary Fields overview in the client-side reference for further details.
  `static final String`
  `OP_FETCH`
  Constant specifying the fetch operation.
  `static final String`
  `OP_GET_FILE`
  Constant specifying a getFile operation.
  `static final String`
  `OP_GET_FILE_VERSION`
  Constant specifying a getFileVersion operation.
  `static final String`
  `OP_HAS_FILE`
  Constant specifying a hasFile operation.
  `static final String`
  `OP_HAS_FILE_VERSION`
  Constant specifying a hasFileVersion operation.
  `static final String`
  `OP_LIST_FILE_VERSIONS`
  Constant specifying a listFileVersions operation.
  `static final String`
  `OP_LIST_FILES`
  Constant specifying a listFiles operation.
  `static final String`
  `OP_LOAD_SCHEMA`
  Constant specifying the loadSchema operation.
  This is an operation used by the client-side framework to load the field definitions from a DataSource, and it is not customizable.
  `static final String`
  `OP_REMOVE`
  Constant specifying the remove operation.
  `static final String`
  `OP_REMOVE_FILE`
  Constant specifying a removeFile operation.
  `static final String`
  `OP_REMOVE_FILE_VERSION`
  Constant specifying a removeFileVersion operation.
  `static final String`
  `OP_RENAME_FILE`
  Constant specifying a renameFile operation.
  `static final String`
  `OP_SAVE_FILE`
  Constant specifying a saveFile operation.
  `static final String`
  `OP_UNIQUE_NAME`
  Constant specifying a uniqueName operation.
  `static final String`
  `OP_UPDATE`
  Constant specifying the update operation.
  `static final String`
  `OP_VALIDATE`
  Constant specifying the validate operation.
  `static final String`
  `OP_VIEW_FILE`
  Constant specifying the viewFile operation.
  See the Binary Fields overview in the client-side reference for further details.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `long`
  `add(Object values)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "add", calling setValues() on it, and then calling its execute() method.
  `long`
  `add(Object values, RPCManager rpc)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "add", calling setValues() and setRPCManager() on it, and then calling its execute() method.
  `static void`
  `addDynamicDSGenerator(DynamicDSGenerator ddsg)`
  Registers the parameter DynamicDSGenerator with the DataSource system by placing it at the top of a stack of DynamicDSGenerators.
  `static void`
  `addDynamicDSGenerator(DynamicDSGenerator ddsg, String prefix)`
  Registers the parameter DynamicDSGenerator with the DataSource system.
  `static void`
  `addDynamicDSGenerator(DynamicDSGenerator ddsg, Pattern regex)`
  Registers the parameter DynamicDSGenerator with the DataSource system.
  `static void`
  `clearDynamicDSGenerators()`
  Removes all user-added DynamicDSGenerators from the system
  `static AdvancedCriteria`
  `convertRelativeDates(AdvancedCriteria advancedCriteria)`
  Converts all relative dates in a critierion to absolute dates relative to the time when this method is called.
  `static AdvancedCriteria`
  `convertRelativeDates(AdvancedCriteria advancedCriteria, Date baseDate)`
  Converts all relative dates in a critierion to absolute dates relative to the time when this method is called.
  `static Criterion`
  `convertRelativeDates(Criterion criterion)`
  Converts all relative dates in a critierion to absolute dates relative to the time when this method is called.
  `static Criterion`
  `convertRelativeDates(Criterion criterion, Date baseDate, DataSource ds)`
  Converts all relative dates in a critierion to absolute dates relative to the time provided as base date.
  `static Criterion`
  `convertRelativeDates(Criterion criterion, Date baseDate, DataSource ds, boolean useEmbeddedTZ)`
  Converts all relative dates in a critierion to absolute dates relative to the time provided as base date.
  `static Object`
  `convertRelativeDates(Map criteria, DSRequest request)`
  Same as `convertRelativeDates(com.isomorphic.criteria.Criterion)`, but for the simple criteria.
  `DSResponse`
  `execute(DSRequest req)`
  This method carries out the actual processing of a DataSource request.
  `DSResponse`
  `executeAdd(DSRequest req)`
  This method is called by DataSource.execute() for "add" operations.
  `DSResponse`
  `executeClientExport(DSRequest req)`
  This method is called by DataSource.execute() for "clientExport" operations.
  `DSResponse`
  `executeCustom(DSRequest req)`
  This method is called by DataSource.execute() for "custom" operations.
  `DSResponse`
  `executeFetch(DSRequest req)`
  This method is called by DataSource.execute() for "fetch" operations.
  `DSResponse`
  `executeFileSource(DSRequest req)`
  This method is called by DataSource.execute() for "fileSource" operations.
  `DSResponse`
  `executeRemove(DSRequest req)`
  This method is called by DataSource.execute() for "remove" operations.
  `DSResponse`
  `executeUpdate(DSRequest req)`
  This method is called by DataSource.execute() for "update" operations.
  `List`
  `fetch(Object criteria)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "fetch", calling setCriteria() on it, and then calling its execute() method.
  `List`
  `fetch(String key, Object value)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "fetch", calling setCriteria() on it, and then calling its execute() method.
  `Map<String,Object>`
  `fetchById(Object id)`
  Same as `fetchById(Object, RPCManager)`, without the optional RPCManager parameter
  `Map<String,Object>`
  `fetchById(Object id, RPCManager rpc)`
  Same as `fetchById(Object, RPCManager, List)`, without the optional outputs parameter
  `Map<String,Object>`
  `fetchById(Object id, RPCManager rpc, List<String> outputs)`
  Returns the record whose primary key matches the supplied id, or null if no such record exists.
  `Map`
  `fetchSingle(Object criteria)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "fetch", calling setCriteria() on it, calling its execute() method, then obtaining just the first item of the results.
  `Map`
  `fetchSingle(String key, Object value)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "fetch", calling setCriteria() on it, calling its execute() method, then obtaining just the first item of the results.
  `List`
  `filter(Object criteria)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "filter", calling setCriteria() on it, and then calling its execute() method.
  `List`
  `filter(String key, Object value)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "filter", calling setCriteria() on it, and then calling its execute() method.
  `static DataSource`
  `fromXML(Reader reader)`
  Instantiates a DataSource from XML using a Reader
  `static DataSource`
  `fromXML(Reader reader, DSRequest dsRequest)`
  Instantiates a DataSource from XML using a Reader, with optional context DSRequest
  `static DataSource`
  `fromXML(String xml)`
  Instantiates a DataSource from a String of XML
  `static DataSource`
  `fromXML(String xml, DSRequest dsRequest)`
  Instantiates a DataSource from a String of XML, with optional context DSRequest
  `static DataSource`
  `fromXML(Document doc)`
  Instantiates a DataSource from an XML Document - the documentElement of this document is expected to contain the DataSource definition.
  `static DataSource`
  `fromXML(Document doc, DSRequest dsRequest)`
  Instantiates a DataSource from an XML Document, with optional context DSRequest - the documentElement of the XML document is expected to contain the DataSource definition.
  `static DataSource`
  `fromXML(Element elem)`
  Instantiates a DataSource from an XML Element
  `static DataSource`
  `fromXML(Element elem, DSRequest dsRequest)`
  Instantiates a DataSource from an XML Element, with optional context DSRequest
  `List<DSField>`
  `get1ManyRelationFields()`
  Returns a List of all the `one-to-many` Relation Fields on this DataSource
  `String`
  `getAuditChangedFieldsFieldName()`
  Get the field name for the audit "changed fields" field configured for this `DataSource`.
  `String`
  `getAuditRevisionFieldName()`
  Get the field name for the audit revision field configured for this `DataSource`.
  `String`
  `getAuditTimestampFieldName()`
  Get the field name for the audit timestamp field configured for this `DataSource`.
  `String`
  `getAuditTypeFieldName()`
  Get the field name for the audit type field configured for this `DataSource`.
  `String`
  `getAuditUserFieldName()`
  Get the field name for the audit user field configured for this `DataSource`.
  `static DynamicDSGenerator`
  `getDefaultDynamicDSGenerator()`
  Returns the default DynamicDSGenerator, or null if there is none
  `List<String>`
  `getDirectFields()`
  Returns the "direct" list of field names, which means roughly the fields that are likely to exist in the actual underlying SQL table (or other source of data depending on the DataSource implementation) that this DataSource is bound to, as opposed to fields derived via joins and other kinds of calculations.
  `static Map`
  `getDynamicDSGenerators()`
  Returns the non-default DynamicDSGenerators (ie, those that were registered against a startsWith string or a regular expression)
  `String`
  `getEnumConstantProperty()`
  Returns the name of the property this DataSource uses for constant name when translating enumerated types.
  `String`
  `getEnumOrdinalProperty()`
  Returns the name of the property this DataSource uses for ordinal number when translating enumerated types.
  `String`
  `getEnumTranslateStrategy()`
  Returns the strategy this DataSource uses for translating Java enumerated types (enums).
  `DSField`
  `getField(String fieldName)`
  Returns the field definition for the specified field.
  `List<String>`
  `getFieldNames()`
  Returns the list of field names in the DataSource as a List of strings.
  `Reader`
  `getFile(DSFileSpec fileSpec)`
  Gets the contents of a file stored in this DataSource.
  `InputStream`
  `getFileAsInputStream(DSFileSpec fileSpec)`
  Gets the contents of a file stored in this DataSource
  `String`
  `getFileAsString(DSFileSpec fileSpec)`
  Gets the contents of a file stored in this DataSource
  `String`
  `getFileContentsField()`
  Get the field name for the fileContents field configured for this DataSource for fileSource operations.
  `String`
  `getFileFormatField()`
  Get the field name for the fileFormat field configured for this DataSource for fileSource operations.
  `String`
  `getFileNameField()`
  Get the field name for the fileName field configured for this DataSource for fileSource operations.
  `String`
  `getFileTypeField()`
  Get the field name for the fileType field configured for this DataSource for fileSource operations.
  `String`
  `getID()`
  Returns the "ID" property of the dataSource, exactly the same as `getName()`
  `String`
  `getJoinDSName(DSField field)`
  If the parameter is a `many-to-many` Relation Field on this DataSource, returns the name of the join DataSource - ie the third dataSource "in the middle" of the relationship
  `String`
  `getJoinDSName(String fieldName)`
  If the parameter is the name of a `many-to-many` Relation Field on this DataSource, returns the name of the join DataSource - ie the third dataSource "in the middle" of the relationship
  `List`
  `getListProperties(List list)`
  Takes a List of beans/Elements/Maps and for all fields on this DataSource that specify a valueXPath, applies the xpath to each object in the List using the Jakarta JXPath library.
  `List`
  `getListProperties(List list, boolean dropExtraFields, boolean dropIgnoredFields)`
  Takes a List of beans/Elements/Maps and for all fields on this DataSource that specify a valueXPath, applies the xpath to each object in the List using the Jakarta JXPath library.
  `List`
  `getListProperty(String key)`
  Returns the value of the specified attribute as a List.
  `List<DSField>`
  `getManyManyRelationFields()`
  Returns a List of all the `many-to-many` Relation Fields on this DataSource
  `com.isomorphic.collections.DataTypeMap`
  `getMapProperty(String key)`
  Returns the value of the specified attribute as a Map.
  `String`
  `getName()`
  Returns the "ID" property of the dataSource
  `List<String>`
  `getNonIncludedFields()`
  Returns the list of "non included" field names, which means all fields except the related ones fetched from other DataSources.
  `Object`
  `getObjectProperty(String key)`
  Returns the value of the specified element(s) as an Object.
  `static Object`
  `getOperationProperty(DSRequest request, String propertyName, Object defaultValue)`
  Returns the value of the specified operationBinding element as an Object.
  `String`
  `getPrimaryKey()`
  Returns the name of the primary key field.
  `Map`
  `getProperties(Object obj)`
  Takes a bean/Element/Map and for all fields on this DataSource that specify a valueXPath, applies the xpath to the object using the Jakarta JXPath library.
  `Map`
  `getProperties(Object obj, boolean dropExtraFields, boolean dropIgnoredFields)`
  Takes a bean/Element/Map and for all fields on this DataSource that specify a valueXPath, applies the xpath to the object using the Jakarta JXPath library.
  `Map`
  `getProperties(Object obj, Collection propsToKeep)`
  Takes a bean/Element/Map and for all fields on this DataSource that specify a valueXPath, applies the xpath to the object using the Jakarta JXPath library.
  `String`
  `getProperty(String key)`
  Returns a property from the DataSource config.
  `Class`
  `getPropertyJavaClass(String propertyName, DSField field, Object value)`
  Override point to allow subclasses to dynamically determine the Java class to use for a given property.
  `String`
  `getRecordXPath()`
  For an XML DataSource, returns the XPath expression used to retrieve the set of XML elements that constitute the records of this DataSource.
  `Map`
  `getRelatedDisplayRecord(String fieldName, Object displayValue)`
  Deprecated.
  `Map`
  `getRelatedDisplayRecord(String fieldName, Object displayValue, DSRequest dsRequest)`
  Returns the "related display record" for a given field.
  `String`
  `getRelatedDSName(DSField field)`
  If the parameter is a `one-to-many` or `many-to-many` Relation Field, returns the name of the related DataSource
  `String`
  `getRelatedDSName(String fieldName)`
  If the parameter is the name of a `one-to-many` or `many-to-many` Relation Field, returns the name of the related DataSource
  `String`
  `getRelatedTableAlias()`
  When `DSField.otherFKs` is involved this property is used to construct related table alias in generated SQL.
  `List<DSField>`
  `getRelationFields()`
  Returns a List of all the Relation Fields on this DataSource, both `one-to-many` and `many-to-many`
  `String`
  `getTableName()`
  Returns the tableName property of the dataSource
  `Object`
  `getTransactionObject(DSRequest req)`
  Returns an object that can be used to manage transactions for this DataSource and other DataSources of the same type and provider.
  `static Object`
  `getTransactionObject(DSRequest req, String transactionObjectKey, String shortTransactionObjectKey)`
  Helper method retrieves connection/transaction object for specified type of data source implementation or `null`.
  `boolean`
  `has1ManyRelationFields()`
  Returns true if this DataSource has at least one `one-to-many` Relation Field
  `boolean`
  `hasFile(DSFileSpec fileSpec)`
  Indicates whether a file exists in this DataSource.
  `boolean`
  `hasManyManyRelationFields()`
  Returns true if this DataSource has at least one `many-to-many` Relation Field
  `boolean`
  `hasRecord(String columnName, Object value)`
  Returns true if the dataSource contains at least one record where the columnName matches the value.
  `boolean`
  `hasRecord(Map criteria)`
  Returns true if the dataSource contains at least one record that matches the supplied criteria
  `boolean`
  `hasRelationFields()`
  Returns true if this DataSource has at least one `one-to-many` or `many-to-many` Relation Field
  `protected void`
  `initialized()`
  This method is called after the DataSource has been initialized allowing further custom initialization.
  `boolean`
  `is1ManyRelationField(String fieldName)`
  Returns true if the parameter is the name of a `one-to-many Relation Field` on this DataSource
  `boolean`
  `isExpensive(DSField field)`
  Returns whether a field is considered expensive to fetch, based on per-field properties only (no request context).
  `boolean`
  `isExpensive(DSField field, DSRequest request)`
  Returns whether a field is considered expensive to fetch in the context of a specific DSRequest.
  `boolean`
  `isManyManyRelationField(String fieldName)`
  Returns true if the parameter is the name of a `many-to-many Relation Field` on this DataSource
  `static boolean`
  `isModificationOperation(String operationType)`
  Convenience method that returns true for operations that modify data and false for those that do not.
  `boolean`
  `isRelationField(String fieldName)`
  Returns true if the parameter is the name of a field on this DataSource that is either a `one-to-many` or `many-to-many` Relation Field
  `boolean`
  `isServerOnly()`
  Returns true if this datasource has been declared with an attribute of "serverOnly=true".
  `List<Map<String,Object>>`
  `listFiles(Object criteria)`
  Get a list of files from the DataSource.
  `List<Map<String,Object>>`
  `listFiles(Object criteria, DSRequest context)`
  Get a list of files from the DataSource.
  `List<Map<String,Object>>`
  `listFiles(String type, String format)`
  Get a list of files from the DataSource.
  `List<Map<String,Object>>`
  `listFiles(String type, String format, DSRequest context)`
  Get a list of files from the DataSource.
  `List<Map<String,Object>>`
  `listFiles(String type, String format, String tenantId)`
  Get a list of files from the DataSource.
  `List<Map<String,Object>>`
  `listFiles(String type, String format, String tenantId, DSRequest context)`
  Get a list of files from the DataSource.
  `long`
  `remove(Object criteria)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "remove", calling setCriteria() on it, and then calling its execute() method.
  `static DynamicDSGenerator`
  `removeDynamicDSGenerator()`
  Unregisters the current default DynamicDSGenerator (ie, the one at the top of the stack).
  `static DynamicDSGenerator`
  `removeDynamicDSGenerator(String prefix)`
  Removes the DynamicDSGenerator registered against the parameter prefix String
  `static DynamicDSGenerator`
  `removeDynamicDSGenerator(Pattern regex)`
  Removes the DynamicDSGenerator registered against the parameter regexp Pattern
  `List<Map<String,Object>>`
  `removeFile(DSFileSpec fileSpec)`
  Remove a file stored in this DataSource.
  `List<Map<String,Object>>`
  `renameFile(DSFileSpec oldSpec, DSFileSpec newSpec)`
  Rename a file stored in this DataSource.
  `Map<String,Object>`
  `saveFile(DSFileSpec fileSpec, Object contents)`
  Save a file to the DataSource.
  `void`
  `setEnumConstantProperty(String newValue)`
  Sets the name of the property this DataSource should use for constant name when translating enumerated types.
  `void`
  `setEnumOrdinalProperty(String newValue)`
  Sets the name of the property this DataSource should use for ordinal number when translating enumerated types.
  `void`
  `setEnumTranslateStrategy(String newValue)`
  Sets the strategy this DataSource uses to translate enumerated types (objects of type enum).
  `void`
  `setOmitNullMapValuesInResponse(boolean omitNullMapValuesInResponse)`
  Configure whether bindings with null values are dropped when sending a `DSResponse` to the browser.
  `Object`
  `setProperties(Map properties, Object target)`
  Applies all properties in a Map to a target bean by calling setter methods according to the Java Beans naming convention ("name" -> setName()), including recursive traversal of sub-beans and JXPath-based setting of properties in nested beans.
  `Object`
  `transformImportValue(String fieldName, Object rawValue, String defaultStrategy)`
  Deprecated.
  `Object`
  `transformImportValue(String fieldName, Object rawValue, String defaultStrategy, DSRequest dsRequest)`
  Transforms the passed-in rawValue and returns the result of the transformation.
  `void`
  `transformMultipleFields(DSRequest req)`
  Transforms the values for fields declared multiple:true in the incoming DSRequest.
  `void`
  `transformMultipleFields(DSResponse res, DSRequest req)`
  Transforms the values for fields declared multiple:true in a DSResponse.
  `void`
  `transformResponse(List untransformed, DSRequest ctxReq, DSResponse ctxResp)`
  Override point to allow your DataSource implementation to perform arbitrary transformations of the response data.
  `long`
  `update(Object criteria, Object values)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "update", calling setValues() and setCriteria() on it, and then calling its execute() method.
  `long`
  `update(Object criteria, Object values, RPCManager rpc)`
  This convenience method is the equivalent of creating a DSRequest with an operationType of "update", calling setValues(), setCriteria() and setRPCManager() on it, and then calling its execute() method.
  `ErrorReport`
  `validate(Map data, boolean reportMissingRequiredFields)`
  Takes a Map of fieldName -> value (data argument) and validates the data using the validators specified on this dataSource.
  `ErrorReport`
  `validate(Map data, boolean reportMissingRequiredFields, ValidationContext context)`
  Takes a Map of fieldName -> value (data argument) and optional ValidationContext and validates the data using the validators specified on this dataSource.
  `ErrorReport`
  `validate(Map data, ErrorReport errors)`
  Deprecated.
  Please use `validateRecord(Map, ErrorReport, ValidationContext)` instead.
  `ErrorReport`
  `validateRecord(Map data, ErrorReport errors, ValidationContext context)`
  Overriding point to add custom validation, that could not be covered using standard per-field validation.

- ## Field Details

  - ### OP_VIEW_FILE

    public static final String OP_VIEW_FILE
    Constant specifying the viewFile operation.
    See the Binary Fields overview in the client-side reference for further details.
    See Also:
      - Constant Field Values

  - ### OP_DOWNLOAD_FILE

    public static final String OP_DOWNLOAD_FILE
    Constant specifying the downloadFile operation.
    See the Binary Fields overview in the client-side reference for further details.
    See Also:
      - Constant Field Values

  - ### OP_LOAD_SCHEMA

    public static final String OP_LOAD_SCHEMA
    Constant specifying the loadSchema operation.
    This is an operation used by the client-side framework to load the field definitions from a DataSource, and it is not customizable.
    See Also:
      - Constant Field Values

  - ### OP_FETCH

    public static final String OP_FETCH
    Constant specifying the fetch operation. The fetch operation typically provides a set of criteria and expects a set of records as a response.
    See Also:
      - Constant Field Values

  - ### OP_ADD

    public static final String OP_ADD
    Constant specifying the add operation. The add operation is a request to add a set of records to a whatever backing store is used as the datasource.
    See Also:
      - Constant Field Values

  - ### OP_REMOVE

    public static final String OP_REMOVE
    Constant specifying the remove operation. The remove operation is a request to remove a set of records from whatever backing store is used as the datasource.
    See Also:
      - Constant Field Values

  - ### OP_UPDATE

    public static final String OP_UPDATE
    Constant specifying the update operation. The update operation typically provides a set of criteria and new values for certain fields and expects that records matching the provided criteria will be updated with the new values.
    See Also:
      - Constant Field Values

  - ### OP_CUSTOM

    public static final String OP_CUSTOM
    Constant specifying a custom operation. SmartClient components do not send operations of this type. It is reserved for programmatic use.
    See Also:
      - Constant Field Values

  - ### OP_VALIDATE

    public static final String OP_VALIDATE
    Constant specifying the validate operation.
    See Also:
      - Constant Field Values

  - ### OP_CLIENT_EXPORT

    public static final String OP_CLIENT_EXPORT
    Constant specifying a clientExport operation. DataBoundComponents send operations of this type when they are bound to a DataSource with a "clientExport" OperationBinding that is named in the call to exportClientData(). These operations can also be sent programmatically, by calling exportClientData() directly on the DataSource, naming the OperationBinding as described above.
    See Also:
      - Constant Field Values

  - ### OP_GET_FILE

    public static final String OP_GET_FILE
    Constant specifying a getFile operation.
    See Also:
      - Constant Field Values

  - ### OP_HAS_FILE

    public static final String OP_HAS_FILE
    Constant specifying a hasFile operation.
    See Also:
      - Constant Field Values

  - ### OP_LIST_FILES

    public static final String OP_LIST_FILES
    Constant specifying a listFiles operation.
    See Also:
      - Constant Field Values

  - ### OP_SAVE_FILE

    public static final String OP_SAVE_FILE
    Constant specifying a saveFile operation.
    See Also:
      - Constant Field Values

  - ### OP_RENAME_FILE

    public static final String OP_RENAME_FILE
    Constant specifying a renameFile operation.
    See Also:
      - Constant Field Values

  - ### OP_REMOVE_FILE

    public static final String OP_REMOVE_FILE
    Constant specifying a removeFile operation.
    See Also:
      - Constant Field Values

  - ### OP_LIST_FILE_VERSIONS

    public static final String OP_LIST_FILE_VERSIONS
    Constant specifying a listFileVersions operation.
    See Also:
      - Constant Field Values

  - ### OP_GET_FILE_VERSION

    public static final String OP_GET_FILE_VERSION
    Constant specifying a getFileVersion operation.
    See Also:
      - Constant Field Values

  - ### OP_HAS_FILE_VERSION

    public static final String OP_HAS_FILE_VERSION
    Constant specifying a hasFileVersion operation.
    See Also:
      - Constant Field Values

  - ### OP_REMOVE_FILE_VERSION

    public static final String OP_REMOVE_FILE_VERSION
    Constant specifying a removeFileVersion operation.
    See Also:
      - Constant Field Values

  - ### OP_UNIQUE_NAME

    public static final String OP_UNIQUE_NAME
    Constant specifying a uniqueName operation.
    See Also:
      - Constant Field Values

- ## Method Details

  - ### setOmitNullMapValuesInResponse

    public void setOmitNullMapValuesInResponse(boolean omitNullMapValuesInResponse)
    Configure whether bindings with null values are dropped when sending a `DSResponse` to the browser. To send such bindings, both this property and the active `DSRequest` property by the same name must be set `false`. (If no setting is specified in the DSRequest, it defaults to `RPCManager.omitNullMapValuesInResponse`.)

    Rules:

    - SQL nulls never make it to dsResponse.data
    - JPA, Hibernate and other beans that return null for accessors will cause nulls to appear in dsResponse.data
    - `JPADataSource` and `HibernateDataSource` automatically set `omitNullValuesInResponse` since nulls are not usually needed client-side
    - Any custom DataSource or dsResponses provided by DMI will deliver nulls to client by default, whether the provided data is Java Beans, List of Maps, or another format. You should consider setting `omitNullMapValuesInResponse` to reduce data if nulls are common and your client-side logic doesn't depend on having explicit nulls.
    Parameters:
      `omitNullMapValuesInResponse` - whether to drop bindings with null values

  - ### addDynamicDSGenerator

    public static void addDynamicDSGenerator(DynamicDSGenerator ddsg)
    Registers the parameter DynamicDSGenerator with the DataSource system by placing it at the top of a stack of DynamicDSGenerators. Whenever the system needs a DataSource in future, it will first call the `DynamicDSGenerator.getDataSource(String, DSRequest)` method of the DynamicDSGenerator. If that returns `null`, it will proceed to call the same method on any other DynamicDSGenerators in the stack, in reverse order of their registration (the normal LIFO ordering of a stack). Only if all the registered DynamicDSGenerators return null does the framework revert to the normal system of obtaining a DataSource instance.

    NOTE:

    - If you use this API, your DynamicDSGenerator will be called for every DataSource that the framework needs, including the very large number of internal usages. You probably don't want this - see `addDynamicDSGenerator(DynamicDSGenerator, String)` and `addDynamicDSGenerator(DynamicDSGenerator, Pattern)` for APIs that are more likely to be suitable
    - If you use this API anyway, you must be certain to return `null` for DataSources that you cannot provide an instance of
    Parameters:
      `ddsg` - A DynamicDSGenerator to register as the new default
    See Also:
      - `removeDynamicDSGenerator()`

  - ### removeDynamicDSGenerator

    public static DynamicDSGenerator removeDynamicDSGenerator()
    Unregisters the current default DynamicDSGenerator (ie, the one at the top of the stack). Does nothing if there is no current default (ie, if the stack is empty).

    If there are other default DynamicDSGenerators on the stack (because `addDynamicDSGenerator(DynamicDSGenerator)` has been called more than once), the next one in the stack becomes the current default. Otherwise, there is no default DynamicDSGenerator and the framework will fall back to using the normal system for obtaining DataSource instances.

    Note, this API applies only to the stack of default DynamicDSGenerators - ie, those that were not registered against a prefix string or regular expression. It is quite possible to remove the last default DynamicDSGenerator and still have all of your DataSources generated dynamically via the prefix- or regex-based generators. See `addDynamicDSGenerator(DynamicDSGenerator, String)` and `addDynamicDSGenerator(DynamicDSGenerator, Pattern)`

    Returns:
      the DynamicDSGenerator we just removed, or null
    See Also:
      - `addDynamicDSGenerator(DynamicDSGenerator, String)`
      - `addDynamicDSGenerator(DynamicDSGenerator, Pattern)`

  - ### addDynamicDSGenerator

    public static void addDynamicDSGenerator(DynamicDSGenerator ddsg, String prefix)
    Registers the parameter DynamicDSGenerator with the DataSource system. The DynamicDSGenerator will only be called for for DataSource ID's which start with the parameter prefix.

    You can add multiple DynamicDSGenerators using this API. If two or more are registered with prefixes that match a given DataSource ID, the one added first wins. Eg, if you have prefixes "c" and "cust" registered, then the "customer" DataSource will be provided by whichever of those two DynamicDSGenerators was registered first. Note that this "earliest first" basis also applies to DynamicDSGenerators registered with a regexp; if you had a DataSource ID that matched both a prefix and a regexp, whichever one of the two registrations came first would be used - there is no priority given to one or the other styles of ID matching.

    Parameters:
      `ddsg` - A DynamicDSGenerator to register as the default
      `prefix` - A prefix String as described above

  - ### addDynamicDSGenerator

    public static void addDynamicDSGenerator(DynamicDSGenerator ddsg, Pattern regex)
    Registers the parameter DynamicDSGenerator with the DataSource system. The DynamicDSGenerator will only be called for for DataSource ID's which match the regexp provided in the parameter `java.util.regex.Pattern`

    You can add multiple DynamicDSGenerators using this API. If two or more are registered with regular expressions that match a given DataSource ID, the one added first wins. This also applies to DynamicDSGenerators registered with a simple prefix String.

    Parameters:
      `ddsg` - A DynamicDSGenerator to register as the default
      `regex` - A Pattern representing a regular expression to match against incoming DataSource IDs

  - ### getDefaultDynamicDSGenerator

    public static DynamicDSGenerator getDefaultDynamicDSGenerator()
    Returns the default DynamicDSGenerator, or null if there is none
    Returns:
      The default DynamicDSGenerator

  - ### getDynamicDSGenerators

    public static Map getDynamicDSGenerators()
    Returns the non-default DynamicDSGenerators (ie, those that were registered against a startsWith string or a regular expression)
    Returns:
      A map of the non-default DynamicDSGenerators, keyed by the startsWith string or regexp

  - ### removeDynamicDSGenerator

    public static DynamicDSGenerator removeDynamicDSGenerator(String prefix)
    Removes the DynamicDSGenerator registered against the parameter prefix String
    Parameters:
      `prefix` - the prefix String to remove the generator for
    Returns:
      The DynamicDSGenerator we just removed

  - ### removeDynamicDSGenerator

    public static DynamicDSGenerator removeDynamicDSGenerator(Pattern regex)
    Removes the DynamicDSGenerator registered against the parameter regexp Pattern
    Parameters:
      `regex` - the regexp Pattern to remove the generator for
    Returns:
      The DynamicDSGenerator we just removed

  - ### clearDynamicDSGenerators

    public static void clearDynamicDSGenerators()
    Removes all user-added DynamicDSGenerators from the system

  - ### initialized

    protected void initialized() throws Exception
    This method is called after the DataSource has been initialized allowing further custom initialization.

    The default implementation does nothing.

    Throws:
      `Exception` - if an error occurs during initialization

  - ### fromXML

    public static DataSource fromXML(Element elem) throws Exception
    Instantiates a DataSource from an XML Element
    Parameters:
      `elem` - XML Element containing the DataSource definition
    Returns:
      an instance of DataSource
    Throws:
      `Exception` - if an error occurs during DataSource instantiation

  - ### fromXML

    public static DataSource fromXML(Element elem, DSRequest dsRequest) throws Exception
    Instantiates a DataSource from an XML Element, with optional context DSRequest
    Parameters:
      `elem` - XML Element containing the DataSource definition
      `dsRequest` - DSRequest to use as a context object if the creation of this DataSource involves the automatic creation of further, possibly user-generated, DataSources (eg, if this DataSource inheritsFrom a user-generated one)
    Returns:
      an instance of DataSource
    Throws:
      `Exception` - if an error occurs during DataSource instantiation

  - ### fromXML

    public static DataSource fromXML(Document doc) throws Exception
    Instantiates a DataSource from an XML Document - the documentElement of this document is expected to contain the DataSource definition.
    Parameters:
      `doc` - XML Document containing the DataSource definition
    Returns:
      an instance of DataSource
    Throws:
      `Exception` - if an error occurs during DataSource instantiation

  - ### fromXML

    public static DataSource fromXML(Document doc, DSRequest dsRequest) throws Exception
    Instantiates a DataSource from an XML Document, with optional context DSRequest - the documentElement of the XML document is expected to contain the DataSource definition.
    Parameters:
      `doc` - XML Document containing the DataSource definition
      `dsRequest` - DSRequest to use as a context object if the creation of this DataSource involves the automatic creation of further, possibly user-generated, DataSources (eg, if this DataSource inheritsFrom a user-generated one)
    Returns:
      an instance of DataSource
    Throws:
      `Exception` - if an error occurs during DataSource instantiation

  - ### fromXML

    public static DataSource fromXML(String xml) throws Exception
    Instantiates a DataSource from a String of XML
    Parameters:
      `xml` - XML string containing the DataSource definition
    Returns:
      an instance of DataSource
    Throws:
      `Exception` - if an error occurs during DataSource instantiation

  - ### fromXML

    public static DataSource fromXML(String xml, DSRequest dsRequest) throws Exception
    Instantiates a DataSource from a String of XML, with optional context DSRequest
    Parameters:
      `xml` - XML string containing the DataSource definition
      `dsRequest` - DSRequest to use as a context object if the creation of this DataSource involves the automatic creation of further, possibly user-generated, DataSources (eg, if this DataSource inheritsFrom a user-generated one)
    Returns:
      an instance of DataSource
    Throws:
      `Exception` - if an error occurs during DataSource instantiation

  - ### fromXML

    public static DataSource fromXML(Reader reader) throws Exception
    Instantiates a DataSource from XML using a Reader
    Parameters:
      `reader` - Reader supplying XML containing the DataSource definition
    Returns:
      an instance of DataSource
    Throws:
      `Exception` - if an error occurs during DataSource instantiation

  - ### fromXML

    public static DataSource fromXML(Reader reader, DSRequest dsRequest) throws Exception
    Instantiates a DataSource from XML using a Reader, with optional context DSRequest
    Parameters:
      `reader` - Reader supplying XML containing the DataSource definition
      `dsRequest` - DSRequest to use as a context object if the creation of this DataSource involves the automatic creation of further, possibly user-generated, DataSources (eg, if this DataSource inheritsFrom a user-generated one)
    Returns:
      an instance of DataSource
    Throws:
      `Exception` - if an error occurs during DataSource instantiation

  - ### getID

    public String getID()
    Returns the "ID" property of the dataSource, exactly the same as `getName()`
    Returns:
      DataSource ID

  - ### getName

    public String getName()
    Returns the "ID" property of the dataSource
    Returns:
      DataSource ID

  - ### getTableName

    public String getTableName()
    Returns the tableName property of the dataSource
    Returns:
      DataSource tableName

  - ### getRelatedTableAlias

    public String getRelatedTableAlias()
    When `DSField.otherFKs` is involved this property is used to construct related table alias in generated SQL. If omitted, the `DataSource ID` is used. Search for `DataSource.relatedTableAlias` and `DataSourceField.otherFKs` in SmartClient Reference for more details.
    Returns:
      DataSource related table alias to use in SQL (when needed)

  - ### getFieldNames

    public List<String> getFieldNames()
    Returns the list of field names in the DataSource as a List of strings.
    Returns:
      the list of field names in the DataSource as a List of strings

  - ### getDirectFields

    public List<String> getDirectFields()
    Returns the "direct" list of field names, which means roughly the fields that are likely to exist in the actual underlying SQL table (or other source of data depending on the DataSource implementation) that this DataSource is bound to, as opposed to fields derived via joins and other kinds of calculations.

    In addition to locally declared fields, this will include inherited fields (for more details look for `DataSource.inheritsFrom` in the SmartClient reference), but will exclude:

    ignored fields (see `DataSourceField.ignore` in the SmartClient reference)
    related fields fetched from another DataSource (`DataSourceField.includeFrom` in the SmartClient reference).
    multiple fields (`DataSourceField.multiple` in the SmartClient reference)
    depending on the DataSource type, this will also exclude fields that are configured to be fetched in any kind of custom way. For example for the `SQLDataSource` built-in DataSource implementation this will include only fields selected from the main SQL table (`DataSource.tableName` in the SmartClient reference), and will exclude fields using features like `customSQL`, `includeSummaryFunction`, `customSelectExpression` etc

    Returns:
      the "direct" list of field names as a List of strings

  - ### getNonIncludedFields

    public List<String> getNonIncludedFields()
    Returns the list of "non included" field names, which means all fields except the related ones fetched from other DataSources.

    In addition to locally declared fields, this will include inherited fields (for more details look for `DataSource.inheritsFrom` in the SmartClient reference), but will exclude:

    ignored fields (see `DataSourceField.ignore` in the SmartClient reference)
    related fields fetched from another DataSource (`DataSourceField.includeFrom` in the SmartClient reference).

    Returns:
      the list of "non included" field names as a List of strings

  - ### getPrimaryKey

    public String getPrimaryKey()
    Returns the name of the primary key field. Avoid this method for DataSources with composite keys (ie, multiple primaryKey fields): it simply returns the first key field it finds, which will rarely be useful for a composite key.
    Returns:
      the name of the primary key field

  - ### getField

    public DSField getField(String fieldName)
    Returns the field definition for the specified field.
    Parameters:
      `fieldName` - the name of the field whose definition you want
    Returns:
      the field definition for the specified field
    See Also:
      - `DSField`

  - ### getRecordXPath

    public String getRecordXPath()
    For an XML DataSource, returns the XPath expression used to retrieve the set of XML elements that constitute the records of this DataSource.

    For example, an "ItemSearch" might return metadata along with the set of `<Item>` elements that one might want to display in a grid. The XPath expression to retrieve just the `<Item>` elements in this case might be "//Item", which would select all `<Item>` elements anywhere in the returned document.

    Returns:
      the XPath expression

  - ### isModificationOperation

    public static boolean isModificationOperation(String operationType)
    Convenience method that returns true for operations that modify data and false for those that do not.
    Parameters:
      `operationType` - the operation type to check
    Returns:
      true if operationType is one of "add", "remove" or "update" and false otherwise.

  - ### transformMultipleFields

    public void transformMultipleFields(DSRequest req)
    Transforms the values for fields declared multiple:true in the incoming DSRequest.

    See client-side docs for DataSourceField.multipleStorage for possible behaviors. This transformation is performed in DataSource.execute(), before operationType-specific methods like executeFetch or executeUpdate() are called.

    Parameters:
      `req` - the DSRequest to be transformed.

  - ### transformMultipleFields

    public void transformMultipleFields(DSResponse res, DSRequest req)
    Transforms the values for fields declared multiple:true in a DSResponse.

    See client-side docs for DataSourceField.multipleStorage for possible behaviors. This transformation is performed in DataSource.execute(), after operationType-specific methods like executeFetch or executeUpdate() are called.

    Parameters:
      `res` - the DSResponse to be transformed.
      `req` - the associated DSRequest, for context

  - ### execute

    public DSResponse execute(DSRequest req) throws Exception
    This method carries out the actual processing of a DataSource request. It can be overridden to provide centralized logic that applies to all operation types (such as transaction management, for example), or to implement special validation rules that cannot be handled by the built-in validation system. Generally, however, it is more appropriate to override the method specific to the operationType - executeFetch(), executeRemove(), executeAdd(), executeUpdate() or executeCustom(). Note that you must call the validate() method to retain correct validation behavior if you override this method and do not invoke super() This method is also the place where the transform happens if dataSourceField.multipleStorage is used. The transform is performed by two methods, `transformMultipleFields(DSRequest)` and `transformMultipleFields(DSResponse)`.
    Parameters:
      `req` - The DSRequest object representing this operation
    Returns:
      A valid DSResponse
    Throws:
      `Exception` - if an error occurs during request execution

  - ### getAuditTypeFieldName

    public String getAuditTypeFieldName()
    Get the field name for the audit type field configured for this `DataSource`.
    Returns:
      the field name representing the type field configured for the audit `DataSource`.

  - ### getAuditRevisionFieldName

    public String getAuditRevisionFieldName()
    Get the field name for the audit revision field configured for this `DataSource`.
    Returns:
      the field name representing the revision field configured for the audit `DataSource`.

  - ### getAuditTimestampFieldName

    public String getAuditTimestampFieldName()
    Get the field name for the audit timestamp field configured for this `DataSource`.
    Returns:
      the field name representing the timestamp field configured for the audit `DataSource`.

  - ### getAuditUserFieldName

    public String getAuditUserFieldName()
    Get the field name for the audit user field configured for this `DataSource`.
    Returns:
      the field name representing the user field configured for the audit `DataSource`.

  - ### getAuditChangedFieldsFieldName

    public String getAuditChangedFieldsFieldName()
    Get the field name for the audit "changed fields" field configured for this `DataSource`.
    Returns:
      the field name representing the "changed fields" field configured for the audit `DataSource`.

  - ### setProperties

    public Object setProperties(Map properties, Object target)
    Applies all properties in a Map to a target bean by calling setter methods according to the Java Beans naming convention ("name" -> setName()), including recursive traversal of sub-beans and JXPath-based setting of properties in nested beans.

    When DataSourceField.valueXPath is set, the valueXPath is treated as a Jakarta JXPath expression. If some intervening objects in a JXPath are `nulls`, this method attempts to create missing objects if possible - see `JXPathContextObjectFactory` documentation for details.

    Entries in the "properties" Map where there is no same-named DataSource field are discarded.

    This method can be used to recursively populate an object graph of Beans and Collections, using multiple DataSources to define rules for how subobjects are populated. Specifically, if the "properties" Map has entries whose values are Maps, if the corresponding field on the DataSource is declared as being of a DataSource type (ie, the type property is set to the ID of some valid DataSource), these properties will be applied via a recursive call to DataSource.setProperties() using the indicated DataSource. This will also occur for a Collection within the "properties" Map if the target bean has a corresponding property of Collection type.

    When the type to use is ambiguous (for example, a property on the target bean is declared as a base type with many sub-types, or is an abstract type like List) the DataSourceField properties javaClass, javaCollectionClass and javaKeyClass can be used to specify a concrete type - see the client-side reference docs for further details.

    Ultimately, this method calls `DataTools.setProperties(Map, Object, DataSource)` - see the documentation of these method for details of treatment of object vs primitive type, Java Generics, Enums, and other cases.

    Parameters:
      `properties` - Map of DataSource fieldName -> value
      `target` - Target object to which the values are to be applied
    Returns:
      the target object with properties applied

  - ### getListProperties

    public List getListProperties(List list)
    Takes a List of beans/Elements/Maps and for all fields on this DataSource that specify a valueXPath, applies the xpath to each object in the List using the Jakarta JXPath library. If no valueXPath is specified, this method attempts to treat each object in the List as a bean for that value. Values specified for fieldNames that are not declared in the datasource on which this method is being called are discarded.
    Parameters:
      `list` - The List of objects that is mined for values.
    Returns:
      A List of Maps of fieldNames -> fieldValues derived as described above.

  - ### getListProperties

    public List getListProperties(List list, boolean dropExtraFields, boolean dropIgnoredFields)
    Takes a List of beans/Elements/Maps and for all fields on this DataSource that specify a valueXPath, applies the xpath to each object in the List using the Jakarta JXPath library. If no valueXPath is specified, this method attempts to treat each object in the List as a bean for that value. The `dropExtraFields` parameter controls whether or not values specified for fieldNames that are not declared in the datasource on which this method is being called are discarded.
    Parameters:
      `list` - The List of objects that is mined for values.
      `dropExtraFields` - If true, values specified for fieldNames that are not declared in the datasource on which this method is being called are discarded, otherwise they are kept.
      `dropIgnoredFields` - If true, values specified for fieldNames that are declared in the datasource on which this method is being called with the property ignore: true are are discarded; otherwise they are kept
    Returns:
      A List of Maps of fieldNames -> fieldValues derived as described above.

  - ### getProperties

    public Map getProperties(Object obj)
    Takes a bean/Element/Map and for all fields on this DataSource that specify a valueXPath, applies the xpath to the object using the Jakarta JXPath library. If no valueXPath is specified, this method attempts to treat the target as a bean for that value. Values specified for fieldNames that are not declared in the datasource on which this method is being called are discarded.
    Parameters:
      `obj` - The object that is mined for values.
    Returns:
      A map of fieldNames -> fieldValues derived as described above.

  - ### getProperties

    public Map getProperties(Object obj, boolean dropExtraFields, boolean dropIgnoredFields)
    Takes a bean/Element/Map and for all fields on this DataSource that specify a valueXPath, applies the xpath to the object using the Jakarta JXPath library. If no valueXPath is specified, this method attempts to treat the target as a bean for that value. The `dropExtraFields` parameter controls whether or not values specified for fieldNames that are not declared in the datasource on which this method is being called are discarded.
    Parameters:
      `obj` - The object that is mined for values.
      `dropExtraFields` - If true, values specified for fieldNames that are not declared in the datasource on which this method is being called are discarded, otherwise they are kept.
      `dropIgnoredFields` - If true, values specified for fieldNames that are declared in the datasource on which this method is being called with the property ignore: true are are discarded; otherwise they are kept
    Returns:
      A map of fieldNames -> fieldValues derived as described above.

  - ### getProperties

    public Map getProperties(Object obj, Collection propsToKeep)
    Takes a bean/Element/Map and for all fields on this DataSource that specify a valueXPath, applies the xpath to the object using the Jakarta JXPath library. If no valueXPath is specified, this method attempts to treat the target as a bean for that value.
    Parameters:
      `obj` - The object that is mined for values.
      `propsToKeep` - specifies an explicit list of properties to retain the datasource on which this method is being called are discarded, otherwise they are kept.
    Returns:
      A map of fieldNames -> fieldValues derived as described above.

  - ### validate

    public ErrorReport validate(Map data, boolean reportMissingRequiredFields) throws Exception
    Takes a Map of fieldName -> value (data argument) and validates the data using the validators specified on this dataSource. Note, this version does not merge validated values back into the data.

    Note: This method validates data without a DSRequest context. Validators that depend on DSRequest information (such as `isUnique`, which needs to know the operation type to properly exclude the current record during updates) will have limited information available. For validation with full DSRequest context, use `DSRequest.validate()` or `validateDSRequest(DSRequest)` instead.

    Parameters:
      `data` - Map of DataSource fieldName -> value
      `reportMissingRequiredFields` - for operations like update or fetch/filter only partial fields may be specified by the user. In those cases you want to ignore missing required fields.
    Returns:
      ErrorReport or null if there were no errors.
    Throws:
      `Exception` - if an exception occurs during validation
    See Also:
      - `DSRequest.validate()`
      - `validateDSRequest(DSRequest)`

  - ### validate

    public ErrorReport validate(Map data, boolean reportMissingRequiredFields, ValidationContext context) throws Exception
    Takes a Map of fieldName -> value (data argument) and optional ValidationContext and validates the data using the validators specified on this dataSource. Note, this version does not merge validated values back into the data
    Parameters:
      `data` - Map of DataSource fieldName -> value
      `reportMissingRequiredFields` - for operations like update or fetch/filter only partial fields may be specified by the user. In those cases you want to ignore missing required fields.
      `context` - `ValidationContext` to use during validation; if `null` is passed, then new `ValidationContext` will be created. NOTE that if passing validationContext, outer code is responsible to call `validationContext.freeResources()`;
    Returns:
      ErrorReport or null if there were no errors.
    Throws:
      `Exception` - if an exception occurs during validation

  - ### validate

    @Deprecated public ErrorReport validate(Map data, ErrorReport errors) throws Exception
    Deprecated.
    Please use `validateRecord(Map, ErrorReport, ValidationContext)` instead.
    Parameters:
      `data` - the data to validate
      `errors` - the error report to populate with validation errors
    Returns:
      the error report with any validation errors
    Throws:
      `Exception` - if an error occurs during validation

  - ### validateRecord

    public ErrorReport validateRecord(Map data, ErrorReport errors, ValidationContext context) throws Exception
    Overriding point to add custom validation, that could not be covered using standard per-field validation. For example based on multiple fields. Add your custom validation errors to existing (if any) validation errors provided in `errors` parameter and return complete `ErrorReport` object or `null` if there were no errors.

    Implementing this method is the right way to introduce whole-record validation behavior that applies to all operationTypes that perform validation ("validate", "add", "update" and possible future types) as well as other contexts where validation is performed, such as the BatchUploader or mass import of records.

    Note this method has no default behavior.

    Parameters:
      `data` - Map of DataSource fieldName -> value
      `errors` - `ErrorReport` with validation errors reported by validators on this datasource, or `null` if there were no errors.
      `context` - `ValidationContext`, where record is validated, see `ValidationContext` javadocs for details.
    Returns:
      ErrorReport or `null` if there were no errors.
    Throws:
      `Exception` - if an exception occurs during validation.

  - ### executeFetch

    public DSResponse executeFetch(DSRequest req) throws Exception
    This method is called by DataSource.execute() for "fetch" operations. It is the appropriate override point if you wish to provide custom handling of a "fetch".
    Parameters:
      `req` - The DSRequest object representing this operation
    Returns:
      A valid DSResponse
    Throws:
      `Exception` - if an error occurs during the fetch operation

  - ### executeRemove

    public DSResponse executeRemove(DSRequest req) throws Exception
    This method is called by DataSource.execute() for "remove" operations. It is the appropriate override point if you wish to provide custom handling of a "remove".
    Parameters:
      `req` - The DSRequest object representing this operation
    Returns:
      A valid DSResponse
    Throws:
      `Exception` - if an error occurs during the remove operation

  - ### executeAdd

    public DSResponse executeAdd(DSRequest req) throws Exception
    This method is called by DataSource.execute() for "add" operations. It is the appropriate override point if you wish to provide custom handling of an "add".
    Parameters:
      `req` - The DSRequest object representing this operation
    Returns:
      A valid DSResponse
    Throws:
      `Exception` - if an error occurs during the add operation

  - ### executeUpdate

    public DSResponse executeUpdate(DSRequest req) throws Exception
    This method is called by DataSource.execute() for "update" operations. It is the appropriate override point if you wish to provide custom handling of an "update".
    Parameters:
      `req` - The DSRequest object representing this operation
    Returns:
      A valid DSResponse
    Throws:
      `Exception` - if an error occurs during the update operation

  - ### executeCustom

    public DSResponse executeCustom(DSRequest req) throws Exception
    This method is called by DataSource.execute() for "custom" operations. It is the appropriate override point to provide logic for handling a "custom" operation.
    Parameters:
      `req` - The DSRequest object representing this operation
    Returns:
      A valid DSResponse
    Throws:
      `Exception` - if an error occurs during the custom operation

  - ### executeClientExport

    public DSResponse executeClientExport(DSRequest req) throws Exception
    This method is called by DataSource.execute() for "clientExport" operations. It is the appropriate override point to provide logic for handling a "clientExport" operation.
    Parameters:
      `req` - The DSRequest object representing this operation
    Returns:
      A valid DSResponse
    Throws:
      `Exception` - if an error occurs during the client export operation

  - ### executeFileSource

    public DSResponse executeFileSource(DSRequest req) throws Exception
    This method is called by DataSource.execute() for "fileSource" operations. It is the appropriate override point to provide logic for handling a "fileSource" operation.

    The actual operation to be performed is indicated by the request's `operationType`. The `operationType` may be one of the following:

    getFile, hasFile, listFiles
      Return a `DSResponse` appropriate for a "fetch" operation, using the fileName, fileType, fileFormat and tenantId (or other criteria) provided by the `DSRequest`. Only include the fileContents in the outputs for the "getFile" operationType.
    saveFile
      Return a DSResponse appropriate for an "add" operation, using the fileName, fileType, fileFormat, tenantId and fileContents provided by the DSRequest (and also potentially a timestamp representing the fileVersion, if the DataSource defines a fileVersionField). Do not include the fileContents in the outputs. Note that the actual operation may be implemented as an "add" or an "update", depending on whether the file exists already, and whether fileVersion is in force
    renameFile
      Return a DSResponse appropriate for an "update" operation. The existing fileName, fileType and fileFormat and tenantId are provided in the request's `oldValues`, since they may be primary keys (so that providing only the new values would not be sufficient).
    removeFile
      Return a DSResponse appropriate for a "remove" opration, using the fileName, fileType, fileFormat and tenantId provided by the DSRequest.

    In each case, the fileName, fileType, fileFormat, tenantId and fileContents are provided in the DSRequest using those field names. The default implementation uses `getFileNameField()`, `getFileTypeField()`, `getFileFormatField()`, `getFileTenantIdField()` and `getFileContentsField()` to construct the appropriate sub-request to access the real fields of the DataSource.

    In constructing a DSResponse, you must use the field names fileName, fileType, fileFormat, tenantId and fileContents rather than the real fields of the DataSource.

    The default implementation issues a sub-request of type "fetch", "add", "remove" or "update", with an operationId equal to the operationType provided here, and with field names translated according the DataSource configuration. Therefore, you can customize the sub-request operationTypes, by checking for the relevant operationId.

    Note that the "renameFile" operationId is implemented, by default, to perform an "update" operation, unless the fileNameField, fileFormatField, fileTypeField or tenantIdField are primary keys. If one of those fields is a primary key, then "renameFile" is instead implemented as a "remove" followed by an "add", since there can be difficulties updating primary keys.

    Parameters:
      `req` - The DSRequest object representing this operation.
    Returns:
      A valid DSResponse, constructed as explained above.
    Throws:
      `Exception` - if an error occurs during the file source operation

  - ### getFileNameField

    public String getFileNameField()
    Get the field name for the fileName field configured for this DataSource for fileSource operations.

    This allows fileSource operations to always use "fileName" as a field name, no matter where this DataSource stores the fileName.

    The default implementation works as follows:

    - If the DataSource configuration specifies a "fileNameField", then that is used.
    - Otherwise, if there is a field named "fileName", "name", or "title", then that field is used.
    - Otherwise, if there is a single primary key, and it has the type "text", then that field is used.
    - Otherwise, an error is logged
    Returns:
      The name of the fileNameField

  - ### getFileContentsField

    public String getFileContentsField()
    Get the field name for the fileContents field configured for this DataSource for fileSource operations. This allows fileSource operations to always use "fileContents" as a field name, no matter where this DataSource stores the fileContents. The default implementation first checks for fileContentsField in the DataSource configuration. If not found, it checks for a field named fileContents or contents. If not found, it will use the longest text field which is not the `fileNameField`, `fileTypeField`, `fileFormatField`. or `fileTenantIdField`.
    Returns:
      The name of the fileContentsField

  - ### getFileTypeField

    public String getFileTypeField()
    Get the field name for the fileType field configured for this DataSource for fileSource operations. This allows fileSource operations to always use "fileType" as a field name, no matter where this DataSource stores the fileType. The default implementation will first look for a fileTypeField specified in the DataSource configuration. If not found, it will look for a field named "fileType". Otherwise, it is assumed that this DataSource does not distinguish between multiple fileTypes, so any supplied fileType is ignored by fileSource operations.
    Returns:
      The name of the fileTypeField

  - ### getFileFormatField

    public String getFileFormatField()
    Get the field name for the fileFormat field configured for this DataSource for fileSource operations. This allows fileSource operations to always use "fileFormat" as a field name, no matter where this DataSource stores the fileFormat. The default implementation will first look for a fileFormatField specified in the DataSource configuration. If not found, it will look for a field named "fileFormat". Otherwise, it is assumed that this DataSource does not distinguish between multiple fileFormats, so any supplied fileFormat is ignored by fileSource operations.
    Returns:
      The name of the fileFormatField

  - ### getFile

    public Reader getFile(DSFileSpec fileSpec) throws Exception
    Gets the contents of a file stored in this DataSource.
    Parameters:
      `fileSpec` - A specifier for the file.
    Returns:
      A Reader for the contents of the file. Null if the file is not found.
    Throws:
      `Exception` - if an error occurs while retrieving the file

  - ### getFileAsString

    public String getFileAsString(DSFileSpec fileSpec) throws Exception
    Gets the contents of a file stored in this DataSource
    Parameters:
      `fileSpec` - A specifier for the file.
    Returns:
      The contents of the file. Null if the file is not found.
    Throws:
      `Exception` - if an error occurs while retrieving the file

  - ### getFileAsInputStream

    public InputStream getFileAsInputStream(DSFileSpec fileSpec) throws Exception
    Gets the contents of a file stored in this DataSource
    Parameters:
      `fileSpec` - A specifier for the file.
    Returns:
      The contents of the file. Null if the file is not found.
    Throws:
      `Exception` - if an error occurs while retrieving the file

  - ### hasFile

    public boolean hasFile(DSFileSpec fileSpec) throws Exception
    Indicates whether a file exists in this DataSource.
    Parameters:
      `fileSpec` - A specifier for the file.
    Returns:
      Whether the file can be found.
    Throws:
      `Exception` - if an error occurs while checking for the file

  - ### saveFile

    public Map<String,Object> saveFile(DSFileSpec fileSpec, Object contents) throws Exception
    Save a file to the DataSource.
    Parameters:
      `fileSpec` - A specifier for the file. A fileName is always required. Some DataSources will require that the fileType, fileFormat, and tenantId be specified.
      `contents` - The contents of the file.
    Returns:
      A map with the saved record, but without the fileContents
    Throws:
      `Exception` - if an error occurs while saving the file

  - ### listFiles

    public List<Map<String,Object>> listFiles(Object criteria, DSRequest context) throws Exception
    Get a list of files from the DataSource.
    Parameters:
      `criteria` - Criteria to apply. References to `fileName`, `fileContents` and `fileType` will be translated to the native field names configured for this DataSource.
      `context` - DSRequest which provides the security context in which this method should run.
    Returns:
      The FileList. The Maps will have the `fileName`, `fileType`, `fileFormat`, and potentially the `tenantId` fields populated, but not the `fileContents` field. (You can use `getFile()` to get the `fileContents`).
    Throws:
      `Exception` - if an error occurs while listing files

  - ### listFiles

    public List<Map<String,Object>> listFiles(Object criteria) throws Exception
    Get a list of files from the DataSource.
    Parameters:
      `criteria` - Criteria to apply. References to `fileName`, `fileContents` and `fileType` will be translated to the native field names configured for this DataSource.
    Returns:
      The FileList. The Maps will have the `fileName`, `fileType`, `fileFormat`, and potentially the `tenantId` fields populated, but not the `fileContents` field. (You can use `getFile()` to get the `fileContents`).
    Throws:
      `Exception` - if an error occurs while listing files

  - ### listFiles

    public List<Map<String,Object>> listFiles(String type, String format, DSRequest context) throws Exception
    Get a list of files from the DataSource.
    Parameters:
      `type` - The file type (e.g. "ds")
      `format` - The file format (e.g. "xml" or "js")
      `context` - DSRequest which provides the security context in which this method should run.
    Returns:
      The FileList. The Maps will have the `fileName`, `fileType`, `fileFormat`, and potentially the `tenantId` fields populated, but not the `fileContents` field. (You can use `getFile()` to get the `fileContents`).
    Throws:
      `Exception` - if an error occurs while listing files

  - ### listFiles

    public List<Map<String,Object>> listFiles(String type, String format, String tenantId, DSRequest context) throws Exception
    Get a list of files from the DataSource.
    Parameters:
      `type` - The file type (e.g. "ds")
      `format` - The file format (e.g. "xml" or "js")
      `tenantId` - The file tenant ID for loading multi-tenant DataSources
      `context` - DSRequest which provides the security context in which this method should run.
    Returns:
      The FileList. The Maps will have the `fileName`, `fileType`, `fileFormat` and potentially the `tenantId` fields populated, but not the `fileContents` field. (You can use `getFile()` to get the `fileContents`).
    Throws:
      `Exception` - if an error occurs while listing files

  - ### listFiles

    public List<Map<String,Object>> listFiles(String type, String format) throws Exception
    Get a list of files from the DataSource.
    Parameters:
      `type` - The file type (e.g. "ds")
      `format` - The file format (e.g. "xml" or "js")
    Returns:
      The FileList. The Maps will have the `fileName`, `fileType`, `fileFormat`, and potentially the `tenantId` fields populated, but not the `fileContents` field. (You can use `getFile()` to get the `fileContents`).
    Throws:
      `Exception` - if an error occurs while listing files

  - ### listFiles

    public List<Map<String,Object>> listFiles(String type, String format, String tenantId) throws Exception
    Get a list of files from the DataSource.
    Parameters:
      `type` - The file type (e.g. "ds")
      `format` - The file format (e.g. "xml" or "js")
      `tenantId` - The file tenant ID for loading multi-tenant DataSources
    Returns:
      The FileList. The Maps will have the `fileName`, `fileType`, `fileFormat` and potentially the `tenantId` fields populated, but not the `fileContents` field. (You can use `getFile()` to get the `fileContents`).
    Throws:
      `Exception` - if an error occurs while listing files

  - ### renameFile

    public List<Map<String,Object>> renameFile(DSFileSpec oldSpec, DSFileSpec newSpec) throws Exception
    Rename a file stored in this DataSource.
    Parameters:
      `oldSpec` - The original fileSpec.
      `newSpec` - The new fileSpec.
    Returns:
      Records represening the renamed file(s). The records will have their `fileName`, `fileType`, `fileFormat`, and potentially the `tenantId` fields populated, but not the `fileContents` field.
    Throws:
      `Exception` - if an error occurs while renaming the file

  - ### removeFile

    public List<Map<String,Object>> removeFile(DSFileSpec fileSpec) throws Exception
    Remove a file stored in this DataSource.
    Parameters:
      `fileSpec` - A specifier for the file.
    Returns:
      Records represening the removed file(s). The records will have their `fileName`, `fileType`, `fileFormat`, and potentially the `tenantId` fields populated, but not the `fileContents` field.
    Throws:
      `Exception` - if an error occurs while removing the file

  - ### fetch

    public List fetch(Object criteria) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "fetch", calling setCriteria() on it, and then calling its execute() method.
    Parameters:
      `criteria` - The criteria to use for the fetch operation
    Returns:
      `List` with retrieved data.
    Throws:
      `Exception` - if an error occurs during the fetch operation

  - ### fetch

    public List fetch(String key, Object value) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "fetch", calling setCriteria() on it, and then calling its execute() method. The criteria object is formed by building a single-key map from the key and value passed in.
    Parameters:
      `key` - The criterion key
      `value` - The criterion value
    Returns:
      `List` with retrieved data.
    Throws:
      `Exception` - if an error occurs during the fetch operation

  - ### filter

    public List filter(Object criteria) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "filter", calling setCriteria() on it, and then calling its execute() method.
    Parameters:
      `criteria` - The criteria to use for the filter operation
    Returns:
      `List` with retrieved data.
    Throws:
      `Exception` - if an error occurs during the filter operation

  - ### filter

    public List filter(String key, Object value) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "filter", calling setCriteria() on it, and then calling its execute() method. The criteria object is formed by building a single-key map from the key and value passed in.
    Parameters:
      `key` - The criterion key
      `value` - The criterion value
    Returns:
      `List` with retrieved data.
    Throws:
      `Exception` - if an error occurs during the filter operation

  - ### add

    public long add(Object values) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "add", calling setValues() on it, and then calling its execute() method.
    Parameters:
      `values` - The values to use for the add operation
    Returns:
      `long` number of affected rows.
    Throws:
      `Exception` - if an error occurs during the add operation

  - ### add

    public long add(Object values, RPCManager rpc) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "add", calling setValues() and setRPCManager() on it, and then calling its execute() method.
    Parameters:
      `values` - The values to use for the add operation
      `rpc` - The RPCManager to use for this operation
    Returns:
      `long` number of affected rows.
    Throws:
      `Exception` - if an error occurs during the add operation

  - ### update

    public long update(Object criteria, Object values) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "update", calling setValues() and setCriteria() on it, and then calling its execute() method.
    Parameters:
      `criteria` - The criteria to use for the update operation
      `values` - The values to use for the update operation
    Returns:
      `long` number of affected rows.
    Throws:
      `Exception` - if an error occurs during the update operation

  - ### update

    public long update(Object criteria, Object values, RPCManager rpc) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "update", calling setValues(), setCriteria() and setRPCManager() on it, and then calling its execute() method.
    Parameters:
      `criteria` - The criteria to use for the update operation
      `values` - The values to use for the update operation
      `rpc` - The RPCManager to use for the update operation
    Returns:
      `long` number of affected rows.
    Throws:
      `Exception` - if an error occurs during the update operation

  - ### remove

    public long remove(Object criteria) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "remove", calling setCriteria() on it, and then calling its execute() method.
    Parameters:
      `criteria` - The criteria to use for the remove operation
    Returns:
      `long` number of affected rows.
    Throws:
      `Exception` - if an error occurs during the remove operation

  - ### fetchSingle

    public Map fetchSingle(String key, Object value) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "fetch", calling setCriteria() on it, calling its execute() method, then obtaining just the first item of the results. The criteria object is formed by building a single-key map from the key and value passed in.

    Note that this method expects the specified criterion to result in a single result, and will throw an exception if the multiple records match the request.

    Parameters:
      `key` - The criterion key
      `value` - The criterion value
    Returns:
      `Map` of retrieved object properties.
    Throws:
      `Exception` - if an error occurs during the fetch operation

  - ### fetchSingle

    public Map fetchSingle(Object criteria) throws Exception
    This convenience method is the equivalent of creating a DSRequest with an operationType of "fetch", calling setCriteria() on it, calling its execute() method, then obtaining just the first item of the results.

    Note that this method expects the specified criteria to result in a single result, and will throw an exception if the multiple records match the request.

    Parameters:
      `criteria` - The criteria to use for the fetch operation
    Returns:
      `Map` of retrieved object properties.
    Throws:
      `Exception` - if an error occurs during the fetch operation

  - ### getTransactionObject

    public Object getTransactionObject(DSRequest req) throws Exception
    Returns an object that can be used to manage transactions for this DataSource and other DataSources of the same type and provider. The object returned is of a type specific to the type of DataSource

    This method is part of the Automatic Transactions feature, which is effective only in Power edition and above.

    Parameters:
      `req` - The DSRequest for which to get the transaction object
    Returns:
      the transaction object for this request
    Throws:
      `Exception` - if an error occurs while getting the transaction object

  - ### getTransactionObject

    public static Object getTransactionObject(DSRequest req, String transactionObjectKey, String shortTransactionObjectKey) throws Exception
    Helper method retrieves connection/transaction object for specified type of data source implementation or `null`.
    - for `SQLDataSource`:

           Connection conn = DataSource.getTransactionObject(req, SQLTransaction.CONNECTION_ATTR + dbName);

      where `dbName` is name of database as configured in server.properties file. Returned `conn` is instance of `java.sql.Connection` to specified data base.
    - for `HibernateDataSource`:

           Transaction tx = DataSource.getTransactionObject(req, HibernateTransaction.TRANSACTION_ATTR);
           Session session = HibernateTransaction.getTransactionSession(tx);

      `session` and `tx` instances of hibernate session and transaction object used by `HibernateDataSource`.
    - for `JPADataSource`:

           JPAConnectionHolder holder = DataSource.getTransactionObject(req, EMF.TRANSACTION_ATTR);

      `JPAConnectionHolder` instance contains references to entity manager and transaction object used by `JPADataSource` instances.

    Transaction is started by first execution of corresponding data source implementation. Following operations should retrieve transaction object and use it but should never commit/rollback. Overall commit/rollback will be issued by `RPCManager` and will be handled by data source object which started transaction.

    This method is part of the Automatic Transactions feature, which is effective only in Power edition and above.

    Parameters:
      `req` - `DSRequest`
      `transactionObjectKey` - `String` Name of transaction object key.
      `shortTransactionObjectKey` - `String` Name of short transaction object key.
    Returns:
      `Object` containing connection/transaction. Actual type of instance depends on passed parameter `transactionObjectKey`. `null` is returned if specified request does not contain reference to `RPCManager` or specified object is not set.
    Throws:
      `Exception` - if provided `DSRequest` is `null`.

  - ### hasRecord

    public boolean hasRecord(String columnName, Object value) throws Exception
    Returns true if the dataSource contains at least one record where the columnName matches the value.
    Parameters:
      `columnName` - The columnName to use in the search criteria
      `value` - The value to look for in the search criteria
    Returns:
      true if at least one record matches the criteria
    Throws:
      `Exception` - if an error occurs during the search

  - ### hasRecord

    public boolean hasRecord(Map criteria) throws Exception
    Returns true if the dataSource contains at least one record that matches the supplied criteria
    Parameters:
      `criteria` - A Map containing the search criteria
    Returns:
      true if at least one record matches the criteria
    Throws:
      `Exception` - if an error occurs during the search

  - ### fetchById

    public Map<String,Object> fetchById(Object id) throws Exception
    Same as `fetchById(Object, RPCManager)`, without the optional RPCManager parameter
    Parameters:
      `id` - The primary key value to match, or a set of primary key values in a Map
    Returns:
      the record matching the ID, or null if not found
    Throws:
      `Exception` - if an error occurs during the fetch

  - ### fetchById

    public Map<String,Object> fetchById(Object id, RPCManager rpc) throws Exception
    Same as `fetchById(Object, RPCManager, List)`, without the optional outputs parameter
    Parameters:
      `id` - The primary key value to match, or a set of primary key values in a Map
      `rpc` - An optional RPCManager to apply to the DSRequest we generate to implement the fetch. If provided, this gives access to the RPCManager's shared state. For example, it may allow the fetch to share a database transaction or give it access to an HttpSession
    Returns:
      the record matching the ID, or null if not found
    Throws:
      `Exception` - if an error occurs during the fetch

  - ### fetchById

    public Map<String,Object> fetchById(Object id, RPCManager rpc, List<String> outputs) throws Exception
    Returns the record whose primary key matches the supplied id, or null if no such record exists. For a DataSource with a composite primary key, this method's behavior is dependent on the type of the "id" parameter:
    - If "id" is a Map, it is assumed to be criteria expressing the full primary key, and is passed directly to the underlying operation. If the full set of primary key values is correctly provided in the Map, this will return the single record whose composite primary key matches that set of values
    - If "id" is not a Map, it is assumed to be a single value to match against the DataSource's first primary key field. In this case, it returns the first record (if any) where the first defined primary key field matches the supplied id
    Parameters:
      `id` - The primary key value to match, or a set of primary key values in a Map
      `rpc` - An optional RPCManager to apply to the DSRequest we generate to implement the fetch. If provided, this gives access to the RPCManager's shared state. For example, it may allow the fetch to share a database transaction or give it access to an HttpSession
      `outputs` - An optional list of outputs to limit the set of fields to fetch
    Returns:
      the record matching the ID, or null if not found
    Throws:
      `Exception` - if an error occurs during the fetch

  - ### getEnumTranslateStrategy

    public String getEnumTranslateStrategy()
    Returns the strategy this DataSource uses for translating Java enumerated types (enums). See `setEnumTranslateStrategy(String)` for details of valid translate strategies
    Returns:
      A String containing the enum translate strategy

  - ### setEnumTranslateStrategy

    public void setEnumTranslateStrategy(String newValue)
    Sets the strategy this DataSource uses to translate enumerated types (objects of type enum). Valid values are:
    "name" Translates to/from a String matching the constant name
    "string" Translates to/from a String matching the `enum.toString()`
    "ordinal" Translates to/from an integer matching the ordinal number of the constant within the enumeration
    "bean" Translates to/from a JS object containing one property for each property defined within the enum. The constant itself and the ordinal number are included in the JS object. By default they are called "_constant" and "_ordinal", but this can be overridden on both JSTranslater and DataSource with the enumOrdinalProperty and enumConstantProperty properties (which have setters named using normal bean semantics)
    Parameters:
      `newValue` - A String containing the new enum translate strategy

  - ### getEnumOrdinalProperty

    public String getEnumOrdinalProperty()
    Returns the name of the property this DataSource uses for ordinal number when translating enumerated types. Defaults to "_ordinal"
    Returns:
      The ordinal property name

  - ### getEnumConstantProperty

    public String getEnumConstantProperty()
    Returns the name of the property this DataSource uses for constant name when translating enumerated types. Defaults to "_constant"
    Returns:
      The constant value property name

  - ### setEnumOrdinalProperty

    public void setEnumOrdinalProperty(String newValue)
    Sets the name of the property this DataSource should use for ordinal number when translating enumerated types.
    Parameters:
      `newValue` - The new ordinal property name

  - ### setEnumConstantProperty

    public void setEnumConstantProperty(String newValue)
    Sets the name of the property this DataSource should use for constant name when translating enumerated types.
    Parameters:
      `newValue` - The new constant value property name

  - ### getProperty

    public String getProperty(String key)
    Returns a property from the DataSource config. This method allows a custom DataSource implementation to be configuration-driven like the built-in DataSource implementations are. So, if you add

            myNewProperty="some useful value"

    to the `<DataSource>` tag in your `.ds.xml` definition file, calling this method with a parameter of `"myNewProperty"` will return `"some useful value"` - the value you assigned in the DataSource definition.

    Note, this API is implemented on `DataSource`, but is only meaningful on instances of `BasicDataSource` and its subclasses. `DataSource` is a base class: every implementation that is configured with a `.ds.xml` file - in reality, every real-world implementation - is a `BasicDataSource`. Therefore, we implement this method and other similar ones on `DataSource` as a convenience, so code that retrieves a `DataSource` from one of the SmartClient APIs does not have to cast it to `BasicDataSource` to use this API. The behavior if you call this API on a `DataSource` that is not a `BasicDataSource` is undefined.

    Parameters:
      `key` - The key to look up
    Returns:
      The property value as a String (non-String objects are converted using toString())

  - ### getObjectProperty

    public Object getObjectProperty(String key)
    Returns the value of the specified element(s) as an Object.

    Custom XML is transformed to Strings or Java collections by the following rules:

    - Elements with just text (no child elements or attributes) become Strings
    - Elements with child elements or attributes become Maps containing data derived from their subelements
    - Repeating elements (more than one of the same name at the same level) become Lists

    Sample XML:

    - Map

      `<DataSource>
      <mapCustomProperty key1="value1" key2="value2" />
      </DataSource>`

    - List of String

      `<DataSource>
      <listCustomProperty>ListElement1</listCustomProperty>
      <listCustomProperty>ListElement2</listCustomProperty> </DataSource>`

    - List of Map

      `<DataSource>
      <mapCustomProperty key1="value1" key2="value2" />
      <mapCustomProperty key1="value1" key2="value2" />
      </DataSource>`

    Note, this API is implemented on `DataSource`, but is only meaningful on instances of `BasicDataSource` and its subclasses. `DataSource` is a base class: every implementation that is configured with a `.ds.xml` file - in reality, every real-world implementation - is a `BasicDataSource`. Therefore, we implement this method and other similar ones on `DataSource` as a convenience, so code that retrieves a `DataSource` from one of the SmartClient APIs does not have to cast it to `BasicDataSource` to use this API. The behavior if you call this API on a `DataSource` that is not a `BasicDataSource` is undefined.

    Also note, the returned Object should be considered read-only: changing it may affect some parts of the framework and not others.

    Parameters:
      `key` - the name of the element(s)
    Returns:
      the value of the specified element(s) as an Object
    See Also:
      - `getProperty(String)`
      - `getListProperty(String)`
      - `getMapProperty(String)`

  - ### getListProperty

    public List getListProperty(String key)
    Returns the value of the specified attribute as a List.

    Note, this API is implemented on `DataSource`, but is only meaningful on instances of `BasicDataSource` and its subclasses. `DataSource` is a base class: every implementation that is configured with a `.ds.xml` file - in reality, every real-world implementation - is a `BasicDataSource`. Therefore, we implement this method and other similar ones on `DataSource` as a convenience, so code that retrieves a `DataSource` from one of the SmartClient APIs does not have to cast it to `BasicDataSource` to use this API. The behavior if you call this API on a `DataSource` that is not a `BasicDataSource` is undefined.

    Also note, the returned List should be considered read-only: changing items within it may affect some parts of the framework and not others, leading to undefined behavior

    Parameters:
      `key` - the name of the attribute
    Returns:
      the value of the specified attribute as a List
    Throws:
      `ClassCastException` - if requested property is not instanceof List
    See Also:
      - `getProperty(String)`
      - `getObjectProperty(String)`
      - `getMapProperty(String)`

  - ### getMapProperty

    public com.isomorphic.collections.DataTypeMap getMapProperty(String key)
    Returns the value of the specified attribute as a Map.

    Note, this API is implemented on `DataSource`, but is only meaningful on instances of `BasicDataSource` and its subclasses. `DataSource` is a base class: every implementation that is configured with a `.ds.xml` file - in reality, every real-world implementation - is a `BasicDataSource`. Therefore, we implement this method and other similar ones on `DataSource` as a convenience, so code that retrieves a `DataSource` from one of the SmartClient APIs does not have to cast it to `BasicDataSource` to use this API. The behavior if you call this API on a `DataSource` that is not a `BasicDataSource` is undefined.

    Also note, the returned Map should be considered read-only: changing properties within it may affect some parts of the framework and not others.

    Parameters:
      `key` - the name of the attribute
    Returns:
      the value of the specified attribute as a Map
    Throws:
      `ClassCastException` - if requested property is not instanceof Map
    See Also:
      - `getProperty(String)`
      - `getObjectProperty(String)`
      - `getListProperty(String)`

  - ### isExpensive

    public boolean isExpensive(DSField field)
    Returns whether a field is considered expensive to fetch, based on per-field properties only (no request context). This is the primary override point for custom DataSource subclasses that need to customize the definition of "expensive" for their schema.

    The default implementation considers a field expensive if any of the following are true:

    - The field has an explicit `expensive:true` declaration in the DataSource descriptor (conversely, `expensive:false` forces the method to return false, overriding all other heuristics)
    - The field is a binary type with `encodeInResponse` set — inline Base64 encoding of binary data in multi-row fetches adds significant payload
    - The field is a text type whose declared length exceeds the configurable threshold (default 255, matching the client-side `longTextEditorThreshold`; override via `sql.expensiveFieldLengthThreshold` in `server.properties`) — large text/CLOB columns in multi-row fetches add significant data transfer
    - The field has an `outputWhen` setting, indicating the developer has already declared it conditionally delivered
    - The field is marked `detail:true`, indicating it is not needed in summary/grid views

    Note: `includeFrom` fields are not considered expensive by this method alone, because the cost depends on whether the join is already required by other fields in the request — that analysis requires request context and is handled by the overload `isExpensive(field, request)`.

    Similarly, fields with a `customSelectExpression` are not considered expensive by default, since such expressions range from trivial to costly and there is no way to assess the SQL cost generically. If you know that a particular `customSelectExpression` is expensive, set `expensive:true` on that field, or override this method.

    Parameters:
      `field` - the field to evaluate
    Returns:
      true if the field is considered expensive to fetch
    See Also:
      - `isExpensive(DSField, DSRequest)`

  - ### isExpensive

    public boolean isExpensive(DSField field, DSRequest request)
    Returns whether a field is considered expensive to fetch in the context of a specific DSRequest. This method extends the per-field `isExpensive(DSField)` check with request-context analysis for `includeFrom` fields: an `includeFrom` field is expensive only if its join is not already required by other fields in the request's outputs.

    If the request's outputs already contain another `includeFrom` field that uses the same join (same related DataSource and same `includeVia` path), then the join is happening regardless — adding another column from the same joined table is essentially free, so the field is not considered expensive.

    The explicit `expensive` flag on the field always takes precedence, in both directions: `expensive:true` forces this method to return true even if the join is free, and `expensive:false` forces false even if the join would be new.

    Parameters:
      `field` - the field to evaluate
      `request` - the current DSRequest, used for join context analysis
    Returns:
      true if the field is considered expensive to fetch
    See Also:
      - `isExpensive(DSField)`

  - ### getPropertyJavaClass

    public Class getPropertyJavaClass(String propertyName, DSField field, Object value) throws Exception
    Override point to allow subclasses to dynamically determine the Java class to use for a given property. The default implementation returns the Class indicated by the passed-in DSField's "javaClass" property. Scan the client-side documentation for "javaClass" for details.

    Your overriding method is passed the name of the property we are trying to populate, the raw value we are going to use to populate it, and the DSField object associated with the property. It is envisaged that this will be sufficient context to dynamically decide on a Class to use. For example, if the raw Map passed in contains a given property, your code might decide to use Class B rather than the normal Class A.

    Note that whatever class you return from this method will be used as the parameter type for the corresponding setter on the bean we are populating. This means that this method (and indeed the declarative "javaClass" property) is only useful if your Javabean property type is a supertype of whatever class you return from this method. Examples of valid use cases would include things like your Javabean property is of an interface or abstract type, or of a general base type like Object.

    Parameters:
      `propertyName` - Name of the property we are trying to populate, for context
      `field` - The associated `DSField`
      `value` - The raw value we are trying to assign to this property. Note this will ordinarily be a Map or a List of Maps
    Returns:
      The Class to use for this property; return the result of calling super() to fall back to default behavior, or null to bypass custom bean Class derivation for this property (we will use Reflection to determine the type to use, as described in the client-side documentation for `DataSourceField.javaClass`)
    Throws:
      `Exception` - if an error occurs while determining the Java class

  - ### convertRelativeDates

    public static AdvancedCriteria convertRelativeDates(AdvancedCriteria advancedCriteria)
    Converts all relative dates in a critierion to absolute dates relative to the time when this method is called. Unlike `convertRelativeDates(com.isomorphic.criteria.AdvancedCriteria, java.util.Date)` this method will assume that the relative point in time to convert against is now.
    Parameters:
      `advancedCriteria` - the AdvancedCriteria for which to convert relative dates.
    Returns:
      an AdvancedCriteria with converted relative dates.
    See Also:
      - `convertRelativeDates(com.isomorphic.criteria.Criterion)`

  - ### convertRelativeDates

    public static AdvancedCriteria convertRelativeDates(AdvancedCriteria advancedCriteria, Date baseDate)
    Converts all relative dates in a critierion to absolute dates relative to the time when this method is called.
    Parameters:
      `advancedCriteria` - the AdvancedCriteria for which to convert relative dates.
      `baseDate` - the relative point in time to convert dates against.
    Returns:
      the AdvancedCriteria with it's relative dates converted.
    See Also:
      - `convertRelativeDates(com.isomorphic.criteria.Criterion, java.util.Date, DataSource)`

  - ### convertRelativeDates

    public static Criterion convertRelativeDates(Criterion criterion)
    Converts all relative dates in a critierion to absolute dates relative to the time when this method is called.

    #### Important Notes

    Normally there is no need to convert relative dates on the server, this is done by default on the client before the request is sent to the server. The primary purpose for converting relative dates on the server is when there is a need to store and use relative dates at a later point such as in an automated job without any involvement from the client.

    In order for the client to automatically send relative dates to the server you need to set DataSource.autoConvertRelativeDates to false.

    If you want to store a client side criteria containing relative dates for later parsing server side you need to use the LOGICAL_DATE_CONSTRUCTOR mode of JSONEncoder when encoding the criteria on the client and then use `AdvancedCriteria.decodeClientCriteria(String)` to decode the criteria on the server.

    #### Examples

    Suppose you have a DataSource with ID "overnightReports" with a field "storedCriteria" where you want to store a criteria created by the end user which will run overnight on the server.

    In such case you would have to serialize the critiera on the client before sending it to the server.

    ##### JavaScript

         var encodedCriteria = isc.JSON.encode(criteria, {dateFormat: 'logicalDateConstructor'});

         // Add the encoded criteria to your datasource.
         overnightReports.addData({
             storedCriteria: encodedCriteria
         });

    ##### SmartGWT

         final JSONEncoder jsonEncoder = new JSONEncoder();
         jsonEncoder.setDateFormat(JSONDateFormat.LOGICAL_DATE_CONSTRUCTOR);

         // Encode the criteria using the above JSONEncoder.
         final String encodedCriteria = JSON.encode(criteria, jsonEncoder);

         // Add the encoded criteria to your datasource.
         final Record newRecord = new Record();
         newRecord.setAttribute("storedCriteria", encodedCriteria);

         overnightReports.addData(newRecord);

    Parameters:
      `criterion` - the criterion to convert relative dates for.
    Returns:
      the criterion after conversion.

  - ### convertRelativeDates

    public static Criterion convertRelativeDates(Criterion criterion, Date baseDate, DataSource ds)
    Converts all relative dates in a critierion to absolute dates relative to the time provided as base date.
    Parameters:
      `criterion` - the criterion to convert relative dates for.
      `baseDate` - the relative point in time to convert dates against.
      `ds` - the DataSource applicable to this criterion. Pass null if you do not have access to the dataSource, but be aware that this may lead to inaccurate handling of date values (because we are unable to distinguish between "date" and "datetime" fields, we treat all values as datetimes)
    Returns:
      the criterion after conversion.

  - ### convertRelativeDates

    public static Criterion convertRelativeDates(Criterion criterion, Date baseDate, DataSource ds, boolean useEmbeddedTZ)
    Converts all relative dates in a critierion to absolute dates relative to the time provided as base date.

    This API can potentially use the client's timezone to determine what is meant by concepts such as "the start of today" or "the end of tomorrow", if the client supplies its timezone in the relativeDate definition (this happens in SmartClient versions from 11.1 onwards, Smart GWT versions 6.1 onwards). However, please note that the client actually sends up its current offest from UTC, not a real timezone identifier; this is unavoidable, as it is not possible to reliably detect the actual timezone identifier from Javascript.

    The offset from GMT/UTC is usually adequate to correctly convert datetimes, but without real timezone information we get subtle errors in cases where a date range straddles the changeover to or from daylight savings time (DST); because we don't really know the timezone, we don't know how DST applies to it.

    For example, consider a user in the US Pacific timezone who specifies a date range of "$yesterday" to "$tomorrow" at local midday on 13 March 2016. On that date, the US Pacific timezone switches from "Pacific Standard Time" to "Pacific Daylight Time" at 1am. Therefore PDT, which is UTC-07:00, is in force, and this is what is sent to the server. However, "$yesterday" should correctly be resolved as local midday on the previous day, March 12. On March 12, the US Pacific timezone was on PST, so the correct UTC start time is 2016-03-12 20:00:00 UTC. But the server doesn't know about the DST changeover because it doesn't know that the client is in US Pacific; it only knows that the offset from UTC is -07:00. Therefore, the start time will be incorrectly calculated as 2016-03-12 19:00:00 UTC.

    Parameters:
      `criterion` - the criterion to convert relative dates for.
      `baseDate` - the relative point in time to convert dates against.
      `ds` - the DataSource applicable to this criterion. Pass null if you do not have access to the dataSource, but be aware that this may lead to inaccurate handling of date values (because we are unable to distinguish between "date" and "datetime" fields, we treat all values as datetimes)
      `useEmbeddedTZ` - If true, uses the timezone offset embedded in a relativeDate definition, if one is present. If this parameter is false, or there is no embedded timezone, the server timezone is used. Note, no embedded timezone information is sent from the client in versions earlier than 11.1 / 6.1 (SmartClient / Smart GWT)
    Returns:
      the criterion after conversion.

  - ### convertRelativeDates

    public static Object convertRelativeDates(Map criteria, DSRequest request)
    Same as `convertRelativeDates(com.isomorphic.criteria.Criterion)`, but for the simple criteria.

    Generally the result would be simple criteria as well, although if relative date value would use `RelativeDateShortcut.TODAY`, `RelativeDateShortcut.YESTERDAY` or `RelativeDateShortcut.TOMORROW` against the field of "datetime" builtin type, then simple criteria would be converted to the `AdvancedCriteria` and the condition replaced with `DateRangeCriterion` using "betweenInclusive" operator and start of the day and end of the day as minValue and maxValue.

    Parameters:
      `criteria` - simple criteria to convert relative dates for
      `request` - request used to convert simple criteria to advanced criteria if needed
    Returns:
      the criteria after conversion

  - ### getRelatedDisplayRecord

    @Deprecated public Map getRelatedDisplayRecord(String fieldName, Object displayValue) throws Exception
    Deprecated.
    This method is still supported for calling, but the new API `getRelatedDisplayRecord(String, Object, DSRequest)` is the one that you must override if you want to customize the behavior.
    Parameters:
      `fieldName` - the field name for which to get the related display record
      `displayValue` - the display value to match
    Returns:
      the related display record
    Throws:
      `Exception` - if an error occurs while retrieving the related display record
    See Also:
      - `getRelatedDisplayRecord(String, Object, DSRequest)`

  - ### getRelatedDisplayRecord

    public Map getRelatedDisplayRecord(String fieldName, Object displayValue, DSRequest dsRequest) throws Exception
    Returns the "related display record" for a given field. This only applies to fields that:
    - Declare a foreignKey
    - Declare a displayField
    - The displayField so declared is an `includeFrom` field on the same DataSource that the foreignKey declaration targets
    Given a field like this, the related display record is the record in the foreign DataSource (the one that is the target of the foreignKey definition) where the field that is the target of the includeFrom definition matches the displayValue parameter passed to this method. For example, consider a dataSource "Widgets" with these fields:

    Now assume that the Colors dataSource contains these records:

            {id: 1, name:"Cyan"}
            {id: 2, name:"Magenta"}
            {id: 3, name:"Yellow"}

    Now, calling `getRelatedDisplayRecord("color", "Yellow")` on an instance of the "Widgets" dataSource will return

            {id: 3, name:"Yellow"}

    The framework uses this method during data import, to map display values provided in import datasets like CSV files, to their proper corresponding key values
    Parameters:
      `fieldName` - The name of the field to obtain the related display record for
      `displayValue` - The display value to use for looking up the related display record
      `dsRequest` - The DSRequest for context
    Returns:
      The related display record, or null if the concept of a related display record is not applicable to the supplied field, or no related display record is found
    Throws:
      `Exception` - if an error occurs while retrieving the related display record

  - ### transformImportValue

    @Deprecated public Object transformImportValue(String fieldName, Object rawValue, String defaultStrategy) throws Exception
    Deprecated.
    This is still supported for calling, but the new API `transformImportValue(String, Object, String, DSRequest)` is the one that you must override if you want to customize the behavior.
    Parameters:
      `fieldName` - the field name for which to transform the import value
      `rawValue` - the raw value to transform
      `defaultStrategy` - the default import strategy to use
    Returns:
      the transformed value
    Throws:
      `Exception` - if an error occurs during value transformation
    See Also:
      - `transformImportValue(String, Object, String, DSRequest)`

  - ### transformImportValue

    public Object transformImportValue(String fieldName, Object rawValue, String defaultStrategy, DSRequest dsRequest) throws Exception
    Transforms the passed-in rawValue and returns the result of the transformation. This method is called during `data import`, immediately before values are translated to an appropriate data type and validated. The base implementation transforms display values in the import dataset to their underlying key values, for fields that have a "related display record". See `getRelatedDisplayRecord(String, Object)` for more details.

    You can add any transformations you require by overriding this method, but if you require the built-in displayValue-to-key transformation described above, be sure to invoke the super-method.

    Transformed values are cached during import process in a `DataImport` instance. All builtin tools use new `DataImport` instance, so this makes no effect on import results, except optimizing performance. But, if you end up using DataImport directly and want to use it for multiple imports, keep in mind that transformed values are cached and may affect the results.

    Parameters:
      `fieldName` - The name of the field for which are performing a transformation
      `rawValue` - The value to transform
      `defaultStrategy` - The importStrategy ("display" or "key") to use if this field declares an importStrategy of "auto", or does not declare an importStrategy. See the client-side `FieldImportStrategy` docs for details
      `dsRequest` - the DSRequest for context
    Returns:
      The key of the related display record, or `null` if the concept of a related display record is not applicable to the supplied field, or if no related display record is found: untransformed original `rawValue` for "display" importStrategy or `null` for "displayRequired" importStrategy.
    Throws:
      `Exception` - if an error occurs during value transformation

  - ### isServerOnly

    public boolean isServerOnly()
    Returns true if this datasource has been declared with an attribute of "serverOnly=true". Otherwise false.
    Returns:
      true if this is a server-side only DataSource.

  - ### transformResponse

    public void transformResponse(List untransformed, DSRequest ctxReq, DSResponse ctxResp) throws Exception
    Override point to allow your DataSource implementation to perform arbitrary transformations of the response data. You should perform the transformations in place on the List passed in to the method.

    If you prefer a script-based approach, search the client-side documentation for "transformResponseScript". Note, if you override this method AND provide a `transformResponseScript`, this method runs first, and any transformations it makes will be visible to the script

    Parameters:
      `untransformed` - the list of response data to transform
      `ctxReq` - the DSRequest for context
      `ctxResp` - the DSResponse for context
    Throws:
      `Exception` - if an error occurs during response transformation

  - ### getOperationProperty

    public static Object getOperationProperty(DSRequest request, String propertyName, Object defaultValue) throws Exception
    Returns the value of the specified operationBinding element as an Object. The operation in question is the one in use by the provided `DSRequest`.

    This is an operation level equivalent of the DataSource-level API, `getObjectProperty(String)`. Please read the docs for that API - all the notes also apply to this API.

    Parameters:
      `request` - DSRequest that indicates the operation to use
      `propertyName` - the name of the element(s)
      `defaultValue` - A default value
    Returns:
      the value of the specified property of the operation as an Object, or the default value if there is no operation, or the operation does not have such an element
    Throws:
      `Exception` - if an error occurs while getting the property
    See Also:
      - `getObjectProperty(String)`

  - ### is1ManyRelationField

    public boolean is1ManyRelationField(String fieldName)
    Returns true if the parameter is the name of a `one-to-many Relation Field` on this DataSource
    Parameters:
      `fieldName` - name of the field to check
    Returns:
      true if the field is a one-to-many relation field, otherwise false

  - ### isManyManyRelationField

    public boolean isManyManyRelationField(String fieldName)
    Returns true if the parameter is the name of a `many-to-many Relation Field` on this DataSource
    Parameters:
      `fieldName` - name of the field to check
    Returns:
      true if the field is a one-to-many relation field, otherwise false

  - ### isRelationField

    public boolean isRelationField(String fieldName)
    Returns true if the parameter is the name of a field on this DataSource that is either a `one-to-many` or `many-to-many` Relation Field
    Parameters:
      `fieldName` - name of the field to check
    Returns:
      true if the field is a relation field, otherwise false

  - ### get1ManyRelationFields

    public List<DSField> get1ManyRelationFields()
    Returns a List of all the `one-to-many` Relation Fields on this DataSource
    Returns:
      List of all 1-to-many relation fields; empty List if there are none

  - ### getManyManyRelationFields

    public List<DSField> getManyManyRelationFields()
    Returns a List of all the `many-to-many` Relation Fields on this DataSource
    Returns:
      List of all many-to-many relation fields; empty List if there are none

  - ### getRelationFields

    public List<DSField> getRelationFields()
    Returns a List of all the Relation Fields on this DataSource, both `one-to-many` and `many-to-many`
    Returns:
      List of all relation fields; empty List if there are none

  - ### has1ManyRelationFields

    public boolean has1ManyRelationFields()
    Returns true if this DataSource has at least one `one-to-many` Relation Field
    Returns:
      true if this DataSource has any one-to-many relation fields

  - ### hasManyManyRelationFields

    public boolean hasManyManyRelationFields()
    Returns true if this DataSource has at least one `many-to-many` Relation Field
    Returns:
      true if this DataSource has any many-to-many relation fields

  - ### hasRelationFields

    public boolean hasRelationFields()
    Returns true if this DataSource has at least one `one-to-many` or `many-to-many` Relation Field
    Returns:
      true if this DataSource has any relation fields

  - ### getRelatedDSName

    public String getRelatedDSName(String fieldName)
    If the parameter is the name of a `one-to-many` or `many-to-many` Relation Field, returns the name of the related DataSource
    Parameters:
      `fieldName` - the name of the field to check
    Returns:
      The name of the related DataSource if the parameter names a field that is a one-to-many or many-to-many relation field, otherwise null

  - ### getRelatedDSName

    public String getRelatedDSName(DSField field)
    If the parameter is a `one-to-many` or `many-to-many` Relation Field, returns the name of the related DataSource
    Parameters:
      `field` - the DSField to check
    Returns:
      The name of the related DataSource if the parameter is a one-to-many or many-to-many relation field, otherwise null

  - ### getJoinDSName

    public String getJoinDSName(String fieldName)
    If the parameter is the name of a `many-to-many` Relation Field on this DataSource, returns the name of the join DataSource - ie the third dataSource "in the middle" of the relationship
    Parameters:
      `fieldName` - the name of the field to check
    Returns:
      The name of the join DataSource if the parameter names a field that is a many-to-many relation field, otherwise null

  - ### getJoinDSName

    public String getJoinDSName(DSField field)
    If the parameter is a `many-to-many` Relation Field on this DataSource, returns the name of the join DataSource - ie the third dataSource "in the middle" of the relationship
    Parameters:
      `field` - the DSField to check
    Returns:
      The name of the join DataSource if the parameter is a many-to-many relation field, otherwise null