blob: d6fc43917a71436a806233271fc1e47289fddbd3 [file] [log] [blame]
// Copyright (c) 2010 The Chromium Embedded Framework 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 "include/wrapper/cef_xml_object.h"
#include <sstream>
#include "include/base/cef_logging.h"
#include "include/base/cef_macros.h"
#include "include/cef_stream.h"
namespace {
class CefXmlObjectLoader {
public:
explicit CefXmlObjectLoader(CefRefPtr<CefXmlObject> root_object)
: root_object_(root_object) {}
bool Load(CefRefPtr<CefStreamReader> stream,
CefXmlReader::EncodingType encodingType,
const CefString& URI) {
CefRefPtr<CefXmlReader> reader(
CefXmlReader::Create(stream, encodingType, URI));
if (!reader.get())
return false;
bool ret = reader->MoveToNextNode();
if (ret) {
CefRefPtr<CefXmlObject> cur_object(root_object_), new_object;
CefXmlObject::ObjectVector queue;
int cur_depth, value_depth = -1;
CefXmlReader::NodeType cur_type;
std::stringstream cur_value;
bool last_has_ns = false;
queue.push_back(root_object_);
do {
cur_depth = reader->GetDepth();
if (value_depth >= 0 && cur_depth > value_depth) {
// The current node has already been parsed as part of a value.
continue;
}
cur_type = reader->GetType();
if (cur_type == XML_NODE_ELEMENT_START) {
if (cur_depth == value_depth) {
// Add to the current value.
cur_value << std::string(reader->GetOuterXml());
continue;
} else if (last_has_ns && reader->GetPrefix().empty()) {
if (!cur_object->HasChildren()) {
// Start a new value because the last element has a namespace and
// this element does not.
value_depth = cur_depth;
cur_value << std::string(reader->GetOuterXml());
} else {
// Value following a child element is not allowed.
std::stringstream ss;
ss << "Value following child element, line "
<< reader->GetLineNumber();
load_error_ = ss.str();
ret = false;
break;
}
} else {
// Start a new element.
new_object = new CefXmlObject(reader->GetQualifiedName());
cur_object->AddChild(new_object);
last_has_ns = !reader->GetPrefix().empty();
if (!reader->IsEmptyElement()) {
// The new element potentially has a value and/or children, so
// set the current object and add the object to the queue.
cur_object = new_object;
queue.push_back(cur_object);
}
if (reader->HasAttributes() && reader->MoveToFirstAttribute()) {
// Read all object attributes.
do {
new_object->SetAttributeValue(reader->GetQualifiedName(),
reader->GetValue());
} while (reader->MoveToNextAttribute());
reader->MoveToCarryingElement();
}
}
} else if (cur_type == XML_NODE_ELEMENT_END) {
if (cur_depth == value_depth) {
// Ending an element that is already in the value.
continue;
} else if (cur_depth < value_depth) {
// Done with parsing the value portion of the current element.
cur_object->SetValue(cur_value.str());
cur_value.str("");
value_depth = -1;
}
// Pop the current element from the queue.
queue.pop_back();
if (queue.empty() ||
cur_object->GetName() != reader->GetQualifiedName()) {
// Open tag without close tag or close tag without open tag should
// never occur (the parser catches this error).
NOTREACHED();
std::stringstream ss;
ss << "Mismatched end tag for "
<< std::string(cur_object->GetName()) << ", line "
<< reader->GetLineNumber();
load_error_ = ss.str();
ret = false;
break;
}
// Set the current object to the previous object in the queue.
cur_object = queue.back().get();
} else if (cur_type == XML_NODE_TEXT || cur_type == XML_NODE_CDATA ||
cur_type == XML_NODE_ENTITY_REFERENCE) {
if (cur_depth == value_depth) {
// Add to the current value.
cur_value << std::string(reader->GetValue());
} else if (!cur_object->HasChildren()) {
// Start a new value.
value_depth = cur_depth;
cur_value << std::string(reader->GetValue());
} else {
// Value following a child element is not allowed.
std::stringstream ss;
ss << "Value following child element, line "
<< reader->GetLineNumber();
load_error_ = ss.str();
ret = false;
break;
}
}
} while (reader->MoveToNextNode());
}
if (reader->HasError()) {
load_error_ = reader->GetError();
return false;
}
return ret;
}
CefString GetLoadError() { return load_error_; }
private:
CefString load_error_;
CefRefPtr<CefXmlObject> root_object_;
DISALLOW_COPY_AND_ASSIGN(CefXmlObjectLoader);
};
} // namespace
CefXmlObject::CefXmlObject(const CefString& name)
: name_(name), parent_(nullptr) {}
CefXmlObject::~CefXmlObject() {}
bool CefXmlObject::Load(CefRefPtr<CefStreamReader> stream,
CefXmlReader::EncodingType encodingType,
const CefString& URI,
CefString* loadError) {
Clear();
CefXmlObjectLoader loader(this);
if (!loader.Load(stream, encodingType, URI)) {
if (loadError)
*loadError = loader.GetLoadError();
return false;
}
return true;
}
void CefXmlObject::Set(CefRefPtr<CefXmlObject> object) {
DCHECK(object.get());
Clear();
name_ = object->GetName();
Append(object, true);
}
void CefXmlObject::Append(CefRefPtr<CefXmlObject> object,
bool overwriteAttributes) {
DCHECK(object.get());
if (object->HasChildren()) {
ObjectVector children;
object->GetChildren(children);
ObjectVector::const_iterator it = children.begin();
for (; it != children.end(); ++it)
AddChild((*it)->Duplicate());
}
if (object->HasAttributes()) {
AttributeMap attributes;
object->GetAttributes(attributes);
AttributeMap::const_iterator it = attributes.begin();
for (; it != attributes.end(); ++it) {
if (overwriteAttributes || !HasAttribute(it->first))
SetAttributeValue(it->first, it->second);
}
}
}
CefRefPtr<CefXmlObject> CefXmlObject::Duplicate() {
CefRefPtr<CefXmlObject> new_obj;
{
base::AutoLock lock_scope(lock_);
new_obj = new CefXmlObject(name_);
new_obj->Append(this, true);
}
return new_obj;
}
void CefXmlObject::Clear() {
ClearChildren();
ClearAttributes();
}
CefString CefXmlObject::GetName() {
CefString name;
{
base::AutoLock lock_scope(lock_);
name = name_;
}
return name;
}
bool CefXmlObject::SetName(const CefString& name) {
DCHECK(!name.empty());
if (name.empty())
return false;
base::AutoLock lock_scope(lock_);
name_ = name;
return true;
}
bool CefXmlObject::HasParent() {
base::AutoLock lock_scope(lock_);
return (parent_ != nullptr);
}
CefRefPtr<CefXmlObject> CefXmlObject::GetParent() {
CefRefPtr<CefXmlObject> parent;
{
base::AutoLock lock_scope(lock_);
parent = parent_;
}
return parent;
}
bool CefXmlObject::HasValue() {
base::AutoLock lock_scope(lock_);
return !value_.empty();
}
CefString CefXmlObject::GetValue() {
CefString value;
{
base::AutoLock lock_scope(lock_);
value = value_;
}
return value;
}
bool CefXmlObject::SetValue(const CefString& value) {
base::AutoLock lock_scope(lock_);
DCHECK(children_.empty());
if (!children_.empty())
return false;
value_ = value;
return true;
}
bool CefXmlObject::HasAttributes() {
base::AutoLock lock_scope(lock_);
return !attributes_.empty();
}
size_t CefXmlObject::GetAttributeCount() {
base::AutoLock lock_scope(lock_);
return attributes_.size();
}
bool CefXmlObject::HasAttribute(const CefString& name) {
if (name.empty())
return false;
base::AutoLock lock_scope(lock_);
AttributeMap::const_iterator it = attributes_.find(name);
return (it != attributes_.end());
}
CefString CefXmlObject::GetAttributeValue(const CefString& name) {
DCHECK(!name.empty());
CefString value;
if (!name.empty()) {
base::AutoLock lock_scope(lock_);
AttributeMap::const_iterator it = attributes_.find(name);
if (it != attributes_.end())
value = it->second;
}
return value;
}
bool CefXmlObject::SetAttributeValue(const CefString& name,
const CefString& value) {
DCHECK(!name.empty());
if (name.empty())
return false;
base::AutoLock lock_scope(lock_);
AttributeMap::iterator it = attributes_.find(name);
if (it != attributes_.end()) {
it->second = value;
} else {
attributes_.insert(std::make_pair(name, value));
}
return true;
}
size_t CefXmlObject::GetAttributes(AttributeMap& attributes) {
base::AutoLock lock_scope(lock_);
attributes = attributes_;
return attributes_.size();
}
void CefXmlObject::ClearAttributes() {
base::AutoLock lock_scope(lock_);
attributes_.clear();
}
bool CefXmlObject::HasChildren() {
base::AutoLock lock_scope(lock_);
return !children_.empty();
}
size_t CefXmlObject::GetChildCount() {
base::AutoLock lock_scope(lock_);
return children_.size();
}
bool CefXmlObject::HasChild(CefRefPtr<CefXmlObject> child) {
DCHECK(child.get());
base::AutoLock lock_scope(lock_);
ObjectVector::const_iterator it = children_.begin();
for (; it != children_.end(); ++it) {
if ((*it).get() == child.get())
return true;
}
return false;
}
bool CefXmlObject::AddChild(CefRefPtr<CefXmlObject> child) {
DCHECK(child.get());
if (!child.get())
return false;
CefRefPtr<CefXmlObject> parent = child->GetParent();
DCHECK(!parent);
if (parent)
return false;
base::AutoLock lock_scope(lock_);
children_.push_back(child);
child->SetParent(this);
return true;
}
bool CefXmlObject::RemoveChild(CefRefPtr<CefXmlObject> child) {
DCHECK(child.get());
base::AutoLock lock_scope(lock_);
ObjectVector::iterator it = children_.begin();
for (; it != children_.end(); ++it) {
if ((*it).get() == child.get()) {
children_.erase(it);
child->SetParent(nullptr);
return true;
}
}
return false;
}
size_t CefXmlObject::GetChildren(ObjectVector& children) {
base::AutoLock lock_scope(lock_);
children = children_;
return children_.size();
}
void CefXmlObject::ClearChildren() {
base::AutoLock lock_scope(lock_);
ObjectVector::iterator it = children_.begin();
for (; it != children_.end(); ++it)
(*it)->SetParent(nullptr);
children_.clear();
}
CefRefPtr<CefXmlObject> CefXmlObject::FindChild(const CefString& name) {
DCHECK(!name.empty());
if (name.empty())
return nullptr;
base::AutoLock lock_scope(lock_);
ObjectVector::const_iterator it = children_.begin();
for (; it != children_.end(); ++it) {
if ((*it)->GetName() == name)
return (*it);
}
return nullptr;
}
size_t CefXmlObject::FindChildren(const CefString& name,
ObjectVector& children) {
DCHECK(!name.empty());
if (name.empty())
return 0;
size_t ct = 0;
base::AutoLock lock_scope(lock_);
ObjectVector::const_iterator it = children_.begin();
for (; it != children_.end(); ++it) {
if ((*it)->GetName() == name) {
children.push_back(*it);
ct++;
}
}
return ct;
}
void CefXmlObject::SetParent(CefXmlObject* parent) {
base::AutoLock lock_scope(lock_);
if (parent) {
DCHECK(parent_ == nullptr);
parent_ = parent;
} else {
DCHECK(parent_ != nullptr);
parent_ = nullptr;
}
}