C++メモ

Uri class の作成

Uri は取扱が面倒である。特に相対 uri から絶対 uri にする部分には注意が 必要である。ここでは、RFC 3986 をベースになるべく文書そのまま実装してみた。 文字列は std::string を使っている。New 演算子を使うメンバは無いので、 copy constructor や代入演算子等は省略時設定でよいはずなので、定義してない。

ヘッダ

#ifndef URI_H_INCLUDED
#define URI_H_INCLUDED

#include <string>

class Uri
{
public:
	Uri(const char* uri);
	Uri(const std::string& struri);
	Uri(const Uri& base, const Uri& ref);
	virtual ~Uri(void);

	const std::string& UriString(void) const;

	const std::string& SchemeString(void) const;
	const std::string& AuthorityString(void) const;
	const std::string& PathString(void) const;
	const std::string& QueryString(void) const;
	const std::string& FragmentString(void) const;

	bool SchemeDefined(void) const;
	bool AuthorityDefined(void) const;
	bool PathEmpty(void) const;
	bool QueryDefined(void) const;
	bool FragmentDefined(void) const;

	bool SameDocument(const Uri& ref) const;

	// Substructure interface
	bool UserInfoDefined(void) const;
	bool HostPortDefined(void) const;
	const std::string& UserInfoString(void) const;
	const std::string& HostPortString(void) const;
	bool HostDefined(void) const;
	bool PortDefined(void) const;
	const std::string& HostString(void) const;
	int Port(void) const;

protected:
	class Authority
	{
	public:
		Authority(void);
		Authority(const std::string& strAuth);
		virtual ~Authority(void);
		const std::string& UserInfoString(void) const;
		const std::string& HostPortString(void) const;
		bool UserInfoDefined(void) const;
		bool HostPortDefined(void) const;

	protected:
		class HostPort
		{
		public:
			HostPort(void);
			HostPort(const std::string& strHostPort);
			virtual ~HostPort(void);
			const std::string& HostString(void) const;
			const std::string& PortString(void) const;
			int Port(void) const;
			bool HostDefined(void) const;
			bool PortDefined(void) const;

		protected:
			bool m_bHost;
			bool m_bPort;
			std::string m_strHost;
			std::string m_strPort;
			int m_nPort;
		};

	protected:
		bool m_bUserInfo;
		bool m_bHostPort;
		std::string m_strUserInfo;
		std::string m_strHostPort;
	public:
		HostPort m_HostPort;
	};

private:
	void Init(void);
	//
	std::string::const_iterator
		GenScheme(std::string::const_iterator puri, std::string::const_iterator pend);
	std::string::const_iterator
		GenAuthority(std::string::const_iterator puri, std::string::const_iterator pend);
	std::string::const_iterator
		GenPath(std::string::const_iterator puri, std::string::const_iterator pend);
	std::string::const_iterator
		GenQuery(std::string::const_iterator, std::string::const_iterator pend);
	std::string::const_iterator
		GenFragment(std::string::const_iterator, std::string::const_iterator pend);
	//
	bool MergePaths(const Uri& base, const Uri& ref);
	bool CopyScheme(const Uri& src);
	bool CopyAuthority(const Uri& src);
	bool CopyPath(const Uri& src);
	bool CopyQuery(const Uri& src);
	bool CopyFragment(const Uri& src);
	void RemoveDotSegments(void);
	void DeCompose(void);
	void ReCompose(void);

private:
	static void Normalize(std::string& str);

protected:
	std::string m_strUri;

	std::string m_strScheme;
	std::string m_strAuthority;
	std::string m_strPath;
	std::string m_strQuery;
	std::string m_strFragment;

	bool m_bScheme;
	bool m_bAuthority;
	bool m_bQuery;
	bool m_bFragment;

	// SubStructure
	Authority m_Authority;
};

#endif	// URI_H_INCLUDED

本体 uri.cpp

// uri.cpp

#include "uri.h"

// RFC3986
//
// Uniform Resource Inditifier (URI): Generic Syntax
// http://gbiv.com/protocols/uri/rfc/rfc3986.html
//

Uri::Authority::HostPort::HostPort(void)
{
	m_bHost = false;
	m_bPort = false;
	m_nPort = 0;
}

Uri::Authority::HostPort::HostPort(const std::string& strHostPort)
{
	m_bHost = false;
	m_bPort = false;
	m_nPort = 0;

	std::string::const_iterator p = strHostPort.begin();
	std::string::const_iterator pcol = strHostPort.end();
	while(p != strHostPort.end())
	{
		if(*p == (char)':')
		{
			pcol = p;
			break;
		}
		++p;
	}
	p = strHostPort.begin();
	while(p != pcol)
		m_strHost.push_back(*p++);

	if(m_strHost.size() > 0)
		m_bHost = true;
	if(pcol == strHostPort.end())
		return;
	p = pcol + 1;
	while(p != strHostPort.end())
	{
		int c = *p;
		if(!::isdigit(c))
			break;
		m_nPort = m_nPort * 10 + c - (int)'0';
		m_strPort.push_back(*p++);
	}
	if(m_strPort.size() > 0)
		m_bPort = true;
}

Uri::Authority::HostPort::~HostPort(void)
{
}

bool
Uri::Authority::HostPort::HostDefined(void) const
{
	return m_bHost;
}

const std::string&
Uri::Authority::HostPort::HostString(void) const
{
	return m_strHost;
}

bool
Uri::Authority::HostPort::PortDefined(void) const
{
	return m_bPort;
}

const std::string&
Uri::Authority::HostPort::PortString(void) const
{
	return m_strPort;
}

int
Uri::Authority::HostPort::Port(void) const
{
	return m_nPort;
}

Uri::Authority::Authority(void)
{
	m_bUserInfo = false;
	m_bHostPort = false;
}

Uri::Authority::Authority(const std::string& strAuth)
{
	m_bUserInfo = false;
	m_bHostPort = false;

	std::string::const_iterator p = strAuth.begin();
	std::string::const_iterator pat = strAuth.end();
	while(p != strAuth.end())
	{
		if(*p == (char)'@')
		{
			pat = p;
			break;
		}
		++p;
	}
	p = strAuth.begin();
	if(pat != strAuth.end())
	{
		while(p != pat)
			m_strUserInfo.push_back(*p++);
		++p;
		m_bUserInfo = true;
	}
	while(p != strAuth.end())
		m_strHostPort.push_back(*p++);
	m_bHostPort = true;
	m_HostPort = HostPort(m_strHostPort);
}

Uri::Authority::~Authority(void)
{
}

bool
Uri::Authority::UserInfoDefined(void) const
{
	return m_bUserInfo;
}

const std::string&
Uri::Authority::UserInfoString(void) const
{
	return m_strUserInfo;
}

bool
Uri::Authority::HostPortDefined(void) const
{
	return m_bHostPort;
}

const std::string&
Uri::Authority::HostPortString(void) const
{
	return m_strHostPort;
}

Uri::Uri(const char* uri) :
m_strUri(uri)
{
	Init();
	DeCompose();

	if(m_strScheme.size() > 0)
		RemoveDotSegments();
	ReCompose();
	if(m_bAuthority)
		m_Authority = Authority(m_strAuthority);
}

Uri::Uri(const std::string& struri) :
m_strUri(struri)
{
	Init();
	DeCompose();

	if(m_strScheme.size() > 0)
		RemoveDotSegments();
	ReCompose();
	if(m_bAuthority)
		m_Authority = Authority(m_strAuthority);
}

Uri::Uri(const Uri& base, const Uri& ref)
{
	Init();
	if(!base.SchemeDefined())
		return;

	bool bRefSchemeDef = ref.SchemeDefined();
	if(bRefSchemeDef)
	{
		CopyScheme(ref);
		CopyAuthority(ref);
		CopyPath(ref);
		CopyQuery(ref);
	}
	else
	{
		CopyScheme(base);
		if(ref.AuthorityDefined())
		{
			CopyAuthority(ref);
			CopyPath(ref);
			CopyQuery(ref);
		}
		else
		{
			CopyAuthority(base);
			if(ref.PathEmpty())
			{
				CopyPath(base);
				if(ref.QueryDefined())
					CopyQuery(ref);
				else
					CopyQuery(base);
			}
			else
			{
				if(*(ref.m_strPath.begin()) == (char)'/')
					CopyPath(ref);
				else
					MergePaths(base, ref);
				CopyQuery(ref);
			}
		}
	}
	CopyFragment(ref);
	RemoveDotSegments();
	ReCompose();
	if(m_bAuthority)
		m_Authority = Authority(m_strAuthority);
}

Uri::~Uri(void)
{
}

void
Uri::Init(void)
{
	m_bScheme = false;
	m_bAuthority = false;
	m_bQuery = false;
	m_bFragment = false;
}

void
Uri::DeCompose(void)
{
	std::string::const_iterator p = m_strUri.begin();
	std::string::const_iterator pend = m_strUri.end();
	p = GenScheme(p, pend);
	p = GenAuthority(p, pend);
	p = GenPath(p, pend);
	p = GenQuery(p, pend);
	p = GenFragment(p, pend);
	Normalize(m_strAuthority);
	Normalize(m_strPath);
	Normalize(m_strQuery);
	Normalize(m_strFragment);
}

const std::string&
Uri::UriString(void) const
{
	return m_strUri;
}

std::string::const_iterator
Uri::GenScheme(std::string::const_iterator puri, std::string::const_iterator pend)
{
	m_strScheme.clear();
	m_bScheme = false;

	if(puri == pend)
		return puri;

	std::string::const_iterator p = puri;
	int n = 0;
	int c = *p++;
	if(!::isalpha(c))
		return puri;
	n = 1;
	while(p != pend)
	{
		c = *p++;
		if((!::isalnum(c)) && (c != '+') &&
			(c != '-') && (c != '.'))
			break;
		++n;
	}
	if((p == pend) || (c != ':'))
		return puri;	// No delimiter found
	p = puri;
	for(int i = 0; i < n; i++)
		m_strScheme.push_back((char)::tolower((int)*p++));
	m_bScheme = true;
	++p;	// next to ':'
	return p;
}

bool
Uri::CopyScheme(const Uri& src)
{
	m_strScheme = src.m_strScheme;
	m_bScheme = src.m_bScheme;
	return true;
}

bool
Uri::SchemeDefined(void) const
{
	return m_bScheme;
}

const std::string&
Uri::SchemeString(void) const
{
	return m_strScheme;
}

std::string::const_iterator
Uri::GenAuthority(std::string::const_iterator puri, std::string::const_iterator pend)
{
	m_strAuthority.clear();
	m_bAuthority = false;

	if(puri == pend)
		return puri;

	std::string::const_iterator p = puri;
	int c = *p++;
	if(c != '/')
		return puri;
	if(p == pend)
		return puri;
	c = *p++;
	if(c != '/')
		return puri;

	while(p != pend)
	{
		c = *p;
		if((c == '/') || (c == '?') || (c == '#'))
			break;
		m_strAuthority.push_back(*p++);
	}
	m_bAuthority = true;
	return p;
}

bool
Uri::CopyAuthority(const Uri& src)
{
	m_strAuthority = src.m_strAuthority;
	m_bAuthority = src.m_bAuthority;
	return true;
}

bool
Uri::AuthorityDefined(void) const
{
	return m_bAuthority;
}

const std::string&
Uri::AuthorityString(void) const
{
	return m_strAuthority;
}

std::string::const_iterator
Uri::GenPath(std::string::const_iterator puri, std::string::const_iterator pend)
{
	m_strPath.clear();

	if(puri == pend)
		return puri;

	std::string::const_iterator p = puri;

	while(p != pend)
	{
		if((*p == (char)'?') || (*p == (char)'#'))
			break;
		m_strPath.push_back(*p++);
	}
	return p;
}

bool
Uri::CopyPath(const Uri& src)
{
	m_strPath = src.m_strPath;
	return true;
}

bool
Uri::PathEmpty(void) const
{
	return m_strPath.empty();
}

const std::string&
Uri::PathString(void) const
{
	return m_strPath;
}

std::string::const_iterator
Uri::GenQuery(std::string::const_iterator puri, std::string::const_iterator pend)
{
	m_strQuery.clear();
	m_bQuery = false;

	if(puri == pend)
		return puri;

	std::string::const_iterator p = puri;

	int c = *p++;
	if(c != '?')
		return puri;

	while(p != pend)
	{
		if(*p == (char)'#')
			break;
		m_strQuery.push_back(*p++);
	}
	m_bQuery = true;
	return p;
}

bool
Uri::CopyQuery(const Uri& src)
{
	m_strQuery = src.m_strQuery;
	m_bQuery = src.m_bQuery;
	return true;
}

bool
Uri::QueryDefined(void) const
{
	return m_bQuery;
}

const std::string&
Uri::QueryString(void) const
{
	return m_strQuery;
}

std::string::const_iterator
Uri::GenFragment(std::string::const_iterator puri, std::string::const_iterator pend)
{
	m_strFragment.clear();
	m_bFragment = false;

	if(puri == pend)
		return puri;

	std::string::const_iterator p = puri;
	int c = *p++;
	if(c != '#')
		return puri;

	while(p != pend)
		m_strFragment.push_back(*p++);
	m_bFragment = true;
	return p;
}

bool
Uri::CopyFragment(const Uri& src)
{
	m_strFragment = src.m_strFragment;
	m_bFragment = src.m_bFragment;
	return true;
}

bool
Uri::FragmentDefined(void) const
{
	return m_bFragment;
}

const std::string&
Uri::FragmentString(void) const
{
	return m_strFragment;
}

void
Uri::RemoveDotSegments(void)
{
	// This will remove the dot segments from the path.
	std::string::iterator pin = m_strPath.begin();
	std::string::iterator pout = pin;
	size_t nStartSize = m_strPath.size();
	size_t nLeft = nStartSize;
	size_t nOut = 0;

	while(nLeft)
	{
		if((nLeft >= 3) && (*pin == '.') && (*(pin + 1) == '.') && (*(pin + 2) == '/'))
		{
			nLeft -= 3;
			pin += 3;
		}
		else if((nLeft >= 2) && (*pin == '.') && (*(pin + 1) == '/'))
		{
			nLeft -= 2;
			pin += 2;
		}
		else
		{
			// B.
			if((nLeft >= 3) && (*pin == '/') && (*(pin + 1) == '.') && (*(pin + 2) == '/'))
			{
				nLeft -= 2;
				pin += 2;
			}
			else if((nLeft == 2) && (*pin == '/') && (*(pin + 1) == '.'))
			{
				nLeft -= 1;
				pin += 1;
				*pin = '/';
			}
			else
			{
				// C.
				if((nLeft >= 4) && (*pin == '/') && (*(pin + 1) == '.') &&
				   (*(pin + 2) == '.') && (*(pin + 3) == '/'))
				{
					nLeft -= 3;
					pin += 3;
					*pin = '/';
					// remove last segment
					while(nOut)
					{
						--nOut; --pout;
						if(*pout == '/')
							break;
					}
				}
				else if((nLeft == 3) && (*pin == '/') && (*(pin + 1) == '.') &&
				    (*(pin + 2) == '.'))
				{
					nLeft -= 2;
					pin += 2;
					*pin = '/';
					// remove last segment
					while(nOut)
					{
						--nOut; --pout;
						if(*pout == '/')
							break;
					}
				}
				else
				{
					// D.
					if((nLeft == 1) && (*pin == '.'))
					{
						nLeft -= 1;
						pin += 1;
					}
					else if((nLeft == 2) && (*pin == '.') && (*(pin + 1) == '.'))
					{
						nLeft -= 2;
						pin += 2;
					}
					else
					{
						// E.
						// Move the first path segment
						*pout++ = *pin++;
						++nOut;
						--nLeft;
						while(nLeft)
						{
							if(*pin == '/')
								break;
							*pout++ = *pin++;
							++nOut;
							--nLeft;
						}
					}
				}
			}
		}
	}
	if(nOut != nStartSize)
		m_strPath.erase(nOut);
}

bool
Uri::MergePaths(const Uri& base, const Uri& ref)
{
	m_strPath.clear();

	if(base.AuthorityDefined() && base.PathEmpty())
	{
		m_strPath.push_back((char)'/');
		m_strPath.append(ref.m_strPath);
	}
	else
	{
		// Remove the last segment (after right-most '/') of the path of base,
		// then concatinate the path of ref.
		std::string::const_iterator it = base.m_strPath.end();
		size_t n = base.m_strPath.size();
		while(n)
		{
			--it;
			if(*it == '/')
				break;
			--n;
		}
		it = base.m_strPath.begin();
		while(n)
		{
			m_strPath.push_back(*it++);
			--n;
		}
		m_strPath.append(ref.m_strPath);
	}
	return true;
}

