home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-11-25 | 9.4 KB | 328 lines | [TEXT/R*ch] |
- /*
- ** Newton Developer Technical Support Sample Code
- **
- ** SoupTour v4 - a quick walk through the soup features, 2.0 Savvy
- **
- ** by Bob Ebert, David Levy and Kent Sandvik, Newton Developer Technical Support
- **
- ** Copyright © 1993-1995 by Apple Computer, Inc. All rights reserved.
- **
- ** You may incorporate this sample code into your applications without
- ** restriction. This sample code has been provided "AS IS" and the
- ** responsibility for its operation is 100% yours. You are not
- ** permitted to modify and redistribute the source as "DTS Sample Code."
- ** If you are going to re-distribute the source, we require that you
- ** make it clear in the source that the code was descended from
- ** Apple-provided sample code, but that you've made changes.
- */
-
-
- // To use:
- // Copy/Paste into NTK inspector.
- // Select blocks of code.
- // Hit ENTER to evaluate.
-
-
- // create a prototypical soup entry. This should really be a
- // constant. This is used so that all pizza soup entries will
- // share the same frame map, thus saving space.
-
- kSoupEntry := {name: nil, xtension: nil, crust: nil, size: nil,
- toppings: nil, cost: nil};
-
- // create a simulated app symbol for the soup xmit functions.
- kAppSymbol := '|SoupTour:PIEDTS|;
-
- // now a function to return a new entry:
-
- NewPizza := func(name, xtension, crust, size, toppings, cost)
- begin
- local newPizza := Clone(kSoupEntry) ;
- newPizza.name := name;
- newPizza.xtension := xtension ;
- newPizza.crust := crust ;
- newPizza.size := size ;
- newPizza.toppings := toppings ;
- newPizza.cost := cost ;
- // return the pizza
- newPizza ;
- end;
-
-
- // get a reference to the RAM store
- // as a rule you would use unionSoup
- // to get at the combination of RAM and card
- theStore:= getStores()[0];
-
- // create a soup with four indexes based on different slots
- thePizzas:= theStore:CreateSoupXMit("pizzaSoup",
- [ {structure: 'slot, path: 'name, type: 'string},
- {structure: 'slot, path: 'size, type: 'int},
- {structure: 'slot, path: 'crust, type: 'symbol},
- {structure: 'slot, path: 'cost, type: 'real}],
- kAppSymbol);
-
- // add some entries to my soup, I could omit or add slots at this stage
- thePizzas:AddXmit(call newPizza with ("Keith", 2242, 'deep, 12,
- ["cheese","pepperoni","sausage"], 12.75), kAppSymbol);
-
- // more of the same for a little while
- thePizzas:AddXmit(call newPizza with ("Nige", 2241, 'thin, 22,
- ["cheese","pepperoni","sausage","pineapple"], 19.25), kAppSymbol);
-
- thePizzas:AddXmit(call newPizza with ("Billy Boy", 2542, 'deep, 16,
- ["cheese","pepperoni"], 9.95), kAppSymbol);
-
- thePizzas:AddXmit(call newPizza with ("Fiona", 2530, 'thin, 10,
- ["cheese","pepperoni","sausage","anchovies"], 11), kAppSymbol);
-
- thePizzas:AddXmit(call newPizza with ("Entropy Dave", 2242, 'thin, 12,
- ["cheese","dead animal","heat"], 15.35 ), kAppSymbol);
-
- // let’s see what indexes are installed
- thePizzas:GetIndexes();
-
- // an example query against a symbol slot
- myCrustCursor := thePizzas:Query({indexPath: 'crust});
-
- // what have we gotten, check what the cursor points to
- myCrustCursor:Entry();
-
- // advance to the next one
- myCrustCursor:Next();
-
- // and the next one as well, notice that the symbols were
- // kept indexed alphabetically
- myCrustCursor:Next();
-
- // a reset puts us back to where we started
- myCrustCursor:Reset();
-
- // lets see just how many pizzas have crusts
- myCrustCursor:CountEntries()
-
- // we can go straight to the first item which matches our key
- // criterion
- myCrustCursor:GotoKey('thin);
-
-
- // another useful query may be to find all people that
- // are into thin crust pizza.
-
- myThinCursor := thePizzas:Query({indexpath: 'crust,
- beginKey: 'thin, endKey: 'thin});
-
- // count 'em
- myThinCursor:CountEntries();
-
- // get the last one
- myThinCursor:ResetToEnd();
-
- // back up one
- myThinCursor:Prev()
-
- // and again
- myThinCursor:Prev()
-
- // now there are no more
- myThinCursor:Prev()
-
- // use beginKey/endKey to limit the range of a cursor in an index.
-
-
- // you can use a similar technique to get a range of
- // numbers, like those who like their pizza between 10 and 16 dollars...
- myCheapCursor := thePizzas:Query({indexPath: 'cost,
- beginKey: 10.0, endKey: 16.0});
-
- // lets get a list of people who like cheap pizzas
- MapCursor(myCheapCursor, func(entry) entry.name);
-
-
- // in this case we set up another cursor against a different
- // indexed slot, and then supply numeric input to the the
- // GotoKey method
- mySizeCursor := thePizzas:Query({indexPath: 'size});
- mySizeCursor:GotoKey(16);
-
-
- // and naturally we can do much the same thing with string keys
- myNameCursor := thePizzas:Query({indexpath: 'name});
- myNameCursor:Entry();
-
- myNameCursor:GotoKey("Fiona");
-
-
- // here we make a query against a series of words which we
- // might want find in a frame. Notice that we get a nil
- // cursor back, since the system requires every word to be
- // present in order for a match
- myHotCursor := thePizzas:Query({words: ["heat", "dust", "entropy"]});
- myHotCursor:Entry();
-
- // sure enough, there are no matches
- myHotCursor:CountEntries();
-
- // here of course it works
- myHotCursor:= thePizzas:Query({words: ["heat", "entropy"]});
- myHotCursor:CountEntries();
-
- myHotCursor:Entry();
-
-
- // More useful, a simple way to extract all the pepperoni entries
- // we also use the 'name index to get the pepperoni lovers in alphabetical order
- myPepperoniCursor := thePizzas:Query({indexPath: 'name, words: ["pepperoni"]});
- myPepperoniCursor:CountEntries();
-
- // also a simple example of moving through the cursor
- myPepperoniCursor:Move(3);
-
- // NOTE: this move is an O(n) (order n) move. That is
- // you may think it goes to the item 3 places away
- // really quickly, but in reality, the above statement
- // is almost equivalent of doing 3 seperate moves!
-
-
- // here we set up a query which will pass every entry to the
- // function defined in validTest. If the test returns non-nil
- // then the entry is counted as a match. In this case it
- // checks for people whose extension starts with “22”
- myXtnCursor := thePizzas:Query({validTest: func (entry)
- begin
- if floor(entry.xtension/100.0)=22 then 1 else nil
- end});
-
- // look at the first few entries
- myXtnCursor:Entry();
-
- myXtnCursor:Next();
-
- // next we add an element to the toppings array
- AddArraySlot(myXtnCursor:Entry().toppings, "chocolate");
-
- myXtnCursor:Entry();
-
-
- // this effectively does a “revert” to the last saved entry,
- // so the chocolate element is now gone
- EntryUndoChanges(myXtnCursor:Entry());
- myXtnCursor:Entry();
-
-
- // however since Nige likes chocolate, we will add it
- // permanently by making the change and “saving” it by
- // calling EntryChangeXmit()
- // the curious may notice the system adding slots to the
- // entries as they are manipulated
- AddArraySlot(myXtnCursor:Entry().toppings, "chocolate");
- EntryChangeXmit(myXtnCursor:Entry(), kAppSymbol);
- myXtnCursor:Entry();
-
-
- // lastly, here is a simple traversal of soup entries to
- // calculate the total tip on the pizzas
- CalcTip := func()
- begin
- // here MapCursor applies each element returned by the query
- // call, in this call all entries, to a function which
- // returns a tip on each pizza
-
- // these returned tips are collected into an array which the
- // foreach loop runs through to accumulate the total tip into
- // theTip
- local theTip := 0 ;
- foreach tip in MapCursor(thePizzas:Query({}),
- func (entry) (entry.cost * 0.15)) do
- theTip:= theTip + tip;
- theTip ;
- end
-
- // this will print the tip rounded appropriately
- FormattedNumberStr(call calcTip with (), "%.2f");
-
- // NOTE: you really need to do more than this if you
- // want to print out monitary units in a locale
- // independant way, see the Newton Programmers Guide for more info
-
- // Finally, remove our dear soup that we have used for this experiment.
-
- // First check out if the soup is still there, should be
- GetStores()[0]:GetSoupNames();
-
- // Get access to the soup itself
- thePizza := GetStores()[0]:GetSoup("pizzaSoup");
-
- // Remove Myself from the store, if nil returned things are fine
- thePizza:RemoveFromStoreXmit(kAppSymbol);
-
- // Check out if the soup is still there?
- GetStores()[0]:GetSoupNames();
-
- // now be really nice and nil out the cursor variables
- // and soup variables.
- // this is what you often MUST do when you quit an App, or else
- // you'll leave lots of large objects in the heap.
-
- thePizzas := nil ;
- myCrustCursor := nil ;
- myThinCursor := nil ;
- myCheapCursor := nil ;
- mySizeCursor := nil ;
- myHotCursor := nil ;
- myPepperoniCursor := nil ;
- myXtnCursor := nil ;
-
- // are we having fun yet?
-
-
- // example of how to dump soup contents
- DumpNameSoup := func()
- begin
- local theSoup, c, val;
-
- // get soup
- theSoup := GetUnionSoup("Names");
-
- // create a cursor (no index spec means use default order, all entries)
- local c := theSoup:Query({});
-
- // while valid entries, dump the name information out
- val := c:Entry();
- while val do
- begin
- if(val.name.class = 'person) then
- begin
- // modify this as you wish
- print(val.name.last);
- print(val.name.first);
- print(val.city);
- print("\n");
- end;
- val := c:Next();
- end
- end; // big end
-
- call DumpNameSoup with ();
-
-
- // example of how to create an empty soup (delete all entries if it exists)
-
- CreateEmptySoup := func(store, soupName, indexes)
- begin
- local s := store:GetSoup(soupName); // get access to the soup
-
- if s then // if not NIL
- s:RemoveAllEntries(); // purge
- else
- s := store:CreateSoup(soupName, indexes);
-
- return s; // return the soup again
- end;
-
- call CreateEmptySoup with (GetStores()[0], "Test:PIEDTS",
- '[{structure: slot, path: theSlotName, type: string}]);
-
-
-
-