Mercurial > hg > jslab
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"); } }; } }