OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / base / ClassServer.cc

199 lines · cc ref:
/*
 * opencog/atoms/base/ClassServer.cc
 *
 * Copyright (C) 2002-2007 Novamente LLC
 * Copyright (C) 2008 by OpenCog Foundation
 * Copyright (C) 2009,2017 Linas Vepstas <linasvepstas@gmail.com>
 * All Rights Reserved
 *
 * Written by Thiago Maia <thiago@vettatech.com>
 *            Andre Senna <senna@vettalabs.com>
 *            Gustavo Gama <gama@vettalabs.com>
 *            Linas Vepstas <linasvepstas@gmail.com>
 *
 * 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 "ClassServer.h"

#include <cassert>
#include <exception>

#include <opencog/atoms/atom_types/types.h>
#include <opencog/atoms/atom_types/atom_types.h>
#include <opencog/atoms/base/Atom.h>
#include <opencog/atoms/value/Value.h>

using namespace opencog;

ClassServer::ClassServer(const NameServer & nameServer):
	_nameServer(nameServer)
{}

/// ClassServer::splice -- install a factory for an atom type.
///
/// This inserts a factory `fact` for the atom type `t` into the
/// array `methods`.  The insertion is performed for not only `t`
/// but also for all of it's sub-types. The insertion for the
/// subtypes is done such that any existing factory is not clobbered
/// by the new one. This allows factories to be installed in any
/// order, and still have them mirror the correct type hierarchy.
///
template<typename T>
void ClassServer::splice(std::vector<T>& methods, Type t, T fact)
{
	// N.B. it is too late to synchronize calls with NameServer using
	// a shared mutex.  If registrations of class names interleave with
	// registration of factories, then the code will not work properly,
	// anyway. Before a factory is registered, the complete class
	// hierarchy must be known.
	
	std::unique_lock<std::mutex> l(factory_mutex);
	methods.resize(_nameServer.getNumberOfClasses());

	// Find all the factories that belong to parents of this type.
	std::set<T> ok_to_clobber;
	for (Type parent=0; parent < t; parent++)
	{
		if (_nameServer.isAncestor(parent, t) and methods[parent])
			ok_to_clobber.insert(methods[parent]);
	}

	// Set the factory for t and all children of its type. Be careful
	// not to clobber any factories that might have been previously
	// declared.
	for (Type chi=t; chi < _nameServer.getNumberOfClasses(); chi++)
	{
		if (not _nameServer.isDefined(t)) continue;
		if (_nameServer.isAncestor(t, chi) and
		    (nullptr == methods[chi] or
		     ok_to_clobber.end() != ok_to_clobber.find(methods[chi])))
		{
			methods[chi] = fact;
		}
	}
}

void ClassServer::addFactory(Type t, AtomFactory* fact)
{
	// This is called in shared-library ctors, and so if there's
	// an error, we can't throw an exception. (Well, we can, but
	// it will hang). So this is a fatal error. Print a helpful
	// message, and exit.
	if (0 == t)
	{
		fprintf(stderr,
			"Error: adding factory before type has been declared!\n"
			"Double check your shared lib link and load order!\n"
			"Make sure the type declaration library is loaded first!\n");
		fflush(stdout);
		assert(0);
	}

	splice(_atomFactory, t, fact);
}

void ClassServer::addValidator(Type t, Validator* checker)
{
	splice(_validator, t, checker);
}

/// update() -- just copy the parent's factory, if any.
template<typename T>
void ClassServer::update(std::vector<T>& methods, Type t)
{
	for (Type parent=0; parent < t; parent++)
	{
		if (_nameServer.isAncestor(parent, t) and methods[parent])
			methods[t] = methods[parent];
	}
}

/// update_factories() -- install inherited factories.
/// This is called by NameServer::endTypeDecls(void) whenever some
/// **other** shared library (not us!) adds some new atom types.
/// This will then install the inherited factories and checkers
/// for those types. They are, of course, welcome to add further
/// factories, after the base type declarations.  (Note that the
/// base type declarations will be running in the shared library
/// ctor, so this code is guaranteed to run before anything else.)
///
void ClassServer::update_factories()
{
	Type nfact = _atomFactory.size();
	if (0 == nfact) return;  // Do nothing for the base atom types.

	Type ntypes = _nameServer.getNumberOfClasses();

	std::unique_lock<std::mutex> l(factory_mutex);

	_atomFactory.resize(ntypes);
	_validator.resize(ntypes);

	for (Type t=nfact; t < ntypes; t++)
	{
		update(_atomFactory, t);
		update(_validator, t);
	}
}

ClassServer::AtomFactory* ClassServer::getFactory(Type t) const
{
	return t < _atomFactory.size() ? _atomFactory[t] : nullptr;
}

ClassServer::Validator* ClassServer::getValidator(Type t) const
{
	return t < _validator.size() ? _validator[t] : nullptr;
}

Handle ClassServer::factory(const Handle& h) const
{
	Handle result;

	// If there is a factory, then use it.
	AtomFactory* fact = getFactory(h->get_type());
	if (fact)
		result = (*fact)(h);
	else
		result = h;

	/* Look to see if we have static typechecking to do */
	Validator* checker =
		classserver().getValidator(result->get_type());

	/* Well, is it OK, or not? */
	if (checker and not checker(result))
		throw SyntaxException(TRACE_INFO,
			"Types fail to agree, invalid Atom syntax: %s",
			result->to_string().c_str());

	return result;
}

ClassServer& opencog::classserver()
{
	static std::unique_ptr<ClassServer> instance(new ClassServer(nameserver()));
	return *instance;
}

// Make sure that the classserver gets initialized when this
// shared lib gets loaded for the first time.
static __attribute__ ((constructor)) void classserver_init(void)
{
	opencog::classserver();
}