home *** CD-ROM | disk | FTP | other *** search
- //
- // MiscMergeDriver.m -- a simple loop for driving bulk merges
- // Written by Don Yacktman Copyright (c) 1995 by Don Yacktman.
- // Version 1.0. All rights reserved.
- // This notice may not be removed from this source code.
- //
- // This object is included in the MiscKit by permission from the author
- // and its use is governed by the MiscKit license, found in the file
- // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file
- // for a list of all applicable permissions and restrictions.
- //
-
- #import <misckit/misckit.h>
- #import <misckit/miscmerge.h>
-
- @implementation MiscMergeDriver
- /*" A MiscMergeDriver is used to merge an ASCII template with
- several dictionaries filled with key/value pairs. Each dictionary
- will be used in turn to generate a new output “document”.
-
- If you only need to generate a single merge, you may wish to
- simply use a MiscMergeEngine object. If you have several
- merges to perform, then a MiscMergeDriver implements the
- required loop to generate the required merges, as well as
- supporting a protocol that allows the merge engine some
- control over the loop. If you create your own loop, instead
- of using a MiscMergeDriver instance, some
- of the merge commands such as “next” will be ignored rather
- than performing the desired function.
-
- To use a MiscMergeDriver you must provide it with a template,
- dictionaries to merge into the template, and, optionally, a
- MiscMergeEngine instance. If a MiscMergeEngine is not provided,
- one will be created to perform the merge. To set up a merge
- template, use the -#{setTemplate:} method. It expects an instance
- of the MiscMergeTemplate class, which comes from an ASCII file or
- from a MiscString object.
-
- The data to be merged into the template
- is set up using the -#{setMergeData:} method. The data should be
- stored as key/value pairs in a MiscDictionary object for each
- merge to be performed. Place all the dictionaries into a List
- object and use the List object as the argument to -#{setMergeData:}.
-
- Finally, use the -#{doMerge:} method to perform the desired
- merge operation. The results will be returned as a List
- object with a MiscString corresponding to each MiscDictionary
- in the List provided to the MiscMergeDriver by the most recent -#{setData:}
- message. For example, the third MiscString will contain the
- results from the merge with the third MiscDictionary. If
- the Merge returned no result (due to an error or an “omit”
- command, for example) then the MiscString will be empty.
-
- If you wish to use a specific subclass of MiscMergeEngine to
- perform the merge, then use the -#{setEngine:} method to set
- up the engine before calling -#{doMerge:}. This engine will
- be used for all subsequent merges unless -#{setEngine:} is sent again.
-
- For more information, please see the IntroMiscMerge.rtfd document.
- It describes the syntax of the merge language and built-in
- commands available. The MiscMergeArchitecture.rtfd document
- describes the architecutre of the various classes used to
- perform merging operations and how to add custom commands to
- the framework.
- "*/
-
- - (MiscMergeEngine *)engine
- /*" Returns the merge engine, an instance of MiscMergeEngine,
- that will be used to perform a merge. If no engine has been
- set up, then nil is returned.
- "*/
- {
- return engine;
- }
-
- - (List *)mergeData
- /*" Returns the List of MiscDictionaries that will be used for the next merge.
- "*/
- {
- return dictionaries;
- }
-
- - (MiscMergeTemplate *)template
- /*" Returns the MiscMergeTemplate that will be used for the next merge.
- "*/
- {
- return template;
- }
-
- - setTemplate:aTemplate
- /*" Sets the MiscMergeTemplate that will be used for the next merge.
- Returns self upon success and nil upon failure. This method fails if a
- merge is in progress.
- "*/
- { // we don't free the old -- watch for memory leaks!
- if (merging) return nil;
- template = aTemplate;
- return self;
- }
-
- - setMergeData:(List *)aList
- /*" Sets the List of MiscDictionaries that will be used for the next merge.
- Returns self upon success and nil upon failure. This method fails if a
- merge is in progress.
- "*/
- { // we don't free the old -- watch for memory leaks!
- if (merging) return nil;
- dictionaries = aList;
- return self;
- }
-
- - (List *)doMerge:sender
- /*" Sets up a merge engine, if necessary, and performs a merge of
- the template with the MiscDictionaries in the data List. Any
- engines created will be destroyed after the merge; engines set
- using -#{setEngine} will persist, however. A List object populated
- with MiscStrings will be returned. There is a one-to-one correspondence
- between the index of the return MiscStrings in the List and the
- MiscDictionaries' indices in the List that was provided via the
- most recent -#{setMergeData}. Thus, if there were six dictionaries
- used for merging, six MiscStrings will be returned, as the result
- of six merges. Note that the “next” command will cause a MiscMergeEngine
- to attempt to skip forward to the next MiscDictionary, while still
- performing a single merge. In this case, an empty MiscString will
- be inserted in the output List as a placeholder and the final
- merge result will be put in the slot corresponding to the last
- dictionary used. Merges that fail or are halted due to an “omit”
- command will also be represented by an empty MiscString in the output.
- "*/
- { // YOU have to free the returned list.
- BOOL createdEngine = NO;
-
- if (merging) return nil; // not re-entrant!!!
- if (!template || !dictionaries) return nil;
- if ([dictionaries count] < 1) return nil;
- if (!engine) {
- createdEngine = YES;
- engine = [MiscMergeEngine newWithTemplate:template];
- }
-
- merging = YES;
- output = [[List alloc] init];
- for (_mergeLoopIndex=0; _mergeLoopIndex<[dictionaries count];
- _mergeLoopIndex++) {
- id ret = [engine mergeWithDictionary:
- [dictionaries objectAt:_mergeLoopIndex]
- sender:self];
- while (_mergeLoopIndex > [output count]) {
- [output addObject:[MiscString new]]; // placeholder for skipped
- }
- if (ret) [output addObject:ret];
- else [output addObject:[MiscString new]]; // placeholder if failed
- }
- if (createdEngine) { // don't keep it hanging around
- [engine free];
- engine = nil;
- }
- merging = NO;
- return output;
- }
-
- - setEngine:(MiscMergeEngine *)anEngine
- /*" Sets up an engine to be used for merging. If no engine is set, a
- temporary engine will be created before and used during a merge. It
- will be destroyed after it is used. Engines set using -#{setEngine:}
- will not be destroyed at the end of a merge and will be used for
- subsequent merges as well. Setting a new engine will not free the
- old engine; the MiscMergeDriver does not “own” the engine; it only
- makes use of it. This way, the same engine could be used by several
- MiscMergeDriver instances. Setting the engine to nil will revert to
- the default create/use/destroy pattern. The engine cannot be changed
- while a merge loop is in progress. Returns self if successful or
- nil if failure occurs.
- "*/
- {
- if (!merging) {
- engine = anEngine;
- return self;
- } else {
- return nil;
- }
- }
-
- // Methods required by the MiscMergeDriver protocol
-
- // Advance merge loop and return the next dictionary, nil if no more left
- - advanceMergeLoop
- /*" During a merge, returns the next dictionary that will be merged and advances the merge loop. Returns nil if not merging or if the loop is
- already performing the last merge.
- "*/
- {
- if ((!merging) || (_mergeLoopIndex >= [dictionaries count])) return nil;
- _mergeLoopIndex++;
- if (_mergeLoopIndex >= [dictionaries count]) return nil;
- return [dictionaries objectAt:_mergeLoopIndex];
- }
-
- - currentDictionary
- /*" During a merge, returns the dictionary that is currently being merged.
- Returns nil otherwise.
- "*/
- {
- if (!merging) return nil;
- if (_mergeLoopIndex >= [dictionaries count]) return nil;
- return [dictionaries objectAt:_mergeLoopIndex];
- }
-
- @end
-