aboutsummaryrefslogtreecommitdiffstats
path: root/vid-app-common/src/main/java/org/onap/vid/services/CsvServiceImpl.java
blob: c34bf4e6531dd0a47e9aa72ce3172c2b6e8a77d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package org.onap.vid.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.vid.utils.Logging.getMethodName;

@Service
public class CsvServiceImpl implements CsvService{


    /** The logger. */
    static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CsvServiceImpl.class);

    private static final String ARRAY_REGEX = "\\[(.*?)\\]";


    /**
     * In UTF-8 the first line starts with "\uFEFF" so need to remove it
     * @param line is first line contains BOM
     * @return line after change
     */
    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;
    }

}