home *** CD-ROM | disk | FTP | other *** search
/ Apple WWDC 1996 / WWDC96_1996 (CD).toast / Technology Materials / Newton Platform Info / Newton 2.0 Sample Code / Data Storage (Soups) / SoupTour-4
Encoding:
Text File  |  1995-11-25  |  9.4 KB  |  328 lines  |  [TEXT/R*ch]

  1. /*
  2. **      Newton Developer Technical Support Sample Code
  3. **
  4. **      SoupTour v4 - a quick walk through the soup features, 2.0 Savvy
  5. **
  6. **      by Bob Ebert, David Levy and Kent Sandvik, Newton Developer Technical Support
  7. **
  8. **      Copyright © 1993-1995 by Apple Computer, Inc.  All rights reserved.
  9. **
  10. **      You may incorporate this sample code into your applications without
  11. **      restriction.  This sample code has been provided "AS IS" and the
  12. **      responsibility for its operation is 100% yours.  You are not
  13. **      permitted to modify and redistribute the source as "DTS Sample Code."
  14. **      If you are going to re-distribute the source, we require that you
  15. **      make it clear in the source that the code was descended from
  16. **      Apple-provided sample code, but that you've made changes.
  17. */
  18.  
  19.  
  20. // To use:
  21. //    Copy/Paste into NTK inspector.
  22. //    Select blocks of code.
  23. //    Hit ENTER to evaluate.
  24.  
  25.  
  26. // create a prototypical soup entry. This should really be a
  27. // constant. This is used so that all pizza soup entries will
  28. // share the same frame map, thus saving space.
  29.  
  30. kSoupEntry := {name: nil, xtension: nil, crust: nil, size: nil,
  31.                     toppings: nil, cost: nil};
  32.  
  33. // create a simulated app symbol for the soup xmit functions.
  34. kAppSymbol := '|SoupTour:PIEDTS|;
  35.  
  36. // now a function to return a new entry:
  37.  
  38. NewPizza := func(name, xtension, crust, size, toppings, cost)
  39. begin
  40.     local newPizza := Clone(kSoupEntry) ;
  41.     newPizza.name := name;
  42.     newPizza.xtension := xtension ;
  43.     newPizza.crust := crust ;
  44.     newPizza.size := size ;
  45.     newPizza.toppings := toppings ;
  46.     newPizza.cost := cost ;
  47.     // return the pizza
  48.     newPizza ;
  49. end;
  50.  
  51.  
  52. // get a reference to the RAM store
  53. // as a rule you would use unionSoup
  54. // to get at the combination of RAM and card
  55. theStore:= getStores()[0];
  56.  
  57. // create a soup with four indexes based on different slots
  58. thePizzas:= theStore:CreateSoupXMit("pizzaSoup", 
  59. [ {structure: 'slot, path: 'name, type: 'string},
  60.   {structure: 'slot, path: 'size, type: 'int},
  61.   {structure: 'slot, path: 'crust, type: 'symbol},
  62.   {structure: 'slot, path: 'cost, type: 'real}],
  63. kAppSymbol);
  64.  
  65. // add some entries to my soup, I could omit or add slots at this stage
  66. thePizzas:AddXmit(call newPizza with ("Keith", 2242, 'deep, 12,
  67.                     ["cheese","pepperoni","sausage"], 12.75), kAppSymbol);
  68.  
  69. // more of the same for a little while
  70. thePizzas:AddXmit(call newPizza with ("Nige", 2241, 'thin, 22,
  71.                     ["cheese","pepperoni","sausage","pineapple"], 19.25), kAppSymbol);
  72.  
  73. thePizzas:AddXmit(call newPizza with ("Billy Boy", 2542, 'deep, 16,
  74.                     ["cheese","pepperoni"], 9.95), kAppSymbol);
  75.  
  76. thePizzas:AddXmit(call newPizza with ("Fiona", 2530, 'thin, 10,
  77.                     ["cheese","pepperoni","sausage","anchovies"], 11), kAppSymbol);
  78.  
  79. thePizzas:AddXmit(call newPizza with ("Entropy Dave", 2242, 'thin, 12,
  80.                     ["cheese","dead animal","heat"], 15.35 ), kAppSymbol);
  81.  
  82. // let’s see what indexes are installed
  83. thePizzas:GetIndexes();
  84.  
  85. // an example query against a symbol slot
  86. myCrustCursor := thePizzas:Query({indexPath: 'crust});
  87.  
  88. // what have we gotten, check what the cursor points to
  89. myCrustCursor:Entry();
  90.  
  91. // advance to the next one
  92. myCrustCursor:Next();
  93.  
  94. // and the next one as well, notice that the symbols were
  95. //  kept indexed alphabetically
  96. myCrustCursor:Next();
  97.  
  98. // a reset puts us back to where we started
  99. myCrustCursor:Reset();
  100.  
  101. // lets see just how many pizzas have crusts
  102. myCrustCursor:CountEntries()
  103.  
  104. // we can go straight to the first item which matches our key
  105. // criterion
  106. myCrustCursor:GotoKey('thin);
  107.  
  108.  
  109. // another useful query may be to find all people that
  110. // are into thin crust pizza.
  111.  
  112. myThinCursor := thePizzas:Query({indexpath: 'crust,
  113.      beginKey: 'thin, endKey: 'thin});
  114.  
  115. // count 'em
  116. myThinCursor:CountEntries();
  117.  
  118. // get the last one
  119. myThinCursor:ResetToEnd();
  120.  
  121. // back up one
  122. myThinCursor:Prev()
  123.  
  124. // and again
  125. myThinCursor:Prev()
  126.  
  127. // now there are no more
  128. myThinCursor:Prev()
  129.  
  130. // use beginKey/endKey to limit the range of a cursor in an index.
  131.  
  132.  
  133. // you can use a similar technique to get a range of
  134. // numbers, like those who like their pizza between 10 and 16 dollars...
  135. myCheapCursor := thePizzas:Query({indexPath: 'cost,
  136.      beginKey: 10.0, endKey: 16.0});
  137.  
  138. // lets get a list of people who like cheap pizzas
  139. MapCursor(myCheapCursor, func(entry) entry.name);
  140.  
  141.  
  142. // in this case we set up another cursor against a different
  143. // indexed slot, and then supply numeric input to the the
  144. // GotoKey method
  145. mySizeCursor := thePizzas:Query({indexPath: 'size});
  146. mySizeCursor:GotoKey(16);
  147.  
  148.  
  149. // and naturally we can do much the same thing with string keys
  150. myNameCursor := thePizzas:Query({indexpath: 'name});
  151. myNameCursor:Entry();
  152.  
  153. myNameCursor:GotoKey("Fiona");
  154.  
  155.  
  156. // here we make a query against a series of words which we
  157. // might want find in a frame.  Notice that we get a nil
  158. // cursor back, since the system requires every word to be
  159. // present in order for a match
  160. myHotCursor := thePizzas:Query({words: ["heat", "dust", "entropy"]});
  161. myHotCursor:Entry();
  162.  
  163. // sure enough, there are no matches
  164. myHotCursor:CountEntries();
  165.  
  166. // here of course it works
  167. myHotCursor:= thePizzas:Query({words: ["heat", "entropy"]});
  168. myHotCursor:CountEntries();
  169.     
  170. myHotCursor:Entry();
  171.  
  172.  
  173. // More useful, a simple way to extract all the pepperoni entries
  174. // we also use the 'name index to get the pepperoni lovers in alphabetical order
  175. myPepperoniCursor := thePizzas:Query({indexPath: 'name, words: ["pepperoni"]});
  176. myPepperoniCursor:CountEntries();
  177.  
  178. // also a simple example of moving through the cursor
  179. myPepperoniCursor:Move(3);
  180.  
  181. // NOTE: this move is an O(n) (order n) move. That is
  182. // you may think it goes to the item 3 places away
  183. // really quickly, but in reality, the above statement
  184. // is almost equivalent of doing 3 seperate moves!
  185.  
  186.  
  187. // here we set up a query which will pass every entry to the 
  188. // function defined in validTest.  If the test returns non-nil
  189. // then the entry is counted as a match.  In this case it
  190. // checks for people whose extension starts with “22”
  191. myXtnCursor := thePizzas:Query({validTest: func (entry)
  192.     begin 
  193.         if floor(entry.xtension/100.0)=22 then 1 else nil
  194.     end});
  195.  
  196. // look at the first few entries
  197. myXtnCursor:Entry();
  198.  
  199. myXtnCursor:Next();
  200.  
  201. // next we add an element to the toppings array
  202. AddArraySlot(myXtnCursor:Entry().toppings, "chocolate");
  203.  
  204. myXtnCursor:Entry();
  205.  
  206.  
  207. // this effectively does a “revert” to the last saved entry,
  208. // so the chocolate element is now gone
  209. EntryUndoChanges(myXtnCursor:Entry());
  210. myXtnCursor:Entry();
  211.  
  212.  
  213. // however since Nige likes chocolate, we will add it 
  214. // permanently by making the change and “saving” it by 
  215. // calling EntryChangeXmit()
  216. // the curious may notice the system adding slots to the 
  217. // entries as they are manipulated
  218. AddArraySlot(myXtnCursor:Entry().toppings, "chocolate");
  219. EntryChangeXmit(myXtnCursor:Entry(), kAppSymbol);
  220. myXtnCursor:Entry();
  221.  
  222.  
  223. // lastly, here is a simple traversal of soup entries to
  224. // calculate the total tip on the pizzas
  225. CalcTip := func() 
  226.     begin
  227.     // here MapCursor applies each element returned by the query
  228.     // call, in this call all entries, to a function which
  229.     // returns a tip on each pizza
  230.     
  231.     // these returned tips are collected into an array which the
  232.     // foreach loop runs through to accumulate the total tip into
  233.     // theTip
  234.         local theTip := 0 ;
  235.         foreach tip in MapCursor(thePizzas:Query({}), 
  236.                 func (entry) (entry.cost * 0.15)) do
  237.             theTip:= theTip + tip;
  238.         theTip ;
  239.     end
  240.  
  241. // this will print the tip rounded appropriately
  242. FormattedNumberStr(call calcTip with (), "%.2f");
  243.  
  244. // NOTE: you really need to do more than this if you
  245. //       want to print out monitary units in a locale
  246. //           independant way, see the Newton Programmers Guide for more info
  247.  
  248. // Finally, remove our dear soup that we have used for this experiment.
  249.  
  250. // First check out if the soup is still there, should be
  251. GetStores()[0]:GetSoupNames();
  252.  
  253. // Get access to the soup itself
  254. thePizza := GetStores()[0]:GetSoup("pizzaSoup");
  255.  
  256. // Remove Myself from the store, if nil returned things are fine
  257. thePizza:RemoveFromStoreXmit(kAppSymbol);
  258.  
  259. // Check out if the soup is still there?
  260. GetStores()[0]:GetSoupNames();
  261.  
  262. // now be really nice and nil out the cursor variables
  263. // and soup variables.
  264. // this is what you often MUST do when you quit an App, or else
  265. // you'll leave lots of large objects in the heap.
  266.  
  267. thePizzas := nil ;
  268. myCrustCursor := nil ;
  269. myThinCursor := nil ;
  270. myCheapCursor := nil ;
  271. mySizeCursor := nil ;
  272. myHotCursor := nil ;
  273. myPepperoniCursor := nil ;
  274. myXtnCursor := nil ;
  275.  
  276. // are we having fun yet?
  277.  
  278.  
  279. // example of how to dump soup contents
  280. DumpNameSoup := func()
  281. begin
  282.     local theSoup, c, val;
  283.  
  284.     // get soup    
  285.     theSoup := GetUnionSoup("Names");
  286.  
  287.     // create a cursor (no index spec means use default order, all entries)
  288.     local c := theSoup:Query({});
  289.     
  290.     // while valid entries, dump the name information out
  291.     val := c:Entry();
  292.     while val do
  293.         begin
  294.             if(val.name.class = 'person) then
  295.             begin
  296.                 // modify this as you wish
  297.                 print(val.name.last);
  298.                 print(val.name.first);
  299.                 print(val.city);
  300.                 print("\n");
  301.             end;
  302.             val := c:Next();
  303.         end
  304. end; // big end
  305.  
  306. call DumpNameSoup with ();
  307.  
  308.  
  309. // example of how to create an empty soup (delete all entries if it exists)
  310.  
  311. CreateEmptySoup := func(store, soupName, indexes)
  312. begin
  313.     local s := store:GetSoup(soupName);        // get access to the soup
  314.     
  315.     if s then        // if not NIL
  316.         s:RemoveAllEntries();                    // purge
  317.     else
  318.         s := store:CreateSoup(soupName, indexes);
  319.     
  320.     return s;        // return the soup again
  321. end;
  322.  
  323. call CreateEmptySoup with (GetStores()[0], "Test:PIEDTS",
  324.                            '[{structure: slot, path: theSlotName, type: string}]);
  325.  
  326.  
  327.  
  328.