IMPLEMENTATION MODULE RegistrationTable;

IMPORT SYSTEM;

  -------------------------
  -- The RegistrationTable class is meant to be used
  -- as a base class for ONCE classes which would be used
  -- to keep a track of various instances of Element which
  -- one whishes to keep accessible from the garbage collector.
  --
  -- This is a typical case of data structures which ought to be
  -- killed explicitely, such as curses window or motif widgets,
  -- without letting the garbage collector take full responsability
  -- of deallocating them. By entering them in a RegistrationTable,
  -- one can be sure that it cannot be deallocated by the garbage 
  -- collector, even if it is not accessible anymore through the 
  -- common variables, locals and attributes of the system.
  -------------------------

  CLASS RegistrationTable(Element);
    VAR
      Arr: ARRAY OF Element;
      
    CONST
      MinSize = 32;
      
    REDEFINE METHOD CREATE(Size: INTEGER);
      BEGIN
      Arr.CREATE (SYSTEM.MAX(MinSize, Size));
      END CREATE;
      
    METHOD Resize (NewSize: INTEGER);
      VAR
        Add: ARRAY OF Element;
        NSize, j: INTEGER;
      BEGIN
      NSize := SYSTEM.MAX (MinSize, NewSize);
      IF NSize > Arr.SIZE THEN
        Add.CREATE (NSize - Arr.SIZE);
        Arr := Arr + Add;              
       ELSIF (NSize < Arr.SIZE) AND (NSize <= Used)  THEN
        Add.CREATE (NSize);        
        FOR i := 0 TO Arr.SIZE - 1 DO
          IF Arr[i] <> VOID THEN
            Add[j] := Arr[i];
            j := j + 1;
            END;
          END;
        ASSERT j = Add.SIZE;
        Arr := Add;
        END;
      END Resize;
      
    METHOD Pos (Value: Element): INTEGER;
      BEGIN
      RESULT := -1;
      FOR i := 0 TO Arr.SIZE - 1 WHILE (RESULT < 0) DO
        IF Arr[i] = Value THEN
          RESULT := i;
          END;
        END;
      END Pos;
      
    METHOD Enter (El: Element);
      VAR
        i: INTEGER;
      BEGIN
      ASSERT El <> VOID;
      IF NOT Exist(El) THEN
        i := Pos(VOID);
        ASSERT i >= 0;
        Arr[i] := El;
        END;
      END Enter;
      
    METHOD Exist(El: Element): BOOLEAN;
      BEGIN
      RESULT := Pos(El) >= 0;
      END Exist;
      
    METHOD Remove(El: Element);
      VAR
        i: INTEGER;
      BEGIN
      i := Pos(El);
      IF i > 0 THEN
        Arr[i] := VOID;
        END;
      END Remove;
      
    METHOD Used: INTEGER;
      BEGIN
      FOR i := 0 TO Arr.SIZE - 1 DO
        IF Arr[i] <> VOID THEN
          RESULT := RESULT + 1;
          END;
        END;
      END Used;
      
  END RegistrationTable;

END RegistrationTable;
