/* ** j###t ########## #### #### ** j###t ########## #### #### ** j###T "###L J###" ** ######P' ########## ######### ** ######k, ########## T######T ** ####~###L #### ** #### q###L ########## .##### ** #### \###L ########## #####" */ package key; import key.util.FilteredEnumeration; import java.util.Vector; import java.util.Enumeration; import java.util.NoSuchElementException; import java.io.*; /** * A specialised group that has 'implies' as * well as the ability to use a 'joined' * type effect for another scape. elements() * and contains() are not overridden, so * they don't always return the _true_ values * that they could. */ public class Rank extends Group implements Targetable { private static final long serialVersionUID = -2727379890374629964L; public static final AtomicElement[] ELEMENTS = { AtomicElement.construct( Rank.class, Implications.class, "implies", AtomicElement.PUBLIC_FIELD, "the inherited rank" ), AtomicElement.construct( Rank.class, Targets.class, "targets", AtomicElement.PUBLIC_FIELD, "the ranks that can be targetted" ), AtomicElement.construct( Rank.class, Scape.class, "joinedTo", AtomicElement.PUBLIC_FIELD, "a scape to link players from this group into as they come online" ) }; public static final AtomicStructure STRUCTURE = new AtomicStructure( Group.STRUCTURE, ELEMENTS ); public final Implications implies = (Implications) Factory.makeAtom( Implications.class, "implies" ); public final Targets targets = (Targets) Factory.makeAtom( Targets.class, "targets" ); private Vector ranksThatImplyUs = new Vector( 0, 5 ); /** * This property is deliberately not * added to the group. We don't * want it to be settable, but * we can do with the reference * routines to point it at things * and storing */ Reference joinedTo = Reference.EMPTY; public Rank() { } public AtomicStructure getDeclaredStructure() { return( STRUCTURE ); } //public void argument( String args ) //{ //joinedTo = Reference.to( args ); //} public void setJoinedTo( Scape s ) { joinedTo = s.getThis(); } public final Enumeration reverseImplications() { return( new FilteredEnumeration( ranksThatImplyUs.elements(), new ReferenceEnumeratorFilter( new ReferenceEnumeratorFilter.EnumeratedThing() { public void noSideEffectRemove( Reference r ) throws NonUniqueKeyException, java.util.NoSuchElementException,BadKeyException { Rank.this.ranksThatImplyUs.removeElement( r ); } } , true ) ) ); } /** * This is a notification routine that is called when a reference * container adds this atom to it. (A non-reference container * calls set_parent). The default implementation does nothing, * but some atoms (such as Rank or Player) may be interested. */ public void addSymbolicParent( Container c ) { super.addSymbolicParent( c ); if( c instanceof Implications ) ranksThatImplyUs.addElement( c.getThis() ); } /** * This is a notification routine that is called when a reference * container removes this atom from it. (A non-reference container * calls set_parent). The default implementation does nothing, * but some atoms (such as Rank or Player) may be interested. */ public void removeSymbolicParent( Container c ) { super.removeSymbolicParent( c ); if( c instanceof Implications ) { // it's okay to pass in a different reference, // the Vector class calls .equals to determine // equality in the Sun Java implementation. ranksThatImplyUs.removeElement( c.getThis() ); System.out.println( " " + getId() + " rank is no longer implied by: " + c.getId() ); } } protected void addInternal( Reference addedr ) throws BadKeyException, NonUniqueKeyException { if( shouldContain( addedr ) ) throw new RedundantException( "redundant add to rank " + getName() + " for atom " + addedr.getName() + " as it is already in a rank which implies us" ); // ultimately - if you're here, we want to remove you from any // groups that we imply. for( Enumeration e = getImplications(); e.hasMoreElements(); ) { Rank r = (Rank) e.nextElement(); r.strip( addedr ); } // this call (in group) will establish() the player super.addInternal( addedr ); Player p = (Player) addedr.get(); p.addReverseRank( this ); } protected void removeInternal( Reference removed ) throws NonUniqueKeyException,NoSuchElementException,BadKeyException { removeInternal( removed, null ); } /** * This routine removes the atom 'removed' from this rank, * ensure that it isn't in this rank in any way, shape, or * form. It will then re-add the player to any ranks that * this rank implies, except for rank s (which may be null). */ protected void removeInternal( Reference removed, Rank s ) throws NonUniqueKeyException,NoSuchElementException,BadKeyException { if( !removed.isValid() ) { noSideEffectRemove( removed ); return; } // we can skip a few cases since a Rank is always // a reference container if( !super.containsReference( removed ) ) { boolean didRemove = false; // this whole algorithm is a little convoluted, // as it skips around the hierarchy quite a bit. // // if it works, I suggest we just leave it as is. // remove them from the ranks which imply this // one, as well. - this is like 'exile // <name> member" when the person is a leader - // we want to remove leader as well. for( Enumeration e = reverseImplications(); e.hasMoreElements(); ) { Implications i = (Implications) e.nextElement(); Rank r = (Rank) i.getParent(); Log.debug( this, "removing '" + removed.getName() + "' from " + r.getName() + " because it implies " + getName() + " and we're removing from that" ); r.permissionList.check( removeFromAction ); r.removeInternal( removed, this ); didRemove = true; } if( !didRemove ) { if( s == null ) throw new NoSuchElementException( removed.getName() + " is not in " + getName() ); else return; } } else noSideEffectRemove( removed ); // remove you from the joinedTo group // order is important here - its possible that // one of the other groups (below) require that // you're in this same scape - they'll add you // back in, if this is the case, and we hardly // want you there twice. ;) Scape jTo = (Scape) joinedTo.get(); if( jTo != null ) jTo.unlinkPlayer( (Player) removed.get() ); if( super.containsReference( removed ) ) { throw new UnexpectedResult( "didn't manage to remove " + removed.getName() + " from " + getName() ); } // it isn't essential that we do this here, as the // Player class has enough intelligence to remove the // rank itself next time it is loaded if !rank.contains( player ) if( removed.isLoaded() ) ((Player)removed.get()).removeReverseRank( this ); // re-add the player to any groups we imply, except // to the rank which called us. for( Enumeration e = getImplications(); e.hasMoreElements(); ) { Rank r = (Rank) e.nextElement(); if( r != s ) { Log.debug( this, "adding '" + removed.getName() + "' to " + r.getName() + " because its implied by " + getName() + " and we're removing from that" ); if( !r.containsReference( removed ) ) r.addInternal( removed ); } else Log.debug( this, "NOT adding '" + removed.getName() + "' to " + r.getName() + " because, while its implied by " + getName() + " and we're removing from that, its the rank which called us." ); } } public boolean doesImply( Rank r ) { for( Enumeration e = getImplications(); e.hasMoreElements(); ) { Rank g = (Rank) e.nextElement(); if( r == g ) return( true ); else if( g.doesImply( r ) ) return( true ); } return( false ); } public boolean isOutRankedBy( Rank rank ) { return( rank.getTargets().contains( this ) ); } public Targets getTargets() { return( targets ); } public Enumeration getImplications() { return( implies.elements() ); } public Implications getImplies() { return( implies ); } public void establish( Player p ) throws BadKeyException,NonUniqueKeyException { if( containsPlayer( p ) ) return; super.establish( p ); for( Enumeration e = getImplications(); e.hasMoreElements(); ) { Group g = (Group) e.nextElement(); if( !g.containsPlayer( (Player) p ) ) { g.establish( p ); } } // add you to the joinedTo group // order is important here - its possible that // one of the other groups (above) removed you // from this scape - if we checked first, you // wouldn't be in it now ;) // // (the way I figure, anyway ;p) Scape jTo = (Scape) joinedTo.get(); if( jTo != null ) { if( !jTo.containsPlayer( p ) ) { jTo.linkPlayer( p ); } } } private void strip( Reference stripped ) { try { noSideEffectRemove( stripped ); } catch( BadKeyException e ) { } catch( NonUniqueKeyException e ) { } for( Enumeration e = getImplications(); e.hasMoreElements(); ) { Rank r = (Rank) e.nextElement(); r.strip( stripped ); } } public boolean containsAtAll( Reference p ) { if( containsReference( p ) ) return( true ); for( Enumeration e = getImplications(); e.hasMoreElements(); ) { Rank g = (Rank) e.nextElement(); if( g.containsAtAll( p ) ) return true; } return false; } public boolean shouldContain( Reference p ) { for( Enumeration e = reverseImplications(); e.hasMoreElements(); ) { Implications i = (Implications) e.nextElement(); if( i != null ) { if( i.containsReference( p ) ) return( true ); } } return( false ); } }