7#include "ImportPdsTable.h"
15#include <QRegularExpression>
16#include <QScopedPointer>
19#include "EndianSwapper.h"
25#include "TableField.h"
26#include "TableRecord.h"
75 const QString &pdsTableFile,
76 const QString &pdsTableName) {
78 load(pdsLabFile, pdsTableFile, pdsTableName);
134 const QString &pdsTableFile,
135 const QString &pdsTableName) {
139 loadLabel(pdsLabFile, tempTblFile, pdsTableName);
140 if (!pdsTableFile.isEmpty()) tempTblFile = pdsTableFile;
144 FileName tableFile(tempTblFile);
146 int tableStartRecord = toInt(tableFile.baseName());
147 tempTblFile = pdsLabFile;
150 catch (IException &e) {
153 if (!tableFile.fileExists()) {
155 FileName tableFileLowercase(tableFile.path() +
"/"
156 + tableFile.name().toLower());
157 if (!tableFileLowercase.fileExists()) {
158 IString msg =
"Unable to import PDS table. Neither of the following "
159 "possible table files were found: ["
160 + tableFile.expanded() +
"] or ["
161 + tableFileLowercase.expanded() +
"]";
162 throw IException(e, IException::Unknown, msg, _FILEINFO_);
164 tableFile = tableFileLowercase.expanded();
165 tempTblFile = tableFile.expanded();
213 const bool &formatted)
const {
214 if ((
int) index >=
columns() - 1) {
215 QString msg =
"Unable to import the binary PDS table [" +
m_tableName
216 +
"] into Isis. The requested column index ["
217 +
toString((
int) index) +
"] exceeds the last column index ["
219 throw IException(IException::Programmer, msg.toStdString(), _FILEINFO_);
245 for (
int i = 0 ; i <
columns() ; i++) {
248 colnames.push_back(
name);
297 const QString &dataType) {
302 return (column != 0);
322 Table table(isisTableName, record);
326 catch (IException &e) {
327 QString msg =
"Unable to import the PDS table [" +
m_tableName
329 throw IException(e, IException::Unknown, msg.toStdString(), _FILEINFO_);
352 const QString &isisTableName) {
353 return (
importTable(colnames.split(
","), isisTableName));
375 const QString &isisTableName) {
377 for (
int i = 0 ; i < colnames.size() ; i++) {
380 QString msg =
"Unable to import the PDS table [" +
m_tableName
381 +
"] into Isis. The requested column name ["
382 + colnames[i] +
"] does not "
384 throw IException(IException::Programmer, msg.toStdString(), _FILEINFO_);
386 ctypes.push_back(*descr);
391 Table table(isisTableName, record);
440 QString &pdsTableFile,
441 const QString &tblname) {
443 Isis::Pvl label(pdsLabFile);
445 QString tableName = ( tblname.isEmpty() ) ?
m_tableName : tblname;
446 if (!label.hasObject(tableName)) {
447 QString msg =
"The PDS file " + pdsLabFile +
448 " does not have the required TABLE object, ["
449 + tableName +
"]. The PDS label file is probably invalid";
450 throw IException(IException::Unknown, msg.toStdString(), _FILEINFO_);
453 PvlObject &tabObj = label.findObject(tableName);
455 if (tabObj.hasKeyword(
"RECORD_BYTES")) {
460 else if (tabObj.hasKeyword(
"ROW_BYTES") && tabObj.hasKeyword(
"ROW_SUFFIX_BYTES")) {
462 (int) tabObj.findKeyword(
"ROW_SUFFIX_BYTES");
470 QString trueTableName;
471 PvlObject *tableDetails = &tabObj;
472 if (label.hasKeyword(
"^" + tableName)) {
473 trueTableName = tableName;
474 pdsTableFile = FileName(pdsLabFile).path() +
"/"
475 + label[
"^" + tableName][0];
477 else if (tabObj.objects() == 1) {
478 trueTableName = tabObj.object(0).name();
479 tableDetails = &tabObj.object(0);
480 pdsTableFile = FileName(pdsLabFile).path() +
"/"
481 + tabObj[
"^" + trueTableName][0];
483 m_trows = (int) tableDetails->findKeyword(
"ROWS");
484 int ncols = (int) tableDetails->findKeyword(
"COLUMNS");
485 m_pdsTableType = QString(tableDetails->findKeyword(
"INTERCHANGE_FORMAT"));
487 QString msg =
"Unable to import the PDS table [" + tableName
488 +
"] from the PDS file ["
489 + pdsTableFile +
"] into Isis. "
491 +
"] is not supported. Valid values are ASCII or BINARY.";
492 throw IException(IException::User, msg.toStdString(), _FILEINFO_);
494 m_rowBytes = tableDetails->findKeyword(
"ROW_BYTES");
497 PvlObject::PvlObjectIterator colobj = tableDetails->beginObject();
499 while (colobj != tableDetails->endObject()) {
500 if (colobj->isNamed(
"COLUMN")) {
510 msg <<
"Number of columns in the COLUMNS label keyword (" << ncols
511 <<
") does not match number of COLUMN objects found ("
513 cout << msg.str() << endl;
539 QString tempTblFile(pdsTableFile);
544 while (tfile.
GetLine(tline,
false)) {
575 cd.
m_name = colobj[
"NAME"][0];
582 cd.
m_dataType = colobj[
"DATA_TYPE"][0].toUpper();
590 if ( colobj.hasKeyword(
"ITEM_BYTES") ) {
595 if ( colobj.hasKeyword(
"ITEMS") ) {
596 cd.
m_items = (int) colobj[
"ITEMS"];
618 if ( (nth >=
columns()) || ( nth < 0) ) {
619 QString mess =
"Index (" + QString::number(nth) +
620 ") into Columns invalid (max: " + QString::number(
columns()) +
")";
621 throw IException(IException::Programmer, mess, _FILEINFO_);
645 ColumnTypes::iterator col =
m_coldesc.begin();
648 if (c.toUpper() == cName.toUpper()) {
return (&(*col)); }
671 ColumnTypes::const_iterator col =
m_coldesc.begin();
674 if (c.toUpper() == cName.toUpper()) {
return (&(*col)); }
693 const QString &delimiter)
const {
715 const QString &delimiter)
const {
724 if ( delimiter.isEmpty() )
return (
QStringList(value));
726 return ( value.split(delimiter, Qt::SkipEmptyParts) );
732 for (
int i = 0 ; i < cdesc.
m_items ; i++) {
733 fields.push_back(value.mid(pos, cdesc.
m_itemBytes));
761 QString cname = QString(colname).replace(QRegularExpression(
"[(),]"),
" ").simplified();
763 bool uppercase =
true;
765 for (
int i = 0 ; i < cname.size() ; i++) {
767 oString.push_back(cname[i].toUpper());
770 else if ( (cname[i] ==
' ') || (cname[i] ==
'_') ) {
774 oString.push_back(cname[i].toLower());
800 return ttype.split(
"_").last();
825 if ( dtype ==
"INTEGER" ) {
826 return (TableField(
name, TableField::Integer));
828 else if ( ((dtype ==
"DOUBLE")
830 || (dtype ==
"FLOAT")) ) {
831 return (TableField(
name, TableField::Double));
859 for (
int i = 0 ; i < ctypes.size() ; i++) {
885 TableField &tfield)
const {
888 IString data(cols[ith]);
889 if (tfield.isInteger()) {
890 data.Trim(
" \t\r\n");
891 tfield = data.ToInteger();
893 else if (tfield.isDouble()) {
894 data.Trim(
" \t\r\n");
895 tfield = data.ToDouble();
898 QString str(tfield.size(),
' ');
899 str.insert(0, data.Trim(
" \t\r\n").ToQt());
903 catch (IException &e) {
904 QString msg =
"Conversion failure of column " + cdesc.
m_name;
905 throw IException(e, IException::Programmer, msg, _FILEINFO_);
927 const ColumnTypes &ctypes,
928 TableRecord &record)
const {
929 for (
int i = 0 ; i < ctypes.size() ; i++) {
930 extract(cols, ctypes[i], record[i]);
952 const ColumnTypes &cols,
953 TableRecord &record)
const {
955 for (
int i = 0 ; i <
m_rows.size() ; i++) {
959 catch (IException &e) {
960 QString msg =
"Failed to convert data in row [" +
toString((
int) i) +
"]";
961 throw IException(e, IException::Programmer, msg, _FILEINFO_);
968 ifstream pdsFileStream(tempTblFile.toLatin1().data(), ifstream::binary);
970 if (!pdsFileStream) {
971 IString msg =
"Unable to open file containing PDS table ["
972 + tempTblFile +
"].";
973 throw IException(IException::Unknown, msg, _FILEINFO_);
977 QScopedPointer<char, QScopedPointerArrayDeleter<char> > rowBuffer(
new char[
m_recordBytes]);
982 for (
int rowIndex = 0; rowIndex <
m_trows; rowIndex++) {
1034 for (
int colIndex = 0; colIndex <
columns(); colIndex++) {
1035 QString columnName =
m_coldesc[colIndex].m_name;
1036 for (
int fieldIndex = 0 ; fieldIndex < record.Fields() ; fieldIndex++) {
1037 QString fieldName = record[fieldIndex].name();
1038 if (fieldName == columnName) {
1039 int startByte =
m_coldesc[colIndex].m_startByte;
1040 int numBytes =
m_coldesc[colIndex].m_numBytes;
1042 if (record[fieldIndex].isInteger()) {
1044 memmove(&columnValue, &rowBuffer[startByte], numBytes);
1046 int fieldValue = endianSwap.
Int(&columnValue);
1047 record[fieldIndex] = fieldValue;
1050 else if (record[fieldIndex].isDouble()) {
1053 memmove(&columnValue, &rowBuffer[startByte], numBytes);
1054 double fieldValue = endianSwap.
Double(&columnValue);
1055 record[fieldIndex] = fieldValue;
1058 else if (record[fieldIndex].isReal()) {
1061 memmove(&columnValue, &rowBuffer[startByte], numBytes);
1062 float fieldValue = endianSwap.
Float(&columnValue);
1063 record[fieldIndex] = fieldValue;
1066 else if (record[fieldIndex].isText()) {
1067 QString fieldValue(numBytes,
'\0');
1068 for (
int byte = 0;
byte < numBytes;
byte++) {
1069 fieldValue[byte] = rowBuffer[startByte + byte];
1071 record[fieldIndex] = fieldValue;
1107 if (dataType ==
"MSB_INTEGER" || dataType ==
"INTEGER"
1108 || dataType ==
"SUN_INTEGER" || dataType ==
"MAC_INTEGER") {
1110 QString msg =
"Only 4 byte integer values are supported in Isis. "
1111 "PDS Column [" + cdesc.
m_name
1112 +
"] has an integer DATA_TYPE with [BYTES = "
1114 throw IException(IException::Unknown, msg, _FILEINFO_);
1117 return TableField(
name, TableField::Integer);
1119 else if (dataType ==
"LSB_INTEGER" || dataType ==
"VAX_INTEGER"
1120 || dataType ==
"PC_INTEGER" ) {
1122 QString msg =
"Only 4 byte integer values are supported in Isis. "
1123 "PDS Column [" + cdesc.
m_name
1124 +
"] has an integer DATA_TYPE with [BYTES = "
1126 throw IException(IException::Unknown, msg, _FILEINFO_);
1129 return TableField(
name, TableField::Integer);
1131 else if (dataType ==
"FLOAT"
1132 || dataType ==
"REAL"
1133 || dataType ==
"SUN_REAL"
1134 || dataType ==
"MAC_REAL"
1135 || dataType ==
"IEEE_REAL" ) {
1138 return TableField(
name, TableField::Double);
1141 return TableField(
name, TableField::Real);
1144 IString msg =
"Only 4 byte or 8 byte real values are supported in Isis. "
1145 "PDS Column [" + cdesc.
m_name
1146 +
"] has a real DATA_TYPE with [BYTES = "
1148 throw IException(IException::Unknown, msg, _FILEINFO_);
1151 else if (dataType ==
"PC_REAL") {
1154 return TableField(
name, TableField::Double);
1157 return TableField(
name, TableField::Real);
1160 QString msg =
"Only 4 byte or 8 byte real values are supported in Isis. "
1161 "PDS Column [" + cdesc.
m_name
1162 +
"] has a real DATA_TYPE with [BYTES = "
1164 throw IException(IException::Unknown, msg, _FILEINFO_);
1167 else if (dataType.contains(
"CHARACTER")
1168 || dataType.contains(
"ASCII")
1169 || dataType ==
"DATE" || dataType ==
"TIME" ) {
1174 IString msg =
"PDS Column [" + cdesc.
m_name
1175 +
"] has an unsupported DATA_TYPE ["
1177 throw IException(IException::Unknown, msg, _FILEINFO_);
1194 QString msg =
"Unable to import the binary PDS table [" +
m_tableName
1195 +
"]. The column DATA_TYPE values indicate differing byte "
1197 throw IException(IException::Unknown, msg.toStdString(), _FILEINFO_);
1215 for (
int i = 0 ; i <
columns() ; i++) {
float Float(void *buf)
Swaps a floating point value.
int Int(void *buf)
Swaps a 4 byte integer value.
double Double(void *buf)
Swaps a double precision value.
void init()
Initialize object variables.
ColumnDescr * findColumn(const QString &colName)
Searches internal column descriptors for a named column.
virtual bool processRow(const int &row, const QString &rowdata)
Process a freshly read PDS table line of data.
void setName(const QString &name="TABLE")
Set the name of the PDS table object.
QString name() const
Return the name of the PDS table.
QString getFormattedName(const QString &colname) const
Converts a column name to a camel-case after it has been cleansed.
int rows() const
Returns the number of rows in the table.
void loadLabel(const QString &labfile, QString &tblfile, const QString &tblname="")
Loads the contents of a PDS table label description.
const ColumnDescr & getColumnDescriptor(const int &nth) const
Retrieve a column description by index.
int m_pdsTableStart
The start byte of the PDS table data.
Table importTable(const QString &isisTableName)
Populate a Table object with the PDS table and return it.
QString m_pdsTableFile
The name of the file containing the table data.
QString getColumnValue(const QString &tline, const ColumnDescr &cdesc, const QString &delimiter="") const
Extracts a column from a QString based upon a description.
void load(const QString &pdsLabFile, const QString &pdsTabFile="", const QString &pdsTableName="TABLE")
Loads a PDS table label and (optional) data file.
void setPdsByteOrder(QString byteOrder)
Sets the byte order for BINARY PDS table files.
int m_rowBytes
The number of bytes for one PDS table row.
QString m_pdsTableType
The INTERCHANGE_FORMAT value for the table.
TableField & extract(const Columns &columns, const ColumnDescr &cdesc, TableField &field) const
Extract a TableField from a PDS column in the text row.
TableRecord makeRecord(const ColumnTypes &ctypes)
Creates a TableRecord for columns.
TableRecord extractBinary(char *rowBuffer, TableRecord &record) const
This method is used to set the field values for the given record.
int m_trows
Number rows in table according to label.
QString m_tableName
The name of the PDS table object.
int m_recordBytes
The number of bytes for one Isis table record.
ColumnTypes m_coldesc
Column descriptions.
void loadTable(const QString &tabfile)
Loads the contents of a PDS table data file.
TableField makeFieldFromBinaryTable(const ColumnDescr &cdesc)
Creates an empty TableField with the appropriate type from a binary PDS table column description.
QString m_byteOrder
The byte order of the PDS table file, if binary.
virtual ~ImportPdsTable()
Destructs the ImportPdsTable object.
bool setType(const QString &colName, const QString &dataType)
Change the datatype for a column.
QString getGenericType(const QString &ttype) const
Determine generic data type of a column.
ColumnDescr getColumnDescription(PvlObject &colobj, int nth) const
Extract a column description from a COLUMN object.
ImportPdsTable()
Default constructor.
QString getType(const QString &colName) const
Get the type associated with the specified column.
bool hasColumn(const QString &colName) const
This method determines whether the PDS table has a column with the given name.
void fillTable(Table &table, const ColumnTypes &columns, TableRecord &record) const
Fill the ISIS Table object with PDS table data.
QStringList getColumnNames(const bool &formatted=true) const
Return the names of all the columns.
QString getColumnName(const unsigned int &index=0, const bool &formatted=true) const
Returns the name of the specifed column.
TableField makeField(const ColumnDescr &cdesc)
Creates a TableField for the column type.
int columns() const
Returns the number of columns in the table.
QStringList getColumnFields(const QString &tline, const ColumnDescr &cdesc, const QString &delimiter="") const
Extracts column fields from a QString based upon a description.
Provides access to sequential ASCII stream I/O.
bool GetLine(QString &line, const bool skipComments=true)
Gets next line from file.
This is free and unencumbered software released into the public domain.
QString toString(const LinearAlgebra::Vector &vector, int precision)
A global function to format LinearAlgebra::Vector as a QString with the given precision.
Namespace for the standard library.
int m_colnum
Column number.
int m_startByte
Starting byte of data.
int m_numBytes
Number bytes in column.
QString m_dataType
PDS table DATA_TYPE of column.
int m_itemBytes
Number bytes per item.
int m_items
Number of items in column.
QString m_name
Name of column.