compiler/libec; eda, extras: const fixes for DB apps
[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 void OnMessage(const 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       bool started = false;
68       int start = 0, end = 0;
69       int readCount = 0;
70       Array<char> buffer { minAllocSize = 4096 };
71
72       //info.charNum = 0;
73       info.lineNum = 0;
74       info.rowNum = 0;
75       info.fieldNum = 0;
76
77       while(!file.Eof() && status)
78       {
79          int c, offset = 0;
80
81          if(started)
82          {
83             offset = readCount - start;
84             if(offset > buffer.minAllocSize / 2)
85                buffer.minAllocSize += 4096;
86             memmove(&buffer[0], &buffer[start], offset);
87             end -= start;
88             start = 0;
89          }
90
91          readCount = offset + file.Read(&buffer[offset], 1, buffer.minAllocSize - offset);
92          for(c = offset; c < readCount && status; c++)
93          {
94             char ch = buffer[c];
95             if(quoted)
96             {
97                if(ch == options.valueQuotes)
98                {
99                   quoted = false;
100                   end = c;
101                }
102             }
103             else
104             {
105                if(ch == options.valueQuotes)
106                {
107                   quoted = true;
108                   start = c + 1;
109                   started = true;
110                }
111                //else if(ch == options.fieldSeparator || ch == '\n')
112                else if(ch == options.fieldSeparator ||
113                      (ch == '\n' && (!options.tolerateNewLineInValues || info.fieldNum >= options.expectedFieldCount-1)))
114                {
115                   if(values.count < options.expectedFieldCount)
116                   {
117                      int len = started ? (end-start) : 0;
118                      String value = new char[len+1];
119                      memcpy(value, &buffer[start], len);
120                      value[len] = 0;
121                      values.Add(value);
122                   }
123                   start = end = 0;
124                   started = false;
125                   info.fieldNum++;
126                   if(ch == '\n')
127                   {
128                      info.lineNum++;
129                      info.rowNum++;
130                      status = OnRowStrings(values);
131                      values.Free();
132                      //info.charNum = 0;
133                      info.fieldNum = 0;
134                   }
135                }
136                else if(ch == '\r');
137                else
138                {
139                   if(!started)
140                   {
141                      start = c;
142                      started = true;
143                   }
144                   end = c+1;
145                }
146             }
147             //info.charNum++;
148          }
149       }
150       if(end > start)
151       {
152          int len = end-start;
153          String value = new char[len+1];
154          memcpy(value, &buffer[start], len);
155          value[len] = 0;
156          values.Add(value);
157       }
158       if(values.count && status)
159       {
160          status = OnRowStrings(values);
161          info.rowNum++;
162       }
163       values.Free();
164       delete values;
165    }
166 }