void
Uri::ReCompose(void)
{
	// RFC-3986 5.3
	m_strUri.clear();
	if(m_bScheme)
	{
		m_strUri.append(m_strScheme);
		m_strUri.push_back((char)':');
	}

	if(m_bAuthority)
	{
		m_strUri.push_back((char)'/');
		m_strUri.push_back((char)'/');
		m_strUri.append(m_strAuthority);
	}

	m_strUri.append(m_strPath);

	if(m_bQuery)
	{
		m_strUri.push_back((char)'?');
		m_strUri.append(m_strQuery);
	}

	if(m_bFragment)
	{
		m_strUri.push_back((char)'#');
		m_strUri.append(m_strFragment);
	}
}

void
Uri::Normalize(std::string& str)
{
	std::string::iterator pin = str.begin();
	std::string::iterator pout = pin;
	size_t nleft = str.size();
	size_t nout = 0;

	while(nleft)
	{
		if((nleft >= 3) && (*pin == '%') &&
			(::isxdigit((int)*(pin + 1))) && (::isxdigit((int)*(pin + 2))))
		{
			int c = 0;
			int c1 = (int)*(pin + 1);
			int c2 = (int)*(pin + 2);
			if(::isdigit(c1))
				c = c1 - (int)'0';
			else
			{
				c1 = ::toupper(c1);
				c = c1 - (int)'A' + 10;
			}
			if(::isdigit(c2))
				c = c * 16 + c2 - (int)'0';
			else
			{
				c2 = ::toupper(c2);
				c = c * 16 + c2 - (int)'A' + 10;
			}
			//
			// check if unreserved
			if(((c >= 0x41) && (c <= 0x5a)) ||
				((c >= 0x61) && (c <= 0x7a)) ||
				((c >= 0x30) && (c <= 0x39)) ||
				(c == 0x2d) || (c == 0x2e) || (c == 0x5f) || (c == 0x7e))
			{
				*pout++ = (char)c;
				nout++;
				pin += 3;
				nleft -= 3;
			}
			else
			{
				*pout++ = *pin++;
				*pout++ = (char)c1;
				*pout++ = (char)c2;
				nout += 3;
				pin += 2;
				nleft -= 3;
			}
		}
		else
		{
			*pout++ = *pin++;
			nout++;
			nleft--;
		}
	}
	if(nout != str.size())
		str.erase(nout);
}

bool
Uri::UserInfoDefined(void) const
{
	return m_Authority.UserInfoDefined();
}

bool
Uri::HostPortDefined(void) const
{
	return m_Authority.HostPortDefined();
}

const std::string&
Uri::UserInfoString(void) const
{
	return m_Authority.UserInfoString();
}

const std::string&
Uri::HostPortString(void) const
{
	return m_Authority.HostPortString();
}

bool
Uri::HostDefined(void) const
{
	return m_Authority.m_HostPort.HostDefined();
}

bool
Uri::PortDefined(void) const
{
	return m_Authority.m_HostPort.PortDefined();
}

const std::string&
Uri::HostString(void) const
{
	return m_Authority.m_HostPort.HostString();
}

int
Uri::Port(void) const
{
	return m_Authority.m_HostPort.Port();
}

bool
Uri::SameDocument(const Uri& ref) const
{
	if(m_bScheme != ref.m_bScheme)
		return false;
	if(m_bScheme && (m_strScheme != ref.m_strScheme))
		return false;
	if(m_bAuthority != ref.m_bAuthority)
		return false;
	if(m_bAuthority && (m_strAuthority != ref.m_strAuthority))
		return false;
	if(m_strPath != ref.m_strPath)
		return false;
	if(m_bQuery != ref.m_bQuery)
		return false;
	if(m_bQuery && (m_strQuery != ref.m_strQuery))
		return false;
	return true;
}

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2008-01-15 (火) 17:39:09