LegacyJavaAppserver.java
/*
** Module : LegacyJavaAppserver.java
** Abstract : Helper APIs to work with legacy JavaOpenClient.
**
** Copyright (c) 2019-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 CA 20190717 Created initial version.
** 002 CA 20191119 Added support for before-table.
** CA 20191125 Added support for relations.
** 003 CA 20200923 API improvements. Added extent support, other data-types. Allow access to the hidden
** temp-table fields.
** CA 20200927 Use IdentityHashMap instead of plain map when the key is a Class.
** CA 20201008 Added HANDLE type. Added addRelation API.
** OM 20201120 Added support for xmlNodeName, errorString temp-table attributes.
** CA 20210213 Modifying the signature of APIs in this class may result in incompatibilities with other
** existing code, either in TRPL rules (for the proxy generation code), or in 3rd party
** customer apps. Always leave the previous signature and mark it as deprecated.
** CA 20210626 Fixed computeUniqueConstraintsClauses - the dataset name must be null if is not defined.
** CA 20210630 If the dataset has a table definition and is in OUTPUT mode, send the table definition.
** CA 20210816 rowid values can be passed as string.
** CA 20210831 Allow clients to always enable the change logging, as for the .NET client OE uses a
** DataTable, which always track changes.
** CA 20210910 Changes to allow support for before-table returned from the remote request via an
** OUTPUT/INPUT-OUTPUT table or dataset.
** CA 20210917 Javadoc fixes.
** OM 20211020 Added _DATASOURCE_ROWID property.
** CA 20210104 Added support for denormalized extent fields, for .NET client compatibility.
** Fixed DATETIME-TZ field problem when the value is a Date, and not a GregorianCalendar.
** CA 20220110 Refactored the temp-table's row-meta state to use constants for the indexes in the array
** used for serialization.
** CA 20220216 Fixed issues with denormalized extent field names: they can collide with other legacy
** field names so the changes ensure they are unique.
** CA 20220615 Added 'getBeforeObjects'.
** CA 20220707 'getOutputParameter' must 'endLogging' only if changes are tracked by default.
** CA 20220923 Another case of 'getOutputParameter' must 'endLogging' only if changes are tracked
** by default.
** CA 20221031 Added JMX instrumentation for table metadata and parameter processing.
** CA 20220428 Fixed TABLE, TABLE-HANDLE, MEMPTR and extent parameter support. Replaced Double with
** BigDecimal and date(time(tz)) with GregorianCalendar. Fixed problems when invoking from
** outside of FWD server.
** CA 20220513 Added 'getTableIndex' helpers.
** CA 20220524 Added helpers which work with table metadata, to retrieve field information.
** CA 20220527 Added helpers for relation metadata.
** CA 20220604 Replaced the FWD proxy with the Java proxy, to allow easy usage from Tomcat.
** CA 20220830 Performance improvements for management of the table metadata (especially related to
** computing the SDO types associated with the table).
** CA 20220929 Added setNormalizedExtent.
** CA 20221006 Small performance improvement, avoid calling 'list.get' multiple times.
** CA 20221031 Added JMX instrumentation for table metadata and parameter processing.
** CA 20221130 Fixed GregorianCalendar conversion to 'date' or 'datetime' instances - in both cases, the
** timezone from the GregorianCalendar must be used at the constructor.
** SVL 20230113 Improved performance by replacing some "for-each" loops with indexed "for" loops.
** CA 20230206 Fixed getter/setter APIs used to access a GregorianCalendar field in a DataObject.
** 004 CA 20230221 Encapsulated the field and relation metadata in their own classes, to avoid the DataObject
** overhead and increase performance. Other performance optimizations.
** Added an option to not marshal the table schema - this needs more analysis, as currently
** this flag is per-FWD connection.
** 005 CA 20230712 Allow a null argument for the metadata at LegacyJavAppserver.getJavaParameter, when
** DATASET-HANDLE or TABLE-HANDLE arguments are built.
** 006 HC 20240222 Enabled JMX on FWD Client.
** 007 CA 20240305 Added namespace URI, namespace prefix and XML node-name for table/graph metadata in
** OpenClient.
** 008 GBB 20240423 Precision for decimal output params set to max.
** 009 CA 20240426 For OUTPUT dataset/table, if the argument has already the metadata set, re-initialize this
** metadata with the actual response argument, while keeping the metadata references intact.
** 010 CA 20240501 A before-table row in ROW-CREATED state will not transfer the full field, but default
** values to the remote side.
** 011 AD 20240509 In method getJavaParameter, now copying the dataset relations from the metadata as well.
** 012 CA 20240707 Performance improvement when building an output table parameter: use a list to collect the
** rows, and set them only once, as adding them one-by-one is expensive.
** 013 SP 20240717 Added safety check in getOutputParameter for TYPE_TABLE case when TableWraper is unknown.
** 014 CA 20240809 Skip using JMX timers when JMX_DEBUG flag is not set.
** 015 LS 20240807 In getOutputParameter, a map with the name and signature of each table is created
** and sent as parameter to initGraphMetadata.
** Updated DsTableDefinition and TableWrapper to use the table name from metadata.
** LS 20240819 Updated getOutputParameter so that, the comparison by name or signature will be done
** based on a flag.
** 016 SP 20241007 Fixed toJavaValue(): ensure no trailing zeros are on the right-side of the decimal
** CA 20241009 For .NET compatibility, a scale of at least one is guaranteed for decimal values.
** 017 AS 20250213 Added high level APIs for index management.
** 018 CA 20250424 .NET compatibiltiy still uses normalized extent fields when transferring them.
** 019 AP 20250513 Improved createDataGraph method to copy dataset relations.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.util;
import java.lang.reflect.*;
import java.math.*;
import java.util.*;
import com.goldencode.p2j.*;
import com.goldencode.p2j.cfg.*;
import com.goldencode.p2j.jmx.*;
import com.goldencode.p2j.persist.*;
import commonj.sdo.*;
/**
* The legacy Java Open Client for appserver relies on Tuscany SDO to create an in-memory DataSet.
* This class provides helper APIs which do not map on Tuscany SDO existing APIs, and also
* additional APIs to connect to the FWD appserver and perform calls.
* <p>
* This will be extended as more features of Java Open Client are required.
*/
public class LegacyJavaAppserver
implements LegacyJavaAppserverApi
{
/** Instrumentation for {@link #addTableMetaData}. */
private static final NanoTimer TABLE_META = NanoTimer.getInstance(FwdServerJMX.TimeStat.AppserverAddTableMetaData);
/** Instrumentation for {@link #getJavaParameter}. */
private static final NanoTimer JAVA_PARAM= NanoTimer.getInstance(FwdServerJMX.TimeStat.ApperverGetJavaParameter);
/** Instrumentation for {@link #getOutputParameter}. */
private static final NanoTimer OUTPUT_PARAM = NanoTimer.getInstance(FwdServerJMX.TimeStat.AppserverGetOutputParameter);
/** Mapping of an integer type to the {@link BaseDataType} type. */
private static final Map<Integer, Class<?>> legacyTypes = new IdentityHashMap<>();
/** Mapping of a {@link BaseDataType} type to its numeric type. */
private static final Map<Class<?>, Integer> types2Legacy = new IdentityHashMap<>();
/** Map of integer modes to their string representation in FWD. */
private static final Map<Integer, String> paramModes = new IdentityHashMap<>();
static
{
legacyTypes.put(TYPE_HANDLE, handle.class);
legacyTypes.put(TYPE_INTEGER, integer.class);
legacyTypes.put(TYPE_INT64, int64.class);
legacyTypes.put(TYPE_DECIMAL, decimal.class);
legacyTypes.put(TYPE_MEMPTR, MemoryBuffer.class);
legacyTypes.put(TYPE_CLOB, clob.class);
legacyTypes.put(TYPE_BLOB, blob.class);
legacyTypes.put(TYPE_CHARACTER, character.class);
legacyTypes.put(TYPE_LONGCHAR, longchar.class);
legacyTypes.put(TYPE_LOGICAL, logical.class);
legacyTypes.put(TYPE_DATE, date.class);
legacyTypes.put(TYPE_DATETIME, datetime.class);
legacyTypes.put(TYPE_DATETIMETZ, datetimetz.class);
legacyTypes.put(TYPE_RAW, raw.class);
legacyTypes.put(TYPE_ROWID, rowid.class);
legacyTypes.put(TYPE_RECID, recid.class);
legacyTypes.put(TYPE_UNKNOWN, unknown.class);
legacyTypes.put(TYPE_DATASET, DataSetContainer.class);
legacyTypes.put(TYPE_DATASET_HANDLE, DataSetContainer.class);
legacyTypes.put(TYPE_TABLE_HANDLE, TableWrapper.class);
paramModes.put(INPUT, "I");
paramModes.put(OUTPUT, "O");
paramModes.put(INPUT_OUTPUT, "U");
for (Integer key : legacyTypes.keySet())
{
types2Legacy.put(legacyTypes.get(key), key);
}
types2Legacy.put(DataSetContainer.class, TYPE_DATASET);
}
/** Flag indicating if the change logging is tracked by default, for all tables created. */
private boolean defaultTrackChanges = false;
/** Flag indicating if the extent fields are normalized or denormalized (expanded). */
private boolean normalizedExtent = true;
/**
* Flag indicating that the temp-table schema can be omitted during serialization, as the temp-table is
* being built from the OpenClient proxy generated code, which is always compatible with the target's table.
*/
private boolean useSchemaMarshalNone;
/** Flag indicating that the {@link BigDecimal} are 'forced digits'; required for .NET compatibility. */
private boolean forceDecimalDigits = false;
/**
* Implicit contstructor, initialize a Java-style legacy client (with {@link #normalizedExtent} set to
* <code>true</code>).
*/
public LegacyJavaAppserver()
{
this(true);
}
/**
* Initialize this client, by specifying the extent mode: normalized (for Java-style) or denormalized
* (for .NET-style).
*
* @param normalizedExtent
* The extent mode.
*/
public LegacyJavaAppserver(boolean normalizedExtent)
{
this.normalizedExtent = normalizedExtent;
}
/**
* Set the flags for .NET compatibility. This includes:
* <ul>
* <li>{@link #normalizedExtent} will be set to false, so denormalized fields are used</li>
* <li>{@link #forceDecimalDigits} will be enabled, so each {@link BigDecimal} will have a scale of at
* least 1</li>
* <li>{@link #defaultTrackChanges} will be enabled</li>
* <li>{@link DataSetSDOHelper#setMergeTables} will enable the 'merge table' behavior for table/dataset
* parameters.</li>
* </ul>
*/
public void setDotNetCompatibility()
{
this.normalizedExtent = false;
this.defaultTrackChanges = true;
this.forceDecimalDigits = true;
LegacyJavaAppserverClientProxy.configureMetadataHelper(true, false);
LegacyJavaAppserverClient.setMergeTables(true);
}
/**
* Mark this connection to not serialize the temp-table schema, as the source and destination always match,
* for OpenClient proxy calls.
*/
public void useSchemaMarshalNone()
{
this.useSchemaMarshalNone = true;
}
/**
* Set the {@link #normalizedExtent} flag.
*
* @param normalizedExtent
* The extent mode.
*/
public void setNormalizedExtent(boolean normalizedExtent)
{
this.normalizedExtent = normalizedExtent;
}
/**
* Set the {@link #defaultTrackChanges} flag.
*
* @param trackChanges
* <code>true</code> if all tables need to {@link #beginLogging}.
*/
public void setDefaultTrackChanges(boolean trackChanges)
{
this.defaultTrackChanges = trackChanges;
}
/**
* Get all the records for the table on the specified index, in the graph metadata.
*
* @param graph
* The graph.
* @param idx
* The table index.
*
* @return The list of rows.
*/
@Override
public List<DataObject> getDataObjects(DataGraph graph, int idx)
{
return DataSetSDOHelper.getDataObjects(graph, idx);
}
/**
* Get all the records for the table with the specified name, in this graph.
*
* @param graph
* The graph.
* @param table
* The table name.
*
* @return The list of rows.
*/
@Override
public List<DataObject> getDataObjects(DataGraph graph, String table)
{
return DataSetSDOHelper.getDataObjects(graph, table);
}
/**
* Get all the records for the specified table.
*
* @param tableMeta
* The table metadata.
*
* @return The list of rows.
*/
@Override
public List<DataObject> getDataObjects(DataObject tableMeta)
{
return DataSetSDOHelper.getDataObjects(tableMeta);
}
/**
* Get the before-table rows for this after-table.
*
* @param tableMeta
* The after-table metadata.
*
* @return The list of before-table rows.
*/
@Override
public List<DataObject> getBeforeObjects(DataObject tableMeta)
{
return DataSetSDOHelper.getBeforeObjects(tableMeta);
}
/**
* Get the child rows for this record.
*
* @param row
* The row.
* @param relation
* The child relation.
*
* @return The child rows.
*/
@Override
public List<DataObject> getChildRows(DataObject row, String relation)
{
return DataSetSDOHelper.getChildRows(row, relation);
}
/**
* Get the row's property on the given index as a {@link GregorianCalendar}.
*
* @param row
* The row.
* @param idx
* The property index.
*
* @return The calendar instance.
*/
@Override
public GregorianCalendar getGregorianCalendar(DataObject row, int idx)
{
GregorianCalendar gc = (GregorianCalendar) row.get(idx);
return gc;
}
/**
* Set the field's property on the given index from the {@link GregorianCalendar} instance.
*
* @param row
* The row.
* @param idx
* The property index.
* @param date
* The property's value.
*/
@Override
public void setGregorianCalendar(DataObject row, int idx, GregorianCalendar date)
{
row.set(idx, date);
}
/**
* Get the row's property as a {@link GregorianCalendar}.
*
* @param row
* The row.
* @param name
* The property name.
*
* @return The calendar instance.
*/
@Override
public GregorianCalendar getGregorianCalendar(DataObject row, String name)
{
GregorianCalendar gc = (GregorianCalendar) row.get(name);
return gc;
}
/**
* Create a new graph to hold a dataset. It holds just the root <code>graph</code> node.
*
* @return A graph with no metadata.
*/
@Override
public DataGraph createDataGraph()
{
// tracking-changes are not enabled for this graph - this API is used only for creating a dataset for
// output mode
return DataSetSDOHelper.createDataGraph();
}
/**
* Create a new metadata object to hold the table details.
*
* @param metadata
* The root graph node.
*
* @return The graph metadata object.
*/
@Override
public DataGraph createDataGraph(Object metadata)
{
DataObject origMetadata = null;
if (metadata instanceof DataObject)
{
DataObject md = (DataObject) metadata;
if (md.getContainer() == null)
{
DataGraph newGraph = DataSetSDOHelper.createDataGraph((DataObject) metadata);
if (defaultTrackChanges)
{
DataSetSDOHelper.beginLogging(newGraph);
}
return newGraph;
}
origMetadata = (DataObject) metadata;
}
else
{
// this must be a graph
DataGraph orig = (DataGraph) metadata;
origMetadata = DataSetSDOHelper.getMetaData(orig);
}
metadata = DataSetSDOHelper.createGraphMetaData(origMetadata);
DataGraph newGraph = DataSetSDOHelper.createDataGraph((DataObject) metadata);
// copy all tables
int nt = DataSetSDOHelper.getNumTables(origMetadata);
for (int i = 0; i < nt; i++)
{
DataObject origTable = DataSetSDOHelper.getTableMetaData(origMetadata, i);
String name = origTable.getString("name");
int numFields = origTable.getInt("numFields");
boolean b4img = origTable.getBoolean("b4img");
int numIndexes = origTable.getInt("numIndexes");
String indexes = origTable.getString("indexes");
String xmlns = origTable.getString("xmlns");
String xmlPrefix = origTable.getString("xmlPrefix");
String xmlNodeName = origTable.getString("xmlNodeName");
String errorString = origTable.getString("errorString");
boolean normalizedExtent = origTable.getBoolean("normalizedExtent");
DataObject newTable = DataSetSDOHelper.createTableMetaData(name, numFields, b4img,
numIndexes, indexes,
xmlns, xmlPrefix, xmlNodeName,
errorString,
normalizedExtent);
List<LegacyFieldMetaData> origFields = DataSetSDOHelper.getFieldMetaData(origTable);
List<LegacyFieldMetaData> fieldList = newTable.getList("fields");
for (int j = 0; j < origFields.size(); j++)
{
LegacyFieldMetaData origf = origFields.get(j);
DataSetSDOHelper.setFieldMetaData(fieldList, newTable, new LegacyFieldMetaData(origf));
}
DataSetSDOHelper.addTableMetaData((DataObject) metadata, newTable);
}
// copy relations
LegacyRelationMetaData[] relations = DataSetSDOHelper.getRelations((DataObject) origMetadata);
for(int i = 0; i < relations.length; i++)
{
LegacyRelationMetaData currentRelation = relations[i];
String name = currentRelation.getName();
String parentBuffer = currentRelation.getParent();
String childBuffer = currentRelation.getChild();
String whereString = currentRelation.getWhereString();
boolean active = currentRelation.isActive();
boolean parentId = currentRelation.isParentId();
String pairs = currentRelation.getPairs();
boolean fkeyHidden = currentRelation.isFkeyHidden();
boolean recursive = currentRelation.isRecursive();
boolean nested = currentRelation.isNested();
boolean reposition = currentRelation.isReposition();
DataSetSDOHelper.addRelation((DataObject) metadata, name, parentBuffer, childBuffer, whereString,
active, parentId, pairs, fkeyHidden, recursive, nested, reposition);
}
if (defaultTrackChanges)
{
DataSetSDOHelper.beginLogging(newGraph);
}
return newGraph;
}
/**
* Create a new row for the specified table. This row is not attached to the table - use
* {@link #addDataObject} to add it.
*
* @param graph
* The graph.
* @param table
* The table name.
*
* @return A new, empty, row.
*/
@Override
public DataObject createDataObject(DataGraph graph, String table)
{
return DataSetSDOHelper.createDataObject(graph, table);
}
/**
* Create a new row for the specified table. This row is not attached to the table - use
* {@link #addDataObject(DataObject, DataObject)} to add it.
*
* @param tableMetaData
* The table metadata.
*
* @return The new row.
*/
@Override
public DataObject createDataObject(DataObject tableMetaData)
{
return DataSetSDOHelper.createDataObject(tableMetaData);
}
/**
* Add the specified row to the graph.
*
* @param graph
* The field's
* @param row
* The row to add to the table.
*/
@Override
public void addDataObject(DataGraph graph, DataObject row)
{
DataSetSDOHelper.addDataObject(graph, row);
}
/**
* Add the specified row to the graph.
*
* @param tableMetaData
* The table metadata.
* @param row
* The row to add to the table.
*/
@Override
public void addDataObject(DataObject tableMetaData, DataObject row)
{
DataSetSDOHelper.addDataObject(tableMetaData, row);
}
/**
* Get the table name for a given row or table metadata.
*
* @param value
* The table row or table metadata.
*
* @return The table name.
*/
@Override
public String getTableName(DataObject value)
{
return DataSetSDOHelper.getTableName(value);
}
/**
* Get the table index for a given row or table metadata.
*
* @param value
* The table row or table metadata.
*
* @return The table index, 0-based.
*
* @throws NullPointerException
* If the row is not attached to a table.
*/
@Override
public int getTableIndex(DataObject value)
{
return DataSetSDOHelper.getTableIndex(value);
}
/**
* Get the table index for a given table name and data graph.
*
* @param graph
* The data graph.
* @param name
* The table name.
*
* @return The table index, 0-based.
*
* @throws IllegalArgumentException
* If the table is not part of this graph.
*/
@Override
public int getTableIndex(DataGraph graph, String name)
{
return DataSetSDOHelper.getTableIndex(graph, name);
}
/**
* Get the table index for a given table name and data graph metadata.
*
* @param metadata
* The graph metadata.
* @param name
* The table name.
*
* @return The table index, 0-based.
*
* @throws IllegalArgumentException
* If the table is not part of this graph.
*/
@Override
public int getTableIndex(DataObject metadata, String name)
{
return DataSetSDOHelper.getTableIndex(metadata, name);
}
/**
* Get the data graph name for the given table metadata.
*
* @param tableMetadata
* The table metadata.
*
* @return The data graph name or <code>null</code> if the table is not part of a graph.
*/
@Override
public String getGraphName(DataObject tableMetadata)
{
return DataSetSDOHelper.getGraphName(tableMetadata);
}
/**
* Get the legacy field metadata from the given table's metadata.
*
* @param tableMetadata
* The table metadata.
*
* @return The metadata for all the table's fields, in their definition order.
*/
@Override
public LegacyFieldMetaData[] getTableFields(DataObject tableMetadata)
{
List<LegacyFieldMetaData> fields = DataSetSDOHelper.getFieldMetaData(tableMetadata);
LegacyFieldMetaData[] res = new LegacyFieldMetaData[fields.size()];
for (int i = 0; i < res.length; i++)
{
res[i] = fields.get(i);
}
return res;
}
/**
* Get the legacy field metadata from the given row.
*
* @param row
* The row.
*
* @return The metadata for all the table's fields, in their definition order.
*/
@Override
public LegacyFieldMetaData[] getFields(DataObject row)
{
DataObject tableMetadata = row.getContainer();
return getTableFields(tableMetadata);
}
/**
* Get the number of fields for a given row.
*
* @param row
* The table row.
*
* @return The number of fields as specified in the table metadata.
*/
@Override
public int getFieldCount(DataObject row)
{
return DataSetSDOHelper.getFieldCount(row);
}
/**
* Get the field names for the specified table meta.
*
* @param tableMeta
* The table metadata.
*
* @return The field names.
*/
@Override
public String[] getTableFieldNames(DataObject tableMeta)
{
return DataSetSDOHelper.getTableFieldNames(tableMeta);
}
/**
* Get the number of fields for the specified table meta.
*
* @param tableMeta
* The table metadata.
*
* @return The field count.
*/
@Override
public int getTableFieldCount(DataObject tableMeta)
{
return DataSetSDOHelper.getTableFieldCount(tableMeta);
}
/**
* Get the name of the field on the given index in the metadata.
*
* @param row
* The table row. Must be attached to a table.
* @param i
* The field index.
*
* @return The field's name.
*/
@Override
public String getFieldName(DataObject row, int i)
{
return DataSetSDOHelper.getFieldName(row, i);
}
/**
* Get the type of the field on the given index in the metadata.
*
* @param row
* The table row. Must be attached to a table.
* @param i
* The field index.
*
* @return The field's type.
*/
@Override
public int getFieldType(DataObject row, int i)
{
return DataSetSDOHelper.getFieldType(row, i);
}
/**
* Check if this table has a field defined with that name.
*
* @param tableMetadata
* The table metadata.
* @param fieldName
* The field name.
*
* @return <code>true</code> if there is a field defined with this name.
*/
@Override
public boolean isTableField(DataObject tableMetadata, String fieldName)
{
return DataSetSDOHelper.isTableField(tableMetadata, fieldName);
}
/**
* Get the field type.
*
* @param tableMetadata
* The table metadata.
* @param fieldName
* The field name.
*
* @return The field type.
*/
@Override
public int getTableFieldType(DataObject tableMetadata, String fieldName)
{
return DataSetSDOHelper.getTableFieldType(tableMetadata, fieldName);
}
/**
* Get the field type.
*
* @param tableMeta
* The table metadata.
* @param fieldIndex
* The field index.
*
* @return See above.
*/
@Override
public int getTableFieldType(DataObject tableMeta, int fieldIndex)
{
return DataSetSDOHelper.getTableFieldType(tableMeta, fieldIndex);
}
/**
* Get all table names from the given graph.
*
* @param graph
* The graph.
*
* @return See above.
*/
@Override
public String[] getTableNames(DataGraph graph)
{
return DataSetSDOHelper.getTableNames(graph);
}
/**
* Get the number of tables for a graph.
*
* @param graph
* The graph.
*
* @return See above.
*/
@Override
public int getNumTables(DataGraph graph)
{
DataObject metadata = getMetaData(graph);
return metadata == null ? 0 : DataSetSDOHelper.getNumTables(metadata);
}
/**
* Get the number of tables for a graph metadata.
*
* @param metaData
* The graph metadata.
*
* @return See above.
*/
@Override
public int getNumTables(DataObject metaData)
{
return DataSetSDOHelper.getNumTables(metaData);
}
/**
* Get the metadata object for this graph, from the <code>metadata</code> property of the root
* object.
*
* @param graph
* The graph.
*
* @return The metadata object.
*/
@Override
public DataObject getMetaData(DataGraph graph)
{
return graph == null ? null : DataSetSDOHelper.getMetaData(graph);
}
/**
* Get the metadata for the table registered on the given index in the graph.
*
* @param metaData
* The graph metadata.
* @param i
* The table index.
*
* @return The table metadata.
*/
@Override
public DataObject getTableMetaData(DataObject metaData, int i)
{
return DataSetSDOHelper.getTableMetaData(metaData, i);
}
/**
* Get the metadata for the specified table.
*
* @param graph
* The graph.
* @param table
* The table name.
*
* @return The table metadata or <code>null</code> if the table is not part of this graph.
*/
@Override
public DataObject getTableMetaData(DataGraph graph, String table)
{
return DataSetSDOHelper.getTableMetaData(graph, table);
}
/**
* Get the metadata list for all tables.
*
* @param graph
* The graph.
*
* @return The metadata for all tables.
*/
@Override
public List<DataObject> getTableMetaData(DataGraph graph)
{
return DataSetSDOHelper.getTableMetaData(graph);
}
/**
* Create an object with a table metadata. This object will have the {@code tablemetadata} type and is not
* attached to the graph.
*
* @param name
* The table's name.
* @param numFields
* The number of fields.
* @param b4img
* Flag indicating if the table has a BEFORE-IMAGE.
* @param numIndexes
* The number of indexes.
* @param indexes
* The index specification (TODO: not validated/implemented at this time).
* @param xmlns
* The XML namespace (TODO: not validated/implemented at this time).
* @param xmlPrefix
* The XML prefix (TODO: not validated/implemented at this time).
*
* @return An object with the table metadata.
*/
@Override
@Deprecated
public DataObject createObjectMetaData(String name,
int numFields,
boolean b4img,
int numIndexes,
String indexes,
String xmlns,
String xmlPrefix)
{
return createObjectMetaData(name, numFields, b4img, numIndexes, indexes, xmlns, xmlPrefix, null, null);
}
/**
* Create an object with a table metadata. This object will have the {@code tablemetadata} type and is not
* attached to the graph.
*
* @param name
* The table's name.
* @param numFields
* The number of fields.
* @param b4img
* Flag indicating if the table has a BEFORE-IMAGE.
* @param numIndexes
* The number of indexes.
* @param indexes
* A list o LegacyIndexDefinition containing all the index information.
* @param xmlns
* The XML namespace (TODO: not validated/implemented at this time).
* @param xmlPrefix
* The XML prefix (TODO: not validated/implemented at this time).
*
* @return An object with the table metadata.
*/
public DataObject createObjectMetaData(String name,
int numFields,
boolean b4img,
int numIndexes,
List<LegacyIndexDefinition> indexes,
String xmlns,
String xmlPrefix)
{
return createObjectMetaData(name, numFields, b4img, numIndexes, DataSetSDOHelper.toFormattedString(indexes), xmlns, xmlPrefix, null, null);
}
/**
* Create an object with a table metadata. This object will have the {@code tablemetadata} type and is not
* attached to the graph.
*
* @param name
* The table's name.
* @param numFields
* The number of fields.
* @param b4img
* Flag indicating if the table has a BEFORE-IMAGE.
* @param numIndexes
* The number of indexes.
* @param indexes
* The index specification (TODO: not validated/implemented at this time).
* @param xmlns
* The XML namespace (TODO: not validated/implemented at this time).
* @param xmlPrefix
* The XML prefix (TODO: not validated/implemented at this time).
* @param xmlNodeName
* The XML node name (TODO: not validated/implemented at this time).
* @param errorString
* The table's ERROR-STRING attribute (TODO: not validated/implemented at this time).
*
* @return An object with the table metadata.
*/
@Override
public DataObject createObjectMetaData(String name,
int numFields,
boolean b4img,
int numIndexes,
String indexes,
String xmlns,
String xmlPrefix,
String xmlNodeName,
String errorString)
{
return DataSetSDOHelper.createTableMetaData(name, numFields, b4img,
numIndexes, indexes,
xmlns, xmlPrefix, xmlNodeName,
errorString,
normalizedExtent);
}
/**
* Create LegacyIndexDefinition objects from the formatted string in table meta
* containing all index information, components, primary/unique flags etc.
*
* @param tableMeta
* The table's metadata object.
* @return A list of LegacyIndexDefinition objects holding the metadata for each index
*/
public List<LegacyIndexDefinition> getLegacyIndexDefinition(DataObject tableMeta)
{
return DataSetSDOHelper.getLegacyIndexDefinition(tableMeta);
}
/**
* A method that returns the table's primary unique index.
*
* @param tableMeta
* The table's metadata object.
*/
public LegacyIndexDefinition getPrimaryUniqueIndex(DataObject tableMeta)
{
return DataSetSDOHelper.getPrimaryUniqueIndex(tableMeta);
}
/**
* A method that returns the LegacyIndexDefinition that has the specified legacy name.
* The requested index can be primary or just unique.
*
* @param indexName
* The string legacy name of the index.
* @param tableMeta
* The table's metadata object.
* @return The requested LegacyIndexDefinition object.
*/
public LegacyIndexDefinition getIndex(String indexName, DataObject tableMeta)
{
return DataSetSDOHelper.getIndex(indexName, tableMeta);
}
/**
* A method that marks an index as primary unique.
* The index should already be part of the indexes.
* The previous primary index will be set as just unique and
* will be swapped in the indexes with the newly set primary index.
*
* @param primaryUniqueIndexName
* Index legacy name that should be set as primary unique.
* @param tableMeta
* The table's metadata object.
* @return True if the primary unique index was set, or false if one was already present.
*/
public boolean setPrimaryUniqueIndex(String primaryUniqueIndexName, DataObject tableMeta)
{
return DataSetSDOHelper.setPrimaryUniqueIndex(primaryUniqueIndexName, tableMeta);
}
/**
* A method that adds an index to indexes.
*
* @param index
* The index to be added. Should not be primary.
* @param tableMeta
* The table's metadata object.
* @return True if the index was added successfully.
*/
public boolean addIndex(LegacyIndexDefinition index, DataObject tableMeta)
{
return DataSetSDOHelper.addIndex(index, tableMeta);
}
/**
* A method that removes any index from the indexes.
*
* @param indexName
* Legacy name of the index to be removed.
* @param tableMeta
* The table's metadata object.
*/
public void removeIndex(String indexName, DataObject tableMeta)
{
DataSetSDOHelper.removeIndex(indexName, tableMeta);
}
/**
* A method that adds a component (field) to the specified index.
*
* @param indexName
* Legacy name of the index.
* @param field
* The field to be added as a new component.
* @param tableMeta
* The table's metadata object.
* @return True if the new component was successfully added.
*/
public boolean addComponentToIndex(String indexName, String field, DataObject tableMeta)
{
return DataSetSDOHelper.addComponentToIndex(indexName, field, tableMeta);
}
/**
* A method that removes a component (field) to the specified index.
*
* @param indexName
* Legacy name of the index.
* @param field
* The field to be removed from the index.
* @param tableMeta
* The table's metadata object.
* @return True if the new component was successfully removed.
*/
public boolean removeComponentFromIndex(String indexName, String field, DataObject tableMeta)
{
return DataSetSDOHelper.removeComponentFromIndex(indexName, field, tableMeta);
}
/**
* A method that converts a List of IndexDefinition
* objects to single a formatted string represtation.
*
* @param indexes
* A list o IndexDefinition containing all the index information.
* @return Formatted string that contains all the index information.
* The format is specified in progress documentation:
* [primeUniqueFlag,primeFld1[,primeFldn]...:primeIdxName.]
*/
public String toFormattedString(List<LegacyIndexDefinition> indexes)
{
return DataSetSDOHelper.toFormattedString(indexes);
}
/**
* Add a new new field with the specified configuration.
*
* @param metaData
* The table metadata, of type <code>tablemetadata</code>. The field will be added
* to the <code>fields</code> property.
* @param idx
* The field's index in the metadata.
* @param name
* The field's name.
* @param extent
* The field's extent.
* @param type
* The legacy type (numeric value based on {@link LegacyJavaAppserver} constants.
* @param order
* The field's order.
* @param xmlMapping
* The field's mapping.
*/
@Override
public void setFieldMetaData(DataObject metaData,
int idx,
String name,
int extent,
int type,
int order,
int xmlMapping)
{
Class<?> cls = legacyTypes.get(type);
DataSetSDOHelper.setFieldMetaData(metaData, idx, name, extent, cls, type, order, xmlMapping);
}
/**
* Create a metadata object with the specified name.
*
* @param name
* The graph name.
*
* @return The metadata object. Will be attached to a graph via {@link #createDataGraph(Object)}.
*/
@Override
public DataObject createGraphMetaData(String name)
{
return DataSetSDOHelper.createGraphMetaData(name);
}
/**
* Add the specified table metadata to the graph. This will create a custom type based on the
* configured fields, so that each row will be created with this type.
*
* @param metaData
* The graph metadata.
* @param table
* The table metadata.
*/
@Override
public void addTableMetaData(DataObject metaData, DataObject table)
{
if (FwdServerJMX.JMX_DEBUG)
{
TABLE_META.timer(() -> DataSetSDOHelper.addTableMetaData(metaData, table));
}
else
{
DataSetSDOHelper.addTableMetaData(metaData, table);
}
}
/**
* Copy the single table from the provided DataGraph to the given graph metadata (i.e. attach a table
* to a data-set).
*
* @param metaData
* The graph metadata.
* @param table
* The table representation, in a {@link #createDataGraph} format.
*/
@Override
public void addTableMetaData(DataObject metaData, DataGraph table)
{
String[] tables = getTableNames(table);
addTableMetaData(metaData, getTableMetaData(table, tables[0]));
}
/**
* Get the graph name from the specified metadata, of type <code>graphmetadata</code>.
*
* @param metadata
* The metadata.
*
* @return The graph name.
*/
@Override
public String getDataGraphName(DataObject metadata)
{
return DataSetSDOHelper.getDataGraphName(metadata);
}
/**
* Get the namespace URI from the given metadata.
*
* @param metadata
* The graph or table metadata.
*
* @return See above.
*/
@Override
public String getNamespaceURI(DataObject metadata)
{
return metadata.getString("xmlns");
}
/**
* Set the namespace URI for the given metadata.
*
* @param metadata
* The graph or table metadata.
* @param xmlns
* The namespace URI.
*/
@Override
public void setNamespaceURI(DataObject metadata, String xmlns)
{
if (xmlns == null)
{
metadata.unset("xmlns");
}
else
{
metadata.setString("xmlns", xmlns);
}
}
/**
* Get the namespace prefix from the given metadata.
*
* @param metadata
* The graph or table metadata.
*
* @return See above.
*/
@Override
public String getNamespacePrefix(DataObject metadata)
{
return metadata.getString("xmlPrefix");
}
/**
* Set the namespace prefix for the given metadata.
*
* @param metadata
* The graph or table metadata.
* @param xmlPrefix
* The namespace prefix.
*/
@Override
public void setNamespacePrefix(DataObject metadata, String xmlPrefix)
{
if (xmlPrefix == null)
{
metadata.unset("xmlPrefix");
}
else
{
metadata.setString("xmlPrefix", xmlPrefix);
}
}
/**
* Get the XML node-name from the given metadata.
*
* @param metadata
* The graph or table metadata.
*
* @return See above.
*/
@Override
public String getXmlNodeName(DataObject metadata)
{
return metadata.getString("xmlNodeName");
}
/**
* Set the XML node-name for the given metadata.
*
* @param metadata
* The graph or table metadata.
* @param xmlNodeName
* The XML node name.
*/
@Override
public void setXmlNodeName(DataObject metadata, String xmlNodeName)
{
if (xmlNodeName == null)
{
metadata.unset("xmlNodeName");
}
else
{
metadata.setString("xmlNodeName", xmlNodeName);
}
}
/**
* Configure a {@link LegacyJavaAppserverParameter} with the given details.
*
* @param mode
* The parameter mode.
* @param type
* The parameter type.
* @param value
* The parameter value.
* @param metadata
* The parameter metadata (in case of DataSet).
*
* @return A new parameter instance.
*/
@Override
public LegacyJavaAppserverParameter getJavaParameter(int mode, int type, Object value, Object metadata)
{
return getJavaParameter(mode, type, SourceNameMapper.NO_EXTENT, value, metadata);
}
/**
* Configure a {@link LegacyJavaAppserverParameter} with the given details.
*
* @param mode
* The parameter mode.
* @param type
* The parameter type.
* @param extent
* The parameter's extent.
* @param pvalue
* The parameter value.
* @param metadata
* The parameter metadata (in case of DataSet).
*
* @return A new parameter instance.
*/
@Override
public LegacyJavaAppserverParameter getJavaParameter(int mode,
int type,
int extent,
Object pvalue,
Object metadata)
{
if (FwdServerJMX.JMX_DEBUG)
{
LegacyJavaAppserverParameter[] res = new LegacyJavaAppserverParameter[1];
JAVA_PARAM.timer(() -> res[0] = getJavaParameterImpl(mode, type, extent, pvalue, metadata));
return res[0];
}
else
{
return getJavaParameterImpl(mode, type, extent, pvalue, metadata);
}
}
/**
* Configure a {@link LegacyJavaAppserverParameter} with the given details.
*
* @param mode
* The parameter mode.
* @param type
* The parameter type.
* @param extent
* The parameter's extent.
* @param pvalue
* The parameter value.
* @param metadata
* The parameter metadata (in case of DataSet).
*
* @return A new parameter instance.
*/
private LegacyJavaAppserverParameter getJavaParameterImpl(int mode,
int type,
int extent,
Object pvalue,
Object metadata)
{
Object value = pvalue;
Object vmetadata;
if (value == null && (type == TYPE_DATASET_HANDLE || type == TYPE_TABLE_HANDLE))
{
value = createDataGraph();
vmetadata = getMetaData((DataGraph) value);
}
else
{
vmetadata = metadata;
}
Object orig = value;
boolean input = mode == INPUT || mode == INPUT_OUTPUT;
boolean output = mode == OUTPUT || mode == INPUT_OUTPUT;
if (type == TYPE_DATASET || type == TYPE_DATASET_HANDLE)
{
List<DsTableDefinition> tableDefinitions = new ArrayList<>();
List<DsRelationDefinition> relationDefinitions = new ArrayList<>();
DataObject graphMeta = (DataObject) vmetadata;
String[] tables = graphMeta == null ? new String[0]
: DataSetSDOHelper.getTableNames(graphMeta);
for (int i = 0; i < tables.length; i++)
{
DataObject tableMeta = DataSetSDOHelper.getTableMetaData(graphMeta, i);
List<DataObject> rows = input ? DataSetSDOHelper.getDataObjects(tableMeta)
: Collections.emptyList();
List<LegacyFieldMetaData> fields = DataSetSDOHelper.getFieldMetaData(tableMeta);
DsTableDefinition dstable = createDsTable(rows, fields, input, output, false);
if (useSchemaMarshalNone && type == TYPE_DATASET)
{
dstable.setSchemaMarshalLevel(AbstractTempTable.SM_NONE);
}
dstable.setName(tables[i]);
dstable.setIndexes(tableMeta.getInt("numIndexes"), tableMeta.getString("indexes"));
dstable.setXmlns(tableMeta.getString("xmlns"));
dstable.setXmlPrefix(tableMeta.getString("xmlPrefix"));
dstable.setXmlNodeName(tableMeta.getString("xmlNodeName"));
dstable.setErrorString(tableMeta.getString("errorString"));
tableDefinitions.add(dstable);
if (tableMeta.getBoolean("b4img"))
{
List<DataObject> beforeRows = input ? DataSetSDOHelper.getBeforeObjects(tableMeta)
: Collections.emptyList();
DsTableDefinition b4table = createDsTable(beforeRows, fields, input, output, true);
if (useSchemaMarshalNone && type == TYPE_DATASET)
{
b4table.setSchemaMarshalLevel(AbstractTempTable.SM_NONE);
}
// TODO: here we must not exceed 32 characters...
b4table.setName("BI" + tables[i]);
dstable.setPeerDef(b4table);
}
}
if (graphMeta != null)
{
List<LegacyRelationMetaData> lrmds = graphMeta.getList("relations");
DsRelationDefinition relationDef;
LegacyRelationMetaData lrmd;
for (int i = 0; i < lrmds.size(); i++)
{
lrmd = lrmds.get(i);
relationDef = new DsRelationDefinition();
relationDef.setActive(lrmd.isActive());
relationDef.setChildBuffer(lrmd.getChild());
relationDef.setFieldPairs(lrmd.getPairs());
relationDef.setFkeyHidden(lrmd.isFkeyHidden());
relationDef.setName(lrmd.getName());
relationDef.setNested(lrmd.isNested());
relationDef.setParentBuffer(lrmd.getParent());
relationDef.setParentId(lrmd.isParentId());
relationDef.setRecursive(lrmd.isRecursive());
relationDef.setReposition(lrmd.isReposition());
relationDef.setWhereString(lrmd.getWhereString());
relationDefinitions.add(relationDef);
}
}
DataSetContainer dsc = new DataSetContainer(input, output, false);
String name = graphMeta == null ? null : DataSetSDOHelper.getDataGraphName(graphMeta);
dsc.setStructureName(name);
dsc.setTableDefinitions(tableDefinitions);
dsc.setRelationDefinitions(relationDefinitions.isEmpty() ? null : relationDefinitions);
if (graphMeta != null)
{
dsc.namespaceUri(graphMeta.getString("xmlns"));
dsc.namespacePrefix(graphMeta.getString("xmlPrefix"));
dsc.setXmlNodeName(graphMeta.getString("xmlNodeName"));
}
value = dsc;
}
else if (type == TYPE_TABLE || type == TYPE_TABLE_HANDLE)
{
DataGraph graph = (DataGraph) value;
DataObject graphMeta = DataSetSDOHelper.getMetaData(graph);
String[] tables = graphMeta == null ? null
: DataSetSDOHelper.getTableNames(graphMeta);
if (tables != null)
{
DataObject tableMeta = DataSetSDOHelper.getTableMetaData(graphMeta, 0);
List<DataObject> rows = input ? DataSetSDOHelper.getDataObjects(tableMeta)
: Collections.emptyList();
List<LegacyFieldMetaData> fields = DataSetSDOHelper.getFieldMetaData(tableMeta);
TableResultSet ttrs = createTableResultSet(rows, fields, input, output, false);
ttrs.setStructureName(tables[0]);
TableWrapper wrapper = new TableWrapper(ttrs);
if (useSchemaMarshalNone && type == TYPE_TABLE)
{
wrapper.setSchemaMarshalLevel(AbstractTempTable.SM_NONE);
}
wrapper.setTableName(tables[0]);
wrapper.setNumIndexes(tableMeta.getInt("numIndexes"));
wrapper.setIndexes(tableMeta.getString("indexes"));
wrapper.setXmlns(tableMeta.getString("xmlns"));
wrapper.setXmlPrefix(tableMeta.getString("xmlPrefix"));
wrapper.setXmlNodeName(tableMeta.getString("xmlNodeName"));
wrapper.setErrorString(tableMeta.getString("errorString"));
if (tableMeta.getBoolean("b4img"))
{
List<DataObject> beforeRows = input ? DataSetSDOHelper.getBeforeObjects(tableMeta)
: Collections.emptyList();
TableResultSet bttrs = createTableResultSet(beforeRows, fields, input, output, true);
TableWrapper b4table = new TableWrapper(bttrs);
if (useSchemaMarshalNone && type == TYPE_TABLE)
{
b4table.setSchemaMarshalLevel(AbstractTempTable.SM_NONE);
}
b4table.setTableName("BI" + tables[0]);
wrapper.setPeerDef(b4table);
}
value = wrapper;
}
else
{
value = new TableWrapper();
}
}
else if (type == TYPE_MEMPTR)
{
// this must always be MemoryBuffer
if (value != null &&
!((value instanceof MemoryBuffer ||
(value.getClass().isArray() && value.getClass().getComponentType() == MemoryBuffer.class))))
{
throw new IllegalArgumentException("memptr must be MemoryBuffer!");
}
else if (value == null)
{
if (extent == 0 || extent == SourceNameMapper.NO_EXTENT)
{
value = new MemoryBuffer(new byte[0]);
}
else if (extent == SourceNameMapper.DYNAMIC_EXTENT)
{
value = new MemoryBuffer[0];
}
else
{
value = new MemoryBuffer[extent];
for (int i = 0; i < extent; i++)
{
Array.set(value, i, new MemoryBuffer(new byte[0]));
}
}
}
}
else
{
value = fromJavaType(value, type, extent, output);
}
return new LegacyJavaAppserverParameter(type, mode, value, orig);
}
/**
* Determine the output parameter value.
*
* @param param
* The parameter as received from the remote side.
*
* @return The parameter's output value.
*/
@Override
public Object getOutputParameter(LegacyJavaAppserverParameter param)
{
if (FwdServerJMX.JMX_DEBUG)
{
Object[] res = new Object[1];
OUTPUT_PARAM.timer(() -> res[0] = getOutputParameterImpl(param));
return res[0];
}
else
{
return getOutputParameterImpl(param);
}
}
/**
* Determine the output parameter value.
*
* @param param
* The parameter as received from the remote side.
*
* @return The parameter's output value.
*/
private Object getOutputParameterImpl(LegacyJavaAppserverParameter param)
{
int type = param.getType();
Object arg = param.getValue();
switch (type)
{
case TYPE_DATASET:
case TYPE_DATASET_HANDLE:
DataGraph graph = (DataGraph) param.getOrig();
DataSetContainer dsc = (DataSetContainer) param.getValue();
DataObject metadata = DataSetSDOHelper.getMetaData(graph);
boolean reuseMetadata = (metadata != null);
if (reuseMetadata)
{
if (dsc.isValid())
{
if(!DataSetSDOHelper.isMergeTables())
{
List<String> tableNames = new ArrayList<>();
for (DsTableDefinition dst : dsc.getTableDefinitions())
{
tableNames.add(dst.getName());
}
// re-build the structures inside the metadata reference
DataSetSDOHelper.initGraphMetaData(metadata,
dsc.getStructureName(),
dsc.namespacePrefix(),
dsc.namespaceURI(),
dsc.getXmlNodeName(),
tableNames);
}
else
{
// create a map with the signature of each table from dataset
Map<String, List<Map<String, String>>> tableSignatures = new LinkedHashMap<>();
for (DsTableDefinition dst : dsc.getTableDefinitions())
{
List<Map<String, String>> signature = getSignature(dst.getProperties());
tableSignatures.put(dst.getName(), signature);
}
// re-build the structures inside the metadata reference
Map<String, String> namePairs = DataSetSDOHelper.initGraphMetaData(
metadata,
dsc.getStructureName(),
dsc.namespacePrefix(),
dsc.namespaceURI(),
dsc.getXmlNodeName(),
tableSignatures);
// update DsTableDefinition to use the table name from metadata
if (!namePairs.isEmpty())
{
for (DsTableDefinition dst : dsc.getTableDefinitions())
{
String newTableName = namePairs.get(dst.getName());
if (newTableName != null)
{
dst.setName(newTableName);
}
}
}
}
}
else
{
// clear the rows
DataSetSDOHelper.clearAllRows(metadata);
}
}
else
{
//create metadata
metadata = DataSetSDOHelper.createGraphMetaData(dsc.getStructureName(),
dsc.namespacePrefix(),
dsc.namespaceURI(),
dsc.getXmlNodeName());
DataSetSDOHelper.setGraphMetaData(graph, metadata);
}
if (dsc.isValid())
{
for (DsRelationDefinition dsr : dsc.getRelationDefinitions())
{
DataSetSDOHelper.addRelation(metadata,
dsr.getName(),
dsr.getParentBuffer(),
dsr.getChildBuffer(),
dsr.getWhereString(),
dsr.isActive(),
dsr.isParentId(),
dsr.getFieldPairs(),
dsr.isFkeyHidden(),
dsr.isRecursive(),
dsr.isNested(),
dsr.isReposition());
}
}
// end the logging
if (defaultTrackChanges)
{
DataSetSDOHelper.endLogging(graph);
}
if (dsc.isValid())
{
for (DsTableDefinition dst : dsc.getTableDefinitions())
{
createOutputTable(graph, dst, reuseMetadata, false);
DsTableDefinition b4dst = dst.getPeerDef();
if (b4dst != null)
{
createOutputTable(graph, b4dst, reuseMetadata, true);
}
}
}
if (defaultTrackChanges)
{
graph.getChangeSummary().beginLogging();
}
return graph;
case TYPE_TABLE:
case TYPE_TABLE_HANDLE:
graph = (DataGraph) param.getOrig();
TableWrapper wrapper = (TableWrapper) param.getValue();
metadata = DataSetSDOHelper.getMetaData(graph);
reuseMetadata = (metadata != null);
if (reuseMetadata)
{
if (wrapper.isValid())
{
if (!DataSetSDOHelper.isMergeTables())
{
List<String> tableNames = new ArrayList<>();
tableNames.add(wrapper.getTableName());
// re-build the structures inside the metadata reference
DataSetSDOHelper.initGraphMetaData(metadata,
wrapper.getTableName(),
null,
null,
null,
tableNames);
}
else
{
// create a map with the signature of the table
Map<String, List<Map<String, String>>> tableSignatures = new LinkedHashMap<>();
List<Map<String, String>> signature = getSignature(wrapper.getProperties());
tableSignatures.put(wrapper.getTableName(), signature);
// re-build the structures inside the metadata reference
Map<String, String> namePairs = DataSetSDOHelper.initGraphMetaData(
metadata,
wrapper.getTableName(),
null,
null,
null,
tableSignatures);
// update TableWrapper to use the table name from metadata
if (!namePairs.isEmpty())
{
String newTableName = namePairs.get(wrapper.getTableName());
if (newTableName != null)
{
wrapper.setTableName(newTableName);
}
}
}
}
else
{
// clear the rows
DataSetSDOHelper.clearAllRows(metadata);
}
}
else
{
//create metadata
metadata = DataSetSDOHelper.createGraphMetaData(wrapper.getTableName());
DataSetSDOHelper.setGraphMetaData(graph, metadata);
}
// end the logging
if (defaultTrackChanges)
{
DataSetSDOHelper.endLogging(graph);
}
if (wrapper.isValid())
{
createOutputTable(graph, wrapper, reuseMetadata, false);
TableWrapper b4wrapper = wrapper.getPeerDef();
if (b4wrapper != null)
{
createOutputTable(graph, b4wrapper, reuseMetadata, true);
}
}
if (defaultTrackChanges)
{
graph.getChangeSummary().beginLogging();
}
return graph;
}
int extent = SourceNameMapper.NO_EXTENT;
if (param.getValue().getClass().isArray())
{
extent = Array.getLength(param.getValue());
}
return toJavaValue(arg, type, false, extent);
}
/**
* Determine the output parameter value on the given index.
*
* @param paramArray
* The parameter array.
* @param i
* The parameter's index.
*
* @return The parameter's output value.
*/
@Override
public Object getOutputParameter(LegacyJavaAppserverParameter[] paramArray, int i)
{
return getOutputParameter(paramArray[i]);
}
/**
* Get a string representation of the argument's modes.
*
* @param paramArray
* The parameter array.
*
* @return The parameter modes.
*/
@Override
public String getArgumentModes(LegacyJavaAppserverParameter[] paramArray)
{
StringBuilder modes = new StringBuilder();
for (LegacyJavaAppserverParameter ljcp : paramArray)
{
modes.append(paramModes.get(ljcp.getMode()));
}
return modes.toString();
}
/**
* Resolve the arguments from the given parameter array.
*
* @param paramArray
* The parameter array.
*
* @return The arguments.
*/
@Override
public Object[] getArguments(LegacyJavaAppserverParameter[] paramArray)
{
Object[] params = new Object[paramArray.length];
int idx = 0;
for (LegacyJavaAppserverParameter ljcp : paramArray)
{
params[idx++] = ljcp.getValue();
}
return params;
}
/**
* Create a memptr corresponding instance.
*
* @param value
* The bytes.
*
* @return A {@link MemoryBuffer}.
*/
@Override
public Object createMemptr(byte[] value)
{
return new MemoryBuffer(value);
}
/**
* Check if the given parameter is a memptr.
*
* @param outputParameter
* The parameter.
*
* @return <code>true</code> if the value is a {@link MemoryBuffer}.
*/
@Override
public boolean isMemptr(Object outputParameter)
{
return outputParameter instanceof MemoryBuffer;
}
/**
* Get the bytes from the given output parameter, which is a {@link MemoryBuffer}.
*
* @param outputParameter
* The output parameter.
*
* @return See above.
*/
@Override
public byte[] getBytes(Object outputParameter)
{
return ((MemoryBuffer) outputParameter).getValue();
}
/**
* Create a new FWd connection with the specified configuration.
*
* @param config
* The configuration for the FWD server connection.
* @param account
* The FWD account to authenticate on the remote side.
* @param appServerName
* The name of the appserver to which the connection will be established.
* @param sessionFree
* <code>true</code> to indicate a session-free operating mode, <code>false</code>
* to indicate a session-managed operating mode.
* @param user
* The username passed to the appserver's connect procedure.
* @param pwd
* The password passed to the appserver's connect procedure.
* @param serverInfo
* The info passed to the appserver's connect procedure.
*
* @return The helper with the connection result (which was established or not).
*
* @throws NumberedException
* If there are connection problems.
*/
@Override
public AppServerHelper getConnection(BootstrapConfig config,
String account,
String appServerName,
boolean sessionFree,
String user,
String pwd,
String serverInfo)
throws NumberedException
{
return AppServerHelper.connect(config, account, appServerName, sessionFree, user, pwd, serverInfo);
}
/**
* Postprocess these arguments - this includes copying state back to the
* {@link LegacyJavaAppserverParameter} instance, for OUTPUT parameters.
*
* @param paramArray
* The parameter array.
* @param args
* The returned arguments.
*/
@Override
public void postProcessArguments(Object paramArray, Object[] args)
{
Object[] params = (Object[]) paramArray;
for (int i = 0; i < params.length; i++)
{
LegacyJavaAppserverParameter ljcp = (LegacyJavaAppserverParameter) params[i];
ljcp.setValue(args[i]);
}
}
/**
* Get the changes in this change summary, as a graph.
*
* @param changeSummary
* The change summary.
*
* @return See above.
*/
@Override
public DataGraph getChanges(Object changeSummary)
{
return ((ChangeSummary) changeSummary).getDataGraph();
}
/**
* End the logging for changes in this object.
*
* @param changeSummary
* The change summary.
*/
@Override
public void endLogging(Object changeSummary)
{
DataSetSDOHelper.endLogging(((ChangeSummary) changeSummary).getDataGraph());
}
/**
* Begin the logging for changes in this object.
*
* @param changeSummary
* The change summary.
*/
@Override
public void beginLogging(Object changeSummary)
{
DataSetSDOHelper.beginLogging(((ChangeSummary) changeSummary).getDataGraph());
}
/**
* Get the change summary object for this graph.
*
* @param graph
* The graph.
*
* @return See above.
*/
@Override
public Object getChangeSummary(DataGraph graph)
{
return graph.getChangeSummary();
}
/**
* Check if the given object has been deleted.
*
* @param changeSummary
* The change summary.
* @param object
* The object.
*
* @return See above.
*/
@Override
public boolean isDeleted(Object changeSummary, DataObject object)
{
return ((ChangeSummary) changeSummary).isDeleted(object);
}
/**
* Check if the given object is newly created.
*
* @param changeSummary
* The change summary.
* @param object
* The object.
*
* @return See above.
*/
@Override
public boolean isCreated(Object changeSummary, DataObject object)
{
return ((ChangeSummary) changeSummary).isCreated(object);
}
/**
* Get all the changed objects in this change summary.
*
* @param changeSummary
* The change summary.
*
* @return See above.
*/
@Override
public List<DataObject> getChangedDataObjects(Object changeSummary)
{
return ((ChangeSummary) changeSummary).getChangedDataObjects();
}
/**
* Retrieve all rows which have an exact match for the specified fields.
*
* @param tableMetadata
* The table metadata.
* @param fields
* The field list.
* @param values
* The values to be matched.
*
* @return See above.
*/
@Override
public List<DataObject> selectRows(DataObject tableMetadata, String[] fields, Object[] values)
{
return DataSetSDOHelper.selectRows(tableMetadata, fields, values);
}
/**
* Add a relation to the graph's metadata.
*
* @param metaData
* The graph metadata.
* @param name
* The relation name.
* @param parentBuffer
* The parent buffer.
* @param childBuffer
* The child buffer.
* @param whereString
* The where string.
* @param active
* Flag indicating if this relation is active.
* @param parentId
* Flag indicating if parent-id is used.
* @param pairs
* The field pairs, in <code>parent, child</code> format.
* @param fkeyHidden
* Flag indicating if fkey-hidden is used.
* @param recursive
* Flag indicating if recursive is used.
* @param nested
* Flag indicating if nested is used.
* @param reposition
* Flag indicating if reposition is used.
*/
@Override
public void addRelation(DataObject metaData,
String name,
String parentBuffer,
String childBuffer,
String whereString,
boolean active,
boolean parentId,
String pairs,
boolean fkeyHidden,
boolean recursive,
boolean nested,
boolean reposition)
{
DataSetSDOHelper.addRelation(metaData,
name,
parentBuffer,
childBuffer,
whereString,
active,
parentId,
pairs,
fkeyHidden,
recursive,
nested,
reposition);
}
/**
* Get the relation metadata with the given name.
*
* @param graph
* The data graph.
* @param name
* The relation name.
*
* @return The relation metadata, or <code>null</code> if it does not exist.
*/
@Override
public LegacyRelationMetaData getRelation(DataGraph graph, String name)
{
return DataSetSDOHelper.getRelation(graph, name);
}
/**
* Get the relation metadata with the given name.
*
* @param metaData
* The graph metadata.
* @param name
* The relation name.
*
* @return The relation metadata, or <code>null</code> if it does not exist.
*/
@Override
public LegacyRelationMetaData getRelation(DataObject metaData, String name)
{
return DataSetSDOHelper.getRelation(metaData, name);
}
/**
* Get the all relation metadata from the given graph.
*
* @param graph
* The data graph.
*
* @return The relations metadata.
*/
@Override
public LegacyRelationMetaData[] getRelations(DataGraph graph)
{
return DataSetSDOHelper.getRelations(graph);
}
/**
* Get the all relation metadata from the given graph.
*
* @param metaData
* The graph metadata.
*
* @return The relations metadata.
*/
@Override
public LegacyRelationMetaData[] getRelations(DataObject metaData)
{
return DataSetSDOHelper.getRelations(metaData);
}
/**
* Create and populate a table from the results received from the request.
* <p>
* If {@link #defaultTrackChanges} is set, then logging is enabled for all rows.
*
* @param graph
* The graph where to add the table.
* @param wrapper
* The table data.
* @param reuseMetadata
* Flag indicating if the table already exists in the graph.
* @param beforeTable
* Flag indicating if this is a before table.
*/
private void createOutputTable(DataGraph graph,
TableWrapper wrapper,
boolean reuseMetadata,
boolean beforeTable)
{
DataObject metadata = DataSetSDOHelper.getMetaData(graph);
ArrayList<PropertyDefinition> props = wrapper.getProperties();
String tableName = beforeTable ? wrapper.getPeerDef().getTableName() : wrapper.getTableName();
if (!beforeTable)
{
DataObject tableMeta = null;
if (reuseMetadata)
{
// re-built the structures inside the metadata reference
tableMeta = getTableMetaData(graph, tableName);
DataSetSDOHelper.initTableMetaData(tableMeta,
tableName,
props.size(),
wrapper.getPeerDef() != null,
wrapper.getNumIndexes(),
wrapper.getIndexes(),
wrapper.getXmlns(),
wrapper.getXmlPrefix(),
wrapper.getXmlNodeName(),
wrapper.getErrorString(),
normalizedExtent);
}
else
{
tableMeta = DataSetSDOHelper.createTableMetaData(tableName,
props.size(),
wrapper.getPeerDef() != null,
wrapper.getNumIndexes(),
wrapper.getIndexes(),
wrapper.getXmlns(),
wrapper.getXmlPrefix(),
wrapper.getXmlNodeName(),
wrapper.getErrorString(),
normalizedExtent);
}
List<LegacyFieldMetaData> fieldList = tableMeta.getList("fields");
int idx = 1; // this is 1-based, this is how .xpxg proxies are emitted.
for (int i = 0; i < props.size(); i++)
{
PropertyDefinition prop = props.get(i);
LegacyFieldMetaData fieldMeta = new LegacyFieldMetaData(idx,
prop.getLegacyName(),
prop.getExtent(),
prop.getType(),
types2Legacy.get(prop.getType()),
idx,
0);
DataSetSDOHelper.setFieldMetaData(fieldList, tableMeta, fieldMeta);
idx++;
}
// after we have the fields set
DataSetSDOHelper.addTableMetaData(metadata, tableMeta);
}
else
{
DataObject table = getTableMetaData(graph, tableName);
List<DataObject> rows = beforeTable ? DataSetSDOHelper.getBeforeObjects(table)
: DataSetSDOHelper.getDataObjects(table);
// clear the rows first
for (DataObject row : rows)
{
row.delete();
}
}
List<Object[]> rowsMeta = wrapper.getRowsMeta();
List<Object[]> rows = wrapper.getRows();
List<DataObject> tableRows = new ArrayList<>(rows.size());
// set rows
for (int recnum = 0; recnum < rows.size(); recnum++)
{
Object[] row = rows.get(recnum);
Object[] rowMeta = rowsMeta.get(recnum);
DataObject drow = DataSetSDOHelper.createDataObject(graph, tableName);
if (!beforeTable && defaultTrackChanges)
{
((FWDDataObjectType) drow).beginLogging();
}
try
{
((FWDDataObjectType) drow).setForceNormalizedFields(true);
// always use normalized fields here
for (int idx = 0; idx < row.length; idx++)
{
Object rval = row[idx];
PropertyDefinition prop = props.get(idx);
Class<?> cls = prop.getType();
Integer ltype = types2Legacy.get(cls);
if (ltype == null)
{
throw new NullPointerException(
"Unknown field type " + cls + " for " + prop.getLegacyName() + " in " + tableName);
}
Object val = toJavaValue(rval, ltype, true, prop.getExtent());
drow.set(idx, val);
}
}
finally
{
((FWDDataObjectType) drow).setForceNormalizedFields(false);
}
((FWDDataObjectType) drow).primaryKey((Long) rowMeta[TempRecord._PK_DATA_INDEX]);
((FWDDataObjectType) drow)._rowState((Integer) rowMeta[TempRecord._ROW_STATE_DATA_INDEX]);
((FWDDataObjectType) drow)._peerRowid((Long) rowMeta[TempRecord._PEER_ROWID_DATA_INDEX]);
((FWDDataObjectType) drow)._originRowid((Long) rowMeta[TempRecord._ORIGIN_ROWID_DATA_INDEX]);
((FWDDataObjectType) drow)._datasourceRowid((Long) rowMeta[TempRecord._DATASOURCE_ROWID_INDEX]);
((FWDDataObjectType) drow)._errorFlags((Integer) rowMeta[TempRecord._ERROR_FLAG_DATA_INDEX]);
((FWDDataObjectType) drow)._errorString((String) rowMeta[TempRecord._ERROR_STRING_DATA_INDEX]);
tableRows.add(drow);
}
if (beforeTable)
{
DataSetSDOHelper.setBeforeRows(graph, tableName, tableRows);
}
else
{
DataSetSDOHelper.setTableRows(graph, tableName, tableRows);
}
}
/**
* Create and populate a table from the results received from the request.
* <p>
* If {@link #defaultTrackChanges} is set, then logging is enabled for all rows.
*
* @param graph
* The graph where to add the table.
* @param dst
* The table data.
* @param reuseMetadata
* Flag indicating if the table already exists in the graph.
* @param beforeTable
* Flag indicating if this is a before table.
*/
private void createOutputTable(DataGraph graph,
DsTableDefinition dst,
boolean reuseMetadata,
boolean beforeTable)
{
DataObject metadata = DataSetSDOHelper.getMetaData(graph);
List<PropertyDefinition> props = dst.getProperties();
String tableName = beforeTable ? dst.getPeerDef().getName() : dst.getName();
if (!beforeTable)
{
DataObject tableMeta = null;
if (reuseMetadata)
{
// re-built the structures inside the metadata reference
tableMeta = getTableMetaData(graph, tableName);
DataSetSDOHelper.initTableMetaData(tableMeta,
tableName,
props.size(),
dst.getPeerDef() != null,
dst.getNumIndexes(),
dst.getIndexes(),
dst.getXmlns(),
dst.getXmlPrefix(),
dst.getXmlNodeName(),
dst.getErrorString(),
normalizedExtent);
}
else
{
tableMeta = DataSetSDOHelper.createTableMetaData(tableName,
props.size(),
dst.getPeerDef() != null,
dst.getNumIndexes(),
dst.getIndexes(),
dst.getXmlns(),
dst.getXmlPrefix(),
dst.getXmlNodeName(),
dst.getErrorString(),
normalizedExtent);
}
List<LegacyFieldMetaData> fieldList = tableMeta.getList("fields");
int idx = 1; // this is 1-based, this is how .xpxg proxies are emitted.
for (PropertyDefinition prop : props)
{
LegacyFieldMetaData fieldMeta = new LegacyFieldMetaData(idx,
prop.getLegacyName(),
prop.getExtent(),
prop.getType(),
types2Legacy.get(prop.getType()),
idx,
0);
DataSetSDOHelper.setFieldMetaData(fieldList, tableMeta, fieldMeta);
idx++;
}
// after we have the fields set
DataSetSDOHelper.addTableMetaData(metadata, tableMeta);
}
else
{
DataObject table = getTableMetaData(graph, tableName);
List<DataObject> rows = beforeTable ? DataSetSDOHelper.getBeforeObjects(table)
: DataSetSDOHelper.getDataObjects(table);
// clear the rows first
for (DataObject row : rows)
{
row.delete();
}
}
List<Object[]> rowsMeta = dst.getRowsMeta();
List<Object[]> rows = dst.getRows();
List<DataObject> tableRows = new ArrayList<>(rows.size());
// set rows
for (int recnum = 0; recnum < rows.size(); recnum++)
{
Object[] row = rows.get(recnum);
Object[] rowMeta = rowsMeta.get(recnum);
DataObject drow = DataSetSDOHelper.createDataObject(graph, tableName);
if (!beforeTable && defaultTrackChanges)
{
((FWDDataObjectType) drow).beginLogging();
}
try
{
// always use normalized fields here
((FWDDataObjectType) drow).setForceNormalizedFields(true);
for (int idx = 0; idx < row.length; idx++)
{
Object rval = row[idx];
PropertyDefinition prop = props.get(idx);
Class<?> cls = prop.getType();
Integer ltype = types2Legacy.get(cls);
if (ltype == null)
{
throw new NullPointerException(
"Unknown field type " + cls + " for " + prop.getLegacyName() + " in " + tableName);
}
Object val = toJavaValue(rval, ltype, true, prop.getExtent());
drow.set(idx, val);
}
}
finally
{
((FWDDataObjectType) drow).setForceNormalizedFields(false);
}
((FWDDataObjectType) drow).primaryKey((Long) rowMeta[TempRecord._PK_DATA_INDEX]);
((FWDDataObjectType) drow)._rowState((Integer) rowMeta[TempRecord._ROW_STATE_DATA_INDEX]);
((FWDDataObjectType) drow)._peerRowid((Long) rowMeta[TempRecord._PEER_ROWID_DATA_INDEX]);
((FWDDataObjectType) drow)._originRowid((Long) rowMeta[TempRecord._ORIGIN_ROWID_DATA_INDEX]);
((FWDDataObjectType) drow)._datasourceRowid((Long) rowMeta[TempRecord._DATASOURCE_ROWID_INDEX]);
((FWDDataObjectType) drow)._errorFlags((Integer) rowMeta[TempRecord._ERROR_FLAG_DATA_INDEX]);
((FWDDataObjectType) drow)._errorString((String) rowMeta[TempRecord._ERROR_STRING_DATA_INDEX]);
tableRows.add(drow);
}
if (beforeTable)
{
DataSetSDOHelper.setBeforeRows(graph, tableName, tableRows);
}
else
{
DataSetSDOHelper.setTableRows(graph, tableName, tableRows);
}
}
/**
* Create a default {@link BaseDataType} value for this field's type.
*
* @param type
* The field's type.
* @param extent
* The field's extent.
*
* @return The default BDT type for this field.
*/
private Object defaultJavaType(int type, int extent)
{
Class<?> cls = legacyTypes.get(type);
if (extent == 0 || extent == SourceNameMapper.NO_EXTENT)
{
return cls == MemoryBuffer.class ? new MemoryBuffer(new byte[0])
: BaseDataType.generateDefault(cls);
}
else if (extent == SourceNameMapper.DYNAMIC_EXTENT)
{
Object res = Array.newInstance(cls, 0);
return res;
}
Object res = Array.newInstance(cls, extent);
for (int i = 0; i < extent; i++)
{
Array.set(res, i, cls == MemoryBuffer.class ? new MemoryBuffer(new byte[0])
: BaseDataType.generateDefault(cls));
}
return res;
}
/**
* Convert the given value to a {@link BaseDataType} compatible instance.
*
* @param value
* The Java value.
* @param type
* The value's type.
* @param extent
* The extent value.
* @param output
* Flag indicating OUTPUT mode.
*
* @return See above.
*/
private Object fromJavaType(Object value, int type, int extent, boolean output)
{
if (value == null)
{
Class<?> cls = legacyTypes.get(type);
if (extent == 0 || extent == SourceNameMapper.NO_EXTENT)
{
return cls == MemoryBuffer.class ? new MemoryBuffer(new byte[0])
: BaseDataType.generateUnknown(cls);
}
else if (extent == SourceNameMapper.DYNAMIC_EXTENT)
{
Object res = Array.newInstance(cls, 0);
return res;
}
else
{
Object res = Array.newInstance(cls, extent);
for (int i = 0; i < extent; i++)
{
Array.set(res, i, cls == MemoryBuffer.class ? new MemoryBuffer(new byte[0])
: BaseDataType.generateUnknown(cls));
}
return res;
}
}
if (extent != 0 && extent != SourceNameMapper.NO_EXTENT)
{
Class<?> elType = legacyTypes.get(type);
if (value instanceof List)
{
int length = ((List) value).size();
Object res = Array.newInstance(elType, length);
for (int i = 0; i < length; i++)
{
Array.set(res, i, fromJavaType(((List) value).get(i), type, SourceNameMapper.NO_EXTENT, output));
}
return res;
}
else if (value.getClass().isArray())
{
int length = Array.getLength(value);
Object res = Array.newInstance(elType, length);
for (int i = 0; i < length; i++)
{
Array.set(res, i, fromJavaType(Array.get(value, i), type, SourceNameMapper.NO_EXTENT, output));
}
return res;
}
// TODO: throw error
}
switch (type)
{
case TYPE_LOGICAL:
return new logical((Boolean) value);
case TYPE_CHARACTER:
return new character((String) value);
case TYPE_HANDLE:
return handle.fromResourceId((Long) value);
case TYPE_LONGCHAR:
return new longchar((String) value);
case TYPE_INTEGER:
return new integer((Number) value);
case TYPE_INT64:
return new int64((Number) value);
case TYPE_DECIMAL:
decimal decimalValue = new decimal((Number) value);
if (output)
{
// should accommodate for any new value for output params
// otherwise precision is determined by the default 0 value
decimalValue.setPrecision(decimal.MAX_SCALE);
}
return decimalValue;
case TYPE_BLOB:
if (value.getClass().isArray() && value.getClass().getComponentType() == byte.class)
{
return new blob(((byte[]) value));
}
else if (value instanceof MemoryBuffer)
{
return new blob(((MemoryBuffer) value).getValue());
}
throw new IllegalStateException("Can not determine the blob value from " + value);
case TYPE_CLOB:
return new clob((String) value);
case TYPE_RAW:
return new raw((byte[]) value);
case TYPE_RECID:
return new recid((long) value);
case TYPE_ROWID:
if (value instanceof String)
{
String s = (String) value;
s = s.trim();
if (s.isEmpty())
{
return new rowid();
}
value = Long.parseLong(s);
}
return new rowid((long) value);
case TYPE_UNKNOWN:
return unknown.UNKNOWN;
case TYPE_MEMPTR:
if (value.getClass().isArray() && value.getClass().getComponentType() == byte.class)
{
return new MemoryBuffer(((byte[]) value));
}
else if (value instanceof MemoryBuffer)
{
return value;
}
throw new IllegalStateException("Can not determine the blob value from " + value);
case TYPE_DATE:
if (value instanceof GregorianCalendar)
{
GregorianCalendar gc = (GregorianCalendar) value;
value = Date.from(gc.toZonedDateTime().toInstant());
return new date((Date) value, gc.getTimeZone());
}
return new date((Date) value);
case TYPE_DATETIME:
if (value instanceof GregorianCalendar)
{
GregorianCalendar gc = (GregorianCalendar) value;
value = Date.from(gc.toZonedDateTime().toInstant());
return new datetime((Date) value, gc.getTimeZone());
}
return new datetime((Date) value);
case TYPE_DATETIMETZ:
if (value instanceof GregorianCalendar)
{
GregorianCalendar gc = (GregorianCalendar) value;
return new datetimetz(gc.toZonedDateTime().toOffsetDateTime());
}
else
{
return new datetimetz((Date) value);
}
}
throw new IllegalArgumentException("Can not convert FWD value of type " + type);
}
/**
* Convert a {@link BaseDataType} instance to a Java value.
*
* @param arg
* The value.
* @param type
* The value's type.
* @param field
* Flag indicating that this call is for a field value.
* @param extent
* The extent value.
*
* @return See above.
*/
private Object toJavaValue(Object arg, int type, boolean field, int extent)
{
if (arg instanceof BaseDataType && ((BaseDataType) arg).isUnknown())
{
return type == memptr() ? new MemoryBuffer() : null;
}
if (extent != 0 && extent != SourceNameMapper.NO_EXTENT)
{
Class<?> ctype = null;
switch (type)
{
case TYPE_LOGICAL:
ctype = Boolean.class;
break;
case TYPE_CHARACTER:
case TYPE_LONGCHAR:
case TYPE_CLOB:
ctype = String.class;
break;
case TYPE_INTEGER:
ctype = Integer.class;
break;
case TYPE_DECIMAL:
ctype = BigDecimal.class;
break;
case TYPE_RAW:
case TYPE_BLOB:
ctype = byte[].class;
break;
case TYPE_HANDLE:
case TYPE_RECID:
case TYPE_INT64:
case TYPE_ROWID:
ctype = Long.class;
break;
case TYPE_MEMPTR:
ctype = MemoryBuffer.class;
break;
case TYPE_DATE:
case TYPE_DATETIME:
case TYPE_DATETIMETZ:
ctype = GregorianCalendar.class;
break;
}
int length = Array.getLength(arg);
Object res = field ? new ArrayList<>() : Array.newInstance(ctype, length);
for (int i = 0; i < length; i++)
{
Object elem = toJavaValue(Array.get(arg, i), type, field, SourceNameMapper.NO_EXTENT);
if (field)
{
((List) res).add(elem);
}
else
{
Array.set(res, i, elem);
}
}
return res;
}
switch (type)
{
case TYPE_LOGICAL:
return ((logical) arg).booleanValue();
case TYPE_CHARACTER:
return ((character) arg).toStringMessage();
case TYPE_HANDLE:
return handle.resourceId(((handle) arg).getResource());
case TYPE_LONGCHAR:
return ((longchar) arg).toStringMessage();
case TYPE_INTEGER:
return ((integer) arg).intValue();
case TYPE_INT64:
return ((int64) arg).longValue();
case TYPE_DECIMAL:
BigDecimal val = ((decimal) arg).toBigDecimal();
if (val.scale() > 0)
{
BigDecimal val2 = val.stripTrailingZeros();
// a negative scale means scientific notation, so use only if there are decimal digits
// for .NET client, we need at least a decimal digit, otherwise it can be treated as 'integer'
// instead of 'decimal', for i.e. JSON serialization
return val2.scale() <= 0 ? val2.setScale(forceDecimalDigits ? 1 : 0) : val2;
}
else
{
return val;
}
case TYPE_BLOB:
return ((blob) arg).getByteArray();
case TYPE_CLOB:
return ((clob) arg).toJavaType();
case TYPE_RAW:
return ((raw) arg).getByteArray();
case TYPE_RECID:
return ((recid) arg).longValue();
case TYPE_ROWID:
return ((rowid) arg).getValue();
case TYPE_UNKNOWN:
return null;
case TYPE_MEMPTR:
return (MemoryBuffer) arg;
case TYPE_DATE:
case TYPE_DATETIME:
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(((date) arg).dateValue());
return gc;
case TYPE_DATETIMETZ:
return ((datetimetz) arg).calendarValue();
}
throw new IllegalArgumentException("Could not resolve java value of type " + type);
}
/**
* Create a table definition part of a dataset parameter.
*
* @param rows
* The table rows.
* @param fields
* The table fields.
* @param input
* Flag indicating if this parameter is in input or input-output mode.
* @param output
* Flag indicating if this parameter is in output or input-output mode.
* @param before
* Flag indicating if this table is a before-table.
*
* @return A {@link DsTableDefinition} instance with the specified metadata and records.
*/
private DsTableDefinition createDsTable(List<DataObject> rows,
List<LegacyFieldMetaData> fields,
boolean input,
boolean output,
boolean before)
{
Iterator<LegacyFieldMetaData> fieldIter = fields.iterator();
Iterator<DataObject> rowIter = rows.iterator();
DsTableDefinition dstable = new DsTableDefinition(new TableResultSet(input, output, false)
{
DataObject nextRow;
@Override
public Object[] nextRow()
{
nextRow = rowIter.next();
int numFields = fields.size();
Object[] data = new Object[numFields];
((FWDDataObjectType) nextRow).setForceNormalizedFields(true);
for (int i = 0; i < data.length; i++)
{
LegacyFieldMetaData field = fields.get(i);
int ftype = field.getLegacyType();
int extent = field.getExtent();
Object fval = nextRow.get(i);
if (before && ((FWDDataObjectType) nextRow)._rowState() == Buffer.ROW_CREATED)
{
data[i] = defaultJavaType(ftype, extent);
}
else
{
data[i] = fromJavaType(fval, ftype, extent, output);
}
}
((FWDDataObjectType) nextRow).setForceNormalizedFields(false);
return data;
}
@Override
public Object[] nextRowMeta()
{
FWDDataObjectType row = (FWDDataObjectType) nextRow;
return row.toArray();
}
@Override
public PropertyDefinition nextProperty()
{
LegacyFieldMetaData fieldMeta = fieldIter.next();
String name = fieldMeta.getName();
Class<?> type = fieldMeta.getType();
int extent = fieldMeta.getExtent();
PropertyDefinition prop = new PropertyDefinition(name, type, extent, name);
return prop;
}
@Override
public boolean hasMoreRows()
{
return rowIter.hasNext();
}
@Override
public boolean hasMoreProperties()
{
return fieldIter.hasNext();
}
});
return dstable;
}
/**
* Serialize the rows and fields in a {@link TableResultSet}, to be sent to the remote side.
*
* @param rows
* The rows.
* @param fields
* The fields.
* @param input
* Flag indicating INPUT mode.
* @param output
* Flag indicating OUTPUT mode.
*
* @return The wrapped rows.
*/
private TableResultSet createTableResultSet(List<DataObject> rows,
List<LegacyFieldMetaData> fields,
boolean input,
boolean output,
boolean before)
{
Iterator<LegacyFieldMetaData> fieldIter = fields.iterator();
Iterator<DataObject> rowIter = rows.iterator();
TableResultSet ttrs = new TableResultSet(input, output, false)
{
DataObject nextRow;
@Override
public Object[] nextRow()
{
nextRow = rowIter.next();
int numFields = fields.size();
Object[] data = new Object[numFields];
((FWDDataObjectType) nextRow).setForceNormalizedFields(true);
for (int i = 0; i < data.length; i++)
{
LegacyFieldMetaData field = fields.get(i);
int ftype = field.getLegacyType();
int extent = field.getExtent();
Object fval = nextRow.get(i);
if (before && ((FWDDataObjectType) nextRow)._rowState() == Buffer.ROW_CREATED)
{
data[i] = defaultJavaType(ftype, extent);
}
else
{
data[i] = fromJavaType(fval, ftype, extent, output);
}
}
((FWDDataObjectType) nextRow).setForceNormalizedFields(false);
return data;
}
@Override
public Object[] nextRowMeta()
{
FWDDataObjectType row = (FWDDataObjectType) nextRow;
return row.toArray();
}
@Override
public PropertyDefinition nextProperty()
{
LegacyFieldMetaData fieldMeta = fieldIter.next();
String name = fieldMeta.getName();
Class<?> type = fieldMeta.getType();
int extent = fieldMeta.getExtent();
return new PropertyDefinition(name, type, extent, name);
}
@Override
public boolean hasMoreRows()
{
return rowIter.hasNext();
}
@Override
public boolean hasMoreProperties()
{
return fieldIter.hasNext();
}
};
return ttrs;
}
/**
* Get the signature of a table based on the properties list
*
* @param properties
* The properties of the table
*
* @return A list containing each field with its type and extent
*/
private List<Map<String, String>> getSignature(List<PropertyDefinition> properties)
{
List<Map<String, String>> signature = new LinkedList<>();
for (int i = 0; i < properties.size(); i++)
{
Map<String, String> field = new LinkedHashMap<>();
PropertyDefinition property = properties.get(i);
int extent = property.getExtent() == -2 ? 0 : property.getExtent();
field.put("type", property.getType().getName());
field.put("extent", String.valueOf(extent));
signature.add(field);
}
return signature;
}
}