diff --git a/src/main/java/eu/omp/irap/vespa/epntapclient/votable/controller/VOTableDataParser.java b/src/main/java/eu/omp/irap/vespa/epntapclient/votable/controller/VOTableDataParser.java new file mode 100644 index 0000000..afcf972 --- /dev/null +++ b/src/main/java/eu/omp/irap/vespa/epntapclient/votable/controller/VOTableDataParser.java @@ -0,0 +1,231 @@ +/** + * This file is a part of EpnTAPClient. + * This program aims to provide EPN-TAP support for software clients, like CASSIS spectrum analyzer. + * See draft specifications: https://voparis-confluence.obspm.fr/pages/viewpage.action?pageId=559861 + * Copyright (C) 2016 Institut de Recherche en Astrophysique et Planétologie. + * + * This program is free software: you can + * redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of + * the GNU General Public License along with this program. If not, see + * . + */ +package eu.omp.irap.vespa.epntapclient.votable.controller; + +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Vector; + +import com.google.gson.Gson; + +import eu.omp.irap.vespa.epntapclient.utils.Log; +import eu.omp.irap.vespa.epntapclient.votable.model.DataType; +import eu.omp.irap.vespa.epntapclient.votable.model.Field; +import eu.omp.irap.vespa.epntapclient.votable.model.Stream; +import eu.omp.irap.vespa.epntapclient.votable.model.Table; +import eu.omp.irap.vespa.epntapclient.votable.model.TableData; + +/** + * @author N. Jourdane + */ +public class VOTableDataParser { + + /** + * A list of arrays, representing data stored in the VOTable. Each element is a VOTable row, + * where arrays elements are in the same order as `columnNames`. + */ + private Vector data; + + /** The name of the columns. `columnNames` length is equals to `data` length */ + private String[] columnsName; + + /** The byte array stored in the VOTable Table stream (if any). */ + byte[] bytes; + + /** The index of the current position in the bytes array. */ + private int cursor; + + /** + * In VOTables, data can be stored in 4 different serializations: BINARY, BINARY2, TABLEDATA and + * FITS. This class aims to provide a unique way to get the data table, regardless of its + * serialization type. + * + * @param table The table on which we want get the data. + * @throws Exception If the VOTable data can not be parsed. + */ + public VOTableDataParser(Table table) throws Exception { + cursor = 0; + + List fields = new ArrayList<>(); + for (Object obj : table.getFIELDOrPARAMOrGROUP()) { + if (obj.getClass() == Field.class) { + fields.add((Field) obj); + } + } + columnsName = new String[fields.size()]; + for (int i = 0; i < fields.size(); i++) { + columnsName[i] = fields.get(i).getName(); + } + Log.LOGGER.info("Columns name: \n " + new Gson().toJson(columnsName)); + + data = new Vector<>(); + + if (table.getDATA().getBINARY() != null) { + parseBinaryStream(table.getDATA().getBINARY().getSTREAM(), fields); + } else if (table.getDATA().getBINARY2() != null) { + parseBinary2Stream(table.getDATA().getBINARY2().getSTREAM(), fields); + } else if (table.getDATA().getTABLEDATA() != null) { + parseTableDataStream(table.getDATA().getTABLEDATA(), fields); + } else if (table.getDATA().getFITS() != null) { + parseFITSStream(table.getDATA().getFITS().getSTREAM(), fields); + } + + String logPath = Log.printObject(data); + Log.LOGGER.info("A json file representing the VOTable data has been created on " + logPath); + } + + /** + * @return the data stored in the VOTable. + */ + public List getDataArray() { + return data; + } + + /** + * @return An array of VOTable fields name. + */ + public String[] getColumnsName() { + return columnsName; + } + + /** + * get the data on its BINARY form. + * + * @param stream the data Stream in the VOTable Table. + * @param fields The Fields corresponding to the Table. + * @throws Exception If a value in the VOTable data is a array (except strings). + */ + private void parseBinaryStream(Stream stream, List fields) throws Exception { + Log.LOGGER.info("Parsing data in BINARY stream..."); + String strStream = stream.getValue().replaceAll("(\\r|\\n)", ""); + Log.clearFile(); + + bytes = Base64.getDecoder().decode(strStream); + Object[] row = new Object[columnsName.length]; + + // System.out.println(strStream + " = " + new Gson().toJson(byteArray)); + + Log.clearFile(); + for (int nValue = 0; cursor < bytes.length; nValue++) { + int nColumn = nValue % columnsName.length; + Field column = fields.get(nColumn); + DataType dataType = column.getDatatype(); + + //@noformat + int blockSize = + dataType.equals(DataType.BOOLEAN) ? 1 : + dataType.equals(DataType.UNSIGNED_BYTE) ? 1 : + dataType.equals(DataType.SHORT) ? 2 : + dataType.equals(DataType.INT) ? 4 : + dataType.equals(DataType.LONG) ? 8 : + dataType.equals(DataType.CHAR) ? 1 : + dataType.equals(DataType.UNICODE_CHAR) ? 2 : + dataType.equals(DataType.FLOAT) ? 4 : + dataType.equals(DataType.DOUBLE) ? 8 : + dataType.equals(DataType.FLOAT_COMPLEX) ? 8 : + dataType.equals(DataType.DOUBLE_COMPLEX) ? 16 : 0; + //@format + + int arraySize; + if (column.getArraysize() == null) { + arraySize = blockSize; + } else if (column.getArraysize().equals("*")) { + arraySize = getValue(4) * blockSize; + } else { + arraySize = Integer.parseInt(column.getArraysize()) * blockSize; + } + + if ((arraySize != blockSize) && !(dataType.equals(DataType.CHAR) + || dataType.equals(DataType.UNICODE_CHAR))) { + throw new Exception("Data values as arrays are not supported yet."); + } + + if (nColumn == 0) { + row = new Object[columnsName.length]; + } + + if (dataType.equals(DataType.CHAR) || dataType.equals(DataType.UNICODE_CHAR)) { + String value = ""; + for (int i = 0; i < arraySize && cursor < bytes.length; i += blockSize) { + value += (char) getValue(blockSize); + } + row[nColumn] = value.trim(); + } else if (dataType.equals(DataType.BOOLEAN) || dataType.equals(DataType.BIT)) { + row[nColumn] = new Boolean(getValue(blockSize) == 0 ? false : true); + } else if (dataType.equals(DataType.UNSIGNED_BYTE) || dataType.equals(DataType.SHORT)) { + row[nColumn] = new Short((short) getValue(blockSize)); + } else if (dataType.equals(DataType.INT)) { + row[nColumn] = new Integer(getValue(blockSize)); + } else if (dataType.equals(DataType.LONG)) { + row[nColumn] = new Long(getValue(blockSize)); + } else { // float + row[nColumn] = new Float(getValue(blockSize)); + } + + Log.logInFile("**" + columnsName[nColumn] + "**: " + row[nColumn]); + + row[nColumn] = row[nColumn]; + if (nColumn == columnsName.length - 1) { + data.add(row); + } + } + + } + + /** + * @param stream the data Stream in the VOTable Table. + * @param fields The Fields corresponding to the Table. + */ + private static void parseBinary2Stream(Stream stream, List fields) { + Log.LOGGER.info("Parsing data in BINARY2 stream..."); + // TODO Implémenter parseBinary2Stream() + } + + /** + * @param tabledata the TableData of the VOTable Table. + * @param fields The Fields corresponding to the Table. + */ + private static void parseTableDataStream(TableData tabledata, List fields) { + Log.LOGGER.info("Parsing data in TABLEDATA stream..."); + // TODO Implémenter parseTableDataStream() + } + + /** + * @param stream the data Stream in the VOTable Table. + * @param fields The Fields corresponding to the Table. + */ + private static void parseFITSStream(Stream stream, List fields) { + Log.LOGGER.info("Parsing data in FITS stream..."); + // TODO Implémenter parseFITSStream() + } + + /** + * Get the integer value of a bytes block. For example, [0x01, 0x05] returns 230, and increment + * the cursor. + * + * @param blockSize The size of the bytes block to get + * @return The integer value of the bytes block. + */ + private int getValue(int blockSize) { + int intVal = 0; + for (int i = 0; i < blockSize; i++) { + intVal += 0xFF & (bytes[cursor + i] << ((blockSize - 1 - i) * 8)); + } + cursor += blockSize; + return intVal; + } +} -- libgit2 0.21.2