1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "osl/process.h"
22 #include "rtl/ustring.hxx"
23 #include "rtl/string.hxx"
24 #include "rtl/strbuf.hxx"
26 #include "osl/thread.h"
27 #include "recently_used_file.hxx"
29 #include "internal/xml_parser.hxx"
30 #include "internal/i_xml_parser_event_handler.hxx"
38 namespace /* private */ {
39 typedef std::vector
<string_t
> string_container_t
;
41 #define TAG_RECENT_FILES "RecentFiles"
42 #define TAG_RECENT_ITEM "RecentItem"
44 #define TAG_MIME_TYPE "Mime-Type"
45 #define TAG_TIMESTAMP "Timestamp"
46 #define TAG_PRIVATE "Private"
47 #define TAG_GROUPS "Groups"
48 #define TAG_GROUP "Group"
50 //------------------------------------------------
51 // compare two string_t's case insensitive, may also be done
52 // by specifying special traits for the string type but in this
53 // case it's easier to do it this way
54 struct str_icase_cmp
:
55 public std::binary_function
<string_t
, string_t
, bool>
57 bool operator() (const string_t
& s1
, const string_t
& s2
) const
58 { return (0 == strcasecmp(s1
.c_str(), s2
.c_str())); }
61 //------------------------------------------------
62 struct recently_used_item
64 recently_used_item() :
70 const string_t
& mime_type
,
71 const string_container_t
& groups
,
72 bool is_private
= false) :
74 mime_type_(mime_type
),
75 is_private_(is_private
),
78 timestamp_
= time(NULL
);
81 void set_uri(const string_t
& character
)
84 void set_mime_type(const string_t
& character
)
85 { mime_type_
= character
; }
87 void set_timestamp(const string_t
& character
)
90 if (sscanf(character
.c_str(), "%ld", &t
) != 1)
96 void set_is_private(SAL_UNUSED_PARAMETER
const string_t
& /*character*/)
97 { is_private_
= true; }
99 void set_groups(const string_t
& character
)
100 { groups_
.push_back(character
); }
102 void set_nothing(SAL_UNUSED_PARAMETER
const string_t
& /*character*/)
105 bool has_groups() const
107 return !groups_
.empty();
110 bool has_group(const string_t
& name
) const
112 string_container_t::const_iterator iter_end
= groups_
.end();
113 return (has_groups() &&
114 iter_end
!= std::find_if(
115 groups_
.begin(), iter_end
,
116 std::bind2nd(str_icase_cmp(), name
)));
119 void write_xml(const recently_used_file
& file
) const
121 write_xml_start_tag(TAG_RECENT_ITEM
, file
, true);
122 write_xml_tag(TAG_URI
, uri_
, file
);
123 write_xml_tag(TAG_MIME_TYPE
, mime_type_
, file
);
125 OString ts
= OString::valueOf((sal_sSize
)timestamp_
);
126 write_xml_tag(TAG_TIMESTAMP
, ts
.getStr(), file
);
129 write_xml_tag(TAG_PRIVATE
, file
);
133 write_xml_start_tag(TAG_GROUPS
, file
, true);
135 string_container_t::const_iterator iter
= groups_
.begin();
136 string_container_t::const_iterator iter_end
= groups_
.end();
138 for ( ; iter
!= iter_end
; ++iter
)
139 write_xml_tag(TAG_GROUP
, (*iter
), file
);
141 write_xml_end_tag(TAG_GROUPS
, file
);
143 write_xml_end_tag(TAG_RECENT_ITEM
, file
);
146 static OString
escape_content(const string_t
&text
)
149 for (sal_uInt32 i
= 0; i
< text
.length(); i
++)
153 case '&': aBuf
.append("&"); break;
154 case '<': aBuf
.append("<"); break;
155 case '>': aBuf
.append(">"); break;
156 case '\'': aBuf
.append("'"); break;
157 case '"': aBuf
.append("""); break;
158 default: aBuf
.append(text
[i
]); break;
161 return aBuf
.makeStringAndClear();
164 void write_xml_tag(const string_t
& name
, const string_t
& value
, const recently_used_file
& file
) const
166 write_xml_start_tag(name
, file
);
167 OString escaped
= escape_content (value
);
168 file
.write(escaped
.getStr(), escaped
.getLength());
169 write_xml_end_tag(name
, file
);
172 void write_xml_tag(const string_t
& name
, const recently_used_file
& file
) const
175 file
.write(name
.c_str(), name
.length());
176 file
.write("/>\n", 3);
179 void write_xml_start_tag(const string_t
& name
, const recently_used_file
& file
, bool linefeed
= false) const
182 file
.write(name
.c_str(), name
.length());
184 file
.write(">\n", 2);
189 void write_xml_end_tag(const string_t
& name
, const recently_used_file
& file
) const
192 file
.write(name
.c_str(), name
.length());
193 file
.write(">\n", 2);
200 string_container_t groups_
;
203 typedef std::vector
<recently_used_item
*> recently_used_item_list_t
;
204 typedef void (recently_used_item::* SET_COMMAND
)(const string_t
&);
206 // thrown if we encounter xml tags that we do not know
207 class unknown_xml_format_exception
{};
209 class recently_used_file_filter
: public i_xml_parser_event_handler
212 recently_used_file_filter(recently_used_item_list_t
& item_list
) :
214 item_list_(item_list
)
216 named_command_map_
[TAG_RECENT_FILES
] = &recently_used_item::set_nothing
;
217 named_command_map_
[TAG_RECENT_ITEM
] = &recently_used_item::set_nothing
;
218 named_command_map_
[TAG_URI
] = &recently_used_item::set_uri
;
219 named_command_map_
[TAG_MIME_TYPE
] = &recently_used_item::set_mime_type
;
220 named_command_map_
[TAG_TIMESTAMP
] = &recently_used_item::set_timestamp
;
221 named_command_map_
[TAG_PRIVATE
] = &recently_used_item::set_is_private
;
222 named_command_map_
[TAG_GROUPS
] = &recently_used_item::set_nothing
;
223 named_command_map_
[TAG_GROUP
] = &recently_used_item::set_groups
;
226 virtual void start_element(
227 const string_t
& /*raw_name*/,
228 const string_t
& local_name
,
229 const xml_tag_attribute_container_t
& /*attributes*/)
231 if ((local_name
== TAG_RECENT_ITEM
) && (NULL
== item_
))
232 item_
= new recently_used_item
;
235 virtual void end_element(const string_t
& /*raw_name*/, const string_t
& local_name
)
237 // check for end tags w/o start tag
238 if( local_name
!= TAG_RECENT_FILES
&& NULL
== item_
)
239 return; // will result in an XML parser error anyway
241 if (named_command_map_
.find(local_name
) != named_command_map_
.end())
242 (item_
->*named_command_map_
[local_name
])(current_element_
);
246 throw unknown_xml_format_exception();
249 if (local_name
== TAG_RECENT_ITEM
)
251 item_list_
.push_back(item_
);
254 current_element_
.clear();
257 virtual void characters(const string_t
& character
)
259 if (character
!= "\n")
260 current_element_
+= character
;
263 virtual void start_document() {}
264 virtual void end_document() {}
266 virtual void ignore_whitespace(const string_t
& /*whitespaces*/)
269 virtual void processing_instruction(
270 const string_t
& /*target*/, const string_t
& /*data*/)
273 virtual void comment(const string_t
& /*comment*/)
276 recently_used_item
* item_
;
277 std::map
<string_t
, SET_COMMAND
> named_command_map_
;
278 string_t current_element_
;
279 recently_used_item_list_t
& item_list_
;
281 recently_used_file_filter(const recently_used_file_filter
&);
282 recently_used_file_filter
& operator=(const recently_used_file_filter
&);
285 //------------------------------------------------
286 void read_recently_used_items(
287 recently_used_file
& file
,
288 recently_used_item_list_t
& item_list
)
291 recently_used_file_filter
ruff(item_list
);
293 xparser
.set_document_handler(&ruff
);
298 if (size_t length
= file
.read(buff
, sizeof(buff
)))
299 xparser
.parse(buff
, length
, file
.eof());
303 //------------------------------------------------
304 // The file ~/.recently_used shall not contain more than 500
305 // entries (see www.freedesktop.org)
306 const int MAX_RECENTLY_USED_ITEMS
= 500;
308 class recent_item_writer
312 recently_used_file
& file
,
313 int max_items_to_write
= MAX_RECENTLY_USED_ITEMS
) :
315 max_items_to_write_(max_items_to_write
),
319 void operator() (const recently_used_item
* item
)
321 if (items_written_
++ < max_items_to_write_
)
322 item
->write_xml(file_
);
325 recently_used_file
& file_
;
326 int max_items_to_write_
;
330 //------------------------------------------------
331 const char* XML_HEADER
= "<?xml version=\"1.0\"?>\n<RecentFiles>\n";
332 const char* XML_FOOTER
= "</RecentFiles>";
334 //------------------------------------------------
335 // assumes that the list is ordered decreasing
336 void write_recently_used_items(
337 recently_used_file
& file
,
338 recently_used_item_list_t
& item_list
)
340 if (!item_list
.empty())
345 file
.write(XML_HEADER
, strlen(XML_HEADER
));
350 recent_item_writer(file
));
352 file
.write(XML_FOOTER
, strlen(XML_FOOTER
));
356 //------------------------------------------------
357 struct delete_recently_used_item
359 void operator() (const recently_used_item
* item
) const
363 //------------------------------------------------
364 void recently_used_item_list_clear(recently_used_item_list_t
& item_list
)
369 delete_recently_used_item());
373 //------------------------------------------------
374 class find_item_predicate
377 find_item_predicate(const string_t
& uri
) :
381 bool operator() (const recently_used_item
* item
) const
382 { return (item
->uri_
== uri_
); }
387 //------------------------------------------------
388 struct greater_recently_used_item
390 bool operator ()(const recently_used_item
* lhs
, const recently_used_item
* rhs
) const
391 { return (lhs
->timestamp_
> rhs
->timestamp_
); }
394 //------------------------------------------------
395 const char* GROUP_OOO
= "openoffice.org";
396 const char* GROUP_STAR_OFFICE
= "staroffice";
397 const char* GROUP_STAR_SUITE
= "starsuite";
399 //------------------------------------------------
400 void recently_used_item_list_add(
401 recently_used_item_list_t
& item_list
, const OUString
& file_url
, const OUString
& mime_type
)
403 OString f
= OUStringToOString(file_url
, RTL_TEXTENCODING_UTF8
);
405 recently_used_item_list_t::iterator iter
=
409 find_item_predicate(f
.getStr()));
411 if (iter
!= item_list
.end())
413 (*iter
)->timestamp_
= time(NULL
);
415 if (!(*iter
)->has_group(GROUP_OOO
))
416 (*iter
)->groups_
.push_back(GROUP_OOO
);
417 if (!(*iter
)->has_group(GROUP_STAR_OFFICE
))
418 (*iter
)->groups_
.push_back(GROUP_STAR_OFFICE
);
419 if (!(*iter
)->has_group(GROUP_STAR_SUITE
))
420 (*iter
)->groups_
.push_back(GROUP_STAR_SUITE
);
424 string_container_t groups
;
425 groups
.push_back(GROUP_OOO
);
426 groups
.push_back(GROUP_STAR_OFFICE
);
427 groups
.push_back(GROUP_STAR_SUITE
);
429 string_t
uri(f
.getStr());
430 string_t
mimetype(OUStringToOString(mime_type
, osl_getThreadTextEncoding()).getStr());
432 if (mimetype
.length() == 0)
433 mimetype
= "application/octet-stream";
435 item_list
.push_back(new recently_used_item(uri
, mimetype
, groups
));
438 // sort decreasing after the timestamp
439 // so that the newest items appear first
443 greater_recently_used_item());
446 //------------------------------------------------
449 cleanup_guard(recently_used_item_list_t
& item_list
) :
450 item_list_(item_list
)
453 { recently_used_item_list_clear(item_list_
); }
455 recently_used_item_list_t
& item_list_
;
458 } // namespace private
461 example (see http::www.freedesktop.org):
462 <?xml version="1.0"?>
465 <URI>file:///home/federico/gedit.txt</URI>
466 <Mime-Type>text/plain</Mime-Type>
467 <Timestamp>1046485966</Timestamp>
473 <URI>file:///home/federico/gedit-2.2.0.tar.bz2</URI>
474 <Mime-Type>application/x-bzip</Mime-Type>
475 <Timestamp>1046209851</Timestamp>
483 extern "C" SAL_DLLPUBLIC_EXPORT
484 void add_to_recently_used_file_list(const OUString
& file_url
,
485 const OUString
& mime_type
)
489 recently_used_file ruf
;
490 recently_used_item_list_t item_list
;
491 cleanup_guard
guard(item_list
);
493 read_recently_used_items(ruf
, item_list
);
494 recently_used_item_list_add(item_list
, file_url
, mime_type
);
495 write_recently_used_items(ruf
, item_list
);
497 catch(const char* ex
)
501 catch(const xml_parser_exception
&)
503 OSL_FAIL("XML parser error");
505 catch(const unknown_xml_format_exception
&)
507 OSL_FAIL("XML format unknown");
512 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */