//
// See the file LICENSE for redistribution information.
//
// Copyright (c) 2002-2003
//	Sleepycat Software.  All rights reserved.
//

static const char revid[] = "$Id: Name.cpp,v 1.18 2003/05/09 00:01:49 mjc Exp $";

#include "dbxml_config.h"
#include "dbxml/XmlPortability.hpp"
#include "dbxml/XmlNamespace.hpp"
#include "Name.hpp"
#include "ScopedDbt.hpp"
#include "Buffer.hpp"

using namespace std;
using namespace DbXml;

const Name Name::dbxml_colon_id(metaDataNamespace_uri, metaDataNamespace_prefix, metaDataName_id);
const Name Name::dbxml_colon_name(metaDataNamespace_uri, metaDataNamespace_prefix, metaDataName_name);
const Name Name::dbxml_colon_content(metaDataNamespace_uri, metaDataNamespace_prefix, metaDataName_content);

Name::Name()
	: known_(0),
	size_(0),
	buffer_(0),
	uri_(0),
	prefix_(0),
	name_(0)
{}

Name::Name(const char *u, const char *p, const char *n)
	: known_(0),
	size_(0),
	buffer_(0),
	uri_(0),
	prefix_(0),
	name_(0)
{
	size_t ul = (u == 0 ? 0 : strlen(u));
	size_t pl = (p == 0 ? 0 : strlen(p));
	size_t nl = (n == 0 ? 0 : strlen(n));
	set(u, ul, p, pl, n, nl);
}

Name::Name(const std::string &u, const std::string &p, const std::string &n)
	: known_(0),
	size_(0),
	buffer_(0),
	uri_(0),
	prefix_(0),
	name_(0)
{
	set(u.c_str(), u.length(), p.c_str(), p.length(), n.c_str(), n.length());
}

Name::Name(const char *uriname)
	: known_(0),
	size_(0),
	buffer_(0),
	uri_(0),
	prefix_(0),
	name_(0)
{
	// uriname= { uri:name | name }
	const char *p = strrchr(uriname, ':');
	if (p == 0) {
		set(0, 0, 0, 0, uriname, strlen(uriname));
	} else {
		++p; // skip ':'
		set(uriname, p - uriname - 1, 0, 0, p, strlen(p));
	}
}

Name::Name(const Name &n)
	: known_(0),
	size_(0),
	buffer_(0),
	uri_(0),
	prefix_(0),
	name_(0)
{
	set(n);
}

Name::~Name()
{
	delete [] buffer_;
}

Name &Name::operator = (const Name &n)
{
	if (this != &n) {
		set(n);
	}
	return *this;
}

bool Name::operator<(const Name &n) const
{
	return (compare(n) < 0);
}

bool Name::operator==(const Name &n) const
{
	return (compare(n) == 0);
}

const char *Name::getURI() const
{
	return known_ ? known_->getURI() : uri_;
}

const char *Name::getPrefix() const
{
	return known_ ? known_->getPrefix() : prefix_;
}

const char *Name::getName() const
{
	return known_ ? known_->getName() : name_;
}

void Name::setThisFromDbt(const DbtOut &dbt, bool swap)
{
	UNUSED(swap);

	// uri \0 prefix \0 name \0
	//
	const char *p = (const char*)dbt.get_data();
	size_t ul = strlen(p);
	size_t pl = strlen(p + ul + 1);
	size_t nl = strlen(p + ul + 1 + pl + 1);
	set(p, ul, p + ul + 1, pl, p + ul + 1 + pl + 1, nl);
}

void Name::setDbtFromThis_PrimaryValue(DbtOut &dbt, bool swap) const
{
	if (known_) {
		known_->setDbtFromThis_PrimaryValue(dbt, swap);
	} else {
		// uri \0 prefix \0 name \0
		//
		dbt.set(buffer_, size_);
	}
}

