MODULE XMLTransformer;	(** AUTHOR "Simon L. Keel"; PURPOSE "transforming XML to BB-text"; *)

IMPORT
	Strings, XML, XMLObjects, Commands, Files, Modules;

TYPE
	String = Strings.String;

	XMLPluginProcInfo = POINTER TO RECORD
		name, procedure : ARRAY 128 OF CHAR;
		next : XMLPluginProcInfo;
	END;

	TransformProc = PROCEDURE (element : XML.Element) : XML.Container;

VAR
	xmlPluginProcs : XMLPluginProcInfo;

PROCEDURE Transform*(elem : XML.Element) : XML.Container;
VAR
	transformProc : TransformProc;
	container : XML.Container;
	s, name : String;
BEGIN
	s := elem.GetName();
	name := Strings.UpperCaseInNew(s^);
	transformProc := FindProcedure(name^);
	IF transformProc # NIL THEN
		RETURN transformProc(elem);
	ELSE
		NEW(container);
		RETURN container;
	END;
END Transform;

PROCEDURE AddContentsOf*(source, target : XML.Container);
VAR
	enum: XMLObjects.Enumerator;
	p : ANY;
BEGIN
	enum := source.GetContents();
	WHILE (enum.HasMoreElements()) DO
		p := enum.GetNext();
		target.AddContent(p(XML.Content));
	END;
END AddContentsOf;

PROCEDURE TransformElemsIn*(container : XML.Container) : XML.Container;
VAR
	c : XML.Container;
	enum: XMLObjects.Enumerator;
	p : ANY;
BEGIN
	NEW(c);
	enum := container.GetContents();
	WHILE (enum.HasMoreElements()) DO
		p := enum.GetNext();
		IF p IS XML.Element THEN
			AddContentsOf(Transform(p(XML.Element)), c);
		END;
	END;
	RETURN c;
END TransformElemsIn;

PROCEDURE GetNewParagraph*(style : ARRAY OF CHAR) : XML.Element;
VAR
	paragraph : XML.Element;
	styleAttrPar : XML.Attribute;
	s : String;
BEGIN
	NEW(paragraph); paragraph.SetName("Paragraph");
	NEW(styleAttrPar); s := Strings.NewString("style"); styleAttrPar.SetName(s^);
	styleAttrPar.SetValue(style);
	paragraph.AddAttribute(styleAttrPar);
	RETURN paragraph;
END GetNewParagraph;

PROCEDURE GetNewSpan*(style : ARRAY OF CHAR) : XML.Element;
VAR
	span : XML.Element;
	styleAttrPar : XML.Attribute;
	s : String;
BEGIN
	NEW(span); span.SetName("Span");
	NEW(styleAttrPar); s := Strings.NewString("style"); styleAttrPar.SetName(s^);
	styleAttrPar.SetValue(style);
	span.AddAttribute(styleAttrPar);
	RETURN span;
END GetNewSpan;

PROCEDURE FindProcedure(name : ARRAY OF CHAR) : TransformProc;
VAR
	transformProc : TransformProc;
	moduleName, procedureName : Modules.Name; msg : ARRAY 32 OF CHAR; res : LONGINT;
	cur : XMLPluginProcInfo;
BEGIN {EXCLUSIVE}
	Strings.Delete(name, 0, 3);
	cur := xmlPluginProcs;
	WHILE cur # NIL DO
		IF cur.name = name THEN
			Commands.Split(cur.procedure, moduleName, procedureName, res, msg);
			IF (res = Commands.Ok) THEN
				GETPROCEDURE(moduleName, procedureName, transformProc);
				RETURN transformProc;
			END;
		END;
		cur := cur.next
	END;
	RETURN NIL
END FindProcedure;

PROCEDURE Register*(context : Commands.Context);
VAR c : XMLPluginProcInfo;
BEGIN {EXCLUSIVE}
	NEW(c);
	context.arg.SkipWhitespace; context.arg.String(c.name);
	context.arg.SkipWhitespace; context.arg.String(c.procedure);
	c.next := xmlPluginProcs; xmlPluginProcs := c;
	StoreXMLPlugins;
END Register;

PROCEDURE StoreXMLPlugins;
VAR f : Files.File;
	w : Files.Writer;
	cur : XMLPluginProcInfo;
	res : LONGINT;
	n0, n1 : ARRAY 64 OF CHAR;
BEGIN
	n0 := "XMLPluginConfig.dat"; n1 := "XMLPluginConfig.dat.Bak";
	Files.Rename(n0, n1, res);
	f := Files.New("XMLPluginConfig.dat");
	Files.OpenWriter(w, f, 0);
	Files.Register(f);
	cur := xmlPluginProcs;
	WHILE cur # NIL DO
		w.Char('"'); w.String(cur.name); w.Char('"'); w.Char(09X);
		w.Char('"'); w.String(cur.procedure); w.Char('"'); w.Ln;
 		cur := cur.next
	END;
	w.Update
END StoreXMLPlugins;

PROCEDURE LoadXMLPlugins;
VAR f : Files.File;
	r : Files.Reader;
	c : XMLPluginProcInfo;
BEGIN
	f := Files.Old("XMLPluginConfig.dat");
	IF f # NIL THEN
		Files.OpenReader(r, f, 0);
		WHILE r.res = 0 DO
			NEW(c);
			r.String(c.name); r.SkipWhitespace;
			r.String(c.procedure);
			IF r.res = 0 THEN c.next := xmlPluginProcs; xmlPluginProcs:= c END;
			r.SkipLn
		END
	END
END LoadXMLPlugins;

BEGIN
	LoadXMLPlugins;
END XMLTransformer.