OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-sensory / opencog / atoms / sensory-v0 / OutputStream.cc

168 lines · cc ref:
/*
 * opencog/atoms/sensory-v0/OutputStream.cc
 *
 * Copyright (C) 2020 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 <errno.h>
#include <string.h> // for strerror()

#include <opencog/util/exceptions.h>
#include <opencog/util/oc_assert.h>
#include <opencog/atoms/base/Link.h>
#include <opencog/atoms/base/Node.h>
#include <opencog/atoms/value/StringValue.h>

#include <opencog/sensory-v0/types/atom_types.h>
#include "OutputStream.h"

using namespace opencog;

OutputStream::OutputStream(Type t)
	: LinkValue(t)
{
	OC_ASSERT(nameserver().isA(_type, OUTPUT_STREAM),
		"Bad OutputStream constructor!");
}

OutputStream::~OutputStream()
{
	printf ("OutputStream dtor\n");
}

// ==============================================================
// Provide a description utility

Handle OutputStream::make_description(
           const char* informal, /* plain English */
           const char* cmd_type, /* e.g. OpenLink */
           const char* stream_type) /* e.g. TerminalStream */
{
	Handle cmd =
		createLink(SECTION,
			createNode(ITEM_NODE, informal),
			createLink(CONNECTOR_SEQ,
				createLink(CONNECTOR,
					createNode(SEX_NODE, "command"),
					createNode(TYPE_NODE, cmd_type)),
				createLink(CONNECTOR,
					createNode(SEX_NODE, "reply"),
					createNode(TYPE_NODE, stream_type))));
	return cmd;
}

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

// Provide an unreasonable default implementation
void OutputStream::do_write(const std::string& str)
{
	throw RuntimeException(TRACE_INFO,
		"Not implemented! What are you doing?");
}

// Provide a reasonable default implementation
void OutputStream::write_one(AtomSpace* as, bool silent,
                             const ValuePtr& content)
{
	if (content->is_type(STRING_VALUE))
	{
		StringValuePtr svp(StringValueCast(content));
		const std::vector<std::string>& strs = svp->value();
		for (const std::string& str : strs)
			do_write(str);
		return;
	}
	if (content->is_type(NODE))
	{
		do_write(HandleCast(content)->get_name());
		return;
	}
	if (content->is_type(LINK_VALUE))
	{
		LinkValuePtr lvp(LinkValueCast(content));
		const ValueSeq& vals = lvp->value();
		for (const ValuePtr& v : vals)
			write_one(as, silent, v);
		return;
	}

	// Backwards-compat: Allow ListLink and SetLink (only!?)
	// Why restrict? I dunno. Seems like the right thing to do.
	Type tc = content->get_type();
	if (LIST_LINK == tc or SET_LINK == tc)
	{
		const HandleSeq& oset = HandleCast(content)->getOutgoingSet();
		for (const Handle& h : oset)
			write_one(as, silent, h);
		return;
	}

	throw RuntimeException(TRACE_INFO,
		"Expecting strings, got %s\n", content->to_string().c_str());
}

// Provide a reasonable default implementation.
ValuePtr OutputStream::do_write_out(AtomSpace* as, bool silent,
                                    const Handle& cref)
{
	ValuePtr content = cref;
	if (cref->is_executable())
	{
		content = cref->execute(as, silent);
		if (nullptr == content)
			throw RuntimeException(TRACE_INFO,
				"Expecting something to write from %s\n",
				cref->to_string().c_str());
	}

	// If it is not a stream, then just print and return.
	if (not content->is_type(STREAM_VALUE))
	{
		write_one(as, silent, content);
		return content;
	}

	// If it is a stream, enter infinite loop, until it is exhausted.
	LinkValuePtr lvp(LinkValueCast(content));
	while (true)
	{
		const ValueSeq& vals = lvp->value();

		// If the stream is returning an empty list, assume we
		// are done. Exit the loop.
		if (0 == vals.size()) break;

		// A different case arises if the stream keeps returning
		// empty LinkValues. This is kind of pathological, and
		// arguably, its a bug upstream somewhere, but for now,
		// we catch this and handle it.
		size_t nprinted = 0;
		for (const ValuePtr& v : vals)
		{
			if (v->is_type(LINK_VALUE) and 0 == v->size()) continue;
			write_one(as, silent, v);
			nprinted ++;
		}
		if (0 == nprinted) break;
	}
	return content;
}

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