void Name::setDbtFromThis_SecondaryKey(DbtOut &dbt, bool swap) const
{
	if (known_) {
		known_->setDbtFromThis_SecondaryKey(dbt, swap);
	} else {
		// { uri : } name
		//
		size_t ul = (uri_ == 0 ? 0 : strlen(uri_));
		size_t nl = (name_ == 0 ? 0 : strlen(name_));
		size_t l = ul + (ul > 0 ? 1 : 0) + nl;
		dbt.set(0, l);
		Buffer b(dbt.get_data(), l, /*wrapper=*/true);
		if (ul > 0) {
			b.write(uri_, ul);
			static const char colon = ':';
			b.write(&colon, sizeof(colon));
		}
		b.write(name_, nl);
	}
}

std::ostream& DbXml::operator<<(std::ostream& s, const Name &n)
{
	s << n.asString();
	return s;
}

std::string Name::asString() const
{
	if (known_) {
		return known_->asString();
	} else {
		if (hasURI() || hasPrefix()) {
			std::string s("{");
			if (hasURI())
				s += uri_;
			s += ",";
			if (hasPrefix())
				s += prefix_;
			s += ",";
			s += name_;
			s += "}";
			return s;
		} else
			return name_;
	}
}

const std::string Name::getURIName() const
{
	if (known_) {
		return known_->getURIName();
	} else {
		std::string r;
		if (hasURI()) {
			r += uri_;
			r += ":";
		}
		r += name_;
		return r;
	}
}

const std::string Name::getPrefixName() const
{
	if (known_) {
		return known_->getPrefixName();
	} else {
		std::string r;
		if (hasPrefix()) {
			r += prefix_;
			r += ":";
		}
		r += name_;
		return r;
	}
}

int Name::compare(const Name &n) const
{
	if (known_) {
		if (n.known_ && known_ == n.known_) {
			return 0;
		} else {
			return known_->compare(n);
		}
	} else {
		int r = 0;
		if (uri_ == 0 && n.uri_ == 0) {
			r = 0;
		} else if (uri_ == 0 && n.uri_ != 0) {
			r = -1;
		} else if (uri_ != 0 && n.uri_ == 0) {
			r = 1;
		} else {
			r = strcmp(uri_, n.uri_);
		}
		if (r == 0) {
			r = strcmp(name_, n.name_);
		}
		return r;
	}
}

void Name::reset()
{
	known_ = 0;
	if (buffer_ != 0)
		delete [] buffer_;
	buffer_ = 0;
	size_ = 0;
	uri_ = 0;
	prefix_ = 0;
	name_ = 0;
}

bool Name::hasPrefix() const
{
	return known_ ? known_->hasPrefix() : (prefix_ != 0 && prefix_[0] != 0);
}

bool Name::hasURI() const
{
	return known_ ? known_->hasURI() : (uri_ != 0 && uri_[0] != 0);
}

void Name::set(const Name &n)
{
	if (
	    &n == &dbxml_colon_id ||
	    &n == &dbxml_colon_name ||
	    &n == &dbxml_colon_content) {
		reset();
		known_ = &n;
	} else {
		size_t ul = (n.uri_ == 0 ? 0 : strlen(n.uri_));
		size_t pl = (n.prefix_ == 0 ? 0 : strlen(n.prefix_));
		size_t nl = (n.name_ == 0 ? 0 : strlen(n.name_));
		set(n.uri_, ul, n.prefix_, pl, n.name_, nl);
	}
}

void Name::set(const char *u, size_t ul, const char *p, size_t pl, const char *n, size_t nl)
{
	known_ = 0;
	if (buffer_ != 0)
		delete [] buffer_;
	size_ = ul + pl + nl + 3;
	buffer_ = new char[size_];
	uri_ = buffer_;
	prefix_ = buffer_ + ul + 1;
	name_ = buffer_ + ul + 1 + pl + 1;
	if (ul == 0)
		uri_[0] = 0;
	else {
		strncpy(uri_, u, ul);
		uri_[ul] = 0;
	}
	if (pl == 0)
		prefix_[0] = 0;
	else {
		strncpy(prefix_, p, pl);
		prefix_[pl] = 0;
	}
	if (nl == 0)
		name_[0] = 0;
	else {
		strncpy(name_, n, nl);
		name_[nl] = 0;
	}
}
