SimanticsExcel interface refresh
[simantics/platform.git] / bundles / org.simantics.excel / native / Interface.cpp
1
2 #include <sstream>
3 #include <map>
4 #include <stdio.h>
5 #include <tchar.h>
6 #include <cstring>
7 #include <atlstr.h>
8
9 using namespace std;
10
11 // mso.dll
12 #import "libid:{2df8d04c-5bfa-101b-bde5-00aa0044de52}"  auto_rename
13 // vb6ext.olb
14 #import "libid:{0002e157-0000-0000-c000-000000000046}"  auto_rename
15 // excel.exe
16 #import "libid:{00020813-0000-0000-c000-000000000046}"  auto_rename
17
18 using namespace Excel;
19
20 #include "jni2.h"
21
22 //#define SIMANTICS_DEBUG(...) \
23 //{ FILE *fp = fopen("d:/excel.log", "ab"); fprintf(fp, __VA_ARGS__); fflush(fp); fclose(fp);  }
24
25 #define SIMANTICS_DEBUG(...)
26
27 //using namespace std;
28 //using namespace Office;
29
30 map<int, _WorksheetPtr> handles;
31 static int handleCounter = 1;
32
33 void replace(std::string &s, const std::string &s1, const std::string &s2) {
34
35         int index = 0;
36
37         index = s.find(s1, index);
38         while(index != std::string::npos) {
39                 s.replace(index, s1.length(), s2.c_str(), s2.length());
40                 index = s.find(s1, index + 1);
41         }
42
43 }
44
45 boolean getString(JNIEnv *env, jstring str, char *target) {
46
47     jboolean iscopy;
48     const char *chars = env->GetStringUTFChars(str, &iscopy);
49         if(!chars) return false;
50
51         strcpy(target, chars);
52
53     env->ReleaseStringUTFChars(str, chars);
54
55         return true;
56
57 }
58
59 string getString(JNIEnv *env, jstring str) {
60
61     jboolean iscopy;
62     const char *chars = env->GetStringUTFChars(str, &iscopy);
63         if(!chars) return 0;
64         string result = chars;
65     env->ReleaseStringUTFChars(str, chars);
66         return result;
67
68 }
69
70 Excel::_ApplicationPtr getApplication(bool create) {
71         
72         Excel::_ApplicationPtr application;
73
74         CLSID clsid;
75         HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid);
76         if(FAILED(hr)) {
77                 return 0;
78         }
79
80         if (!SUCCEEDED(application.GetActiveObject(clsid)))
81         {
82                 if(create) {
83                         hr = application.CreateInstance(clsid);
84                         if(FAILED(hr)) {
85                                 return 0;
86                         }
87                 } else {
88                         return 0;
89                 }
90         }
91
92         application->EnableEvents = VARIANT_FALSE;
93
94         return application;
95
96 }
97
98 JNIEXPORT jint JNICALL Java_org_simantics_excel_Excel_init(JNIEnv *env, jobject) {
99
100         SIMANTICS_DEBUG("Excel init\r\n");
101
102         HRESULT hr = ::OleInitialize(NULL);
103         if(FAILED(hr)) {
104                 return hr;
105         }
106
107         /*
108         Excel::_ApplicationPtr application = getApplication(true);
109         if(!application) return -1;
110         bstr_t version = application->GetVersion(0);
111         ostringstream os;
112         os << version << endl;
113         SIMANTICS_DEBUG("Init successful with version %s\r\n",  os.str().c_str());
114
115         application->put_Interactive( 0, VARIANT_FALSE );
116         application->put_Visible( 0, VARIANT_FALSE );
117         */
118
119         return 0;
120
121 }
122
123 Excel::_WorkbookPtr getWorkbook(const Excel::_ApplicationPtr &application, const string &fileName) {
124
125         SIMANTICS_DEBUG("getWorkbook(%s)\r\n", fileName.c_str());
126
127         for(int i=0;i<application->Workbooks->Count;i++) {
128                 _bstr_t fullName;
129                 Excel::_WorkbookPtr workbook = application->Workbooks->GetItem( _variant_t(i+1) );
130                 workbook->get_FullName(0, &fullName.GetBSTR());
131                 std::string fullNameStr = fullName;
132                 SIMANTICS_DEBUG("'%s'\r\n", fullNameStr.c_str());
133                 if(fullNameStr == fileName) return workbook;
134         }
135
136         return 0;
137
138 }
139
140 Excel::_WorksheetPtr getWorksheet(const Excel::_WorkbookPtr workbook, const string &sheetName) {
141
142         SIMANTICS_DEBUG("getWorksheet(%s)\r\n", sheetName.c_str());
143
144         for(int i=0;i<workbook->Sheets->Count;i++) {
145                 Excel::_WorksheetPtr sheet = workbook->Sheets->GetItem(_variant_t(i+1));
146                 std:string name = sheet->Name;
147                 SIMANTICS_DEBUG("'%s'\r\n", name.c_str());
148                 if(name == sheetName) return sheet;
149         }
150
151         return 0;
152
153 }
154
155 char message[1024];
156
157 JNIEXPORT jstring JNICALL Java_org_simantics_excel_Excel_open(JNIEnv *env, jobject, jstring fileName, jstring sheetName_) {
158
159         string name = getString(env, fileName);
160         string sheetName = getString(env, sheetName_);
161
162         _variant_t varOption( (long) DISP_E_PARAMNOTFOUND, VT_ERROR );
163
164         Excel::_ApplicationPtr application = getApplication(true);
165         if(!application) return env->NewStringUTF("Excel application could not be started.");
166
167         Excel::_WorkbookPtr workbook = getWorkbook(application, name);
168         if(!workbook) {
169
170                 SIMANTICS_DEBUG("getWorkbook(%s) book was not found. Opening.\r\n", name.c_str());
171
172                 FILE *fp = fopen(name.c_str(), "r");
173                 if(fp) {
174
175                 SIMANTICS_DEBUG("getWorkbook(%s) book was found. Opening file.\r\n", name.c_str());
176
177                         fclose(fp);
178                         workbook = application->Workbooks->OpenXML(_bstr_t(name.c_str()), 
179                                 varOption, varOption);
180                         if(!workbook) return env->NewStringUTF("File could not be opened.");
181
182                 } else {
183
184                 SIMANTICS_DEBUG("getWorkbook(%s) book was not found. Creating file.\r\n", name.c_str());
185
186                         workbook = application->Workbooks->Add(varOption);
187
188                         if(!workbook) return env->NewStringUTF("New workbook could not be created.");
189
190                         try {
191
192                                 Excel::_WorksheetPtr sheet = workbook->Sheets->Add();
193                                 sheet->Name = _bstr_t(sheetName.c_str());
194
195                                 /*
196                                 // Does not work for some unknown reason
197                                 for(int i=0;i<workbook->Sheets->Count;i++) {
198                                         Excel::_WorksheetPtr sheet = workbook->Sheets->GetItem(_variant_t(i+1));
199                                         if(name != sheetName)
200                                                 sheet->PutVisible(0, Excel::XlSheetVisibility::xlSheetVeryHidden);
201                                 }
202                                 */
203
204                                 workbook->SaveAs(_bstr_t(name.c_str()), varOption, varOption, varOption, varOption,
205                                         varOption, Excel::xlExclusive, varOption, varOption, varOption, varOption, 0);
206
207                         } catch (_com_error& e) {
208                                 _bstr_t bstr = _bstr_t(e.ErrorMessage());
209                                 string s(bstr);
210                                 _bstr_t bstr2 = _bstr_t(e.Description());
211                                 string s2(bstr2);
212                                 SIMANTICS_DEBUG("getWorkbook(%s) book was not found. Creation error 1 '%s' '%s'\r\n", name.c_str(), s.c_str(), s2.c_str());
213                                 strcpy(message, s2.c_str());
214                                 return env->NewStringUTF(message);
215                         } catch (exception& e) {
216                                 SIMANTICS_DEBUG("getWorkbook(%s) book was not found. Creation error 2 '%s'\r\n", name.c_str(), e.what());
217                                 return env->NewStringUTF(e.what());
218                         } catch (...) {
219                                 SIMANTICS_DEBUG("getWorkbook(%s) book was not found. Creation error 3 \r\n", name.c_str());
220                                 return env->NewStringUTF("Undefined error");
221                         }
222                 
223
224                 }
225
226         }
227
228         application->put_Interactive( 0, VARIANT_TRUE );
229         application->put_Visible( 0, VARIANT_TRUE );
230
231         workbook->Activate();
232
233         SIMANTICS_DEBUG("getWorkbook(%s) searches for sheet '%s'\r\n", name.c_str(), sheetName.c_str());
234
235         try {
236                 Excel::_WorksheetPtr sheet = getWorksheet(workbook, sheetName);
237                 if(!sheet) {
238                         SIMANTICS_DEBUG("creating sheet %s for '%s'\r\n", sheetName.c_str(), name.c_str());
239                         sheet = workbook->Sheets->Add();
240                         sheet->Name = _bstr_t(sheetName.c_str());
241                 }
242             int handle = handleCounter++;
243         SIMANTICS_DEBUG("opened handle %d for '%s'\r\n", handle, name.c_str());
244         handles[handle] = sheet;
245                 ostringstream os;
246                 os << handle;
247                 return env->NewStringUTF(os.str().c_str());
248
249         } catch (_com_error& e) {
250                 _bstr_t bstr = _bstr_t(e.ErrorMessage());
251                 string s(bstr);
252                 _bstr_t bstr2 = _bstr_t(e.Description());
253                 string s2(bstr2);
254                 SIMANTICS_DEBUG("getWorkbook(%s) book was not found. Creation error 1 '%s' '%s'\r\n", name.c_str(), s.c_str(), s2.c_str());
255                 return env->NewStringUTF(s2.c_str());
256         } catch (exception& e) {
257                 SIMANTICS_DEBUG("getWorkbook(%s) book was not found. Creation error 2 '%s'\r\n", name.c_str(), e.what());
258                 return env->NewStringUTF(e.what());
259         } catch (...) {
260                 SIMANTICS_DEBUG("getWorkbook(%s) book was not found. Creation error\r\n", name.c_str());
261                 return env->NewStringUTF("Unhandled exception.");
262         }
263
264
265         //Excel::_WorksheetPtr sheet = workbook->ActiveSheet;
266         //if(!sheet) return -4;
267
268
269 }
270
271 string cellName(int row, int column) {
272
273         string result;
274
275         SIMANTICS_DEBUG("cellName(%d, %d) -> ", row, column);
276
277         do {
278                 char rem = column % 26;
279                 char c = 'A' + rem;
280                 result += c;
281                 column -= rem;
282                 column /= 26;
283         } while(column);
284
285         ostringstream os;
286         os << (row + 1);
287         result += os.str();
288
289         SIMANTICS_DEBUG("%s\r\n", result.c_str());
290
291         return result;
292
293 }
294
295 /*
296 Excel::_WorkbookPtr getWorkbook(int handle) {
297         map<int, Excel::_WorkbookPtr>::iterator it = handles.find(handle);
298         if(it != handles.end()) return (*it).second;
299         else return 0;
300 }
301 */
302
303 Excel::_WorksheetPtr getWorksheet(int handle) {
304         map<int, Excel::_WorksheetPtr>::iterator it = handles.find(handle);
305         if(it != handles.end()) return (*it).second;
306         else return 0;
307 }
308
309 JNIEXPORT jint JNICALL Java_org_simantics_excel_Excel_setDouble(JNIEnv *env, jobject, jint handle, jint row, jint column, jdouble value) {
310
311         SIMANTICS_DEBUG("setDouble(%d, %d, %f)\r\n", row, column, value);
312
313         Excel::_WorksheetPtr sheet = getWorksheet(handle);
314         if(!sheet) return -1;
315         Excel::RangePtr range = sheet->Cells;
316         if(!range) return -2;
317         range->Item[row+1][column+1] = value;
318
319         return 0;
320
321 }
322
323 JNIEXPORT jint JNICALL Java_org_simantics_excel_Excel_setString(JNIEnv *env, jobject, jint handle, jint row, jint column, jstring value) {
324
325         string val = getString(env, value);
326
327         SIMANTICS_DEBUG("setString(%d, %d, %s)\r\n", row, column, val.c_str());
328
329         Excel::_WorksheetPtr sheet = getWorksheet(handle);
330         if(!sheet) return -1;
331         Excel::RangePtr range = sheet->Cells;
332         if(!range) return -2;
333
334         const char *text = val.c_str();
335
336         range->Item[row+1][column+1] = text;
337         //range->Name[row+1][column+1] = text;
338
339         return 0;
340
341 }
342
343 JNIEXPORT jint JNICALL Java_org_simantics_excel_Excel_setName(JNIEnv *env, jobject, jint handle, jint row, jint column, jstring value) {
344
345         string val = getString(env, value);
346
347         SIMANTICS_DEBUG("setName(%d, %d, %s)\r\n", row, column, val.c_str());
348
349         Excel::_WorksheetPtr sheet = getWorksheet(handle);
350         if(!sheet) return -1;
351         //sheet->c
352         Excel::RangePtr range = sheet->GetRange(_variant_t(cellName(row, column).c_str()));
353         if(!range) return -2;
354         range->Name = _variant_t(val.c_str());
355
356
357         //range->Item[row+1][column+1] = text;
358         //range->Name[row+1][column+1] = text;
359
360         return 0;
361
362 }
363
364 JNIEXPORT jint JNICALL Java_org_simantics_excel_Excel_setVisible(JNIEnv *env, jobject, jint handle, jboolean value) {
365
366         SIMANTICS_DEBUG("setVisible(%d)\r\n", value);
367
368         Excel::_WorksheetPtr sheet = getWorksheet(handle);
369         if(!sheet) return -1;
370         Excel::_ApplicationPtr application = sheet->Application;
371         if(!application) return -2;
372
373         if(value) {
374                 application->put_Interactive( 0, VARIANT_TRUE );
375                 application->put_Visible( 0, VARIANT_TRUE );
376         } else {
377                 application->put_Interactive( 0, VARIANT_FALSE );
378                 application->put_Visible( 0, VARIANT_FALSE );
379         }
380
381         return 0;
382
383 }
384
385 JNIEXPORT jint JNICALL Java_org_simantics_excel_Excel_close(JNIEnv *env, jobject, jint handle) {
386
387         SIMANTICS_DEBUG("closing handle %d\r\n", handle);
388
389         Excel::_WorksheetPtr sheet = getWorksheet(handle);
390         if(!sheet) return -1;
391         Excel::_ApplicationPtr application = sheet->Application;
392         if(!application) return -2;
393
394         Excel::_WorkbookPtr workbook = (Excel::_WorkbookPtr)sheet->Parent;
395         if(!workbook) return -3;
396
397 //      SIMANTICS_DEBUG("About to save\r\n");
398         HRESULT hr = workbook->Save();
399 //      workbook->put_Saved(0, VARIANT_TRUE);
400         if(FAILED(hr)) {
401                 SIMANTICS_DEBUG("Save failed with %d\r\n", hr);
402                 return hr;
403         }
404 //      SIMANTICS_DEBUG("About to AcceptAllChanges\r\n");
405 //      HRESULT hr = workbook->AcceptAllChanges();
406 //      if(FAILED(hr)) {
407
408         //SIMANTICS_DEBUG("Close failed with %d\r\n", hr);
409         //      return hr;
410         //}
411
412         // Release this reference for Close
413         handles.erase(handle);
414
415         map<int, Excel::_WorksheetPtr>::iterator it = handles.begin();
416         for(;it != handles.end();++it) {
417                 Excel::_WorksheetPtr sheet2 = (*it).second;
418                 Excel::_WorkbookPtr book = (Excel::_WorkbookPtr)sheet2->Parent;
419                 // The book remains open
420                 if(book->Name == workbook->Name) return 0;
421         }
422
423         //SIMANTICS_DEBUG("About to PutUserControl\r\n");
424         //workbook->PutUserControl(VARIANT_FALSE);
425         SIMANTICS_DEBUG("About to Close\r\n");
426         hr = workbook->Close(VARIANT_FALSE);
427         if(FAILED(hr)) {
428                 SIMANTICS_DEBUG("Close failed with %d\r\n", hr);
429                 return hr;
430         }
431
432         long remaining = 0;
433
434         application->Workbooks->get_Count(&remaining);
435         if(!remaining) {
436                 application->put_Interactive( 0, VARIANT_FALSE );
437                 application->put_Visible( 0, VARIANT_FALSE );
438                 hr = application->Quit();
439                 if(FAILED(hr)) {
440                         SIMANTICS_DEBUG("Close failed with %d\r\n", hr);
441                         return hr;
442                 }
443         }
444
445 //      SIMANTICS_DEBUG("About to hide");
446
447 //      application->put_Interactive( 0, VARIANT_FALSE );
448 //      application->put_Visible( 0, VARIANT_FALSE );
449
450 //      if(handles.empty())
451 //              application->Quit();
452
453         return 0;
454
455 }
456
457
458 JNIEXPORT jdouble JNICALL Java_org_simantics_excel_Excel_getDouble(JNIEnv *env, jobject, jint handle, jint row, jint column) {
459
460         SIMANTICS_DEBUG("getDouble(%d, %d)\r\n", row, column);
461
462         Excel::_WorksheetPtr sheet = getWorksheet(handle);
463         if(!sheet) return 0.0; // TODO: NAN
464         Excel::RangePtr range = sheet->Cells;
465         if(!range) return 0.0; // TODO: NAN
466
467         variant_t val = range->Item[row+1][column+1];
468         if (val.vt != VT_NULL) {
469                 try {
470                         double d = val;
471                         SIMANTICS_DEBUG("%f\r\n",d);
472                         return d;
473                 } catch (...) {
474                         return 0.0; // TODO: NAN
475                 }
476         }
477         return 0.0; // TODO: NAN
478
479 }
480
481 JNIEXPORT jstring JNICALL Java_org_simantics_excel_Excel_getString(JNIEnv *env, jobject, jint handle, jint row, jint column) {
482         USES_CONVERSION;
483         //string val = getString(env, value);
484
485         SIMANTICS_DEBUG("getString(%d, %d)\r\n", row, column);
486
487         Excel::_WorksheetPtr sheet = getWorksheet(handle);
488         if(!sheet) return NULL;
489         Excel::RangePtr range = sheet->Cells;
490         if(!range) return NULL;
491
492         _bstr_t val = _bstr_t(range->Item[row + 1][column + 1]);
493         //if (val.vt != VT_NULL) {  // val.vt seems to be always VT_DISPATCH.
494                 //try {
495                         //this call will crash the application, if the cell is empty!
496                         //_bstr_t bstrt = val;
497                         //std::string text(val);
498                         //CString text(val);
499                         //text.
500                         //TCHAR text[1024];
501                         // TODO: this uses wide character (16-bit) while NewStringUTF expext regular character (8-bit), hence Java gets only the fisrt charater of the whole string.
502                         //_stprintf(text, _T("%s"), (LPCTSTR)bstrt);
503                         //const char *text = bstrt;
504                         SIMANTICS_DEBUG("%s\r\n",text);
505
506                         string text(val);
507
508
509                         return env->NewStringUTF(text.c_str());
510
511                 //} catch (...) {
512                 //      return NULL; 
513                 //}
514         //} 
515         //SIMANTICS_DEBUG("%d\r\n",val.vt);
516         //return NULL; 
517
518 }