home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / SourceCode / Database / OTC_EOFBetaExamples_V1.0 / EOFramework / EOModelInspector / EOModelInspector.m < prev    next >
Encoding:
Text File  |  1994-06-07  |  14.7 KB  |  619 lines

  1. /*--------------------------------------------------------------------------
  2.  *
  3.  *     You may freely copy, distribute, and reuse the code in this example.
  4.  *     SHL Systemhouse disclaims any warranty of any kind, expressed or
  5.  *    implied, as to its fitness for any particular use.
  6.  *
  7.  *
  8.  *    EOModelInspector
  9.  *
  10.  *    Inherits From:        WMInspector
  11.  *
  12.  *    Conforms To:        none
  13.  *
  14.  *    Declared In:        EOModelInspector.h
  15.  *
  16.  *------------------------------------------------------------------------*/
  17.  
  18. #import "EOModelInspector.h"
  19. #import "EOModelBrowserCell.h"
  20. #import "SwapView.h"
  21. #import "EntityPropertiesCategory.h"
  22.  
  23.  
  24.  
  25. /*--------------------------------------------------------------------------
  26.  *    Some basic macros for the cells of our matrices
  27.  *------------------------------------------------------------------------*/
  28. #define ADAPTOR                    [connectionDictionaryMatrix cellAt:0 :0]
  29. #define DB_AND_HOST                [connectionDictionaryMatrix cellAt:1 :0]
  30. #define USER_AND_PASSWORD        [connectionDictionaryMatrix cellAt:2 :0]
  31.  
  32. #define VALUE_CLASS_AND_TYPE    [attributeSettingsMatrix cellAt:0 :0]
  33. #define EXTERN_TYPE                [attributeSettingsMatrix cellAt:1 :0]
  34. #define ATTRIBUTE_EXTERN_NAME    [attributeSettingsMatrix cellAt:2 :0]
  35.  
  36. #define CLASS                    [entitySettingsMatrix cellAt:0 :0]
  37. #define ENTITY_EXTERN_NAME        [entitySettingsMatrix cellAt:1 :0]
  38.  
  39.  
  40. static id
  41.     _SELF = nil;
  42.  
  43.  
  44. @implementation EOModelInspector
  45.  
  46. + new
  47. {
  48.     char
  49.         path[MAXPATHLEN+1];
  50.     id
  51.         bundle;
  52.  
  53.     if (_SELF)
  54.         {
  55.         return _SELF;
  56.         }
  57.  
  58.     _SELF = self = [super new];
  59.  
  60.     if (!(bundle = [NXBundle bundleForClass:[self class]]))
  61.         {
  62.         return nil;
  63.         }
  64.     
  65.     if (![bundle getPath:path forResource:[(Object *)[self class] name] ofType:"nib"]) 
  66.         {
  67.         return nil;
  68.         }
  69.     
  70.     [NXApp loadNibFile:path owner:self withNames:NO fromZone:[(Object *)self zone]];
  71.  
  72.     /*
  73.      * Let's load in a bunch of images to be used in our inspector...
  74.      */
  75.     
  76.     if ([bundle getPath:path forResource:"SymbolKey" ofType:"tiff"])
  77.         {
  78.         keyImage = [[NXImage alloc] initFromFile:path];
  79.         }
  80.     else
  81.         {
  82.         NSLog (@"no key image: %s", path);
  83.         }
  84.  
  85.     if ([bundle getPath:path forResource:"SymbolClass" ofType:"tiff"])
  86.         {
  87.         classImage = [[NXImage alloc] initFromFile:path];
  88.         }
  89.     else
  90.         {
  91.         NSLog (@"no class image: %s", path);
  92.         }
  93.  
  94.     if ([bundle getPath:path forResource:"SymbolLock" ofType:"tiff"])
  95.         {
  96.         lockImage = [[NXImage alloc] initFromFile:path];
  97.         }
  98.     else
  99.         {
  100.         NSLog (@"no lock image: %s", path);
  101.         }
  102.     
  103.     /*
  104.      * Let's setup the splitview
  105.      */
  106.     [splitView addSubview:connectionDictionaryView];
  107.     [splitView addSubview:browser];
  108.     [splitView addSubview:propertySwapView];
  109.      
  110.     /*
  111.      * Use our custom browser cell
  112.      */
  113.     [browser setCellClass:[EOModelBrowserCell class]];
  114.     
  115.     /*
  116.      * We'll determine the column titles, thank you very much.
  117.      */
  118.     [browser getTitleFromPreviousColumn:NO];
  119.      return _SELF;
  120. }
  121.  
  122.  
  123. - _entityForCurrentSelectionInColumn: (int) theColumn
  124. /*--------------------------------------------------------------------------
  125.  *    Walks the browser from theColumn and finds/returns the entity that
  126.  *    corresponds to the current selection.  Let's say I have a relationship
  127.  *    selected...I need to find out it's destination entity.
  128.  *
  129.  *    NOTE: The reason this seems to be necessary is that when we go to
  130.  *    fill in a matrix column (after a relationship selection), all we
  131.  *    know is that we've got a selected cell, it has a name and a tag.
  132.  *
  133.  *    More explanation of what we're doing here...
  134.  *------------------------------------------------------------------------*/
  135. {
  136.     id
  137.         /*
  138.          * First let's get the "root" entity...that is the entity selected
  139.          * the first column.
  140.          */
  141.         myEntity = [[model entities] objectAtIndex:[[[browser matrixInColumn:0] selectedCell] tag]];
  142.     int
  143.         x;
  144.     
  145.     /*
  146.      * Now, let's walk from column 1, until we get to the
  147.      * column that's selected...
  148.      */
  149.     for (x = 1; x < theColumn; x++)
  150.         {
  151.         id
  152.             myProperty;
  153.  
  154.         /*
  155.          * Get the property
  156.          */
  157.         myProperty = [[myEntity properties] objectAtIndex:[[[browser matrixInColumn:x] selectedCell] tag]];
  158.         
  159.         if ([myProperty isKindOfClass:[EOAttribute class]])
  160.             {
  161.             myEntity = [myProperty entity];
  162.             }
  163.         else
  164.             {
  165.             myEntity = [myProperty destinationEntity];
  166.             }
  167.         }
  168.     
  169.     return myEntity;
  170. }
  171.  
  172.  
  173. - (int) _fillInBrowserMatrix: theMatrix
  174.     withEntities: (NSArray *) theEntities
  175. /*--------------------------------------------------------------------------
  176.  *    Called by our browser delegate method, to fill in the given matrix
  177.  *    with "theEntities".  This is only called for the first column.
  178.  *------------------------------------------------------------------------*/
  179. {
  180.     int
  181.         x = 0;
  182.     id
  183.         myCell,
  184.         myEntity,
  185.         myString;
  186.     
  187.     while (x < [theEntities count]) 
  188.         {
  189.         myEntity = [theEntities objectAtIndex:x];
  190.         [theMatrix addRow];
  191.         myCell = [theMatrix cellAt:x :0];
  192.         [myCell setTag:x];
  193.         myString = [NSString stringWithFormat:@"%@", [(EOEntity *)myEntity name]];
  194.         [myCell setStringValue:[myString cString]];
  195.         
  196.         if ([myEntity isReadOnly])
  197.             {
  198.             [myCell setTextColor:NX_COLORDKGRAY];
  199.             }
  200.         
  201.         [myCell setLeaf:NO];
  202.         [myCell setLoaded:YES];
  203.         x++;
  204.         }
  205.     
  206.     return x;
  207. }
  208.  
  209.  
  210. - (int) _fillInBrowserMatrix: theMatrix
  211.     withPropertiesFromEntity: (EOEntity *) theEntity
  212. /*--------------------------------------------------------------------------
  213.  *    Called by our browser delegate method, to fill in the given matrix
  214.  *    with the properties (attributes & relationships) for "theEntity".
  215.  *    This is called many times, particularly for a selected relationship
  216.  *    that must display the properties for its destination entity.
  217.  *------------------------------------------------------------------------*/
  218. {
  219.     int
  220.         x = 0;
  221.     id
  222.         myCell,
  223.         myProperties = [theEntity properties],
  224.         myProperty,
  225.         myString;
  226.     
  227.     while (x < [myProperties count]) 
  228.         {
  229.         myProperty = [myProperties objectAtIndex:x];
  230.         [theMatrix addRow];
  231.         myCell = [theMatrix cellAt:x :0];
  232.         [myCell setTag:x];
  233.         
  234.         if ([myProperty isKindOfClass:[EOAttribute class]])
  235.             {
  236.             myString = [NSString stringWithFormat:@"%@", [(EOAttribute *)myProperty name]];
  237.             
  238.             if ([myProperty isReadOnly])
  239.                 {
  240.                 [myCell setTextColor:NX_COLORDKGRAY];
  241.                 }
  242.             
  243.             [myCell setLeaf:YES];
  244.             }
  245.         else
  246.             {
  247.             myString = [NSString stringWithFormat:@"%@", [(EORelationship *)myProperty name]];
  248.             [myCell setIsRelationship:YES];
  249.             [myCell setIsToManyRelationship:[myProperty isToMany]];
  250.             [myCell setLeaf:NO];
  251.             }
  252.  
  253.         [myCell setStringValue:[myString cString]];
  254.         [myCell setLoaded:YES];
  255.         x++;
  256.         }
  257.  
  258.     return x;
  259. }
  260.  
  261.  
  262. - revert: sender
  263. {
  264.     char
  265.         myPath[MAXPATHLEN+1];
  266.     
  267.     /*
  268.      * Let's get the path of the selected item
  269.      */
  270.     [self selectionPathsInto:myPath separator:'\0'];
  271.     
  272.     /*
  273.      * Let's get rid of the old model
  274.      */
  275.     if (model)
  276.         {
  277.         [model release];
  278.         model = nil;
  279.         }
  280.         
  281.     /*
  282.      * Now create a model from our (.eomodel) file
  283.      */
  284.     if (model = [[EOModel alloc] initWithContentsOfFile:[NSString stringWithCString:myPath]])
  285.         {
  286.         id
  287.             myConnectionDictionary,
  288.             myString,
  289.             myStringA,
  290.             myStringB,
  291.             myDatabaseKey,
  292.             myHostKey,
  293.             myPasswordKey,
  294.             myUserNameKey,
  295.             myStringTable;
  296.         char
  297.             myPath[MAXPATHLEN+1];
  298.         
  299.         /*
  300.          * Let's display the viewer
  301.          */
  302.         [swapView swapView:[viewerPanel contentView]];
  303.         
  304.         /*
  305.          * Let's set the strings for the main model attributes
  306.          *
  307.          *         adaptor name, database/server name, host name/machine, user name & password
  308.          */
  309.         
  310.         /*
  311.          * First the adaptor name
  312.          */
  313.         [ADAPTOR setStringValue:[[model adaptorName] cString]];
  314.         
  315.         /*
  316.          * Now we need the "connectionDictionary" to get the rest
  317.          */
  318.         myConnectionDictionary = [model connectionDictionary];
  319.     
  320.         /*
  321.          * database/server name, host name/machine, get 'em then set 'em...
  322.          *
  323.          * Like: database/server_name (host_name/machine)
  324.          *
  325.          * NOTE:    This is tricky, since the Sybase and Oracle adaptors use
  326.          *            slightly different keys (ARRRGGGHHH!).
  327.          *
  328.          *            Sybase uses: databaseName & hostName
  329.          *            Oracle uses: serverId & hostMachine
  330.          *
  331.          * Okay...ready?  Here's what we're gonna do.  Assuming that these two
  332.          * keys could be different for any adaptor (since NeXT was too stupid
  333.          * to force 'em all to be the same), we'll simply gave a (string) table
  334.          * for each kind of adaptor.  This table will contain the keys:
  335.          * "database" and "host" which will map to that specific adaptor's key
  336.          * name.  Then we'll use that value as a key to our dictionary to get
  337.          * the real value that we want.  Whew...follow?
  338.          */
  339.         
  340.         /*
  341.          * Let's use Sybase's key names as a default...
  342.          */
  343.         myDatabaseKey = [NSString stringWithCString:"databaseName"];
  344.         myHostKey = [NSString stringWithCString:"hostName"];
  345.         myPasswordKey = [NSString stringWithCString:"password"];
  346.         myUserNameKey = [NSString stringWithCString:"userName"];
  347.         
  348.         /*
  349.          * Now, let's get a ".strings" file of the same name as the adaptor
  350.          * (according to the model)...
  351.          */
  352.         [[NXBundle bundleForClass:[self class]] getPath:myPath
  353.                 forResource:[[model adaptorName] cString]
  354.                                                 ofType:"strings"];
  355.         
  356.         /*
  357.          * If there's such a file...
  358.          */
  359.         if (*myPath)
  360.             {
  361.             /*
  362.              * make a string table
  363.              */
  364.             myStringTable = [[NXStringTable alloc] init];
  365.             
  366.             /*
  367.              * read from the file
  368.              */
  369.             if ([myStringTable readFromFile:myPath])
  370.                 {
  371.                 /*
  372.                  * get the values for our standard keys: "database" and "host"
  373.                  */
  374.                 myDatabaseKey = [NSString stringWithCString:[myStringTable valueForStringKey:"database"]];
  375.                 myHostKey = [NSString stringWithCString:[myStringTable valueForStringKey:"host"]];
  376.                 myPasswordKey = [NSString stringWithCString:[myStringTable valueForStringKey:"password"]];
  377.                 myUserNameKey = [NSString stringWithCString:[myStringTable valueForStringKey:"username"]];
  378.                 }
  379.             
  380.             [myStringTable free];
  381.             }
  382.         
  383.         /*
  384.          * Now we get a couple of strings based on our keys.
  385.          * remember, is there was no file for this adaptor, we used defaults
  386.          * which are Sybase (for now).
  387.          */
  388.         myStringA = [myConnectionDictionary objectForKey:myDatabaseKey];
  389.         myStringB = [myConnectionDictionary objectForKey:myHostKey];
  390.         myString = [NSString stringWithFormat:@"%@ (%@)", myStringA, myStringB];
  391.         [DB_AND_HOST setStringValue:[myString cString]];
  392.         
  393.         /*
  394.          * user name & password, get 'em then set 'em...
  395.          *
  396.          * Like: user_name (password)
  397.          */
  398.         myStringA = [myConnectionDictionary objectForKey:myUserNameKey];
  399.         myStringB = [myConnectionDictionary objectForKey:myPasswordKey];
  400.         myString = [NSString stringWithFormat:@"%@ (%@)", myStringA, myStringB];
  401.         [USER_AND_PASSWORD setStringValue:[myString cString]];
  402.         
  403.         /*
  404.          * Let's tell the browser to fill up, and force a click...
  405.          */
  406.         [browser loadColumnZero];
  407.         [self itemSelect:nil];
  408.         }
  409.     else
  410.         {
  411.         /*
  412.          * If there was an error reading the file, let's display the error view
  413.          */
  414.         [swapView swapView:[errorPanel contentView]];
  415.         }
  416.         
  417.     [super revert:sender];
  418.     return self;
  419. }
  420.  
  421.  
  422. - itemSelect: sender
  423. {
  424.     if ([sender selectedColumn] > 0)
  425.         {
  426.         id
  427.             myEntity = [self _entityForCurrentSelectionInColumn:[sender selectedColumn]],
  428.             myProperty = [[myEntity properties] objectAtIndex:[[sender selectedCell] tag]];
  429.             
  430.         if ([myProperty isKindOfClass:[EOAttribute class]])
  431.             {
  432.             /*
  433.              * Get the proper attribute (the for this column's selected cell).
  434.              */
  435.             id
  436.                 myString;
  437.             
  438.             /*
  439.              * Swap in the attribute view
  440.              */
  441.             [propertySwapView swapView:[attributePanel contentView]];
  442.             [propertySwapView display];
  443.             
  444.             if ([[[myProperty entity] primaryKeyAttributes] containsObject:myProperty])
  445.                 {
  446.                 [attributeKeyButton setImage:[keyImage copy]];
  447.                 }
  448.             else
  449.                 {
  450.                 [attributeKeyButton setImage:nil];
  451.                 }
  452.             
  453.             if ([[[myProperty entity] attributesUsedForLocking] containsObject:myProperty])
  454.                 {
  455.                 [attributeLockingButton setImage:[lockImage copy]];
  456.                 }
  457.             else
  458.                 {
  459.                 [attributeLockingButton setImage:nil];
  460.                 }
  461.  
  462.             if ([[[myProperty entity] classProperties] containsObject:myProperty])
  463.                 {
  464.                 [attributeClassButton setImage:[classImage copy]];
  465.                 }
  466.             else
  467.                 {
  468.                 [attributeClassButton setImage:nil];
  469.                 }
  470.             
  471.             /*
  472.              * Set the "value classname" & "value type" string
  473.              *
  474.              * We don't want anything in parenthesis is there is no "value
  475.              * type" (which is most cases).  But, if I don't do the "if" here,
  476.              * I get a string like:
  477.              *
  478.              *        NSString (nil)
  479.              *
  480.              * Which isn't what I want.
  481.              */
  482.             myString = [NSString stringWithFormat:@"%s", [myProperty valueClassName]];
  483.             
  484.             if ([myProperty valueType])
  485.                 {
  486.                 myString = [myString stringByAppendingFormat:@" (%@)", [myProperty valueType]];
  487.                 }
  488.             
  489.             [VALUE_CLASS_AND_TYPE setStringValue:[myString cString]];
  490.             
  491.             /*
  492.              * Set the "external type" string
  493.              */
  494.             [EXTERN_TYPE setStringValue:[[myProperty externalType] cString]];
  495.             
  496.             if ([myProperty isDerived] || [myProperty isFlattened])
  497.                 {
  498.                 [ATTRIBUTE_EXTERN_NAME setStringValue:[(NSString *)[myProperty definition] cString]];
  499.                 }
  500.             else
  501.                 {
  502.                 [ATTRIBUTE_EXTERN_NAME setStringValue:[(NSString *)[myProperty columnName] cString]];
  503.                 }
  504.             }
  505.         else
  506.             {
  507.             /*
  508.              * Swap in the relationship view
  509.              */
  510.             [propertySwapView swapView:[relationshipPanel contentView]];
  511.             [propertySwapView display];
  512.             
  513.             /*
  514.              * Set our radio button to reflect the "manyness" of the
  515.              * relationship.
  516.              */
  517.             [relationshipManynessMatrix selectCellAt:0 :([myProperty isToMany] ? 1 : 0)];
  518.             }
  519.         }
  520.     else
  521.         {
  522.         /*
  523.          * If there is no selected column, or it is the entities column,
  524.          * there ARE NO attribute settings.
  525.          */
  526.         id
  527.             myEntity = [self _entityForCurrentSelectionInColumn:[sender selectedColumn]];
  528.         
  529.         [propertySwapView swapView:[entityPanel contentView]];
  530.         [propertySwapView display];
  531.         
  532.         [CLASS setStringValue:[myEntity className]];
  533.         [ENTITY_EXTERN_NAME setStringValue:[(NSString *)[myEntity externalName] cString]];
  534.         }
  535.     
  536.     return self;
  537. }
  538.  
  539.  
  540.  
  541. /*--------------------------------------------------------------------------
  542.  *    NXBrowser delegate methods
  543.  *------------------------------------------------------------------------*/
  544.  
  545. - (int) browser: sender
  546.     fillMatrix: matrix
  547.     inColumn: (int) column
  548. {
  549.     int
  550.         x = 0;
  551.     
  552.     if (column == 0)
  553.         {
  554.         /*
  555.          * If we're the "root" column...
  556.          */
  557.         x = [self _fillInBrowserMatrix:matrix
  558.                     withEntities:[model entities]];
  559.         }
  560.     else
  561.         {
  562.         /*
  563.          * Otherwise...
  564.          */
  565.         x = [self _fillInBrowserMatrix:matrix
  566.                     withPropertiesFromEntity:[self _entityForCurrentSelectionInColumn:column]];
  567.         }
  568.  
  569.     return x;
  570. }
  571.  
  572.  
  573. - (const char *) browser: sender
  574.     titleOfColumn: (int) column
  575. {
  576.     NSString
  577.         *columnTitle;
  578.     
  579.     if (column == 0)
  580.         {
  581.         columnTitle = [NSString stringWithCString:"Entities"];
  582.         }
  583.     else
  584.         {
  585.         columnTitle = [(EOEntity *)[self _entityForCurrentSelectionInColumn:column] name];
  586.         }
  587.     
  588.     return [columnTitle cString];
  589. }
  590.  
  591.  
  592. /*--------------------------------------------------------------------------
  593.  *    NXSplitView delegate methods
  594.  *------------------------------------------------------------------------*/
  595.  
  596. - splitView:sender
  597.     getMinY: (NXCoord *) minY
  598.     maxY: (NXCoord *) maxY
  599.     ofSubviewAt: (int) offset
  600. {
  601.     switch (offset)
  602.         {
  603.         case 0:
  604.             *minY = 0.0;
  605.             *maxY = 45.0;
  606.             break;
  607.         
  608.         case 1:
  609.             *minY = 155.0;
  610.             *maxY = 225.0;
  611.             break;
  612.         }
  613.     
  614.     return self;
  615. }
  616.  
  617.  
  618. @end
  619.