/* * $Id: ImportTextCSV.cpp,v 1.21 2006/06/23 06:33:15 ozawa Exp $ * * Copyright 2005- ONGS Inc. All rights reserved. * * author: Masanori OZAWA (ozawa@ongs.co.jp) * version: $Revision: 1.21 $ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY ONGS INC ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL ONGS INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the ONGS Inc. * */ #include "xdtp.h" #include "modules/imports/ImportTextCSV.h" #include "CSVAnalyzer.h" #include "Utilities.h" #include "URLTool.h" #include "XMLTool.h" using namespace XDTP; #define COL_INFO_ALIGN_LEFT 0x00000000 #define COL_INFO_ALIGN_RIGHT 0x00000001 #define COL_INFO_ALIGN_CENTER 0x00000002 #define COL_INFO_ALIGN_RESERVED 0x00000003 #define COL_INFO_ALIGN_MASK 0x00000003 #define GET_COL_INFO(info, mask) (info & mask) #define SET_COL_INFO(info, val, mask) (info = (info & ~mask) | (val & mask)) ImportTextCSV::ImportTextCSV() { } ImportTextCSV::~ImportTextCSV() { } /** * XSL変換後のXML文章を保存する直前に呼び出されます。 * * @param aDocument XML文章 */ void ImportTextCSV::treatPostDocument(xmlDocPtr aDocument) { GLSDImportModuleAdapter::treatPostDocument(aDocument); int count; XMLTool tool; xmlNodePtr root = xmlDocGetRootElement(aDocument); xmlNodeSetPtr nodes; xmlXPathObjectPtr obj = tool.getNodeList(root, "//import[@type=\"text/csv\"]"); if (obj) { nodes = obj->nodesetval; for (count = 0; count < nodes->nodeNr; count++) { if (!transform(aDocument, nodes->nodeTab[count])) { THROW("ImportTextCSV: transform failed."); } } xmlXPathFreeObject(obj); } } bool ImportTextCSV::transform(xmlDocPtr aDocument, xmlNodePtr aNode) { Glib::ustring ref; Glib::ustring caption; Glib::ustring encoding; if (!getImportAttributes(aNode, ref, caption, encoding)) { return false; } if ("html" == m_OutputType) { return csvTo_nHTML(true, aDocument, aNode, ref, caption, encoding); } else if ("xhtml" == m_OutputType) { return csvTo_nHTML(false, aDocument, aNode, ref, caption, encoding); } else if ("text" == m_OutputType) { return preserveImport(aDocument, aNode, ref, caption, encoding); } // otherwise, do not operation. return true; } bool ImportTextCSV::csvTo_nHTML (bool isHTML, xmlDocPtr aDocument, xmlNodePtr aNode, const Glib::ustring& aRef, const Glib::ustring& aCaption, const Glib::ustring& anEncoding) { // スタイルシートを取得 xmlNodePtr comment = getStyleSheet(aDocument, "text/css"); if (comment) { // スタイルシートにイメージのスタイルの指定があるか確認 Glib::ustring content = (comment->content ? (const char*)comment->content : ""); Glib::ustring::size_type pos = content.find("table.csv"); if (Glib::ustring::npos == pos) { // スタイルシートにイメージのスタイルを追加 content += LINE_SEP; content += " table.csv {" LINE_SEP; content += " border-collapse: collapse;" LINE_SEP; content += " margin: 1.5mm auto 3mm auto;" LINE_SEP; content += " empty-cells: show;" LINE_SEP; content += " caption-side: top;" LINE_SEP; content += " width: 90%;" LINE_SEP; content += " }" LINE_SEP; content += " table.csv tr th," LINE_SEP; content += " table.csv tr td {" LINE_SEP; content += " border: 2px solid gray;" LINE_SEP; content += " }" LINE_SEP; content += " caption.csv {" LINE_SEP; content += " margin-top: 3mm;" LINE_SEP; content += " }" LINE_SEP; xmlNodeSetContent(comment, (const xmlChar*)content.c_str()); } } // テーブルエレメント作成 xmlNodePtr table = xmlNewNode(NULL, (const xmlChar*)"table"); xmlSetProp(table, (const xmlChar*)"border", (const xmlChar*)"1"); xmlSetProp(table, (const xmlChar*)"class", (const xmlChar*)"csv"); // 表題作成 xmlNodePtr caption = xmlNewNode(NULL, (const xmlChar*)"caption"); xmlSetProp(caption, (const xmlChar*)"class", (const xmlChar*)"csv"); if (!aCaption.empty()) { xmlAddChild(caption, xmlNewText((const xmlChar*)aCaption.c_str())); } xmlAddChild(table, caption); // csvの解析とテーブル構造の構築 IntVector info; if (!createTable(table, info, aRef, anEncoding)) { xmlFreeNode(table); return false; } // align処理 bool result = true; try { const int cols = info.size(); int count, count2; char buf[64]; Glib::ustring align; XMLTool tool; xmlXPathObjectPtr obj; xmlNodeSetPtr nodes; for (count = 0; count < cols; count++) { switch (GET_COL_INFO(info[count], COL_INFO_ALIGN_MASK)) { case COL_INFO_ALIGN_RIGHT: align = "right"; break; case COL_INFO_ALIGN_CENTER: align = "center"; break; default: continue; } bzero(buf, sizeof(buf)); snprintf(buf, sizeof(buf) -1, "tr/td[%d]", count +1); obj = tool.getNodeList(table, buf); if (obj) { nodes = obj->nodesetval; for (count2 = 0; count2 < nodes->nodeNr; count2++) { xmlSetProp(nodes->nodeTab[count2], (const xmlChar*)"align", (const xmlChar*)align.c_str()); } xmlXPathFreeObject(obj); } } } catch (std::exception exception) { fprintf(stderr, "%s: %s (%s)" LINE_SEP, APP_NAME, exception.what(), aRef.c_str()); result = false; } catch (...) { fprintf(stderr, "%s: ImportTextCSV: unknown error occured. (%s)" LINE_SEP, APP_NAME, aRef.c_str()); result = false; } if (result) { // import タグと table を交換 xmlFreeNode(xmlReplaceNode(aNode, table)); } else { xmlFreeNode(table); } return result; } bool ImportTextCSV::createTable(xmlNodePtr aTable, IntVector& anInfo, const Glib::ustring& aRef, const Glib::ustring& anEncoding) { bool result = true; int cols, count; std::string tmpfile; try { Glib::RefPtr ioc = getIOChannel(aRef, tmpfile); setEncoding(ioc, anEncoding); Glib::ustring line; StringList list; StringList::iterator iter, end; CSVAnalyzer analyzer; xmlNodePtr tr, th, td; // 一行目は列題名として扱う if (ioc->read_line(line) == Glib::IO_STATUS_NORMAL) { line = Utilities::strTrimCRLF(line); if (!analyzer.analyzeLine(line, list)) { ioc->close(); THROW("CSV data is broken. : " + aRef + " : " LINE_SEP + line); } tr = xmlNewNode(NULL, (const xmlChar*)"tr"); xmlAddChild(aTable, tr); end = list.end(); for (iter = list.begin(); iter != end; iter++) { th = xmlNewNode(NULL, (const xmlChar*)"th"); xmlAddChild(th, xmlNewText((const xmlChar*)(*iter).c_str())); xmlAddChild(tr, th); } } cols = list.size(); anInfo.resize(cols, COL_INFO_ALIGN_RIGHT); // 二行目以降の処理 while (ioc->read_line(line) == Glib::IO_STATUS_NORMAL) { line = Utilities::strTrimCRLF(line); if (!analyzer.analyzeLine(line, list)) { ioc->close(); THROW("CSV data is broken. : " + aRef + " : " LINE_SEP + line); } if (cols < (int)list.size()) { ioc->close(); THROW("CSV data is broken. (too long column) : " + aRef + " : " LINE_SEP + line); } tr = xmlNewNode(NULL, (const xmlChar*)"tr"); xmlAddChild(aTable, tr); end = list.end(); for (iter = list.begin(), count = 0; iter != end; iter++, count++) { td = xmlNewNode(NULL, (const xmlChar*)"td"); xmlAddChild(tr, td); // 内容物が数値でない場合は左寄せで出力するように処理 if (!isNumber((*iter))) { SET_COL_INFO(anInfo[count], COL_INFO_ALIGN_LEFT, COL_INFO_ALIGN_MASK); } // 内容物がハイパーリンクの対象である場合ハイパーリンク // 指定で出力するように処理 if (isHyperLink((*iter))) { // ','や', 'で区切られている場合、複数のリンク // や複数の文字列から成っているものとして処理 // する必要がある。よって, ここで文字列を ',' // によって分割し、分割したものそれぞれに対して // 処理を行う。 StringList tmplist; StringList::iterator tmpbegin, tmpiter, tmpend; xmlNodePtr a; if (analyzer.analyzeLine((*iter), tmplist)) { tmpend = tmplist.end(); for (tmpbegin = tmpiter = tmplist.begin(); tmpiter != tmpend; tmpiter++) { if (tmpiter != tmpbegin) { xmlAddChild(td, xmlNewText((const xmlChar*)", ")); } if (isHyperLink((*tmpiter))) { a = xmlNewNode(NULL, (const xmlChar*)"a"); xmlAddChild(a, xmlNewText((const xmlChar*)(*tmpiter).c_str())); xmlSetProp(a, (const xmlChar*)"href", (const xmlChar*)(*tmpiter).c_str()); xmlAddChild(td, a); } else { xmlAddChild(td, xmlNewText((const xmlChar*)(*tmpiter).c_str())); } } continue; } } xmlAddChild(td, xmlNewText((const xmlChar*)(*iter).c_str())); } } ioc->close(); } catch (Exception exception) { exception.print(stderr); result = false; } catch (Glib::Error error) { std::string msg = Glib::locale_from_utf8(error.what()); fprintf(stderr, "%s: %s (%s)" LINE_SEP, APP_NAME, msg.c_str(), aRef.c_str()); result = false; } catch (std::exception exception) { fprintf(stderr, "%s: %s (%s)" LINE_SEP, APP_NAME, exception.what(), aRef.c_str()); result = false; } catch (...) { fprintf(stderr, "%s: ImportTextCSV: unknown error occured. (%s)" LINE_SEP, APP_NAME, aRef.c_str()); result = false; } if (0 < tmpfile.length()) { remove(tmpfile.c_str()); } return result; } bool ImportTextCSV::isNumber(const Glib::ustring& aString) { Glib::ustring str = aString.lowercase(); // 先頭の +/- チェック if (0 >= str.length()) return false; if ("+" == str.substr(0, 1) || "-" == str.substr(0, 1)) { str = str.substr(1); } // 数値チェック if (0 >= str.length()) return false; bool bDot = false; bool bOK = false; Glib::ustring::iterator iter = str.begin(); Glib::ustring::iterator end = str.end(); for (; iter != end; iter++) { if (('0' <= *iter && '9' >= *iter) || (0xb0a3 <= *iter && 0xb9a3 >= *iter)) { bOK = true; } else if ('.' == *iter || 0xa5a1 == *iter) { if (bDot) return false; bDot = true; } else if (',' == *iter || 0xa4a1 == *iter) { // カンマは無視。ただし、小数点以降のカンマは拒否。 if (bDot) return false; } else if (0xc1a1 == *iter) { // 終端の'〜'は許可 iter++; if (iter != end) return false; break; } else { return false; } } return bOK; } bool ImportTextCSV::isHyperLink(const Glib::ustring& aString) { int len = aString.length(); Glib::ustring str = aString.lowercase(); if (6 < len && "ftp://" == str.substr(0, 6)) return true; else if (7 < len && "http://" == str.substr(0, 7)) return true; else if (8 < len && "https://" == str.substr(0, 8)) return true; return false; }