MODULE OSCRegistry;
IMPORT Strings, OSC, KernelLog;
CONST
ContainerInitalArraysize = 4;
rootadr = "/";
rootname = "<root>";
dumpident = " ";
OK* = 0;
BadClassUsed = 99;
ImplementatonError = 101;
Trace* = FALSE;
LogErrors* = TRUE;
TYPE
String* = Strings.String;
OSCMethodHandler = PROCEDURE { DELEGATE } (m: OSC.OSCMessage);
OSCMethodTree = OBJECT
VAR
fullname-: String;
name-: String;
PROCEDURE &InitOSCMethodTree*(fullname, name: String);
BEGIN
SELF.name := name;
SELF.fullname := fullname;
END InitOSCMethodTree;
PROCEDURE nextAddrPartIndex(s: String; pos: INTEGER; VAR nextpos: INTEGER );
BEGIN
ASSERT(s[pos] = '/');
nextpos := pos+1;
WHILE (s[nextpos] # '/') & (s[nextpos] # 0X) DO
INC(nextpos);
END;
END nextAddrPartIndex;
PROCEDURE dump(ident: INTEGER); BEGIN HALT(BadClassUsed); END dump;
END OSCMethodTree;
Childs = POINTER TO ARRAY OF OSCMethodTree;
OSCMethodContainer = OBJECT(OSCMethodTree)
VAR
size: INTEGER;
childs: Childs;
PROCEDURE &Init*(fullname, name: String);
BEGIN
InitOSCMethodTree(fullname, name);
size := 0;
NEW(childs, ContainerInitalArraysize)
END Init;
PROCEDURE runMessage(adr: String; pos: INTEGER; m: OSC.OSCMessage);
VAR
i: INTEGER;
name: String;
nextpos: INTEGER;
element: OSCMethodTree;
BEGIN
nextAddrPartIndex(adr, pos, nextpos);
name := Strings.Substring(pos+1, nextpos, adr^);
FOR i := 0 TO size-1 DO
IF match(name, childs[i].name) THEN
element := childs[i];
IF (adr[nextpos] = '/') & (element IS OSCMethodContainer) THEN
WITH element: OSCMethodContainer DO
element.runMessage(adr, nextpos, m);
END;
ELSIF (adr[nextpos] = 0X) & (element IS OSCMethod) THEN
WITH element: OSCMethod DO
element.runMessage(m);
END;
END;
END;
END;
END runMessage;
PROCEDURE deleteMethod(adr: String; pos: INTEGER): INTEGER;
VAR
element: OSCMethodTree;
name: String;
nextpos, elementpos: INTEGER;
count: INTEGER;
BEGIN
nextAddrPartIndex(adr, pos, nextpos);
name := Strings.Substring(pos+1, nextpos, adr^);
element := searchpos(name, elementpos);
IF element = NIL THEN
RETURN 0;
ELSIF (adr[nextpos] = 0X) & (element IS OSCMethod) THEN
childs[elementpos] := childs[size-1];
childs[size-1] := NIL;
DEC(size);
RETURN 1;
ELSIF (adr[nextpos] = '/') & (element IS OSCMethodContainer) THEN
WITH element: OSCMethodContainer DO
count := element.deleteMethod(adr, nextpos);
IF element.size = 0 THEN
childs[elementpos] := childs[size-1];
childs[size-1] := NIL;
DEC(size); INC(count);
END;
RETURN count;
END;
END;
HALT(ImplementatonError);
END deleteMethod;
PROCEDURE insertMethod(adr: String; pos: INTEGER; method: OSCMethodHandler): OSCMethod;
VAR
nextpos: INTEGER;
subaddr, name: String;
newcontainer: OSCMethodContainer;
newmethod: OSCMethod;
element: OSCMethodTree;
BEGIN
nextAddrPartIndex(adr, pos, nextpos);
IF nextpos = pos + 1 THEN RETURN NIL END;
name := Strings.Substring(pos+1, nextpos, adr^);
element := search(name);
IF element = NIL THEN
IF size = LEN(childs) THEN SELF.grow END;
IF adr[nextpos] = 0X THEN
NEW(newmethod, adr, name, method);
childs[size] := newmethod;
INC(size);
RETURN newmethod;
ELSE
subaddr := Strings.Substring(0, nextpos, adr^);
NEW(newcontainer, subaddr, name);
childs[size] := newcontainer;
INC(size);
RETURN newcontainer.insertMethod(adr, nextpos, method);
END;
ELSE
IF adr[nextpos] = 0X THEN
RETURN NIL
ELSE
IF element IS OSCMethodContainer THEN
WITH element: OSCMethodContainer DO
RETURN element.insertMethod(adr, nextpos, method);
END
ELSE
RETURN NIL;
END
END;
END;
RETURN NIL;
END insertMethod;
PROCEDURE search(name: String): OSCMethodTree;
VAR i: INTEGER;
BEGIN
RETURN searchpos(name, i);
END search;
PROCEDURE searchpos(name: String; VAR pos: INTEGER): OSCMethodTree;
VAR i: INTEGER;
BEGIN
FOR i := 0 TO SELF.size - 1 DO
IF Strings.Equal(name, childs[i].name) THEN
pos := i;
RETURN childs[i];
END;
END;
RETURN NIL;
END searchpos;
PROCEDURE grow;
VAR
newchilds: Childs;
i: INTEGER;
BEGIN
NEW(newchilds, LEN(childs)*2);
FOR i := 0 TO size-1 DO
newchilds[i] := childs[i];
END;
childs := newchilds;
END grow;
PROCEDURE matchcclass(name: String; namepos: INTEGER; pattern: String; patternpos: INTEGER): BOOLEAN;
VAR
n, p: INTEGER;
negated, result, done: BOOLEAN;
BEGIN
n := namepos; p := patternpos;
INC(p);
negated := FALSE;
IF pattern[p] = '!' THEN
negated := TRUE;
INC(p);
END;
done := FALSE;
WHILE (~done) &(pattern[p] # ']') DO
IF pattern[p] = 0X THEN
RETURN FALSE;
END;
IF (pattern[p+1] = '-') & (pattern[p+2] # 0X) THEN
IF (name[n] >= pattern[p]) & (name[n] <= pattern[p+2]) THEN
result := ~ negated;
done := TRUE;
END;
ELSIF name[n] = pattern[p] THEN
result := ~ negated;
done := TRUE;
END;
IF ~ done THEN INC(p); END;
END;
IF pattern[p] = ']' THEN
result := negated;
END;
IF ~ result THEN RETURN FALSE; END;
WHILE pattern[p] # ']' DO
IF pattern[p] = 0X THEN
RETURN FALSE;
END;
INC(p);
END;
RETURN matchpos(name, n+1, pattern, p+1);
END matchcclass;
PROCEDURE matchlist(name: String; namepos: INTEGER; pattern: String; patternpos: INTEGER): BOOLEAN;
VAR
restofpattern, p, n: INTEGER;
BEGIN
p := patternpos; n := namepos;
restofpattern := p;
WHILE pattern[restofpattern] # '}' DO
IF pattern[restofpattern] = 0X THEN
RETURN FALSE;
END;
INC(restofpattern);
END;
INC(restofpattern);
INC(p);
LOOP
IF pattern[p] = ',' THEN
IF matchpos(name, n, pattern, restofpattern) THEN
RETURN TRUE;
ELSE
n := namepos;
INC(p);
END;
ELSIF pattern[p] = '}' THEN
RETURN matchpos(name, n, pattern, restofpattern);
ELSIF pattern[p] = name[n] THEN
INC(p); INC(n);
ELSE
n := namepos;
WHILE (pattern[p] # ',' ) & (pattern[p] # '}') DO INC(p); END;
IF pattern[p] = ',' THEN INC(p); END;
END;
END;
HALT(99);
END matchlist;
PROCEDURE matchpos(name: String; namepos: INTEGER; pattern: String; patternpos: INTEGER): BOOLEAN;
VAR
p, n: INTEGER;
BEGIN
n := namepos;
p := patternpos;
ASSERT(name # NIL);
ASSERT(pattern # NIL);
IF pattern[p] = 0X THEN RETURN name[n] = 0X; END;
IF name[n] = 0X THEN
IF pattern[p] = '*' THEN
RETURN matchpos(name, n, pattern, p+1);
ELSE
RETURN FALSE;
END;
END;
CASE pattern[p] OF
'?': RETURN matchpos(name, n+1, pattern, p+1);
| '*':
IF matchpos(name, n, pattern, p+1) THEN
RETURN TRUE;
ELSE
RETURN matchpos(name, n+1, pattern, p);
END;
| ']':
IF LogErrors THEN
KernelLog.String("Unmatched ] in Pattern: "); KernelLog.String(pattern^);
KernelLog.String(" at position: "); KernelLog.Int(p, 3);
END;
RETURN FALSE;
| '}':
IF LogErrors THEN
KernelLog.String("Unmatched } in Pattern: "); KernelLog.String(pattern^);
KernelLog.String(" at position: "); KernelLog.Int(p, 3);
END;
RETURN FALSE;
| '[': RETURN matchcclass(name, n, pattern, p);
| '{': RETURN matchlist(name, n, pattern, p);
ELSE
IF name[n] = pattern[p] THEN
RETURN matchpos(name, n+1, pattern, p+1);
ELSE
RETURN FALSE;
END;
END;
HALT(ImplementatonError);
END matchpos;
PROCEDURE match(name, pattern: String): BOOLEAN;
BEGIN
RETURN matchpos(name, 0, pattern, 0);
END match;
PROCEDURE dump(ident: INTEGER);
VAR i: INTEGER;
BEGIN
FOR i := 0 TO ident DO KernelLog.String(dumpident); END;
KernelLog.String("Container: "); KernelLog.String(SELF.fullname^);
KernelLog.String("{ name: "); KernelLog.String(SELF.name^);
KernelLog.String(" size: "); KernelLog.Int(SELF.size, 5);
KernelLog.String(" LEN(childs): "); KernelLog.Int(LEN(childs), 5);
KernelLog.Ln;
FOR i := 0 TO size-1 DO childs[i].dump(ident+1); END;
FOR i := 0 TO ident DO KernelLog.String(dumpident); END;
KernelLog.String("}"); KernelLog.Ln
END dump;
END OSCMethodContainer;
OSCMethod = OBJECT(OSCMethodTree)
VAR
method: OSCMethodHandler;
PROCEDURE &InitMethod*(fullname, name: String; m: OSCMethodHandler);
BEGIN
InitOSCMethodTree(fullname, name);
SELF.method := m;
END InitMethod;
PROCEDURE runMessage(m: OSC.OSCMessage);
BEGIN
ASSERT(SELF.method # NIL);
SELF.method(m);
END runMessage;
PROCEDURE dump(ident: INTEGER);
VAR i: INTEGER;
BEGIN
FOR i := 0 TO ident DO KernelLog.String(dumpident); END;
KernelLog.String("Method: "); KernelLog.String(SELF.fullname^);
KernelLog.String("{ name: "); KernelLog.String(SELF.name^);
KernelLog.String(" }"); KernelLog.Ln;
END dump;
END OSCMethod;
TYPE OSCRegistry* = OBJECT
VAR
root: OSCMethodContainer;
PROCEDURE &Init*;
VAR
rootadr2, rootname2: String;
BEGIN
rootadr2 := Strings.NewString(rootadr);
rootname2 := Strings.NewString(rootname);
NEW(root, rootadr2, rootname2);
END Init;
PROCEDURE AddMethod*(adr: String; method: OSCMethodHandler);
VAR
insertedmethod: OSCMethod;
BEGIN
IF ~ OSC.CheckOSCAdr(adr) THEN RETURN END;
insertedmethod := root.insertMethod(adr, 0, method);
RETURN;
END AddMethod;
PROCEDURE RemoveMethod*(adr: String);
VAR
i: INTEGER;
BEGIN
i := root.deleteMethod(adr, 0);
END RemoveMethod;
PROCEDURE Run*(m: OSC.OSCMessage);
BEGIN
root.runMessage(m.address, 0, m);
END Run;
PROCEDURE DumpRegistry*;
VAR
ident: INTEGER;
BEGIN
ident := 0;
KernelLog.String("Dump of registry starts:"); KernelLog.Ln;
root.dump(ident);
KernelLog.String("Dump of registry done"); KernelLog.Ln;
END DumpRegistry;
END OSCRegistry;
PROCEDURE Test*;
VAR
reg: OSCRegistry;
adr: String;
BEGIN
KernelLog.String("Running test:");
adr := Strings.NewString("/containerA/containerA1/methodM1");
NEW(reg);
reg.AddMethod(adr, DummyHandler);
adr := Strings.NewString("/containerA/methodM2");
reg.AddMethod(adr, DummyHandler);
adr := Strings.NewString("/containerA/containerA1/methodM1");
reg.AddMethod(adr, DummyHandler);
reg.AddMethod(Strings.NewString("/containerA/methodM3"), DummyHandler);
reg.AddMethod(Strings.NewString("/containerA/methodM4"), DummyHandler);
reg.AddMethod(Strings.NewString("/containerA/methodM5"), DummyHandler);
reg.AddMethod(Strings.NewString("/containerA/methodM6"), DummyHandler);
reg.DumpRegistry;
reg.RemoveMethod(Strings.NewString("/containerA/methodM2"));
reg.RemoveMethod(Strings.NewString("/containerA/containerA1/methodM1"));
reg.RemoveMethod(Strings.NewString("/containerB"));
reg.DumpRegistry;
KernelLog.String(" done"); KernelLog.Ln;
END Test;
PROCEDURE TestMatcher*;
VAR
container: OSCMethodContainer;
BEGIN
NEW(container, NIL, NIL);
KernelLog.Boolean(container.match(Strings.NewString('abcd'), Strings.NewString('abcd'))); KernelLog.Ln;
KernelLog.Boolean(container.match(Strings.NewString('abcde'), Strings.NewString('abcd'))); KernelLog.Ln;
KernelLog.Boolean(container.match(Strings.NewString('abcd'), Strings.NewString('abcd?'))); KernelLog.Ln;
KernelLog.Boolean(container.match(Strings.NewString('abcd'), Strings.NewString('ab?d'))); KernelLog.Ln;
KernelLog.Boolean(container.match(Strings.NewString('abcd'), Strings.NewString('ab*'))); KernelLog.Ln;
KernelLog.Boolean(container.match(Strings.NewString('abcd'), Strings.NewString('a*d'))); KernelLog.Ln;
KernelLog.Boolean(container.match(Strings.NewString('abcd'), Strings.NewString('a[a-d]cd'))); KernelLog.Ln;
KernelLog.Boolean(container.match(Strings.NewString('abcd'), Strings.NewString('a*[b]*{xx,yy,cd,aa}*'))); KernelLog.Ln;
END TestMatcher;
PROCEDURE DummyHandler*(m: OSC.OSCMessage);
BEGIN
IF Trace THEN
KernelLog.String("Called dummy handler with message: "); KernelLog.Ln;
m.dump(0);
END;
END DummyHandler;
END OSCRegistry.
OSCRegistry.Test ~
OSCRegistry.TestMatcher ~