1 package org.onap.vid.services;
3 import com.opencsv.CSVReader;
4 import org.json.JSONArray;
5 import org.json.JSONObject;
6 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
7 import org.springframework.stereotype.Service;
8 import org.springframework.web.multipart.MultipartFile;
10 import javax.ws.rs.BadRequestException;
11 import java.io.FileReader;
12 import java.io.IOException;
13 import java.io.InputStreamReader;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.List;
18 import static org.onap.vid.utils.Logging.getMethodName;
21 public class CsvServiceImpl implements CsvService{
25 static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CsvServiceImpl.class);
27 private static final String ARRAY_REGEX = "\\[(.*?)\\]";
31 * In UTF-8 the first line starts with "\uFEFF" so need to remove it
32 * @param line is first line contains BOM
33 * @return line after change
35 private String [] removeBOMFromCsv(String [] line){
37 line[0] = line[0].replaceFirst("\uFEFF","").replaceFirst("","");
42 * read a csv file and puts its content in list of string arrays (without the empty lines)
43 * @param filePath - the path of file to read
44 * @return the content of file
48 public List<String[]> readCsv(String filePath) throws IOException {
49 CSVReader reader = new CSVReader(new FileReader(filePath));
50 return readCsv(reader);
54 public List<String[]> readCsv(MultipartFile file) throws IOException {
55 CSVReader reader = new CSVReader(new InputStreamReader(file.getInputStream()));
56 return readCsv(reader);
59 private List<String[]> addLineWithoutSpaces(List<String[]> myEntries, String [] line){
60 line = Arrays.stream(line).filter(x -> !"".equals(x)).toArray(String[]::new);
67 private List<String[]> readCsv(CSVReader reader) throws IOException {
69 List<String[]> myEntries = new ArrayList<>() ;
71 Boolean firstLine = true;
72 while ((line = reader.readNext())!= null) {
74 line = removeBOMFromCsv(line);
77 myEntries = addLineWithoutSpaces(myEntries, line);
82 logger.error("error during reading CSV file. exception:" + e.getMessage());
89 * main function that call to the recursive function with initial parameters
90 * @param myEntries - the matrix with file content
93 * @throws InstantiationException
94 * @throws IllegalAccessException
97 public JSONObject convertCsvToJson (List<String[]> myEntries) throws InstantiationException, IllegalAccessException {
99 return buildJSON(myEntries, 0, 0, myEntries.size(), JSONObject.class);
102 logger.error("error during parsing CSV file. exception:" + e.getMessage());
109 * it goes over the matrix column while the values are the same and returns the index of changed value
110 * @param myEntries the matrix
111 * @param i row index refer to the whole matrix
112 * @param j column index
113 * @param numLines the length of the current inner matrix
114 * @param startLine row index of inner matrix
115 * @return the index of changed line
117 private int findIndexOfChangedLine(List<String[]> myEntries, final int i, final int j, final int numLines, final int startLine) {
119 for(k = 0; k + i - startLine < numLines && myEntries.get(i)[j].equals(myEntries.get(k + i)[j]) ; k++);
124 * check in array if its first element or if the key already exist in the previous item
125 * @param jsonArray - the array to search in
126 * @param key - the key to check
127 * @return if exists or first element return true, otherwise- false
129 private Boolean keyExistsOrFirstElement( JSONArray jsonArray,String key){
130 Boolean exists = false;
131 Boolean first = false;
132 JSONObject lastItem = lastItemInArray(jsonArray);
133 if (lastItem == null) {
137 if (lastItem.has(key)) {
141 return exists||first;
145 * return last json in json array
147 * @return last item or null if the array is empty
149 private JSONObject lastItemInArray(JSONArray jsonArray){
150 JSONObject lastItem = null;
151 if(jsonArray.length()>0) {
152 lastItem = (JSONObject) jsonArray.get(jsonArray.length() - 1);
158 * append current json to the main json
159 * @param json - the main json to append to it
160 * @param key - key to append
161 * @param values - value(s) to append
162 * @param <T> can be JSONObject or JSONArray
163 * @param <E> string or jsonObject or jsonArray
164 * @return json after put
165 * @throws IllegalAccessException
166 * @throws InstantiationException
168 private <T, E> T putJson(T json, String key, E values) throws IllegalAccessException, InstantiationException {
169 if (json instanceof JSONArray){
170 JSONArray currentJson= ((JSONArray)json);
171 if (values == null) //array of strings (for last item)
173 currentJson.put(key);
176 if (keyExistsOrFirstElement(currentJson, key)) {
177 currentJson.put(new JSONObject().put(key, values));
179 JSONObject lastItem = lastItemInArray(currentJson);
180 if(lastItem != null){
181 lastItem.put(key, values);
187 if (json instanceof JSONObject){
189 throw new BadRequestException("Invalid csv file");
190 ((JSONObject)json).put(key,values);
197 * recursive function to build JSON. Each time it counts the same values in left and send the smaller matrix
198 * (until the changed value) to the next time.
200 * @param myEntries - the whole matrix
201 * @param i- row index of the whole matrix
202 * @param j - column index
203 * @param numLines - number of lines of inner matrix (num of same values in the left column)
204 * @param clazz JSONArray or JSONObject
205 * @param <T> JSONArray or JSONObject
206 * @return the json object
207 * @throws IllegalAccessException
208 * @throws InstantiationException
210 private <T> T buildJSON(List<String[]> myEntries, int i, final int j, final int numLines, Class<T> clazz) throws IllegalAccessException, InstantiationException {
211 logger.debug(EELFLoggerDelegate.debugLogger, "start {}({}, {}, {})", getMethodName(), i, j, numLines);
212 T json = clazz.newInstance();
214 while(i < numLines + startLine){
215 String[] currentRow = myEntries.get(i);
216 int length = currentRow.length;
217 int currentDuplicateRows = findIndexOfChangedLine(myEntries,i,j,numLines, startLine);
218 String key = currentRow[j];
220 json = putJson(json,currentRow[j],null);
224 json = buildJsonRow(myEntries, i, j, json, currentRow, length, currentDuplicateRows, key);
226 i += currentDuplicateRows;
228 logger.debug(EELFLoggerDelegate.debugLogger, "end {} json = {}", getMethodName(), json);
232 private <T> T buildJsonRow(List<String[]> myEntries, int i, int j, T json, String[] currentRow, int length, int currentDuplicateRows, String key) throws IllegalAccessException, InstantiationException {
233 if (key.matches(ARRAY_REGEX)){
234 JSONArray arrayObjects = buildJSON(myEntries, i, j + 1, currentDuplicateRows, JSONArray.class);
235 json = putJson(json,key.replaceAll("\\[","").replaceAll("]",""),arrayObjects);
238 if (j < length - 2) {
239 json = putJson(json, currentRow[j], buildJSON(myEntries, i, j + 1, currentDuplicateRows, JSONObject.class));
243 if (j == length - 2)//last object
245 if(currentDuplicateRows > 1) {
246 throw new BadRequestException("Invalid csv file");
248 json = putJson(json, currentRow[j], currentRow[j + 1]);