extras/csv: Set sensible defaults options for CSVParser
[sdk] / extras / CSVParser.ec
1 #include <stdarg.h>
2
3 public import "ecere"
4
5 // to be moved in ecere?
6 public class FileHandler
7 {
8    public File file;
9
10    ~FileHandler()
11    {
12       delete file;
13    }
14 }
15
16 public struct CSVParserParameters
17 {
18    char fieldSeparator;
19    char valueQuotes;
20    int expectedFieldCount;
21    bool tolerateNewLineInValues;
22    //bool checkNulls;
23    //bool checkCurlies;
24 };
25
26 CSVParserParameters classicParameters = { ',', '\"', 0, false };
27
28 public struct CSVParserState
29 {
30    uint lineNum;
31    uint charNum;
32    uint rowNum;
33    uint fieldNum;
34 };
35
36 public class CSVParser : public FileHandler
37 {
38 public:
39    CSVParserParameters options { ',', '\"', 0, false };
40    CSVParserState info;
41
42    void PrintMessage(typed_object object, ...)
43    {
44       va_list args;
45       char buffer[4096];
46       va_start(args, object);
47       PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args);
48       va_end(args);
49       OnMessage(buffer);
50    }
51
52    virtual bool OnMessage(String message)
53    {
54       ::PrintLn(this._class.name, ": ", message,
55             " lineNum=", info.lineNum,
56             " charNum=", info.charNum,
57             " rowNum=", info.rowNum,
58             " fieldNum=", info.fieldNum);
59    }
60
61    virtual bool OnRowStrings(Array<String> strings);
62
63    virtual void Process()
64    {
65       bool quoted = false, status = true;
66       Array<String> values { };
67       int start = 0, end = 0;
68       int readCount = 0;
69       Array<char> buffer { minAllocSize = 4096 };
70
71       //info.charNum = 0;
72       info.lineNum = 0;
73       info.rowNum = 0;
74       info.fieldNum = 0;
75
76       while(!file.Eof() && status)
77       {
78          int c, offset = 0;
79
80          if(start)
81          {
82             offset = readCount - start;
83             if(offset > buffer.minAllocSize / 2)
84                buffer.minAllocSize += 4096;
85             memmove(&buffer[0], &buffer[start], offset);
86             end -= start;
87             start = 0;
88          }
89
90          readCount = offset + file.Read(&buffer[offset], 1, buffer.minAllocSize - offset);
91          for(c = offset; c < readCount && status; c++)
92          {
93             char ch = buffer[c];
94             if(quoted)
95             {
96                if(ch == options.valueQuotes)
97                {
98                   quoted = false;
99                   end = c;
100                }
101             }
102             else
103             {
104                if(ch == options.valueQuotes)
105                {
106                   quoted = true;
107                   start = c + 1;
108                }
109                //else if(ch == options.fieldSeparator || ch == '\n')
110                else if(ch == options.fieldSeparator ||
111                      (ch == '\n' && (!options.tolerateNewLineInValues || info.fieldNum == options.expectedFieldCount-1)))
112                {
113                   int len = end-start;
114                   String value = new char[len+1];
115                   memcpy(value, &buffer[start], len);
116                   value[len] = 0;
117                   values.Add(value);
118                   start = end = 0;
119                   info.fieldNum++;
120                   if(ch == '\n')
121                   {
122                      info.lineNum++;
123                      info.rowNum++;
124                      status = OnRowStrings(values);
125                      values.Free();
126                      //info.charNum = 0;
127                      info.fieldNum = 0;
128                   }
129                }
130                else if(ch == '\r');
131                else
132                {
133                   if(!start)
134                      start = c;
135                   end = c;
136                }
137             }
138             //info.charNum++;
139          }
140       }
141       if(end > start)
142       {
143          int len = end-start;
144          String value = new char[len+1];
145          memcpy(value, &buffer[start], len);
146          value[len] = 0;
147          values.Add(value);
148       }
149       if(values.count && status)
150       {
151          status = OnRowStrings(values);
152          info.rowNum++;
153       }
154       values.Free();
155       delete values;
156    }
157 }