Merge "Refresh offered-APIs doc"
[vid.git] / vid-app-common / src / main / java / org / onap / vid / services / CsvServiceImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.vid.services;
22
23 import com.opencsv.CSVReader;
24 import org.json.JSONArray;
25 import org.json.JSONObject;
26 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
27 import org.springframework.stereotype.Service;
28 import org.springframework.web.multipart.MultipartFile;
29
30 import javax.ws.rs.BadRequestException;
31 import java.io.FileReader;
32 import java.io.IOException;
33 import java.io.InputStreamReader;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.List;
37
38 import static org.onap.vid.utils.Logging.getMethodName;
39
40 @Service
41 public class CsvServiceImpl implements CsvService{
42
43
44     /** The logger. */
45     static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CsvServiceImpl.class);
46
47     private static final String ARRAY_REGEX = "\\[(.*?)\\]";
48
49
50     /**
51      * In UTF-8 the first line starts with "\uFEFF" so need to remove it
52      * @param line is first line contains BOM
53      * @return line after change
54      */
55     private String [] removeBOMFromCsv(String [] line){
56         if (line.length>0)
57             line[0] = line[0].replaceFirst("\uFEFF","").replaceFirst("","");
58         return line;
59     }
60
61     /**
62      * read a csv file and puts its content in list of string arrays (without the empty lines)
63      * @param filePath - the path of file to read
64      * @return the content of file
65      * @throws IOException
66      */
67     @Override
68     public List<String[]> readCsv(String filePath) throws IOException {
69         CSVReader reader = new CSVReader(new FileReader(filePath));
70         return readCsv(reader);
71     }
72
73     @Override
74     public List<String[]> readCsv(MultipartFile file) throws IOException {
75         CSVReader reader = new CSVReader(new InputStreamReader(file.getInputStream()));
76         return readCsv(reader);
77     }
78
79     private List<String[]> addLineWithoutSpaces(List<String[]> myEntries, String [] line){
80         line = Arrays.stream(line).filter(x -> !"".equals(x)).toArray(String[]::new);
81         if(line.length > 0)
82             myEntries.add(line);
83         return myEntries;
84     }
85
86
87     private  List<String[]> readCsv(CSVReader reader) throws IOException {
88         try {
89             List<String[]> myEntries = new ArrayList<>() ;
90             String [] line;
91             Boolean firstLine = true;
92             while ((line = reader.readNext())!= null) {
93                 if (firstLine) {
94                     line = removeBOMFromCsv(line);
95                     firstLine = false;
96                 }
97                 myEntries = addLineWithoutSpaces(myEntries, line);
98             }
99             return myEntries;
100         }
101         catch (Exception e){
102             logger.error("error during reading CSV file. exception:" + e.getMessage());
103             throw e;
104         }
105
106     }
107
108     /**
109      * main function that call to the recursive function with initial parameters
110      * @param myEntries - the matrix with file content
111      * @return the json
112      * @throws IOException
113      * @throws InstantiationException
114      * @throws IllegalAccessException
115      */
116     @Override
117     public JSONObject convertCsvToJson (List<String[]> myEntries) throws InstantiationException, IllegalAccessException {
118         try {
119             return buildJSON(myEntries, 0, 0, myEntries.size(), JSONObject.class);
120         }
121         catch (Exception e){
122             logger.error("error during parsing CSV file. exception:" + e.getMessage());
123             throw e;
124         }
125
126     }
127
128     /**
129      * it goes over the matrix column while the values are the same and returns the index of changed value
130      * @param myEntries the matrix
131      * @param i row index refer to the whole matrix
132      * @param j column index
133      * @param numLines the length of the current inner matrix
134      * @param startLine row index of inner matrix
135      * @return the index of changed line
136      */
137     private int findIndexOfChangedLine(List<String[]> myEntries, final int i, final int j, final int numLines, final int startLine) {
138         int k;
139         for(k = 0; k + i - startLine < numLines && myEntries.get(i)[j].equals(myEntries.get(k + i)[j]) ; k++);
140         return k;
141     }
142
143     /**
144      *  check in array if its first element or if the key already exist in the previous item
145      * @param jsonArray - the array to search in
146      * @param key - the key to check
147      * @return if exists or first element return true, otherwise- false
148      */
149     private Boolean keyExistsOrFirstElement( JSONArray jsonArray,String key){
150         Boolean exists = false;
151         Boolean first = false;
152         JSONObject lastItem = lastItemInArray(jsonArray);
153         if (lastItem == null) {
154             first = true;
155         }
156         else {
157             if (lastItem.has(key)) {
158                 exists = true;
159             }
160         }
161         return exists||first;
162     }
163
164     /**
165      * return last json in json array
166      * @param jsonArray
167      * @return last item or null if the array is empty
168      */
169     private JSONObject lastItemInArray(JSONArray jsonArray){
170         JSONObject lastItem = null;
171         if(jsonArray.length()>0) {
172             lastItem = (JSONObject) jsonArray.get(jsonArray.length() - 1);
173         }
174         return lastItem;
175     }
176
177     /**
178      * append current json to the main json
179      * @param json - the main json to append to it
180      * @param key - key to append
181      * @param values - value(s) to append
182      * @param <T> can be JSONObject or JSONArray
183      * @param <E> string or jsonObject or jsonArray
184      * @return json after put
185      * @throws IllegalAccessException
186      * @throws InstantiationException
187      */
188     private <T, E> T putJson(T json, String key, E values) {
189         if (json instanceof JSONArray){
190             JSONArray currentJson= ((JSONArray)json);
191             if (values == null) //array of strings (for last item)
192             {
193                 currentJson.put(key);
194             }
195             else {
196                 if (keyExistsOrFirstElement(currentJson, key)) {
197                     currentJson.put(new JSONObject().put(key, values));
198                 } else {
199                 JSONObject lastItem = lastItemInArray(currentJson);
200                     lastItem.put(key, values);
201                 }
202             }
203         }
204         if (json instanceof JSONObject){
205             if (values == null)
206                 throw new BadRequestException("Invalid csv file");
207             ((JSONObject)json).put(key,values);
208         }
209         return json;
210     }
211
212
213     /**
214      *  recursive function to build JSON. Each time it counts the same values in left and send the smaller matrix
215      *  (until the changed value) to the next time.
216      *
217      * @param myEntries - the whole matrix
218      * @param i- row index of the whole matrix
219      * @param j - column index
220      * @param numLines - number of lines of inner matrix (num of same values in the left column)
221      * @param clazz JSONArray or JSONObject
222      * @param <T> JSONArray or JSONObject
223      * @return the json object
224      * @throws IllegalAccessException
225      * @throws InstantiationException
226      */
227     private <T> T buildJSON(List<String[]> myEntries, int i, final int j, final int numLines, Class<T> clazz) throws IllegalAccessException, InstantiationException {
228         logger.debug(EELFLoggerDelegate.debugLogger, "start {}({}, {}, {})", getMethodName(), i, j, numLines);
229         T json = clazz.newInstance();
230         int startLine = i;
231         while(i < numLines + startLine){
232             String[] currentRow = myEntries.get(i);
233             int length = currentRow.length;
234             int currentDuplicateRows = findIndexOfChangedLine(myEntries,i,j,numLines, startLine);
235             String key = currentRow[j];
236             if (j == length-1) {
237                 json = putJson(json,currentRow[j],null);
238             }
239             else
240             {
241                 json = buildJsonRow(myEntries, i, j, json, currentRow, length, currentDuplicateRows, key);
242             }
243             i += currentDuplicateRows;
244         }
245         logger.debug(EELFLoggerDelegate.debugLogger, "end {} json = {}", getMethodName(), json);
246         return json;
247     }
248
249     private <T> T buildJsonRow(List<String[]> myEntries, int i, int j, T json, String[] currentRow, int length, int currentDuplicateRows, String key) throws IllegalAccessException, InstantiationException {
250         if (key.matches(ARRAY_REGEX)){
251             JSONArray arrayObjects = buildJSON(myEntries, i, j + 1, currentDuplicateRows, JSONArray.class);
252             json = putJson(json,key.replaceAll("\\[","").replaceAll("]",""),arrayObjects);
253         }
254         else {
255             if (j < length - 2) {
256                 json = putJson(json, currentRow[j], buildJSON(myEntries, i, j + 1, currentDuplicateRows, JSONObject.class));
257             }
258             else
259             {
260                 if (j == length - 2)//last object
261                 {
262                     if(currentDuplicateRows > 1) {
263                         throw new BadRequestException("Invalid csv file");
264                     }
265                     json = putJson(json, currentRow[j], currentRow[j + 1]);
266                 }
267             }
268         }
269         return json;
270     }
271
272 }
273