view src/samer/core_/Environment.java @ 8:5e3cbbf173aa tip

Reorganise some more
author samer
date Fri, 05 Apr 2019 22:41:58 +0100
parents bf79fb79ee13
children
line wrap: on
line source
/*
 *	Environment.java
 *
 *	Copyright (c) 2001, Samer Abdallah, King's College London.
 *	All rights reserved.
 *
 *	This software is provided AS IS and WITHOUT ANY WARRANTY;
 *	without even the implied warranty of MERCHANTABILITY or
 *	FITNESS FOR A PARTICULAR PURPOSE.
 */

package samer.core;

/**
	An Environment is a way of managing named values.
	The names form a heirarchical system using dot (.) as
	a seperator. (See Node class)

	Each Environment has a parent Environment, except for the
	singleton top level Environment.

	Each Environment has a Node which serves as a 'current
	directory'. Environments form a chain: if one
	Environment cannot satisfy a request for a named
	binding, it should ask its parent.

	@see samer.core.Node
	@see samer.core.X
 */

public class Environment
{
	protected Environment	parent;
	protected Node			base;

	/** Construct a new Environment with the given parent
		and base node */
	public Environment(Environment parent, Node base) { this.parent=parent; this.base=base; }

	/** Construct a new Environment with the given parent
		and a base node relative to the parent Environment.
	  	That is, <code>node = new Node(name, parent.node());</code>
	*/
	public Environment(Environment parent, String name) {
		this(parent,new Node(name,parent.base));
	}

	/** Return this environment's parent, or null of top level */
	public final Environment parent() { return parent; }

	/** Return this environment's associated node */
	public final Node node() { return base; }

	/*
	 *	these methods form the main implementation of the Environment and
	 * are meant to be overridden by subclasses.
	 */

	 /** Add and return a new named binding */
	public Binding	add(String nm, Object o)      { return parent.add(abs(nm),o); }

	 /** Add anonymous datum */
	public void		add(Object obj)               { parent.add(obj); }

	public void		store(String nm, Autocoder o) { parent.store(abs(nm),o); }
	public void		store(String nm, Object o, Codec c) { parent.store(abs(nm),o,c); }

	/** Return next anonymous datum */
	public Datum	datum()            { return parent.datum(); }

	/** Return Datum that best matches given name */
	public Datum	datum(String name) { return parent.datum(abs(name)); }

	/** Return Binding that best matches given name */
	public Binding	binding(String name) { return parent.binding(abs(name)); }

	/** Return an Iterator that will step through all data in this
		Environment and it's ancestors */
	public Iterator data() { return parent.data(); }

	// ... Interfaces ....................................................

	/**
		Basic interface for getting at a particular stored object or
		value.
		There is no concept of returning the value as-is: it must
		be translated in to an object of some desired class.
		Value can be extracted in one of two ways:
		a Codec, which is an object which knows how to convert
		between stored data and some desired class of object.
		The other way is via an Autocoder, which is basically
		an object with it's own Codec built in.
	*/

	public interface Datum {

		/** the stored name of this Datum */
		String	name();

		/** goodness of name matching procedure used to find this Datum */
		int		score();

//		Object	get() throws Exception;					// return intrinsic class ??
//		Object	get(Codec c) throws Exception; 		// no default supplied

		/** return object as decoded by Codec c. If this Datum is a
			null Datum, ie contains no data, return def */
		Object	get(Codec c, Object def); // ?? copy semantics?	 exception?

		/** decode value in to Autocoder obj */
		void get(Autocoder obj) throws Exception;

	}

	/**
		A Binding is a Datum in which the value is an accessible Object,
		ie one that doesn't need decoding. Unlike a Datum, this value
		can be set or removed from the Environment.
	*/
	public interface Binding extends Datum {
		Object	get() throws Exception;
		void		set(Object obj);
		void		remove();
	}

	/**
		A null Binding that has no data.
		All attempts to read value throw an Exception, except get()
		with a default supplied.
		*/
	public final static Binding Null = new Binding() {
		public String	name()  { return "null"; }
		public int		score() { return 1000000000; } // Mr. Billion

		public Object	get() throws Exception { throw new Exception(); }
		public Object	get(Codec c, Object def) { return def==null ? def : c.decode(def); } // ??
		// public void		get(Autocoder obj) throws Exception { throw new Exception(); }
		public void		get(Autocoder obj) {}

		public void		set(Object obj) { throw new RuntimeException(); }
		public void		remove() { throw new RuntimeException(); }
	};

	/** This is an object which knows how to code and decode itself. */
	public interface Autocoder
	{
		/** return this object as a string */
		String string(); 		// as string

		/** return this object as any object of a convenient class.
			Strictly speaking, should return an <i>immutable</i>
			representation of the object.
		*/
		Object object();

		/** set this object by decoding supplied object, which may be
			a string. This method can be as smart as you like, behaving
			differently for different classes of supplied object.
		*/
		void   decode(Object o);
	}

	/** This is an object which knows how to code and decode on behalf
		of some other class which represents the actual data. */
	public interface Codec {
		/** return Class of object that this Codec deals with */
		Class  targetClass();

		// methods for encoding object for storage
		String string(Object o); // convert object to string
		Object object(Object o); // convert object to another object
		Object decode(Object o); // convert other object to target class
	}

	/** For iterating through data in environment,
		returning a Datum for each item
	*/
	public interface Iterator {
		boolean	hasNext();
		Datum		next();
	}

	/** Steps through two iterators consecutively */
	protected static class CompoundIterator implements Iterator {
		Iterator i1, i2;
		public CompoundIterator(Iterator a, Iterator b) { i1=a; i2=b; }
		public boolean hasNext() {
			if (i1.hasNext()) return true;
			if (i2==null) return false;
			i1=i2; i2=null; return i1.hasNext();
		}
		public Datum next() {
			if (i1.hasNext())	return i1.next();
			i1=i2; i2=null; return i1.next();
		}
	}

	// ...................................................................

	/** Return absolute node path for given name. If name begins
		with '.', then it is already an absolute name. Otherwise, the
		base node for this Environment is prepended.
	*/
	public final String abs(String name) {
		return Node.isAbsolute(name) ? name : base.fullNameFor(name);
	}

	/** Return node name relative to this Environment's node.
		Throws an Exception if the supplied name is absolute and
		NOT a child of this Environment's node.
	*/
	public final String rel(String name) {
		if (!Node.isAbsolute(name)) return name;
		String fullbase=base.fullName();
		if (!name.startsWith(fullbase)) throw new Error("nonlocal node name:" + name);
		return name.substring(fullbase.length()+1); // miss out dot!
	}

	/** Returns true if the given name is a subnode of this environment's node,
		ie, if it can belong in this environment
		*/
	protected boolean belongs(String name) { return base.isSubnode(name); }
	
	/** Return a top level environment */
	public  static Environment top() { 
		return new Environment(null,Node.root()) {
			public Binding	add(String n, Object o) { throw error(); }
			public void		add(Object o)				{ throw error(); }
			public void		store(String nm, Object o, Codec c) { throw error(); }
			public void		store(String nm, Autocoder o)       { throw error(); }
	
			public Datum	datum()            { return Null; }
			public Datum	datum(String name) { return Null; }
			public Binding binding(String name) { return Null; }
	
			public Iterator data() {
				return new Iterator() {
					public boolean	hasNext() { return false; }
					public Datum	next() { return Null; }
				};
			}
	
			RuntimeException error() { return new RuntimeException("Environment.top"); }
		};													 
	}
}