1 |
// Gnomoradio - rainbow/rdf-resource.cc |
2 |
// Copyright (C) 2003 Jim Garrison |
3 |
// |
4 |
// This program is free software; you can redistribute it and/or modify |
5 |
// it under the terms of the GNU General Public License as published by |
6 |
// the Free Software Foundation; either version 2 of the License, or |
7 |
// (at your option) any later version. |
8 |
// |
9 |
// This program is distributed in the hope that it will be useful, |
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
// GNU General Public License for more details. |
13 |
// |
14 |
// You should have received a copy of the GNU General Public License |
15 |
// along with this program; if not, write to the Free Software |
16 |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
17 |
|
18 |
#include "rdf-resource.h" |
19 |
#include <deque> |
20 |
|
21 |
using namespace Rainbow; |
22 |
using namespace sigc; |
23 |
using namespace std; |
24 |
|
25 |
map<Glib::ustring,RdfResource*> Rainbow::RdfResource::resource_map; |
26 |
|
27 |
void Rainbow::RdfResource::get_and_do (const Glib::ustring &uri, |
28 |
const sigc::slot<void,xmlpp::Element*,ref_ptr<RdfResource> > &slot) |
29 |
{ |
30 |
ref_ptr<RdfResource> resource; |
31 |
Glib::ustring base_uri(uri), id; |
32 |
remove_tag_from_uri(base_uri, id); |
33 |
map<Glib::ustring,RdfResource*>::iterator p = resource_map.find(base_uri); |
34 |
if (p != resource_map.end()) { |
35 |
resource = ref_ptr<RdfResource>(p->second); |
36 |
if (resource->downloaded) { // already downloaded |
37 |
map<Glib::ustring,xmlpp::Element*>::iterator el = resource->id_map.find(id); |
38 |
signal<void,xmlpp::Element*,ref_ptr<RdfResource> > sig; |
39 |
sig.connect(slot); |
40 |
sig(el != resource->id_map.end() ? el->second : 0, resource); |
41 |
return; |
42 |
} |
43 |
} else { |
44 |
resource = ref_ptr<RdfResource>(new RdfResource(base_uri)); |
45 |
|
46 |
// make sure it is actually a valid url |
47 |
Glib::ustring host, file; |
48 |
unsigned short port; |
49 |
if (!HttpClient::parse_url(uri, host, port, file)) { |
50 |
signal<void,xmlpp::Element*,ref_ptr<RdfResource> > sig; |
51 |
sig.connect(slot); |
52 |
sig(0, resource); |
53 |
return; |
54 |
} |
55 |
} |
56 |
|
57 |
// connect mem_fun to appropriate signal |
58 |
map<Glib::ustring,signal<void,xmlpp::Element*, ref_ptr<RdfResource> >*>::iterator sig; |
59 |
sig = resource->signal_map.find(id); |
60 |
if (sig != resource->signal_map.end()) { |
61 |
// found existing signal for id |
62 |
sig->second->connect(slot); |
63 |
} else { |
64 |
// need to create new signal to handle id |
65 |
signal<void,xmlpp::Element*,ref_ptr<RdfResource> > *signal_ = new signal<void,xmlpp::Element*,ref_ptr<RdfResource> >; |
66 |
signal_->connect(slot); |
67 |
resource->signal_map.insert(make_pair(id, signal_)); |
68 |
} |
69 |
} |
70 |
|
71 |
void Rainbow::RdfResource::make_absolute_uri (Glib::ustring &uri, |
72 |
const Glib::ustring &base) |
73 |
{ |
74 |
if (uri.size() && uri[0] == '#') { |
75 |
Glib::ustring b(base), id; |
76 |
remove_tag_from_uri(b, id); |
77 |
uri = b + uri; |
78 |
} |
79 |
} |
80 |
|
81 |
void Rainbow::RdfResource::remove_tag_from_uri (Glib::ustring &uri, |
82 |
Glib::ustring &id) |
83 |
{ |
84 |
Glib::ustring::size_type pound = uri.find('#'); |
85 |
if (pound != Glib::ustring::npos) { |
86 |
id = uri.substr(pound + 1); |
87 |
uri = uri.substr(0, pound); |
88 |
} |
89 |
} |
90 |
|
91 |
Rainbow::RdfResource::RdfResource (const Glib::ustring &uri) |
92 |
: refcnt(0), |
93 |
resource_uri(uri), |
94 |
downloaded(false) |
95 |
{ |
96 |
base_uri = resource_uri; |
97 |
Glib::ustring tmpid; |
98 |
remove_tag_from_uri(base_uri, tmpid); |
99 |
|
100 |
resource_map.insert(make_pair(uri, this)); |
101 |
|
102 |
// cache |
103 |
const unsigned int cache_size = 10; |
104 |
static deque<ref_ptr<RdfResource> > cache; |
105 |
if (cache.size() == cache_size) |
106 |
cache.pop_front(); |
107 |
cache.push_back(ref_ptr<RdfResource>(this)); |
108 |
|
109 |
alarm.signal_alarm().connect(mem_fun(*this, &RdfResource::get)); |
110 |
get(); |
111 |
} |
112 |
|
113 |
void Rainbow::RdfResource::get () |
114 |
{ |
115 |
Glib::ustring host, file; |
116 |
unsigned short port; |
117 |
HttpClient::parse_url(base_uri, host, port, file); // error case is taken care of above |
118 |
|
119 |
http.reset(new HttpClient(host, port)); |
120 |
http->signal_request_done.connect(mem_fun(*this, &RdfResource::on_downloaded)); |
121 |
ref(); |
122 |
http->get(file); |
123 |
} |
124 |
|
125 |
Rainbow::RdfResource::~RdfResource () |
126 |
{ |
127 |
// remove all occurances from global map |
128 |
for (map<Glib::ustring,RdfResource*>::iterator i = resource_map.begin(); i != resource_map.end(); ++i) { |
129 |
if (i->second == this) |
130 |
resource_map.erase(i); |
131 |
} |
132 |
|
133 |
clear_signal_map(); |
134 |
} |
135 |
|
136 |
void Rainbow::RdfResource::on_downloaded (bool success) |
137 |
{ |
138 |
xmlpp::Element *root; |
139 |
xmlpp::Node::NodeList nodes; |
140 |
xmlpp::Attribute *xml_base; |
141 |
string buffer; |
142 |
|
143 |
downloaded = true; |
144 |
|
145 |
if (!success) |
146 |
goto error; |
147 |
|
148 |
// this block is for rdf files embedded in something larger |
149 |
{ |
150 |
string::size_type rdf_block_begin = http->get_buffer().find("<rdf:RDF"); |
151 |
string::size_type rdf_block_end = http->get_buffer().find("</rdf:RDF>"); |
152 |
if (rdf_block_begin != string::npos |
153 |
&& rdf_block_end != string::npos |
154 |
&& rdf_block_begin < rdf_block_end) { |
155 |
buffer = http->get_buffer().substr(rdf_block_begin, |
156 |
rdf_block_end - rdf_block_begin + 10); |
157 |
} else |
158 |
buffer = http->get_buffer(); |
159 |
} |
160 |
|
161 |
try { |
162 |
xml_tree.parse_memory(buffer); |
163 |
} catch (...) { |
164 |
goto error; |
165 |
} |
166 |
|
167 |
// iterate through document and take note of ID tags and about tags |
168 |
root = dynamic_cast<xmlpp::Element*>(xml_tree.get_document()->get_root_node()); |
169 |
if (!root || root->get_name() != "RDF") |
170 |
goto error; |
171 |
id_map.insert(make_pair(Glib::ustring(), root)); |
172 |
xml_base = root->get_attribute("base"); |
173 |
if (xml_base && xml_base->get_value().size()) |
174 |
base_uri = xml_base->get_value(); |
175 |
nodes = root->get_children(); |
176 |
for (xmlpp::Node::NodeList::iterator i = nodes.begin(); i != nodes.end(); ++i) { |
177 |
xmlpp::Element *node = dynamic_cast<xmlpp::Element*>(*i); |
178 |
if (!node) |
179 |
continue; |
180 |
|
181 |
xmlpp::Attribute *id = node->get_attribute("ID"); |
182 |
if (id) |
183 |
id_map.insert(make_pair(id->get_value(), node)); |
184 |
else { |
185 |
xmlpp::Attribute *about = node->get_attribute("about"); |
186 |
if (about) |
187 |
about_map.insert(make_pair(about->get_value(), node)); |
188 |
} |
189 |
} |
190 |
|
191 |
error: |
192 |
// call all signals |
193 |
map<Glib::ustring,signal<void,xmlpp::Element*,ref_ptr<RdfResource> >*>::iterator signal; |
194 |
for (signal = signal_map.begin(); signal != signal_map.end(); ++signal) { |
195 |
map<Glib::ustring,xmlpp::Element*>::iterator el = id_map.find(signal->first); |
196 |
signal->second->emit(el != id_map.end() ? el->second : 0, ref_ptr<RdfResource>(this)); |
197 |
} |
198 |
clear_signal_map(); |
199 |
|
200 |
http.reset(); |
201 |
alarm.set_relative(3600 * 12); // FIXME: make this time interval configurable |
202 |
unref(); |
203 |
} |
204 |
|
205 |
void Rainbow::RdfResource::clear_signal_map () |
206 |
{ |
207 |
map<Glib::ustring,signal<void,xmlpp::Element*,ref_ptr<RdfResource> >*>::iterator signal; |
208 |
for (signal = signal_map.begin(); signal != signal_map.end(); ++signal) |
209 |
delete signal->second; |
210 |
signal_map.clear(); |
211 |
} |
212 |
|
213 |
xmlpp::Element *Rainbow::RdfResource::get_secondary_info (const Glib::ustring &uri) |
214 |
{ |
215 |
map<Glib::ustring,xmlpp::Element*>::iterator p = about_map.find(uri); |
216 |
return p != about_map.end() ? p->second : 0; |
217 |
} |