blob: e95ab28bbf3d7caf203efc0bc6446937df1a9db2 [file] [log] [blame]
/*-
* ============LICENSE_START=======================================================
* OSAM
* ================================================================================
* Copyright (C) 2018 AT&T
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============LICENSE_END=========================================================
*/
package org.onap.osam.services;
import com.opencsv.CSVReader;
import org.json.JSONArray;
import org.json.JSONObject;
import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.ws.rs.BadRequestException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.onap.osam.utils.Logging.getMethodName;
@Service
public class CsvServiceImpl implements ICsvService {
static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CsvServiceImpl.class);
private static final String ARRAY_REGEX = "\\[(.*?)\\]";
private String [] removeBOMFromCsv(String [] line){
if (line.length>0)
line[0] = line[0].replaceFirst("\uFEFF","").replaceFirst("","");
return line;
}
/**
* read a csv file and puts its content in list of string arrays (without the empty lines)
* @param filePath - the path of file to read
* @return the content of file
* @throws IOException
*/
@Override
public List<String[]> readCsv(String filePath) throws IOException {
CSVReader reader = new CSVReader(new FileReader(filePath));
return readCsv(reader);
}
@Override
public List<String[]> readCsv(MultipartFile file) throws IOException {
CSVReader reader = new CSVReader(new InputStreamReader(file.getInputStream()));
return readCsv(reader);
}
private List<String[]> addLineWithoutSpaces(List<String[]> myEntries, String [] line){
line = Arrays.stream(line).filter(x -> !"".equals(x)).toArray(String[]::new);
if(line.length > 0)
myEntries.add(line);
return myEntries;
}
private List<String[]> readCsv(CSVReader reader) throws IOException {
try {
List<String[]> myEntries = new ArrayList<>() ;
String [] line;
Boolean firstLine = true;
while ((line = reader.readNext())!= null) {
if (firstLine) {
line = removeBOMFromCsv(line);
firstLine = false;
}
myEntries = addLineWithoutSpaces(myEntries, line);
}
return myEntries;
}
catch (Exception e){
logger.error("error during reading CSV file. exception:" + e.getMessage());
throw e;
}
}
/**
* main function that call to the recursive function with initial parameters
* @param myEntries - the matrix with file content
* @return the json
* @throws IOException
* @throws InstantiationException
* @throws IllegalAccessException
*/
@Override
public JSONObject convertCsvToJson (List<String[]> myEntries) throws InstantiationException, IllegalAccessException {
try {
return buildJSON(myEntries, 0, 0, myEntries.size(), JSONObject.class);
}
catch (Exception e){
logger.error("error during parsing CSV file. exception:" + e.getMessage());
throw e;
}
}
/**
* it goes over the matrix column while the values are the same and returns the index of changed value
* @param myEntries the matrix
* @param i row index refer to the whole matrix
* @param j column index
* @param numLines the length of the current inner matrix
* @param startLine row index of inner matrix
* @return the index of changed line
*/
private int findIndexOfChangedLine(List<String[]> myEntries, final int i, final int j, final int numLines, final int startLine) {
int k;
for(k = 0; k + i - startLine < numLines && myEntries.get(i)[j].equals(myEntries.get(k + i)[j]) ; k++);
return k;
}
/**
* check in array if its first element or if the key already exist in the previous item
* @param jsonArray - the array to search in
* @param key - the key to check
* @return if exists or first element return true, otherwise- false
*/
private Boolean keyExistsOrFirstElement( JSONArray jsonArray,String key){
Boolean exists = false;
Boolean first = false;
JSONObject lastItem = lastItemInArray(jsonArray);
if (lastItem == null) {
first = true;
}
else {
if (lastItem.has(key)) {
exists = true;
}
}
return exists||first;
}
/**
* return last json in json array
* @param jsonArray
* @return last item or null if the array is empty
*/
private JSONObject lastItemInArray(JSONArray jsonArray){
JSONObject lastItem = null;
if(jsonArray.length()>0) {
lastItem = (JSONObject) jsonArray.get(jsonArray.length() - 1);
}
return lastItem;
}
/**
* append current json to the main json
* @param json - the main json to append to it
* @param key - key to append
* @param values - value(s) to append
* @param <T> can be JSONObject or JSONArray
* @param <E> string or jsonObject or jsonArray
* @return json after put
* @throws IllegalAccessException
* @throws InstantiationException
*/
private <T, E> T putJson(T json, String key, E values) throws IllegalAccessException, InstantiationException {
if (json instanceof JSONArray){
JSONArray currentJson= ((JSONArray)json);
if (values == null) //array of strings (for last item)
{
currentJson.put(key);
}
else {
if (keyExistsOrFirstElement(currentJson, key)) {
currentJson.put(new JSONObject().put(key, values));
} else {
JSONObject lastItem = lastItemInArray(currentJson);
if(lastItem != null){
lastItem.put(key, values);
}
}
}
}
if (json instanceof JSONObject){
if (values == null)
throw new BadRequestException("Invalid csv file");
((JSONObject)json).put(key,values);
}
return json;
}
/**
* recursive function to build JSON. Each time it counts the same values in left and send the smaller matrix
* (until the changed value) to the next time.
*
* @param myEntries - the whole matrix
* @param i- row index of the whole matrix
* @param j - column index
* @param numLines - number of lines of inner matrix (num of same values in the left column)
* @param clazz JSONArray or JSONObject
* @param <T> JSONArray or JSONObject
* @return the json object
* @throws IllegalAccessException
* @throws InstantiationException
*/
private <T> T buildJSON(List<String[]> myEntries, int i, final int j, final int numLines, Class<T> clazz) throws IllegalAccessException, InstantiationException {
logger.debug(EELFLoggerDelegate.debugLogger, "start {}({}, {}, {})", getMethodName(), i, j, numLines);
T json = clazz.newInstance();
int startLine = i;
while(i < numLines + startLine){
String[] currentRow = myEntries.get(i);
int length = currentRow.length;
int currentDuplicateRows = findIndexOfChangedLine(myEntries,i,j,numLines, startLine);
String key = currentRow[j];
if (j == length-1) {
json = putJson(json,currentRow[j],null);
}
else
{
json = buildJsonRow(myEntries, i, j, json, currentRow, length, currentDuplicateRows, key);
}
i += currentDuplicateRows;
}
logger.debug(EELFLoggerDelegate.debugLogger, "end {} json = {}", getMethodName(), json);
return json;
}
private <T> T buildJsonRow(List<String[]> myEntries, int i, int j, T json, String[] currentRow, int length, int currentDuplicateRows, String key) throws IllegalAccessException, InstantiationException {
if (key.matches(ARRAY_REGEX)){
JSONArray arrayObjects = buildJSON(myEntries, i, j + 1, currentDuplicateRows, JSONArray.class);
json = putJson(json,key.replaceAll("\\[","").replaceAll("]",""),arrayObjects);
}
else {
if (j < length - 2) {
json = putJson(json, currentRow[j], buildJSON(myEntries, i, j + 1, currentDuplicateRows, JSONObject.class));
}
else
{
if (j == length - 2)//last object
{
if(currentDuplicateRows > 1) {
throw new BadRequestException("Invalid csv file");
}
json = putJson(json, currentRow[j], currentRow[j + 1]);
}
}
}
return json;
}
}