blob: 6d6274a2cf2393323e051f9d46d511b711d75f22 [file] [log] [blame]
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/bookmarks/bookmark_codec.h"
#include "base/string_util.h"
#include "base/values.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/common/l10n_util.h"
#include "googleurl/src/gurl.h"
#include "generated_resources.h"
using base::Time;
const wchar_t* BookmarkCodec::kRootsKey = L"roots";
const wchar_t* BookmarkCodec::kRootFolderNameKey = L"bookmark_bar";
const wchar_t* BookmarkCodec::kOtherBookmarFolderNameKey = L"other";
const wchar_t* BookmarkCodec::kVersionKey = L"version";
const wchar_t* BookmarkCodec::kTypeKey = L"type";
const wchar_t* BookmarkCodec::kNameKey = L"name";
const wchar_t* BookmarkCodec::kDateAddedKey = L"date_added";
const wchar_t* BookmarkCodec::kURLKey = L"url";
const wchar_t* BookmarkCodec::kDateModifiedKey = L"date_modified";
const wchar_t* BookmarkCodec::kChildrenKey = L"children";
const wchar_t* BookmarkCodec::kTypeURL = L"url";
const wchar_t* BookmarkCodec::kTypeFolder = L"folder";
// Current version of the file.
static const int kCurrentVersion = 1;
Value* BookmarkCodec::Encode(BookmarkModel* model) {
return Encode(model->GetBookmarkBarNode(), model->other_node());
}
Value* BookmarkCodec::Encode(BookmarkNode* bookmark_bar_node,
BookmarkNode* other_folder_node) {
DictionaryValue* roots = new DictionaryValue();
roots->Set(kRootFolderNameKey, EncodeNode(bookmark_bar_node));
roots->Set(kOtherBookmarFolderNameKey, EncodeNode(other_folder_node));
DictionaryValue* main = new DictionaryValue();
main->SetInteger(kVersionKey, kCurrentVersion);
main->Set(kRootsKey, roots);
return main;
}
bool BookmarkCodec::Decode(BookmarkModel* model, const Value& value) {
if (value.GetType() != Value::TYPE_DICTIONARY)
return false; // Unexpected type.
const DictionaryValue& d_value = static_cast<const DictionaryValue&>(value);
int version;
if (!d_value.GetInteger(kVersionKey, &version) || version != kCurrentVersion)
return false; // Unknown version.
Value* roots;
if (!d_value.Get(kRootsKey, &roots))
return false; // No roots.
if (roots->GetType() != Value::TYPE_DICTIONARY)
return false; // Invalid type for roots.
DictionaryValue* roots_d_value = static_cast<DictionaryValue*>(roots);
Value* root_folder_value;
Value* other_folder_value;
if (!roots_d_value->Get(kRootFolderNameKey, &root_folder_value) ||
root_folder_value->GetType() != Value::TYPE_DICTIONARY ||
!roots_d_value->Get(kOtherBookmarFolderNameKey, &other_folder_value) ||
other_folder_value->GetType() != Value::TYPE_DICTIONARY)
return false; // Invalid type for root folder and/or other folder.
DecodeNode(model, *static_cast<DictionaryValue*>(root_folder_value),
NULL, model->GetBookmarkBarNode());
DecodeNode(model, *static_cast<DictionaryValue*>(other_folder_value),
NULL, model->other_node());
// Need to reset the type as decoding resets the type to FOLDER. Similarly
// we need to reset the title as the title is persisted and restored from
// the file.
model->GetBookmarkBarNode()->type_ = history::StarredEntry::BOOKMARK_BAR;
model->other_node()->type_ = history::StarredEntry::OTHER;
model->GetBookmarkBarNode()->SetTitle(
l10n_util::GetString(IDS_BOOMARK_BAR_FOLDER_NAME));
model->other_node()->SetTitle(
l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME));
return true;
}
Value* BookmarkCodec::EncodeNode(BookmarkNode* node) {
DictionaryValue* value = new DictionaryValue();
value->SetString(kNameKey, node->GetTitle());
value->SetString(kDateAddedKey,
Int64ToWString(node->date_added().ToInternalValue()));
if (node->GetType() == history::StarredEntry::URL) {
value->SetString(kTypeKey, kTypeURL);
value->SetString(kURLKey,
UTF8ToWide(node->GetURL().possibly_invalid_spec()));
} else {
value->SetString(kTypeKey, kTypeFolder);
value->SetString(kDateModifiedKey,
Int64ToWString(node->date_group_modified().
ToInternalValue()));
ListValue* child_values = new ListValue();
value->Set(kChildrenKey, child_values);
for (int i = 0; i < node->GetChildCount(); ++i)
child_values->Append(EncodeNode(node->GetChild(i)));
}
return value;
}
bool BookmarkCodec::DecodeChildren(BookmarkModel* model,
const ListValue& child_value_list,
BookmarkNode* parent) {
for (size_t i = 0; i < child_value_list.GetSize(); ++i) {
Value* child_value;
if (!child_value_list.Get(i, &child_value))
return false;
if (child_value->GetType() != Value::TYPE_DICTIONARY)
return false;
if (!DecodeNode(model, *static_cast<DictionaryValue*>(child_value), parent,
NULL)) {
return false;
}
}
return true;
}
bool BookmarkCodec::DecodeNode(BookmarkModel* model,
const DictionaryValue& value,
BookmarkNode* parent,
BookmarkNode* node) {
std::wstring title;
if (!value.GetString(kNameKey, &title))
return false;
// TODO(sky): this should be more flexible. Don't hoark if we can't parse it
// all.
std::wstring date_added_string;
if (!value.GetString(kDateAddedKey, &date_added_string))
return false;
std::wstring type_string;
if (!value.GetString(kTypeKey, &type_string))
return false;
if (type_string != kTypeURL && type_string != kTypeFolder)
return false; // Unknown type.
if (type_string == kTypeURL) {
std::wstring url_string;
if (!value.GetString(kURLKey, &url_string))
return false;
// TODO(sky): this should ignore the node if not a valid URL.
if (!node)
node = new BookmarkNode(model, GURL(WideToUTF8(url_string)));
if (parent)
parent->Add(parent->GetChildCount(), node);
node->type_ = history::StarredEntry::URL;
} else {
std::wstring last_modified_date;
if (!value.GetString(kDateModifiedKey, &last_modified_date))
return false;
Value* child_values;
if (!value.Get(kChildrenKey, &child_values))
return false;
if (child_values->GetType() != Value::TYPE_LIST)
return false;
if (!node)
node = new BookmarkNode(model, GURL());
node->type_ = history::StarredEntry::USER_GROUP;
node->date_group_modified_ =
Time::FromInternalValue(StringToInt64(last_modified_date));
if (parent)
parent->Add(parent->GetChildCount(), node);
if (!DecodeChildren(model, *static_cast<ListValue*>(child_values), node))
return false;
}
node->SetTitle(title);
node->date_added_ =
Time::FromInternalValue(StringToInt64(date_added_string));
return true;
}