pkg://jd_1.9.8-071228.orig.tar.gz:493957/
jd-1.9.8-071228/
src/
xml/dom.cpp
downloads
// License: GPL2
//#define _DEBUG
#include "jddebug.h"
#include "dom.h"
#include "jdlib/miscutil.h"
#include "global.h"
#include "tools.h"
#include "bbslist/columns.h"
#include <sstream>
enum
{
SIZE_OF_RAWDATA = 2 * 1024 * 1024
};
using namespace XML;
// staticなHTMLの要素名リストを再定義
std::set< std::string > Dom::m_static_html_elements;
// コンストラクタ
Dom::Dom( const int& type, const std::string& name, const bool html )
: m_html( html ),
m_nodeType( type ),
m_nodeName( name ),
m_parentNode( 0 )
{
// HTMLの場合に空要素として扱う物の内で、使われていそうな物( 小文字で統一 )
if( html && m_static_html_elements.empty() )
{
m_static_html_elements.insert( "base" );
m_static_html_elements.insert( "br" );
m_static_html_elements.insert( "hr" );
m_static_html_elements.insert( "img" );
m_static_html_elements.insert( "link" );
m_static_html_elements.insert( "meta" );
}
}
// デストラクタ
Dom::~Dom()
{
#ifdef _DEBUG
std::cout << "~Dom() : " << m_nodeName << ", " << m_childNodes.size() << std::endl;
#endif
clear();
}
// コピーコンストラクタ
Dom::Dom( const Dom& dom )
: m_html( dom.m_html ),
m_nodeType( dom.m_nodeType ),
m_nodeName( dom.m_nodeName ),
m_nodeValue( dom.m_nodeValue ),
m_parentNode( 0 ),
m_attributes( dom.m_attributes )
{
copy_childNodes( dom );
}
//
// 子ノードのクリア
//
void Dom::clear()
{
std::list< Dom* >::iterator it = m_childNodes.begin();
while( it != m_childNodes.end() )
{
if( *it ) delete *it;
++it;
}
m_childNodes.clear();
}
//
// XMLの文字列をパースして子ノードを追加する
//
void Dom::parse( const std::string& str )
{
if( str.empty() || str.size() > SIZE_OF_RAWDATA ) return;
size_t current_pos = 0, tag_lt_pos = 0, tag_gt_pos = 0;
const size_t str_length = str.length();
while( current_pos < str_length )
{
//プロパティなどに使う変数
int type = NODE_TYPE_UNKNOWN;
std::string name;
std::string value;
std::map< std::string, std::string > attributes_pair;
std::string next_source;
// "<"を探す
tag_lt_pos = str.find( "<", current_pos );
// タグの前のテキストノード
if( current_pos < tag_lt_pos )
{
type = NODE_TYPE_TEXT;
name = "#text";
value = str.substr( current_pos, tag_lt_pos - current_pos );
current_pos = tag_lt_pos;
}
// 全てがテキストノード
else if( tag_lt_pos == std::string::npos )
{
type = NODE_TYPE_TEXT;
name = "#text";
value = str;
current_pos = str.length();
}
// 要素ノード
else if( current_pos == tag_lt_pos )
{
tag_gt_pos = str.find( ">", tag_lt_pos + 1 );
current_pos = tag_gt_pos + 1;
// 開始タグの中身( <element attr="value"> )を取り出す
std::string open_tag = str.substr( tag_lt_pos + 1, tag_gt_pos - tag_lt_pos - 1 );
// タグの中身がアルファベットで始まっているか
bool is_alpha = ( ( open_tag[0] >= 'A' && open_tag[0] <= 'Z' ) || ( open_tag[0] >= 'a' && open_tag[0] <= 'z' ) );
// タグ構造が壊れてる場合
size_t broken_pos = 0;
if( open_tag.empty() || ! is_alpha ) continue;
else if( ( broken_pos = open_tag.find( "<" ) ) != std::string::npos )
{
current_pos += broken_pos;
continue;
}
// XMLの場合に空要素として扱う要素( <element /> )かどうか
bool empty_element = false;
if( ! m_html && open_tag.compare( open_tag.length() - 1, 1, "/" ) == 0 )
{
empty_element = true;
open_tag.erase( open_tag.length() - 1 );
}
// 要素名を取り出す
std::string element_name;
size_t i = 0;
const size_t open_tag_length = open_tag.length();
while( i < open_tag_length
&& open_tag[i] != '\n'
&& open_tag[i] != '\t'
&& open_tag[i] != ' ' )
{
element_name += open_tag[i];
++i;
}
if( element_name.empty() ) continue;
// 要素ノードのタイプを設定
type = NODE_TYPE_ELEMENT;
// 要素名は小文字に統一
name = MISC::tolower_str( element_name );
// 属性ペアのリストを作成
attributes_pair = create_attribute( open_tag.substr( i ) );
// HTMLの場合に空要素として扱う要素かどうか
if( m_html && m_static_html_elements.find( name ) != m_static_html_elements.end() ) empty_element = true;
// 終了タグがある要素からの中身の取り出し( <element>中身</element> )
if( ! empty_element )
{
// count は見つける必要がある終了タグの数
size_t close_tag_lt_pos = 0, close_tag_gt_pos = 0, count = 1;
while( ( close_tag_lt_pos = str.find( "<", current_pos ) ) != std::string::npos
&& ( close_tag_gt_pos = str.find( ">", close_tag_lt_pos + 1 ) ) != std::string::npos )
{
current_pos = close_tag_gt_pos + 1;
// タグの中身を取り出す
const std::string close_tag = str.substr( close_tag_lt_pos + 1, close_tag_gt_pos - close_tag_lt_pos - 1 );
// タグ構造が壊れてる場合
if( close_tag.empty() ) continue;
else if( ( broken_pos = close_tag.find( "<" ) ) != std::string::npos )
{
current_pos += broken_pos;
continue;
}
// 空要素でない同名の開始タグを見つけたらカウントを増やす
if( close_tag.compare( 0, element_name.length(), element_name ) == 0
&& close_tag.compare( close_tag.length() - 1, 1, "/" ) != 0 ) ++count;
// 終了タグを見つけたらカウントを減らす
else if( close_tag.compare( 0, element_name.length() + 1, "/" + element_name ) == 0 ) --count;
// 終了タグを見つける必要数が 0 になったらループを抜ける
if( count <= 0 ) break;
}
// 必要な終了タグが見付からないまま上記のループを抜けた場合は、全体のループを抜ける
if( count > 0 ) break;
// 次の再帰で使う"子ノード"の素材になる文字列
next_source = str.substr( tag_gt_pos + 1, close_tag_lt_pos - tag_gt_pos - 1 );
}
}
#ifdef _DEBUG
std::cout << "Dom:parse():---------------------------------------\n";
std::cout << "nodeName : " << name << "\n";
std::cout << "nodeType : " << type << "\n";
std::map< std::string, std::string >::iterator it = attributes_pair.begin();
while( it != attributes_pair.end() )
{
std::cout << "Attribute: " << "name=" << (*it).first << ", value=" << (*it).second << std::endl;
++it;
}
std::cout << "nodeValue: " << value << std::endl;
#endif
// ノードを追加
Dom* node = appendChild( type, name );
node->attributes( attributes_pair );
node->nodeValue( value );
// 再帰
if( ! next_source.empty() ) node->parse( next_source );
}
}
//
// 属性ペアのリストを作成
//
std::map< std::string, std::string > Dom::create_attribute( const std::string& str )
{
std::map< std::string, std::string > attributes_pair;
if( str.empty() ) return attributes_pair;
size_t i = 0, str_length = str.length();
while( i < str_length )
{
std::string attr_key;
std::string attr_value;
// 余分なスペース等を詰める
while( i < str_length
&& ( str[i] == '\n'
|| str[i] == '\t'
|| str[i] == ' ' ) ) ++i;
// "="かスペース等でなければ属性名とする
while( i < str_length
&& str[i] != '\n'
&& str[i] != '\t'
&& str[i] != ' '
&& str[i] != '=' )
{
attr_key += str[i];
++i;
}
// 余分なスペース等を詰める
while( i < str_length
&& ( str[i] == '\n'
|| str[i] == '\t'
|| str[i] == ' ' ) ) ++i;
// "="の区切りがある場合のみ属性の値をセットする
if( str[i] == '=' )
{
// "="の分進める
++i;
// 余分なスペース等を詰める
while( i < str_length
&& ( str[i] == '\n'
|| str[i] == '\t'
|| str[i] == ' ' ) ) ++i;
// 属性の値 "〜"
if( str[i] == '"' )
{
++i;
while( i < str_length && str[i] != '"' )
{
attr_value += str[i];
++i;
}
if( str[i] == '"' ) ++i;
}
// 属性の値 '〜'
else if( str[i] == '\'' )
{
++i;
while( i < str_length && str[i] != '\'' )
{
attr_value += str[i];
++i;
}
if( str[i] == '\'' ) ++i;
}
// 属性の値 その他( 本来ならXMLとしてエラー )
else
{
while( i < str_length
&& str[i] != '\n'
&& str[i] != '\t'
&& str[i] != ' ' )
{
attr_value += str[i];
++i;
}
}
}
// 属性を追加
if( ! attr_key.empty() && ! attr_value.empty() )
{
// 属性名は小文字に統一する
attributes_pair.insert( make_pair( MISC::tolower_str( attr_key ), attr_value ) );
}
}
return attributes_pair;
}
//
// XMLタグ構造の文字列を生成
//
std::string Dom::get_xml( const int n )
{
if( ! this ) return std::string();
std::stringstream xml;
// インデント
std::string indent;
// テキストノードの文字列
std::string text;
// 属性
std::map< std::string, std::string >::iterator attr_it = m_attributes.begin();
// 子要素
std::list< Dom* >::iterator child_it = m_childNodes.begin();
// ノードの種類別に処理
switch( m_nodeType )
{
// 要素
case NODE_TYPE_ELEMENT:
// インデントを追加
for( int i = 0; i < n; ++i ) indent += " ";
// タグの始まり
xml << indent << "<" << m_nodeName;
// 属性を追加
while( attr_it != m_attributes.end() )
{
xml << " " << (*attr_it).first << "=\"" << (*attr_it).second << "\"";
++attr_it;
}
// 子要素がある場合
if( ! m_childNodes.empty() )
{
xml << ">\n";
while( child_it != m_childNodes.end() )
{
// 子要素をたどる
xml << (*child_it)->get_xml( n + 2 );
++child_it;
}
xml << indent << "</" << m_nodeName << ">\n";
}
// 空要素の場合
else xml << " />\n";
break;
// テキストノード
case NODE_TYPE_TEXT:
text = MISC::remove_spaces( m_nodeValue );
if( ! text.empty() )
{
// インデントを追加
for( int i = 0; i < n; ++i ) indent += " ";
xml << indent << text << "\n";
}
break;
// ドキュメントノード
case NODE_TYPE_DOCUMENT:
if( ! m_childNodes.empty() ) xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
while( child_it != m_childNodes.end() )
{
// 子要素をたどる
xml << (*child_it)->get_xml();
++child_it;
}
break;
}
return xml.str();
}
//
// Gtk::TreeModel::Children からノードツリーを生成
//
void Dom::parse( const Gtk::TreeModel::Children& children )
{
if( ! this || children.empty() ) return;
BBSLIST::TreeColumns columns;
// Gtk::TreeModel::Children を走査
Gtk::TreeModel::iterator it = children.begin();
while( it != children.end() )
{
Gtk::TreeModel::Row row = *it;
// 各値を取得( bbslist/columns.h を参照 )
const int type = row[ columns.m_type ];
const Glib::ustring url = row[ columns.m_col_url ];
const Glib::ustring name = row[ columns.m_col_name ];
const bool expand = row[ columns.m_expand ];
if( type != TYPE_UNKNOWN )
{
// タイプにより要素名を決定( board や link など)
const std::string node_name = XML::get_name( type );
if( ! node_name.empty() )
{
Dom* node = appendChild( NODE_TYPE_ELEMENT, node_name );
if( type == TYPE_DIR && expand ) node->setAttribute( "open", "y" );
if( ! name.empty() ) node->setAttribute( "name", name );
if( ! url.empty() ) node->setAttribute( "url", url );
// 再帰
if( ! row.children().empty() ) node->parse( row.children() );
}
}
++it;
}
}
//
// ノードを分解して Gtk::TreeStore へ Gtk::TreeModel::Row を追加
//
// list_path_expand は開いてるツリーを格納するための参照渡し
//
void Dom::append_treestore( Glib::RefPtr< Gtk::TreeStore >& treestore,
std::list< Gtk::TreePath >& list_path_expand,
const Gtk::TreeModel::Row& parent )
{
if( ! this ) return;
BBSLIST::TreeColumns columns;
// ノードの子要素を走査
std::list< Dom* >::iterator it = m_childNodes.begin();
while( it != m_childNodes.end() )
{
const int node_type = (*it)->nodeType();
if( node_type == NODE_TYPE_ELEMENT )
{
const int type = XML::get_type( (*it)->nodeName() );
if( type != TYPE_UNKNOWN )
{
Gtk::TreeModel::Row row;
// Gtk::TreeStore::append() の追加ポイント
if( parent ) row = *( treestore->append( parent.children() ) );
else row = *( treestore->append() );
// 各値をセット
columns.setup_row( row, (*it)->getAttribute( "url" ), (*it)->getAttribute( "name" ), type );
// 開いているツリーを追加
if( type == TYPE_DIR
&& (*it)->getAttribute( "open" ) == "y" ) list_path_expand.push_back( treestore->get_path( row ) );
// 再帰
if( (*it)->hasChildNodes() ) (*it)->append_treestore( treestore, list_path_expand, row );
}
}
++it;
}
}
//
// プロパティを扱うアクセッサ
//
const int Dom::nodeType()
{
if( ! this ) return NODE_TYPE_UNKNOWN;
return m_nodeType;
}
const std::string Dom::nodeName()
{
if( ! this ) return std::string();
return m_nodeName;
}
std::string Dom::nodeValue()
{
if( ! this ) return std::string();
return m_nodeValue;
}
void Dom::nodeValue( const std::string& value )
{
if( this ) m_nodeValue = value;
}
//
// getElementById()
//
Dom* Dom::getElementById( const std::string& id )
{
Dom* node = 0;
if( ! this ) return node;
std::list< Dom* >::iterator it = m_childNodes.begin();
while( it != m_childNodes.end() )
{
if( (*it)->nodeType() == NODE_TYPE_ELEMENT )
{
if( (*it)->getAttribute( "id" ) == id ) node = *it;
// 再帰
else if( (*it)->hasChildNodes() ) node = (*it)->getElementById( id );
if( node ) break;
}
++it;
}
return node;
}
//
// getElementsByTagName()
//
DomList Dom::getElementsByTagName( const std::string& name )
{
DomList domlist;
if( ! this ) return domlist;
std::list< Dom* >::iterator it = m_childNodes.begin();
while( it != m_childNodes.end() )
{
if( (*it)->nodeType() == NODE_TYPE_ELEMENT )
{
if( (*it)->nodeName() == name ) domlist.push_back( *it );
// 再帰
DomList sub_nodes = (*it)->getElementsByTagName( name );
domlist.merge( sub_nodes );
}
++it;
}
return domlist;
}
//
// ノード:ownerDocument
//
Dom* Dom::ownerDocument()
{
if( ! this ) return 0;
Dom* parent = m_parentNode;
while( parent )
{
if( parent->nodeType() == NODE_TYPE_DOCUMENT ) break;
else parent = parent->parentNode();
}
return parent;
}
//
// ノード:parentNode
//
Dom* Dom::parentNode()
{
if( ! this ) return 0;
return m_parentNode;
}
void Dom::parentNode( Dom* parent )
{
if( this ) m_parentNode = parent;
}
//
// ノード:hasChildNodes
//
const bool Dom::hasChildNodes()
{
if( this ) return ! m_childNodes.empty();
return false;
}
//
// ノード:childNodes
//
DomList Dom::childNodes()
{
DomList result;
// DomList に std::list< Dom* > を代入している
if( this ) result = m_childNodes;
return result;
}
//
// dom の子ノードをコピーする
//
void Dom::copy_childNodes( const Dom& dom )
{
clear();
DomList children;
children = dom.m_childNodes;
if( this && children.size() ){
std::list< Dom* >::iterator it = children.begin();
while( it != children.end() )
{
Dom* node = new Dom( (*it)->nodeType(), (*it)->nodeName() );
node->nodeValue( (*it)->nodeValue() );
node->parentNode( this );
node->attributes( (*it)->attributes() );
node->copy_childNodes( *(*it) );
m_childNodes.push_back( node );
++it;
}
}
}
//
// ノード:firstChild
//
Dom* Dom::firstChild()
{
if( ! this || m_childNodes.empty() ) return 0;
return m_childNodes.front();
}
//
// ノード:lastChild
//
Dom* Dom::lastChild()
{
if( ! this || m_childNodes.empty() ) return 0;
return m_childNodes.back();
}
//
// ノード:appendChild()
//
Dom* Dom::appendChild( const int node_type, const std::string& node_name )
{
Dom* node = 0;
if( this )
{
node = new Dom( node_type, node_name, m_html );
node->parentNode( this );
m_childNodes.push_back( node );
}
return node;
}
//
// ノード:removeChild()
//
bool Dom::removeChild( Dom* node )
{
if( ! this || ! node ) return false;
m_childNodes.remove( node );
if( node ) delete node;
return true;
}
//
// ノード:replaceChild()
//
Dom* Dom::replaceChild( const int node_type, const std::string& node_name, Dom* oldNode )
{
Dom* newNode = 0;
if( ! this && ! oldNode ) return newNode;
newNode = new Dom( node_type, node_name );
std::list< Dom* >::iterator it = m_childNodes.begin();
while( it != m_childNodes.end() )
{
if( *it == oldNode )
{
newNode->parentNode( oldNode->parentNode() );
m_childNodes.erase( it );
m_childNodes.insert( it, newNode );
break;
}
++it;
}
if( oldNode ) delete oldNode;
return newNode;
}
//
// ノード:insertBefore()
//
Dom* Dom::insertBefore( const int node_type, const std::string& node_name, Dom* insNode )
{
Dom* newNode = 0;
if( ! this && ! insNode ) return newNode;
newNode = new Dom( node_type, node_name );
std::list< Dom* >::iterator it = m_childNodes.begin();
while( it != m_childNodes.end() )
{
if( *it == insNode )
{
newNode->parentNode( insNode->parentNode() );
m_childNodes.insert( it, newNode );
break;
}
++it;
}
return newNode;
}
//
// ノード:previousSibling
//
Dom* Dom::previousSibling()
{
Dom* previous = 0;
if( ! this ) return previous;
DomList brothers = m_parentNode->childNodes();
std::list< Dom* >::iterator it = brothers.begin();
while( it != brothers.end() )
{
if( it != brothers.begin() && *it == this )
{
previous = *( --it );
break;
}
++it;
}
return previous;
}
//
// ノード:nextSibling
//
Dom* Dom::nextSibling()
{
Dom* next = 0;
if( ! this ) return next;
DomList brothers = m_parentNode->childNodes();
std::list< Dom* >::iterator it = brothers.begin();
while( it != brothers.end() )
{
if( *it == this )
{
++it;
if( it != brothers.end() ) next = *it;
break;
}
++it;
}
return next;
}
//
// 属性:attributes
//
std::map< std::string, std::string > Dom::attributes()
{
std::map< std::string, std::string > result;
if( this ) result = m_attributes;
return result;
}
void Dom::attributes( const std::map< std::string, std::string > attributes )
{
if( this && ! attributes.empty() ) m_attributes = attributes;
}
//
// 属性:hasAttributes()
//
const bool Dom::hasAttributes()
{
if( this ) return ! m_attributes.empty();
return false;
}
//
// 属性:hasAttribute()
//
const bool Dom::hasAttribute( const std::string& name )
{
if( ! this || name.empty() ) return false;
return m_attributes.find( name ) != m_attributes.end();
}
//
// 属性:getAttribute()
//
std::string Dom::getAttribute( const std::string& name )
{
std::string value;
if( ! this || name.empty() ) return value;
std::map< std::string, std::string >::iterator it = m_attributes.find( name );
if( it != m_attributes.end() ) value = MISC::html_unescape( (*it).second );
return value;
}
//
// 属性:setAttribute( std::string )
//
bool Dom::setAttribute( const std::string& name, const std::string& value )
{
if( ! this
|| name.empty()
|| value.empty()
|| m_nodeType != NODE_TYPE_ELEMENT ) return false;
// 属性名は小文字に統一
const std::string attr_name = MISC::tolower_str( name );
m_attributes.insert( make_pair( attr_name, MISC::html_escape( value ) ) );
return true;
}
//
// 属性:setAttribute( int )
//
bool Dom::setAttribute( const std::string& name, const int& value )
{
if( ! this
|| name.empty()
|| m_nodeType != NODE_TYPE_ELEMENT ) return false;
// 属性名は小文字に統一
const std::string attr_name = MISC::tolower_str( name );
m_attributes.insert( make_pair( attr_name, MISC::itostr( value ) ) );
return true;
}
//
// 属性:removeAttribute()
//
bool Dom::removeAttribute( const std::string& name )
{
bool result = false;
if( ! this || name.empty() ) return result;
std::map< std::string, std::string >::iterator it = m_attributes.find( name );
if( it != m_attributes.end() )
{
m_attributes.erase( it );
result = true;
}
return result;
}