/*
* $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<Glib::IOChannel> 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;
}
syntax highlighted by Code2HTML, v. 0.9.1