/* ** j###t ########## #### #### ** j###t ########## #### #### ** j###T "###L J###" ** ######P' ########## ######### ** ######k, ########## T######T ** ####~###L #### ** #### q###L ########## .##### ** #### \###L ########## #####" ** ** $Id$ ** ** Class History ** ** Date Name Description ** ---------|------------|----------------------------------------------- ** 19Aug98 subtle start of recorded history ** */ package key; import key.util.SeperatedIdentifier; import java.io.*; import java.util.StringTokenizer; /** * A reference is an immutable pointer to an object, stored in a * manner that allows it to be maintained through swaps of the * referenced atom back to disk, as well as system resets. */ public abstract class Reference implements Serializable, Searchable, Symbol, Cloneable, key.io.Replaceable { private static final long serialVersionUID = -1279870814310783156L; public static final Reference to( Atom a ) { return( new NamedDirectReference( a ) ); } public Object clone() { try { return( super.clone() ); } catch( CloneNotSupportedException e ) { throw new UnexpectedResult( e.toString() + " in reference::clone()" ); } } public static final Reference to( String id, Atom context ) { if( id.equals( "$" ) ) return( Reference.EMPTY ); Search s = new Search( id, context ); if( s.dirty || s.result == null ) return( new IndirectReference( id ) ); else return( ((Atom)s.result).getThis() ); } public static final Reference to( String id ) { if( id.equals( "$" ) ) return( Reference.EMPTY ); Search s = new Search( id, Key.instance() ); if( s.dirty || s.result == null ) return( new IndirectReference( id ) ); else return( ((Atom)s.result).getThis() ); } public static final Reference to( Atom a, boolean named ) { if( named ) return( new NamedDirectReference( a ) ); else return( new DirectReference( a ) ); } /** * @param id the full path (leading with a '/' or '~') of the reference * @param named true if the name of the atom should be stored in the * reference * @param noDirect true if a indirect reference is required - no lookup * translation should be performed. This is useful if * a property can change or move and we still wish to * point to it. This is the equivalent of a unix softlink. */ public static final Reference to( String id, boolean named, boolean noDirect ) { if( id.equals( "$" ) ) return( Reference.EMPTY ); if( noDirect ) return( new IndirectReference( id ) ); Search s = new Search( id, Key.instance() ); if( s.dirty || (s.result == null) ) return( new IndirectReference( id ) ); else return( new DirectReference( (Atom)s.result ) ); } public abstract Atom get(); public abstract Atom getType( Class c ); /** * @return true iff these two references point to the same * atom. */ public abstract boolean equals( Object o ); public int hashCode() { return( get().hashCode() ); } /** * @return true iff this reference points to this atom */ public abstract boolean contains( Atom a ); /** * @return false if this atom is definately not in memory. May * return true if the atom is not in memory, particularly * if this is a symbol reference. Use this routine as a * guide, not as a definitive result. should work for * getThis() references, though. */ public abstract boolean isLoaded(); public int getIndex() { return( get().index ); } public int getTimestamp() { return( get().timestamp ); } public String getName() { return( "" ); } public void search( Search s ) throws InvalidSearchException { s.result = get(); } public Object getKey() { return( this ); } public void setKey( Object k ) { throw new UnexpectedResult( "setKey::called on Reference" ); } /** * Return false if this reference can be replaced by EMPTY */ public boolean isValid() { return( true ); } public Object getReplacement() { return( this ); } public static final EmptyReference EMPTY = new EmptyReference(); } class EmptyReference extends Reference { private static final long serialVersionUID = -5345429757003014274L; public EmptyReference() { } public Object getReplacement() { return( Reference.EMPTY ); } public Object clone() { return( Reference.EMPTY ); } public int hashCode() { return( 0 ); } public boolean isLoaded() { return false; } public boolean equals( Object o ) { return false; } public boolean contains( Atom a ) { return false; } public Atom get() { return( null ); } public Atom getType( Class c ) { return( null ); } public boolean isValid() { return( false ); } } /** * A reference that is stored with a string id that is looked up * at each get() operation. * * This is useful for references that need to travel through transit * atoms (references that need to change). */ class IndirectReference extends Reference implements Symbol { String id; public IndirectReference( String path ) { id = path; } public Atom get() { Search s = new Search( id, Key.instance() ); return( (Atom) s.result ); } public Atom getType( Class c ) { Atom a = get(); if( a == null || c.isInstance( a ) ) return( a ); else return( null ); } public int hashCode() { return( id.hashCode() ); } public Object getKey() { return( new SeperatedIdentifier( id ).id ); } public String getName() { return( (String) getKey() ); } public void setKey( Object k ) { SeperatedIdentifier si = new SeperatedIdentifier( id ); StringBuffer sb = new StringBuffer( si.location ); if( si.property ) sb.append( '.' ); else sb.append( '/' ); sb.append( k.toString() ); id = sb.toString(); } public boolean equals( Object o ) { if( o instanceof IndirectReference ) return( id.equalsIgnoreCase( ((IndirectReference)o).id ) ); else if( o instanceof Reference ) return( get().equals( ((Reference)o).get() ) ); else return( false ); } public boolean contains( Atom a ) { return( get() == a ); } public boolean isLoaded() { return( true ); } } /** * A reference that stores the integer index of this atom in the * system registry. This allows faster lookups than the string * reference, but does not permit changing references to be stored. * * (Such as those through a transit symbol match, such as #me) */ class DirectReference extends Reference { private static final long serialVersionUID = -1786646569320699878L; final int index; final int timestamp; public DirectReference( int registry_index, int registry_timestamp ) { index = registry_index; timestamp = registry_timestamp; } public DirectReference( Atom a ) { index = a.index; timestamp = a.timestamp; } public Atom get() { return( Registry.instance.get( index, timestamp ) ); } public int getIndex() { return( index ); } public int getTimestamp() { return( timestamp ); } public Atom getType( Class c ) { try { Atom a = get(); if( a == null || c.isInstance( a ) ) return( a ); else return( null ); } catch( OutOfDateReferenceException e ) { return( null ); } } public int hashCode() { return( timestamp ); } public boolean isValid() { return( Registry.instance.isReferenceValid( index, timestamp ) ); } public Object getReplacement() { try { Atom a = Registry.instance.getIfInDatabase( index, timestamp ); if( a != null ) return( a.getThis() ); else return( this ); } catch( OutOfDateReferenceException e ) { Log.debug( this, "replacing reference " + toString() + " with empty" ); return( Reference.EMPTY ); } } public boolean equals( Object o ) { if( o instanceof DirectReference ) { DirectReference dr = (DirectReference) o; return( (index == dr.index) && (timestamp == dr.timestamp) ); } else if( o instanceof Reference ) { Atom a = ((Reference)o).get(); if( a != null ) return( (a.index == index) && (a.timestamp == timestamp) ); else return( false ); } else return( false ); } public boolean contains( Atom a ) { return( (a.index == index) && (a.timestamp == timestamp) ); } public boolean isLoaded() { return( Registry.instance.indexLoaded( index, timestamp ) ); } } class NamedDirectReference extends DirectReference implements Symbol { private static final long serialVersionUID = 6458100074905379530L; Object key; public NamedDirectReference( Atom a ) { super( a.index, a.timestamp ); key = a.getKey(); } private NamedDirectReference( int index, int timestamp, Object key ) { super( index, timestamp ); this.key = key; } public Object getKey() { return( key ); } public void setKey( Object k ) { key = k; } public String getName() { Object o = getKey(); if( o instanceof String ) return( (String) o ); else return( o.toString() ); } public Object getReplacement() { Reference r = (Reference) super.getReplacement(); if ( (r != this) && (r instanceof NamedDirectReference) && (key.equals( ((NamedDirectReference)r).key )) ) { return( r ); } return( this ); } public String toString() { return( "NamedDirectReference to #" + index + ", " + key.toString() ); } }