OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / value / LinkValue.cc

167 lines · cc ref:
/*
 * opencog/atoms/value/LinkValue.cc
 *
 * Copyright (C) 2015 Linas Vepstas
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atoms/base/Atom.h>
#include <opencog/atoms/base/Link.h>
#include <opencog/atoms/value/LinkValue.h>
#include <opencog/atoms/value/ValueFactory.h>

using namespace opencog;

HandleSeq LinkValue::to_handle_seq(void) const
{
	update();
	HandleSeq hs;
	for (const ValuePtr& v : _value)
	{
		if (v->is_atom())
			hs.push_back(HandleCast(v));

		// Recursively convert any value lists into atom lists
		else if (nameserver().isA(v->get_type(), LINK_VALUE))
		{
			HandleSeq hsr(LinkValueCast(v)->to_handle_seq());
			Handle h(createLink(std::move(hsr), LIST_LINK));
			hs.push_back(h);
		}
	}
	return hs;
}

HandleSet LinkValue::to_handle_set(void) const
{
	update();
	HandleSet hs;
	for (const ValuePtr& v : _value)
	{
		if (v->is_atom())
			hs.insert(HandleCast(v));

		// Recursively convert any value lists into atom lists
		else if (nameserver().isA(v->get_type(), LINK_VALUE))
		{
			HandleSeq hsr(LinkValueCast(v)->to_handle_seq());
			Handle h(createLink(std::move(hsr), LIST_LINK));
			hs.insert(h);
		}
	}
	return hs;
}

// ==============================================================

bool LinkValue::operator==(const Value& other) const
{
	// We do content-compare, and only loose type compare.
	// As long as other is a derviced type, we're good if
	// the actual values compare
	if (not other.is_type(LINK_VALUE)) return false;

	const LinkValue* lov = (const LinkValue*) &other;

	if (_value.size() != lov->_value.size()) return false;

	// Content-compare, NOT pointer-compare!
	size_t len = _value.size();
	for (size_t i=0; i<len; i++)
		if (*(_value[i]) != *(lov->_value[i])) return false;
	return true;
}

bool LinkValue::operator<(const Value& other) const
{
	// Compare by type name.
	if (_type != other.get_type())
		return nameserver().getTypeName(_type) < nameserver().getTypeName(other.get_type());

	// Compare by vector length.
	const LinkValue* lov = (const LinkValue*) &other;
	if (_value.size() != lov->_value.size())
		return _value.size() < lov->_value.size();

	// Compare individual values lexicographically.
	// This works because std::less<ValuePtr> is specialized to compare content.
	return _value < lov->_value;
}

// ==============================================================

std::string LinkValue::to_string(const std::string& indent, Type t) const
{
	std::string more_indent = indent + "  "; // two spaces, same as Link
	std::string rv = indent + "(" + nameserver().getTypeName(t) + "\n";

	SAFE_UPDATE(rv,
	{
		for (const ValuePtr& v :_value)
		{
			if (v->is_atom())
				rv += v->to_short_string(more_indent) + "\n";
			else
				rv += v->to_string(more_indent) + "\n";
		}

		// Remove trailing newline before writing the last paren
		rv.pop_back();
	});
	rv += ")";
	return rv;
}

/// Similar to above, except that it does not write any newlines.
/// This avoids cogserver issues, where the newline is interpreted
/// as an end-of-messege marker by GenericShell. The SchemeShell
/// deals with pending input just fine, but the SexprShell does not:
/// its a waste of CPU-cycles to work around newlines by scanning
/// for balanced parens in strings. So ... no newlines, here.
/// Also, no indentation. Without newlines, indentation does not make
/// sense.
std::string LinkValue::to_short_string(const std::string& indent) const
{
	update();
	std::string rv = "(" + nameserver().getTypeName(_type) + " ";
	for (const ValuePtr& v :_value)
	{
		// Well .. this is kind of insane and mildly unpleasant, but...
		// It can happen that Values get created that have null pointers
		// in them. Yes, that's ugly and bad. We should not throw here,
		// because this is long after the creation of the Value; we
		// are here because atomspace-storage is trying to serialize us.
		// We hae two remaining choices: do nothing and print nothing,
		// or print (VoidValue) to indicate end-of-stream, end-of-file,
		// no-data, whatever. See that just right now, this is a better
		// idea than printing nothing at all. We'll see. The correct
		// long-term fix is to avoid null pointers in ValueSeq's.
		if (v)
			rv += v->to_short_string("");
		else
			rv += "(VoidValue)";
	}

	rv += ")";
	return rv;
}

// Adds factory when library is loaded.
DEFINE_VALUE_FACTORY(LINK_VALUE, createLinkValue, ValueSeq&&)
DEFINE_VALUE_FACTORY(LINK_VALUE, createLinkValue, const HandleSeq&)