PersistentObjects.Mod 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179
  1. MODULE PersistentObjects; (** AUTHOR "fof"; PURPOSE "objects that can be stored with a generic reader / writer"; *)
  2. IMPORT XML, XMLParser, XMLScanner, Basic := FoxBasic, Strings, StringPool, Streams, Commands, FoxBasic, Files, XMLObjects, Modules, D:= Debugging;
  3. CONST
  4. Persistent = 0;
  5. None* = -1; (* no index *)
  6. EnableTrace = FALSE;
  7. TYPE
  8. (** the translation object is used to translate enumeration values to integers (and reverse) *)
  9. Translation* = OBJECT
  10. TYPE
  11. Entry = RECORD name: ARRAY 32 OF CHAR; key: LONGINT END;
  12. Table= POINTER TO ARRAY OF Entry;
  13. VAR
  14. table: Table;
  15. len: LONGINT;
  16. PROCEDURE & Init*;
  17. BEGIN len := 0; NEW(table,4);
  18. END Init;
  19. PROCEDURE Grow;
  20. VAR i: LONGINT; new: Table;
  21. BEGIN
  22. NEW(new, 2*LEN(table));
  23. FOR i := 0 TO LEN(table)-1 DO new[i] := table[i] END;
  24. table := new
  25. END Grow;
  26. PROCEDURE Add*(CONST name: ARRAY OF CHAR; key: LONGINT);
  27. VAR i: LONGINT;
  28. BEGIN
  29. IF len = LEN(table) THEN Grow END;
  30. COPY(name, table[len].name); table[len].key := key;
  31. INC(len);
  32. END Add;
  33. PROCEDURE Key*(CONST name: ARRAY OF CHAR; VAR key: LONGINT): BOOLEAN;
  34. VAR i: LONGINT;
  35. BEGIN
  36. FOR i := 0 TO len-1 DO
  37. IF table[i].name = name THEN key := table[i].key; RETURN TRUE END;
  38. END;
  39. RETURN FALSE
  40. END Key;
  41. PROCEDURE Name*(index: SIZE; VAR name: ARRAY OF CHAR): BOOLEAN;
  42. VAR i: LONGINT;
  43. BEGIN
  44. FOR i := 0 TO len-1 DO
  45. IF table[i].key = index THEN COPY(table[i].name,name); RETURN TRUE END;
  46. END;
  47. HALT(100);
  48. END Name;
  49. END Translation;
  50. Action*=PROCEDURE {DELEGATE} (o: Object);
  51. Class* = ENUM Char*,Object*, String*, Integer*, Float*, Boolean*, Enum*, Name*, Range*, Set* END;
  52. Name= ARRAY 128 OF CHAR;
  53. Content*= OBJECT
  54. VAR
  55. class*: Class;
  56. name*, type*: Name;
  57. string*: Strings.String;
  58. persistent*: BOOLEAN;
  59. object*: Object;
  60. char*: CHAR;
  61. integer*: HUGEINT;
  62. float*: LONGREAL;
  63. boolean*: BOOLEAN;
  64. translation*: Translation;
  65. range*: RANGE;
  66. set*: SET;
  67. success*: BOOLEAN;
  68. PROCEDURE SetClass*(class: Class; persistent: BOOLEAN);
  69. BEGIN
  70. SELF.class := class; SELF.persistent := persistent
  71. END SetClass;
  72. PROCEDURE GetChar*(VAR char: CHAR);
  73. BEGIN
  74. IF SELF.class = Class.Char THEN char := SELF.char; success := TRUE ELSE HALT(200) END;
  75. END GetChar;
  76. PROCEDURE SetChar*(char: CHAR);
  77. BEGIN
  78. SELF.class := Class.Char; SELF.char := char;success := TRUE; persistent := TRUE;
  79. END SetChar;
  80. PROCEDURE GetString*(VAR string: Strings.String);
  81. BEGIN
  82. IF SELF.class = Class.String THEN string := SELF.string; success := TRUE ELSE HALT(200) END;
  83. END GetString;
  84. PROCEDURE SetString*(string: Strings.String);
  85. BEGIN
  86. SELF.class := Class.String; SELF.string := string;success := TRUE; persistent := TRUE;
  87. END SetString;
  88. PROCEDURE SetAsString*(CONST s: ARRAY OF CHAR);
  89. VAR split: Strings.StringArray; first, last, step: LONGINT; int: LONGINT;
  90. BEGIN
  91. CASE class OF
  92. Class.String: string := Strings.NewString(s)
  93. |Class.Name: COPY(s, name);
  94. |Class.Boolean: boolean := (s="true") OR (s="1") OR (s="yes") OR (s="TRUE");
  95. |Class.Integer: Strings.StrToInt(s, int); integer := int;
  96. |Class.Float: Strings.StrToFloat(s, float);
  97. |Class.Enum: Strings.StrToInt(s, int); integer := int;
  98. |Class.Range:
  99. split := Strings.Split(s, ":");
  100. Strings.StrToInt(split[0]^, first);
  101. IF (LEN(split) > 1) & (split[1]^ # "") THEN
  102. Strings.StrToInt(split[1]^, last)
  103. ELSE
  104. last := MAX(LONGINT)
  105. END;
  106. IF (LEN(split) >2) & (split[2]^ # "") THEN
  107. Strings.StrToInt(split[2]^, step)
  108. ELSE
  109. step := 1
  110. END;
  111. range := first .. last BY step;
  112. |Class.Set:
  113. Strings.StrToSet(s, set);
  114. ELSE HALT(100)
  115. END;
  116. END SetAsString;
  117. PROCEDURE Equals*(CONST s: ARRAY OF CHAR): BOOLEAN;
  118. VAR int: LONGINT; flt: LONGREAL; st: SET; split: Strings.StringArray; first, last, step: LONGINT;
  119. BEGIN
  120. CASE class OF
  121. Class.String: RETURN (string # NIL) & (string^ = s)
  122. |Class.Name: RETURN (s = name)
  123. |Class.Boolean: RETURN boolean = (s="true") OR (s="1") OR (s="yes") OR (s="TRUE");
  124. |Class.Integer: Strings.StrToInt(s, int); RETURN integer = int
  125. |Class.Float: Strings.StrToFloat(s, flt); RETURN float = flt
  126. |Class.Enum: Strings.StrToInt(s, int); RETURN integer = int
  127. |Class.Range:
  128. split := Strings.Split(s, ":");
  129. Strings.StrToInt(split[0]^, first);
  130. IF (LEN(split) > 1) & (split[1]^ # "") THEN
  131. Strings.StrToInt(split[1]^, last)
  132. ELSE
  133. last := MAX(LONGINT)
  134. END;
  135. IF (LEN(split) >2) & (split[2]^ # "") THEN
  136. Strings.StrToInt(split[2]^, step)
  137. ELSE
  138. step := 1
  139. END;
  140. RETURN range = first .. last BY step;
  141. |Class.Set:
  142. Strings.StrToSet(s, st); RETURN set = st
  143. ELSE RETURN FALSE
  144. END;
  145. END Equals;
  146. PROCEDURE GetName*(VAR name: ARRAY OF CHAR);
  147. BEGIN
  148. IF SELF.class = Class.Name THEN COPY(SELF.name, name); success := TRUE ELSE HALT(200) END;
  149. END GetName;
  150. PROCEDURE SetName*(CONST name: ARRAY OF CHAR);
  151. BEGIN
  152. SELF.class := Class.Name; COPY(name, SELF.name);success := TRUE; persistent := TRUE;
  153. END SetName;
  154. PROCEDURE GetInteger*(VAR integer: HUGEINT);
  155. BEGIN
  156. IF SELF.class = Class.Integer THEN integer := SELF.integer; success := TRUE ELSE HALT(200) END;
  157. END GetInteger;
  158. PROCEDURE SetInteger*(integer: SIZE);
  159. BEGIN
  160. SELF.class := Class.Integer; SELF.integer := integer;success := TRUE; persistent := TRUE;
  161. END SetInteger;
  162. PROCEDURE GetSet*(VAR set: SET);
  163. BEGIN
  164. IF SELF.class = Class.Set THEN set := SELF.set; success := TRUE ELSE HALT(200) END;
  165. END GetSet;
  166. PROCEDURE SetSet*(set: SET);
  167. BEGIN
  168. SELF.class := Class.Set; SELF.set := set;success := TRUE; persistent := TRUE;
  169. END SetSet;
  170. PROCEDURE GetEnum*(translation: Translation; VAR integer: HUGEINT);
  171. BEGIN
  172. SELF.translation := translation;
  173. IF SELF.class = Class.Enum THEN integer := SELF.integer; success := TRUE ELSE HALT(200) END;
  174. END GetEnum;
  175. PROCEDURE SetEnum*(translation: Translation; integer: HUGEINT);
  176. BEGIN
  177. SELF.translation := translation;
  178. SELF.class := Class.Enum; SELF.integer := integer; success := TRUE; persistent := TRUE;
  179. END SetEnum;
  180. PROCEDURE GetRange*(VAR range: RANGE);
  181. BEGIN
  182. IF SELF.class = Class.Range THEN range := SELF.range; success := TRUE ELSE HALT(200) END;
  183. END GetRange;
  184. PROCEDURE SetRange*(CONST range: RANGE);
  185. BEGIN
  186. SELF.class := Class.Range; SELF.range := range; success := TRUE; persistent := TRUE;
  187. END SetRange;
  188. PROCEDURE GetFloat*(VAR float: LONGREAL);
  189. BEGIN
  190. IF SELF.class = Class.Float THEN float := SELF.float; success := TRUE ELSE HALT(200) END;
  191. END GetFloat;
  192. PROCEDURE SetFloat*(float: LONGREAL);
  193. BEGIN
  194. SELF.class := Class.Float; SELF.float := float;success := TRUE; persistent := TRUE;
  195. END SetFloat;
  196. PROCEDURE GetBoolean*(VAR boolean: BOOLEAN);
  197. BEGIN
  198. IF SELF.class = Class.Boolean THEN boolean := SELF.boolean; success := TRUE ELSE HALT(200) END;
  199. END GetBoolean;
  200. PROCEDURE SetBoolean*(boolean: BOOLEAN);
  201. BEGIN
  202. SELF.class := Class.Boolean; SELF.boolean := boolean;success := TRUE; persistent := TRUE;
  203. END SetBoolean;
  204. PROCEDURE GetObject*(VAR object: Object);
  205. BEGIN
  206. IF SELF.class = Class.Object THEN object := SELF.object; success := TRUE ELSE HALT(200) END;
  207. END GetObject;
  208. PROCEDURE SetObject*(object: Object; CONST optionalType: ARRAY OF CHAR);
  209. BEGIN
  210. SELF.class := Class.Object; SELF.object := object; COPY(optionalType, SELF.type); success := TRUE; persistent := TRUE;
  211. END SetObject;
  212. END Content;
  213. Enumerator* = PROCEDURE{DELEGATE} (CONST name: ARRAY OF CHAR; array: BOOLEAN);
  214. (** the interpretation record contains interpretable strings that are associated with attributes of an object
  215. an interpreter can use the strings in order to resolve values at runtime.
  216. *)
  217. Interpretation*=
  218. POINTER TO RECORD
  219. name-, str-: Strings.String;
  220. next-: Interpretation;
  221. END;
  222. Object* = OBJECT
  223. VAR
  224. reader: Reader;
  225. writer: Writer;
  226. content: Content;
  227. action: Action;
  228. firstTranslation-: Interpretation;
  229. CONST
  230. StrType = "type";
  231. VAR
  232. type*: ARRAY 64 OF CHAR;
  233. PROCEDURE & InitObject *;
  234. BEGIN NEW(content); type := "Object";
  235. END InitObject;
  236. (*
  237. PROCEDURE Write*(w: Writer);
  238. BEGIN
  239. END Write;
  240. PROCEDURE Read*(w: Reader): BOOLEAN;
  241. BEGIN
  242. RETURN TRUE
  243. END Read;
  244. *)
  245. PROCEDURE ActionEnumerator(CONST name: ARRAY OF CHAR; array: BOOLEAN);
  246. VAR index: LONGINT;
  247. BEGIN
  248. index := 0;
  249. REPEAT
  250. Get(name, index, content);
  251. IF content.success THEN
  252. CASE content.class OF
  253. |Class.Object:
  254. IF content.object = NIL THEN (* break when no object any more in list *)
  255. content.success := FALSE
  256. ELSE
  257. action(content.object)
  258. END;
  259. ELSE
  260. END;
  261. END;
  262. INC(index);
  263. UNTIL ~content.success OR ~array
  264. END ActionEnumerator;
  265. PROCEDURE Traverse*(action: Action);
  266. BEGIN
  267. IF content = NIL THEN NEW(content) END;
  268. SELF.action := action;
  269. Enumerate(ActionEnumerator);
  270. END Traverse;
  271. PROCEDURE Enumerate*(enum: Enumerator);
  272. BEGIN enum(StrType,FALSE);
  273. END Enumerate;
  274. PROCEDURE Set*(CONST name: ARRAY OF CHAR; index: LONGINT; c: Content);
  275. BEGIN
  276. IF name = StrType THEN c.GetName(type);
  277. ELSIF c.class = Class.Object THEN reader.Error("can not set attribute ", name);
  278. ELSIF reader # NIL THEN reader.Error("unsupported attribute (Set)", name);
  279. END;
  280. END Set;
  281. PROCEDURE Get*(CONST name: ARRAY OF CHAR; index: LONGINT; c: Content);
  282. BEGIN
  283. IF name = StrType THEN c.SetName(type);
  284. ELSIF reader # NIL THEN reader.Error("unsupported attribute (Get)", name);
  285. END;
  286. END Get;
  287. PROCEDURE AddTranslation*(CONST name: ARRAY OF CHAR; str: Strings.String);
  288. VAR translation: Interpretation;
  289. BEGIN
  290. NEW(translation); translation.name := Strings.NewString(name); translation.str := str;
  291. translation.next := firstTranslation;
  292. firstTranslation := translation;
  293. END AddTranslation;
  294. PROCEDURE FindTranslation*(CONST name: ARRAY OF CHAR; VAR translation: Interpretation): BOOLEAN;
  295. BEGIN
  296. translation := firstTranslation;
  297. WHILE (translation # NIL) & (translation.name^ # name) DO
  298. translation := translation.next;
  299. END;
  300. RETURN translation # NIL;
  301. END FindTranslation;
  302. PROCEDURE RemoveTranslation*(CONST name: ARRAY OF CHAR): BOOLEAN;
  303. VAR translation, prev: Interpretation;
  304. BEGIN
  305. IF name = "*" THEN
  306. firstTranslation := NIL; RETURN TRUE
  307. END;
  308. prev := NIL;
  309. translation := firstTranslation;
  310. WHILE (translation # NIL) & ((translation.name^ # name)) DO
  311. prev := translation;
  312. translation := translation.next;
  313. END;
  314. IF translation # NIL THEN
  315. IF prev = NIL THEN firstTranslation := translation.next
  316. ELSE prev.next := translation.next
  317. END;
  318. END;
  319. RETURN translation # NIL;
  320. END RemoveTranslation;
  321. PROCEDURE ReadContent*(CONST name: ARRAY OF CHAR; array: BOOLEAN);
  322. VAR index: LONGINT; str: Strings.String;
  323. BEGIN
  324. index := 0;
  325. REPEAT
  326. Get(name, index, content);
  327. IF content.success (*& content.persistent*) THEN
  328. IF reader.AttributeNeedingTranslation(name, str) THEN
  329. AddTranslation(name, str);
  330. END;
  331. CASE content.class OF
  332. |Class.String: content.success := reader.StringAttribute(name, content.string);
  333. |Class.Object: content.success := reader.ReadObject(name, content.type, index, content.object);
  334. |Class.Name: content.success := reader.NameAttribute(name, content.name);
  335. |Class.Boolean: content.success := reader.BooleanAttribute(name, content.boolean);
  336. |Class.Integer: content.success := reader.IntegerAttribute(name, content.integer);
  337. |Class.Float: content.success := reader.FloatAttribute(name, content.float);
  338. |Class.Enum: content.success := reader.EnumAttribute(name, content.translation, content.integer)
  339. |Class.Range: content.success := reader.RangeAttribute(name, content.range)
  340. |Class.Set: content.success := reader.SetAttribute(name, content.set)
  341. END;
  342. IF content.success THEN
  343. Set(name, index, content)
  344. END;
  345. END;
  346. INC(index);
  347. UNTIL ~content.success OR ~array
  348. END ReadContent;
  349. PROCEDURE WriteContent*(CONST name: ARRAY OF CHAR; array: BOOLEAN);
  350. VAR index: LONGINT; translation: Interpretation;
  351. BEGIN
  352. index := 0;
  353. REPEAT
  354. content.success := FALSE;
  355. Get(name, index, content);
  356. IF content.persistent & (~array OR content.success) THEN
  357. IF FindTranslation(name, translation) THEN
  358. writer.StringAttribute(name, translation.str);
  359. ELSE
  360. CASE content.class OF
  361. |Class.String: writer.StringAttribute(name, content.string);
  362. |Class.Object:
  363. IF content.object = NIL THEN
  364. content.success := FALSE
  365. ELSE
  366. writer.WriteObject(name, index, content.object)
  367. END;
  368. |Class.Name: writer.NameAttribute(name, content.name);
  369. |Class.Boolean: writer.BooleanAttribute(name, content.boolean);
  370. |Class.Integer: writer.IntegerAttribute(name, content.integer);
  371. |Class.Float: writer.FloatAttribute(name, content.float);
  372. |Class.Enum: writer.EnumAttribute(name, content.translation, content.integer)
  373. |Class.Range: writer.RangeAttribute(name, content.range)
  374. |Class.Set: writer.SetAttribute(name, content.set)
  375. END;
  376. END;
  377. END;
  378. INC(index);
  379. UNTIL ~array OR ~content.success
  380. END WriteContent;
  381. PROCEDURE Write*(w: Writer);
  382. VAR translation: Interpretation; prev: Writer;
  383. BEGIN
  384. prev := writer;
  385. IF content = NIL THEN NEW(content) END;
  386. writer := w;
  387. Enumerate(WriteContent);
  388. translation := firstTranslation;
  389. WHILE translation # NIL DO
  390. IF EnableTrace THEN D.Str("translation "); D.Str(translation.name^); D.Str("==>"); D.Str(translation.str^); D.Ln; END;
  391. translation := translation.next;
  392. END;
  393. writer := prev;
  394. END Write;
  395. PROCEDURE Read*(r: Reader): BOOLEAN;
  396. VAR prev: Reader;
  397. BEGIN
  398. IF content = NIL THEN NEW(content) END;
  399. prev := reader;
  400. reader := r;
  401. Enumerate(ReadContent);
  402. reader := prev;
  403. RETURN TRUE
  404. END Read;
  405. PROCEDURE Dump*(log: Streams.Writer; CONST name: ARRAY OF CHAR);
  406. VAR writer: Writer;
  407. BEGIN
  408. writer := NewXMLWriter(log);
  409. writer.WriteObject(name, None, SELF);
  410. writer.Close;
  411. END Dump;
  412. END Object;
  413. (** Object list *)
  414. ObjectList* = OBJECT (Object)
  415. VAR
  416. list*: FoxBasic.List;
  417. baseType*: Name;
  418. PROCEDURE &InitList*(initialSize: LONGINT; CONST baseType: ARRAY OF CHAR);
  419. BEGIN
  420. InitObject;
  421. NEW(list, initialSize);
  422. COPY(baseType, SELF.baseType);
  423. type := "ObjectList";
  424. END InitList;
  425. PROCEDURE Length*(): LONGINT;
  426. BEGIN RETURN list.Length()
  427. END Length;
  428. PROCEDURE GetElement*(i: LONGINT): Object;
  429. VAR obj: ANY;
  430. BEGIN
  431. IF (i >= 0) & (i < list.Length()) THEN
  432. obj := list.Get(i);
  433. IF obj # NIL THEN RETURN obj(Object); ELSE RETURN NIL; END;
  434. ELSE RETURN NIL;
  435. END;
  436. END GetElement;
  437. PROCEDURE SetElement*(i: LONGINT; o: Object);
  438. BEGIN
  439. (*WHILE list.Length() <= i DO list.Add(NIL) END;*)
  440. IF list.Length() = i THEN list.Add(o) ELSE list.Set(i,o) END;
  441. END SetElement;
  442. PROCEDURE Enumerate(enum: Enumerator);
  443. BEGIN
  444. Enumerate^(enum);
  445. enum("element", TRUE);
  446. END Enumerate;
  447. PROCEDURE Get*(CONST name: ARRAY OF CHAR; index: LONGINT; c: Content);
  448. BEGIN
  449. IF (name = "element") OR (name="") THEN
  450. c.SetObject(GetElement(index), baseType);
  451. c.success := TRUE; (* irrespective of content that can be nil, success should be considered given *)
  452. ELSE Get^(name, index, c)
  453. END;
  454. END Get;
  455. PROCEDURE Set*(CONST name: ARRAY OF CHAR; index: LONGINT; c: Content);
  456. VAR object: Object;
  457. BEGIN
  458. IF (name = "element") OR (name="") THEN
  459. c.GetObject(object); SetElement(index, object);
  460. c.success := TRUE;
  461. ELSE Set^(name, index, c)
  462. END;
  463. END Set;
  464. PROCEDURE Add*(o: Object);
  465. BEGIN
  466. list.Add(o)
  467. END Add;
  468. PROCEDURE Contains*(o: Object): BOOLEAN;
  469. BEGIN
  470. RETURN list.Contains(o);
  471. END Contains;
  472. PROCEDURE Traverse*(action: Action);
  473. VAR i: LONGINT;
  474. BEGIN
  475. FOR i := 0 TO Length()-1 DO
  476. action(GetElement(i));
  477. END;
  478. END Traverse;
  479. PROCEDURE IndexOf*(o: Object): LONGINT;
  480. BEGIN
  481. RETURN list.IndexOf(o)
  482. END IndexOf;
  483. END ObjectList;
  484. Generator = PROCEDURE {DELEGATE} (CONST type: ARRAY OF CHAR): Object;
  485. Reader* = OBJECT
  486. VAR generator: Generator;
  487. error: Streams.Writer;
  488. err-: BOOLEAN;
  489. filename*: Files.FileName; (* debugging *)
  490. PROCEDURE & InitReader(gen: Generator);
  491. BEGIN
  492. SELF.generator := gen;
  493. error := Commands.GetContext().error;
  494. END InitReader;
  495. PROCEDURE Error(CONST s1,s2: ARRAY OF CHAR);
  496. BEGIN
  497. err := TRUE;
  498. error.String("error in file "); error.String(filename); error.String(" ");
  499. error.Update;
  500. END Error;
  501. PROCEDURE StringAttribute*(CONST name: ARRAY OF CHAR; VAR str: Strings.String): BOOLEAN;
  502. END StringAttribute;
  503. PROCEDURE ReadObject*(CONST name, optionalType: ARRAY OF CHAR; index: LONGINT; VAR o: Object): BOOLEAN ;
  504. BEGIN
  505. END ReadObject;
  506. PROCEDURE NeedsTranslation(CONST s: ARRAY OF CHAR): BOOLEAN;
  507. VAR i: LONGINT; start: BOOLEAN;
  508. BEGIN
  509. i := 0; start := FALSE;
  510. WHILE s[i] # 0X DO
  511. IF s[i] = "?" THEN start := TRUE
  512. ELSIF start THEN
  513. IF s[i] = "{" THEN RETURN TRUE
  514. ELSE start := FALSE
  515. END;
  516. END;
  517. INC(i);
  518. END;
  519. RETURN FALSE
  520. END NeedsTranslation;
  521. PROCEDURE AttributeNeedingTranslation*(CONST name: ARRAY OF CHAR; VAR str: Strings.String): BOOLEAN;
  522. BEGIN
  523. IF StringAttribute(name, str) & NeedsTranslation(str^) THEN RETURN TRUE ELSE RETURN FALSE END;
  524. END AttributeNeedingTranslation;
  525. PROCEDURE NameAttribute*(CONST name: ARRAY OF CHAR; VAR str: ARRAY OF CHAR): BOOLEAN;
  526. VAR s: Strings.String;
  527. BEGIN
  528. IF StringAttribute(name, s) THEN COPY(s^, str); RETURN TRUE ELSE RETURN FALSE END;
  529. END NameAttribute;
  530. PROCEDURE BooleanAttribute*(CONST name: ARRAY OF CHAR; VAR value: BOOLEAN): BOOLEAN;
  531. VAR s: ARRAY 32 OF CHAR;
  532. BEGIN
  533. IF NameAttribute(name, s) THEN value := (s="true") OR (s="1") OR (s="yes") OR (s="TRUE"); RETURN TRUE ELSE RETURN FALSE END;
  534. END BooleanAttribute;
  535. PROCEDURE IntegerAttribute*(CONST name: ARRAY OF CHAR; VAR value: HUGEINT): BOOLEAN;
  536. VAR s: ARRAY 64 OF CHAR; v: LONGINT;
  537. BEGIN
  538. IF NameAttribute(name, s) THEN Strings.StrToInt(s, v); value := v; RETURN TRUE ELSE RETURN FALSE END;
  539. END IntegerAttribute;
  540. PROCEDURE FloatAttribute*(CONST name: ARRAY OF CHAR; VAR value: LONGREAL): BOOLEAN;
  541. VAR str: ARRAY 64 OF CHAR;
  542. BEGIN
  543. IF NameAttribute(name, str) THEN
  544. Strings.StrToFloat(str, value);
  545. RETURN TRUE
  546. ELSE
  547. RETURN FALSE
  548. END
  549. END FloatAttribute;
  550. PROCEDURE EnumAttribute*(CONST name: ARRAY OF CHAR; translation: Translation; VAR value:HUGEINT): BOOLEAN;
  551. VAR str: ARRAY 32 OF CHAR; v: LONGINT;
  552. BEGIN
  553. IF NameAttribute(name, str) & translation.Key(str, v) THEN value := v; RETURN TRUE ELSE RETURN FALSE END;
  554. END EnumAttribute;
  555. PROCEDURE RangeAttribute*(CONST name: ARRAY OF CHAR; VAR value: RANGE): BOOLEAN;
  556. VAR str: ARRAY 64 OF CHAR; first, last , step: LONGINT; split:Strings.StringArray;
  557. BEGIN
  558. IF NameAttribute(name, str) THEN
  559. split := Strings.Split(str, ":");
  560. Strings.StrToInt(split[0]^, first);
  561. IF (LEN(split) > 1) & (split[1]^ # "") THEN
  562. Strings.StrToInt(split[1]^, last)
  563. ELSE
  564. last := MAX(LONGINT)
  565. END;
  566. IF (LEN(split) >2) & (split[2]^ # "") THEN
  567. Strings.StrToInt(split[2]^, step)
  568. ELSE
  569. step := 1
  570. END;
  571. value := first .. last BY step;
  572. RETURN TRUE
  573. ELSE
  574. RETURN FALSE
  575. END
  576. END RangeAttribute;
  577. PROCEDURE SetAttribute*(CONST name: ARRAY OF CHAR; VAR set: SET): BOOLEAN;
  578. VAR str: ARRAY 64 OF CHAR;
  579. BEGIN
  580. IF NameAttribute(name, str) THEN
  581. Strings.StrToSet(str, set);
  582. RETURN TRUE
  583. ELSE
  584. RETURN FALSE
  585. END;
  586. END SetAttribute;
  587. END Reader;
  588. Writer* = OBJECT
  589. PROCEDURE & InitWriter*;
  590. BEGIN
  591. END InitWriter;
  592. PROCEDURE Close*;
  593. END Close;
  594. (* minimal interface to be implemented *)
  595. PROCEDURE NameAttribute*(CONST name, str: ARRAY OF CHAR);
  596. END NameAttribute;
  597. PROCEDURE WriteObject*(CONST name: ARRAY OF CHAR; index: LONGINT; o: Object);
  598. END WriteObject;
  599. PROCEDURE StartObjectArray*(CONST name: ARRAY OF CHAR);
  600. BEGIN
  601. END StartObjectArray;
  602. (* functions that provide necessary functionaliy based on minimal methods above but can be overwritten for optimisations *)
  603. PROCEDURE StringAttribute*(CONST name: ARRAY OF CHAR; str: Strings.String);
  604. BEGIN
  605. IF str # NIL THEN
  606. NameAttribute(name, str^)
  607. END;
  608. END StringAttribute;
  609. PROCEDURE IntegerAttribute*(CONST name: ARRAY OF CHAR; value: HUGEINT);
  610. VAR str: ARRAY 64 OF CHAR;
  611. BEGIN
  612. Strings.IntToStr(LONGINT(value), str);
  613. NameAttribute(name, str);
  614. END IntegerAttribute;
  615. PROCEDURE FloatAttribute*(CONST name: ARRAY OF CHAR; value: LONGREAL);
  616. VAR str: ARRAY 64 OF CHAR; sw: Streams.StringWriter;
  617. BEGIN
  618. NEW(sw, 32); sw.Float(value, 31); sw.Update;
  619. sw.Get(str); Strings.TrimWS(str); NameAttribute(name, str)
  620. END FloatAttribute;
  621. PROCEDURE BooleanAttribute*(CONST name: ARRAY OF CHAR; value: BOOLEAN);
  622. BEGIN
  623. IF value THEN NameAttribute(name,"true") ELSE NameAttribute(name,"false") END;
  624. END BooleanAttribute;
  625. PROCEDURE EnumAttribute*(CONST name: ARRAY OF CHAR; translation: Translation; value: HUGEINT);
  626. VAR str: ARRAY 32 OF CHAR;
  627. BEGIN
  628. IF translation.Name(LONGINT(value), str) THEN NameAttribute(name, str) ELSE NameAttribute(name, "unknown") END;
  629. END EnumAttribute;
  630. PROCEDURE RangeAttribute*(CONST name: ARRAY OF CHAR; value: RANGE);
  631. VAR str: ARRAY 64 OF CHAR;
  632. BEGIN
  633. Strings.IntToStr(FIRST(value), str);
  634. Strings.Append(str, ":");
  635. IF LAST(value) # MAX(LONGINT) THEN
  636. Strings.AppendInt(str, LAST(value));
  637. END;
  638. IF STEP(value) # 1 THEN
  639. Strings.Append(str,":");
  640. Strings.AppendInt(str, STEP(value));
  641. END;
  642. NameAttribute(name, str);
  643. END RangeAttribute;
  644. PROCEDURE SetAttribute*(CONST name: ARRAY OF CHAR; value: SET);
  645. VAR str: ARRAY 64 OF CHAR;
  646. BEGIN
  647. Strings.SetToStr(value, str);
  648. NameAttribute(name, str);
  649. END SetAttribute;
  650. END Writer;
  651. WrittenTable = OBJECT (Basic.HashTable)
  652. TYPE
  653. ObjectId = POINTER TO RECORD num: LONGINT END;
  654. VAR length: LONGINT;
  655. PROCEDURE Enter(o: Object; VAR entry: LONGINT): BOOLEAN;
  656. VAR any: ANY; id: ObjectId;
  657. BEGIN
  658. any := Get(o);
  659. IF any # NIL THEN entry := any(ObjectId).num; RETURN FALSE
  660. ELSE entry := length; INC(length); NEW(id); id.num := entry; Put(o, id); RETURN TRUE
  661. END;
  662. END Enter;
  663. END WrittenTable;
  664. XMLWriter*= OBJECT (Writer)
  665. VAR w: Streams.Writer;
  666. document-: XML.Document; element: XML.Element; current: XML.Container;
  667. scope: Scope;
  668. written: WrittenTable;
  669. PROCEDURE & InitXMLWriter*(writer: Streams.Writer);
  670. BEGIN
  671. w := writer; NEW(document); NEW(written,16); current := document; NEW(scope,current);
  672. END InitXMLWriter;
  673. PROCEDURE Close;
  674. BEGIN
  675. IF w # NIL THEN
  676. document.Write(w,NIL,-1); w.Update;
  677. END
  678. END Close;
  679. PROCEDURE NameAttribute(CONST name, str: ARRAY OF CHAR);
  680. BEGIN
  681. element.SetAttributeValue(name, str);
  682. END NameAttribute;
  683. PROCEDURE Enter(CONST name: ARRAY OF CHAR; o: Object);
  684. VAR e: XML.Element;
  685. BEGIN
  686. NEW(e); e.SetName(name); current.AddContent(e);
  687. scope.EnterElement(e);
  688. scope.Enter(e); current := e; element := e;
  689. END Enter;
  690. PROCEDURE Exit(CONST name: ARRAY OF CHAR);
  691. BEGIN
  692. scope.Exit(current);
  693. IF (current IS XML.Element) THEN element := current(XML.Element) ELSE element := NIL END;
  694. END Exit;
  695. PROCEDURE WriteObject(CONST name: ARRAY OF CHAR; index: LONGINT; o: Object);
  696. VAR guid: LONGINT;
  697. BEGIN
  698. IF o # NIL THEN
  699. Enter(name,o);
  700. IF written.Enter(o,guid) THEN
  701. o.Write(SELF);
  702. IntegerAttribute("guid", guid);
  703. ELSE
  704. IntegerAttribute("guid_reference",guid)
  705. END;
  706. Exit(name);
  707. END;
  708. END WriteObject;
  709. END XMLWriter;
  710. ReadTable = OBJECT (Basic.List)
  711. PROCEDURE Enter(o: Object);
  712. BEGIN Add(o);
  713. END Enter;
  714. PROCEDURE GetObject(index: HUGEINT): Object;
  715. BEGIN
  716. RETURN Get(LONGINT(index))(Object)
  717. END GetObject;
  718. END ReadTable;
  719. Element=POINTER TO RECORD
  720. index: LONGINT;
  721. e: XML.Element;
  722. next: Element;
  723. END;
  724. Symbol = POINTER TO RECORD
  725. name: LONGINT;
  726. first, last: Element;
  727. numberElements: LONGINT;
  728. next: Symbol;
  729. END;
  730. Stack = POINTER TO RECORD
  731. container: XML.Container;
  732. symbols: Basic.HashTableInt;
  733. firstSymbol: Symbol;
  734. used: Basic.HashTable;
  735. next: Stack
  736. END;
  737. Scope = OBJECT
  738. VAR
  739. stack: Stack;
  740. PROCEDURE & InitScope(c: XML.Container);
  741. BEGIN
  742. stack := NIL; Enter(c);
  743. END InitScope;
  744. PROCEDURE Enter(c: XML.Container);
  745. VAR new: Stack;
  746. BEGIN
  747. Use(c);
  748. NEW(new);
  749. new.container := c; NEW(new.symbols,32); NEW(new.used,4);
  750. new.next := stack; new.firstSymbol := NIL;
  751. stack := new;
  752. Register(c);
  753. END Enter;
  754. PROCEDURE Register(c: XML.Container);
  755. VAR e: XML.Content;
  756. BEGIN
  757. e := c.GetFirst();
  758. WHILE e # NIL DO
  759. IF (e IS XML.Element) (* & ~scope.Used(e) *) THEN
  760. EnterElement(e(XML.Element));
  761. END;
  762. e := c.GetNext(e);
  763. END;
  764. END Register;
  765. PROCEDURE Exit(VAR c: XML.Container);
  766. BEGIN
  767. stack := stack.next;
  768. c := stack.container;
  769. END Exit;
  770. PROCEDURE Use(o: ANY);
  771. BEGIN
  772. IF (stack # NIL) & ~stack.used.Has(o) THEN stack.used.Put(o,o) END;
  773. END Use;
  774. PROCEDURE Used(o: ANY): BOOLEAN;
  775. BEGIN
  776. RETURN stack.used.Has(o)
  777. END Used;
  778. PROCEDURE AddSymbol(CONST name: ARRAY OF CHAR): Symbol;
  779. VAR id: LONGINT; any: ANY; symbol: Symbol;
  780. BEGIN
  781. id := StringPool.GetIndex1(name);
  782. any := stack.symbols.Get(id);
  783. IF any = NIL THEN
  784. NEW(symbol);
  785. stack.symbols.Put(id, symbol);
  786. symbol.name := id;
  787. symbol.next := stack.firstSymbol;
  788. symbol.numberElements := 0;
  789. stack.firstSymbol := symbol;
  790. ELSE
  791. symbol := any(Symbol)
  792. END;
  793. RETURN symbol
  794. END AddSymbol;
  795. PROCEDURE FindElement(CONST name: ARRAY OF CHAR; index: LONGINT): XML.Element;
  796. VAR id: LONGINT; any: ANY; symbol: Symbol; element: Element;
  797. BEGIN
  798. IF name = "" THEN
  799. symbol := stack.firstSymbol;
  800. WHILE (symbol # NIL) & (index >= symbol.numberElements) DO
  801. DEC(index, symbol.numberElements);
  802. symbol := symbol.next;
  803. END;
  804. IF symbol = NIL THEN RETURN NIL END;
  805. ELSE
  806. id := StringPool.GetIndex1(name);
  807. any := stack.symbols.Get(id);
  808. IF any = NIL THEN
  809. RETURN NIL
  810. ELSE
  811. symbol := any(Symbol)
  812. END;
  813. END;
  814. element := symbol.first;
  815. WHILE (element # NIL) & (element.index < index) DO
  816. element := element.next;
  817. END;
  818. IF element = NIL THEN RETURN NIL
  819. ELSE RETURN element.e
  820. END;
  821. END FindElement;
  822. (* fifo *)
  823. PROCEDURE PutElement(symbol: Symbol; element: Element);
  824. BEGIN
  825. IF symbol.first = NIL THEN
  826. symbol.first := element; symbol.last := element; element.index := 0;
  827. ELSE
  828. element.index := symbol.last.index + 1;
  829. symbol.last.next := element; symbol.last := element
  830. END;
  831. INC(symbol.numberElements);
  832. END PutElement;
  833. PROCEDURE EnterElement(e: XML.Element);
  834. VAR name: Strings.String; symbol: Symbol; element: Element;
  835. BEGIN
  836. name := e.GetName();
  837. symbol := AddSymbol(name^);
  838. NEW(element); element.e := e;
  839. PutElement(symbol, element);
  840. END EnterElement;
  841. PROCEDURE Write(w: Streams.Writer);
  842. PROCEDURE WriteStack(s: Stack);
  843. VAR name: Strings.String;
  844. BEGIN
  845. IF s # NIL THEN
  846. WriteStack(s.next);
  847. IF s.container IS XML.Element THEN
  848. name := s.container(XML.Element).GetName();
  849. IF name # NIL THEN w.String("/"); w.String(name^) END
  850. END
  851. END;
  852. END WriteStack;
  853. BEGIN
  854. WriteStack(stack);
  855. END Write;
  856. END Scope;
  857. XMLReader* = OBJECT (Reader)
  858. VAR
  859. element: XML.Element;
  860. current: XML.Container;
  861. scope: Scope;
  862. read: ReadTable;
  863. PROCEDURE ReportXMLError(pos, line,col: LONGINT; CONST msg: ARRAY OF CHAR);
  864. BEGIN
  865. IF ~err THEN
  866. error.Char(CHR(9H)); error.Char(CHR(9H)); error.String("pos "); error.Int(pos, 6);
  867. error.String(", line "); error.Int(line, 0); error.String(", column "); error.Int(col, 0);
  868. error.String(" "); error.String(msg); error.Ln
  869. END;
  870. err := TRUE;
  871. END ReportXMLError;
  872. PROCEDURE & InitXMLReader*(reader: Streams.Reader; generator: Generator);
  873. VAR scanner: XMLScanner.Scanner; parser: XMLParser.Parser;
  874. BEGIN
  875. InitReader(generator);
  876. NEW(scanner, reader);
  877. NEW(parser, scanner);
  878. err := FALSE;
  879. parser.reportError := ReportXMLError;
  880. current := parser.Parse();
  881. NEW(scope, current);
  882. element := NIL;
  883. NEW(read,16);
  884. END InitXMLReader;
  885. PROCEDURE Error(CONST s1,s2: ARRAY OF CHAR);
  886. BEGIN
  887. err := TRUE;
  888. error.String("error in file "); error.String(filename); error.String(" ");
  889. error.String("in scope "); scope.Write(error); error.String(": "); error.String(s1); error.String(" "); error.String(s2); error.Ln;
  890. error.Update;
  891. END Error;
  892. PROCEDURE StringAttribute(CONST name: ARRAY OF CHAR; VAR str: Strings.String): BOOLEAN;
  893. BEGIN
  894. IF element # NIL THEN
  895. str := element.GetAttributeValue(name);
  896. scope.Use(element.GetAttribute(name));
  897. END;
  898. RETURN str # NIL;
  899. END StringAttribute;
  900. PROCEDURE Enter(CONST name: ARRAY OF CHAR; index: LONGINT; VAR o: Object): BOOLEAN;
  901. VAR e: XML.Element;
  902. BEGIN
  903. e := scope.FindElement(name, index);
  904. IF e # NIL THEN
  905. element := e;
  906. current := element;
  907. scope.Enter(current);
  908. RETURN TRUE
  909. ELSE
  910. RETURN FALSE
  911. END;
  912. END Enter;
  913. PROCEDURE Exit(CONST name: ARRAY OF CHAR);
  914. BEGIN
  915. scope.Exit(current);
  916. IF current IS XML.Element THEN element := current(XML.Element) ELSE element := NIL END;
  917. END Exit;
  918. PROCEDURE CheckUse(o: ANY);
  919. VAR e: XML.Content; enum: XMLObjects.Enumerator; name: XML.String; a: ANY; n: ARRAY 512 OF CHAR;
  920. type: Modules.TypeDesc;
  921. BEGIN
  922. IF current IS XML.Element THEN
  923. enum := current(XML.Element).GetAttributes();
  924. WHILE enum.HasMoreElements() DO
  925. a := enum.GetNext();
  926. IF ~scope.Used(a) THEN
  927. name := a(XML.Attribute).GetName();
  928. type := Modules.TypeOf(o);
  929. COPY(name^, n);
  930. Strings.Append(n," in type ");
  931. Strings.Append(n, type.mod.name);
  932. Strings.Append(n,".");
  933. Strings.Append(n, type.name);
  934. Error("not used ", n);
  935. END;
  936. END;
  937. END;
  938. e := current.GetFirst();
  939. WHILE e # NIL DO
  940. IF (e IS XML.Element) & ~scope.Used(e) THEN
  941. name := e(XML.Element).GetName();
  942. type := Modules.TypeOf(o);
  943. COPY(name^, n);
  944. Strings.Append(n," in type ");
  945. Strings.Append(n,type.mod.name);
  946. Strings.Append(n,".");
  947. Strings.Append(n,type.name);
  948. Error("not used ", n);
  949. END;
  950. e := current.GetNext(e);
  951. END;
  952. END CheckUse;
  953. PROCEDURE ReadObject(CONST name, optionalType: ARRAY OF CHAR; index: LONGINT; VAR o: Object): BOOLEAN;
  954. VAR type: ARRAY 32 OF CHAR; id: HUGEINT;
  955. BEGIN
  956. (*IF err THEN RETURN FALSE END;*)
  957. IF Enter(name, index, o) THEN
  958. IF IntegerAttribute("guid_reference", id) THEN
  959. o := read.GetObject(id);
  960. ELSE
  961. IF IntegerAttribute("guid", id) THEN (* ignore *) END;
  962. IF ~NameAttribute("type",type) THEN COPY(optionalType, type) END;
  963. o := generator(type);
  964. IF o = NIL THEN
  965. Error(name,"could not be created");
  966. Exit(name);
  967. RETURN FALSE
  968. ELSE
  969. read.Enter(o);
  970. IF ~o.Read(SELF) THEN
  971. Error(name,"could not be read");
  972. END;
  973. END;
  974. END;
  975. CheckUse(o);
  976. Exit(name);
  977. RETURN TRUE
  978. ELSE
  979. RETURN FALSE
  980. END;
  981. END ReadObject;
  982. END XMLReader;
  983. PROCEDURE NewXMLWriter*(w: Streams.Writer): Writer;
  984. VAR writer: XMLWriter;
  985. BEGIN
  986. NEW(writer, w); RETURN writer
  987. END NewXMLWriter;
  988. PROCEDURE NewXMLReader*(r: Streams.Reader; generator: Generator): Reader;
  989. VAR reader: XMLReader;
  990. BEGIN
  991. NEW(reader, r, generator); RETURN reader
  992. END NewXMLReader;
  993. PROCEDURE Clone*(o: Object; gen: Generator): Object;
  994. VAR w: XMLWriter; r : XMLReader; f: Files.File; writer: Files.Writer; reader: Files.Reader; clone: Object;
  995. BEGIN
  996. f := Files.New(""); (* anonymous file *)
  997. Files.OpenWriter(writer,f,0);
  998. NEW(w, writer);
  999. w.WriteObject("object",None, o);
  1000. w.Close;
  1001. writer.Update;
  1002. NEW(reader, f, 0);
  1003. NEW(r,reader, gen);
  1004. IF ~r.ReadObject("object","",None, clone) THEN TRACE(clone) END;
  1005. RETURN clone
  1006. END Clone;
  1007. PROCEDURE Trace*(o: Object);
  1008. VAR w: Streams.Writer; writer: Writer;
  1009. BEGIN
  1010. writer := NewXMLWriter(D.Log);
  1011. writer.WriteObject("specification", None, o);
  1012. writer.Close;
  1013. D.Ln;
  1014. END Trace;
  1015. END PersistentObjects.