home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / Apps / DevTools / ManPagesFromHeaders / Source / Translator.m < prev   
Encoding:
Text File  |  1993-05-11  |  16.0 KB  |  610 lines

  1. /*
  2.  * Written by Guy Roberts of Object Skills Ltd, drifting
  3.  * in cyberspace without a domain or email address right 
  4.  * now. 
  5.  * May 7 1993. You can use this as a base for a better application
  6.  * as long as it is made publically available.
  7.  */
  8.  
  9. /* Abandon hop all who enter here */
  10.  
  11. #import "Translator.h"
  12.  
  13. #define CLASSNAMEMARKER "@interface"
  14. #define ENDOFCLASSMARKER "@end"
  15. #define COPYRIGHTNOTICE "Release x.x  Copyright 1993\n\n\n" 
  16. #define    TITLEFONTSIZE        28
  17. #define TEXTFONTSIZE        14
  18. #define    SUBTITLEFONTSIZE    18
  19. #define    ONEINCHINTWIPS        1440
  20. #define    INITIALCOUNT        0 /* Number elements in storage object */
  21. #define BUFSIZE                1024 /* A temporary char buffer size */
  22.  
  23. #define TRANSLATOR_DEBUG1 1
  24.  
  25. @implementation Translator
  26.  
  27. - initForHeaderFile: (char *) theName withTable: table
  28. {    
  29.     int        length, maxLength;
  30.     char    *streamBuffer;
  31.     
  32.     name = NXCopyStringBuffer(theName);
  33.     numInstanceMethods = numClassMethods = 0;
  34.     
  35.     rtfInstance = [[RTF    alloc] init];
  36.  
  37.     if ((fileStream = NXMapFile(name, NX_READONLY)) == NULL)    {
  38.         NXRunAlertPanel(NULL, [table    valueForStringKey: "fileNotFound"]
  39.             , NULL, NULL, NULL, name);
  40.         /* Should order out the window, and free everything, ie. close */
  41.         return nil;
  42.     }
  43.  
  44.     NXGetMemoryBuffer(fileStream, &streamBuffer, &length, &maxLength);
  45.  
  46.     if (!fileStream)
  47.         return self;
  48.     else {
  49.         const char    *defaultDirPath = NXReadDefault(OWNER, "defaultDirectory");
  50.         char        manPageName[BUFSIZE];
  51.         char        *pathEnd;
  52.         
  53.         if (!defaultDirPath) { /* Use the same path as the header file */
  54.             sprintf(manPageName, "%s", name);
  55.             pathEnd = rindex(manPageName, '.');
  56.             *pathEnd = 0;
  57.             strcat(manPageName, ".rtf");
  58.         }
  59.         else { /* A default directory path is to be used */
  60.             char    *fileName = rindex(name, '/');
  61.             
  62.             sprintf(manPageName, "%s%s", defaultDirPath, fileName);
  63.             pathEnd = rindex(manPageName, '.');
  64.             *pathEnd = 0;
  65.             strcat(manPageName, ".rtf");
  66.         }    
  67.         /* Parse the header file */
  68.         if (![self    parseBuffer: streamBuffer withKeyWordTable: table])
  69.             return nil;
  70.         
  71.         /* Write the file out */
  72.         NXSaveToFile([rtfInstance    stream], manPageName);
  73.         [rtfInstance free]; /* This closes the stream */
  74.     
  75.         /* Only launch edit if the user chooses */
  76.         {
  77.             const char    *buffer = 
  78.                 NXReadDefault(OWNER, "automaticallyLaunchEdit");
  79.     
  80.             /* Launch the editor if the default value is not set or
  81.              * if it is set and has the value "YES" */
  82.             if ( (!buffer) || ((buffer) && (strcmp(buffer, "YES") == 0)) )
  83.                 [[Application workspace]    
  84.                     openFile: manPageName withApplication: "Edit"];
  85.         }
  86.         
  87.         NXCloseMemory(fileStream, NX_FREEBUFFER);
  88.     }
  89.     
  90.     /* Free all */
  91.     [self    free];
  92.     
  93.     return self;
  94. }
  95.  
  96. /* This looks a mess. It is a mess. Messages are sent to the RTF instance
  97.  * to ask for tabulation, newlines and different font treatments. This
  98.  * code was finished off by my dog while I was asleep. Critiques should
  99.  * be addressed to rover@objskills.demon.co.uk.
  100.  */
  101. - (BOOL) parseBuffer: (char *) buffer withKeyWordTable: table
  102. {
  103.     id        instanceMethodHT;
  104.     id        classMethodHT;
  105.     char    *className;
  106.     char    *parentClassName;
  107.     const char    *toBeWritten = [table    valueForStringKey: "toBeWritten"];
  108.     
  109.     /* Get the class name */
  110.     if (![self getClass:  &className andParentName:  &parentClassName
  111.                fromBuffer: &buffer]) {
  112.         NXRunAlertPanel(NULL, [table    valueForStringKey: "invalidHeader"], 
  113.                 NULL, NULL, NULL, name);
  114.         /* Should order out the window, and free everything, ie. close */
  115.         return NO;
  116.     }
  117.     
  118.     /* Set up the page */
  119.     [rtfInstance    changeToHelvetica];
  120.     [rtfInstance    append: COPYRIGHTNOTICE];
  121.     [[rtfInstance    setFontSize: TITLEFONTSIZE] bold: YES];
  122.     [rtfInstance    setLeftMargin: ONEINCHINTWIPS];
  123.     /* Add the class name */
  124.     [rtfInstance    changeToHelvetica];
  125.     [[[rtfInstance     tab] append: className] append: "\n\n"];
  126.     [rtfInstance    changeToHelvetica];
  127.     [rtfInstance    setFontSize: TEXTFONTSIZE];
  128.     [[rtfInstance    tab]    
  129.             append: [table    valueForStringKey: "inheritsFrom"]];
  130.     [rtfInstance    changeToTimes];
  131.     [[[[[rtfInstance    tab]  bold: NO]    
  132.             append:    parentClassName]    append: "\n\n"] bold: YES];
  133.     [rtfInstance    changeToHelvetica];
  134.     [[rtfInstance    tab]    
  135.             append: [table    valueForStringKey: "declaredIn"]];
  136.     [rtfInstance            append: "\n\n\n"];
  137.     [rtfInstance    setFontSize: SUBTITLEFONTSIZE];
  138.     [rtfInstance    append: [table    valueForStringKey: "classDescription"]];
  139.     [self    insertOrdinaryText: [table    valueForStringKey: "toBeWritten"]];
  140.     [rtfInstance    append: [table    valueForStringKey: "instanceVariables"]];
  141.     
  142.     /* This method writes out a list of formatted instance variables */
  143.     [self    listInstanceVariables: &buffer];
  144.     [rtfInstance    append: [table    valueForStringKey: "methodTypes"]];
  145.     
  146.     /* Gather up all of the strings for class methods into a storage
  147.      * object and do the same for instance methods. The strings are 
  148.      * stored because they are used twice when composing the manual
  149.      * page.
  150.      */
  151.     [self    getInstanceMethods: &instanceMethodHT 
  152.                 andClassMethods: &classMethodHT
  153.                 fromBuffer:  &buffer];
  154.  
  155.     /* Using the information in the storage objects, write out the 
  156.      * message names without the types or arguments.
  157.      */
  158.      
  159.     [rtfInstance    setFontSize: TEXTFONTSIZE];
  160.     [self    writeMessageNames: classMethodHT];
  161.     [self    writeMessageNames: instanceMethodHT];
  162.     [rtfInstance    append: "\n\n\n"];
  163.     [rtfInstance    setFontSize: SUBTITLEFONTSIZE];
  164.  
  165.     [rtfInstance    append: [table    valueForStringKey: "classMethods"]];
  166.     [self    writeMethods: classMethodHT    stringTable: table];
  167.     [rtfInstance    append: [table    valueForStringKey: "instanceMethods"]];
  168.     [self    writeMethods: instanceMethodHT    stringTable: table];
  169.  
  170.     [rtfInstance    append: 
  171.                         [table    valueForStringKey: "constantsAndDefinedTypes"]];
  172.     [rtfInstance    append: "\n"];
  173.     [self    insertOrdinaryText: [table    valueForStringKey: "toBeWritten"]];
  174.  
  175.     {
  176.         /* Free all of the strings in the hash tables */
  177.         int        i, j;
  178.         
  179.         for (i = 0; i < [classMethodHT count]; i++)
  180.             NX_FREE([classMethodHT valueForKey: (const void *) i]);
  181.             
  182.         for (j = 0; j < [instanceMethodHT count]; j++)
  183.             NX_FREE([instanceMethodHT valueForKey: (const void *) j]);
  184.             
  185.         /* Dispose of the tables */
  186.         [classMethodHT    free];
  187.         [instanceMethodHT        free];
  188.     }
  189.     
  190.     return YES;
  191. }
  192.  
  193. /* Put all of the class methods defined in the header file into a
  194.  * table of strings. The keys to the table are of no meaning. In
  195.  * fact I should have used the Storage class to save the strings.
  196.  */
  197. - (BOOL) getInstanceMethods: (id *) instanceMethodHT 
  198.         andClassMethods: (id *) classMethodHT
  199.         fromBuffer: (char **) buffer
  200. {
  201.     char    *start = NULL;
  202.     char    *end = NULL;
  203.     char    *tmpBufferOne = NXCopyStringBuffer(*buffer);
  204.     char    *tmpBufferTwo = NXCopyStringBuffer(*buffer);
  205.     
  206.     *instanceMethodHT = [[HashTable    alloc] 
  207.                                 initKeyDesc: "i" valueDesc:"*"];
  208.     *classMethodHT     = [[HashTable    alloc]
  209.                                 initKeyDesc: "i" valueDesc:"*"];
  210.     
  211.     /* Collect the class method definitions */
  212.     start = tmpBufferOne;
  213.     while (start = index(start, '+') ) {
  214.         char    *newString = NULL;
  215.         
  216.         /* Create a copy of the method definition string */
  217.         end = index(start, ';');
  218.         *end = '\0';
  219.         newString = NXCopyStringBuffer(start);
  220.         [*classMethodHT    insertKey: (const void *) numClassMethods++ 
  221.                 value: newString];
  222.         
  223.         start = end + 1; /* Hop over the null character */
  224.     }
  225.  
  226.     /* Collect the instance method definitions */
  227.     start = tmpBufferTwo;
  228.     while (start = index(start, '-') ) {
  229.         char    *newString = NULL;
  230.         
  231.         /* Create a copy of the method definition string */
  232.         end = index(start, ';');
  233.         *end = '\0';
  234.         newString = NXCopyStringBuffer(start);
  235.         [*instanceMethodHT    insertKey: (const void *) numInstanceMethods++ 
  236.                 value: newString];
  237.  
  238.         start = end + 1; /* Hop over the null character */
  239.     }
  240.     
  241.     /* Tidy up */
  242.     NX_FREE(tmpBufferOne);
  243.     NX_FREE(tmpBufferOne);
  244.     
  245.     return NO;
  246. }
  247.  
  248. /* This method is used to go through a table of strings and
  249.  * break down each one into the parts of a message name. They
  250.  * need to be broken apart so as to be able to make bits of
  251.  * string bold or italic.
  252.  */
  253. - writeMethods: table stringTable: stringTable
  254. {
  255.     int        i;
  256.     char    *namePart = NULL;
  257.     char    *typePart = NULL;
  258.     char    *argPart = NULL;
  259.     
  260.     [rtfInstance    setFontSize: TEXTFONTSIZE];
  261.     
  262.     for (i = 0; i < [table count]; i++) {
  263.         char    buffer[BUFSIZE], *bufPtr;
  264.         BOOL    isFirstNamePart = YES;
  265.  
  266.         memset(buffer, '\0', BUFSIZE);
  267.         strcpy(buffer, [table valueForKey: (const void *) i]);
  268.     
  269.         bufPtr = buffer;
  270.  
  271.         while (bufPtr && (strcmp(bufPtr, "") != 0) ) {
  272.             [self    getNamePart:  &namePart typePart: &typePart
  273.                 argPart:  &argPart    fromString:  &bufPtr];
  274.         
  275.             [rtfInstance    bold: YES];
  276.             if (isFirstNamePart) {    
  277.                 char    tmpName[BUFSIZE];
  278.  
  279.                 {
  280.                     /* Get rid of the '+' or '-' character in the name.
  281.                      * You are deep inside Hack land here.
  282.                      */
  283.                     char     *ptr = NULL;
  284.                     
  285.                     tmpName[0] = '\0';
  286.                     strcat(tmpName, namePart);
  287.                     ptr = index(tmpName, '+');
  288.                     if (!ptr)
  289.                         ptr = index(tmpName, '-');
  290.                     if (ptr)
  291.                         *ptr = ' ';
  292.                 }
  293.                 [rtfInstance    changeToHelvetica];
  294.                 isFirstNamePart = NO;
  295.                 [[rtfInstance    append:"\n"] tab];
  296.                 [rtfInstance    append: tmpName];
  297.                 [[[rtfInstance    append:"\n"] tab] tab];
  298.             }
  299.             [rtfInstance    changeToTimes];
  300.     
  301.             [rtfInstance    append: namePart];
  302.             [rtfInstance    bold: NO];
  303.             [rtfInstance    append:" "];
  304.             [rtfInstance    append: typePart];
  305.             [rtfInstance    italic: YES];
  306.             [rtfInstance    append: argPart];
  307.             [rtfInstance    italic: NO];
  308.             [[[[rtfInstance    append: "\n"] tab] tab] tab];
  309.             
  310.             /* Free the three strings */
  311.             NX_FREE(namePart);
  312.             NX_FREE(typePart);
  313.             NX_FREE(argPart);
  314.         }
  315.         [[[rtfInstance    append:"\n"] tab]
  316.                 append: [stringTable    valueForStringKey: "toBeWritten"]];
  317.         
  318.     } /* for */    
  319.     
  320.     [[rtfInstance    setFontSize: SUBTITLEFONTSIZE] bold: YES];
  321.     [rtfInstance    append:"\n\n"];
  322.     
  323.     return self;
  324. }
  325.  
  326. /* 
  327.  * This method is used to write out a summary of every message
  328.  * name in a hash table. This goes near the top of the manual 
  329.  * page. It is called twice, once with a hash table of instance
  330.  * method strings and once with the class methods.
  331.  */
  332. - writeMessageNames: table
  333. {
  334.     int        i;
  335.     char    *namePart = NULL;
  336.     char    *typePart = NULL;
  337.     char    *argPart = NULL;
  338.     
  339.     [rtfInstance    changeToTimes];
  340.  
  341.     for (i = 0; i < [table count]; i++) {
  342.         char    buffer[BUFSIZE], *bufPtr;
  343.         
  344.         memset(buffer, '\0', BUFSIZE);
  345.         strcpy(buffer, [table valueForKey: (const void *) i]);
  346.     
  347.         bufPtr = buffer;
  348.         [[[rtfInstance    append: "\n"] tab]    bold: NO];
  349.  
  350.         while (bufPtr && (strcmp(bufPtr, "") != 0) ) {
  351.             [self    getNamePart:  &namePart typePart: &typePart
  352.                 argPart:  &argPart    fromString:  &bufPtr];
  353.         
  354.             [rtfInstance    append: namePart];
  355.             [rtfInstance    append: ""];
  356.             
  357.             /* Free the three strings */
  358.             if (namePart)
  359.                 NX_FREE(namePart);
  360.             if (typePart)
  361.                 NX_FREE(typePart);
  362.             if (argPart)
  363.                 NX_FREE(argPart);
  364.         }
  365.  
  366.     } /* for */    
  367.     
  368.     [rtfInstance    changeToHelvetica];
  369.     [rtfInstance    bold: YES];
  370.     
  371.     return self;
  372. }
  373.  
  374. - listInstanceVariables: (char **) buffer
  375. {
  376.     char    *pos = index(*buffer, '{');
  377.     char    type[BUFSIZE];
  378.     char    nom[BUFSIZE];
  379.     
  380.     [rtfInstance    setFontSize: TEXTFONTSIZE];
  381.     [rtfInstance    append: "\n"];
  382.     [rtfInstance    changeToTimes];
  383.  
  384.     while ([self getNextType: type andVariableName: nom fromBuffer: buffer]) {
  385.         [rtfInstance    tab];
  386.         [rtfInstance    bold: NO];
  387.         [rtfInstance    append: type];
  388.         [rtfInstance    tab];
  389.         [rtfInstance    bold: YES];
  390.         [rtfInstance    append: nom];
  391.         [rtfInstance    append: "\n"];
  392.     }
  393.     
  394.     [rtfInstance    setFontSize: SUBTITLEFONTSIZE];
  395.     [rtfInstance    append: "\n"];
  396.  
  397.     [rtfInstance    changeToHelvetica];
  398.  
  399.     return self;
  400. }
  401.  
  402. /* 
  403.  * Used to collect the instance variable parts.
  404.  */
  405. - (BOOL) getNextType: (char *) type andVariableName: (char *) nom 
  406.     fromBuffer: (char **) buffer
  407. {
  408.     int        i = 0, j = 0;
  409.     
  410.     if (**buffer == '{')
  411.         (*buffer)++;
  412.         
  413.     /* Eat white space */
  414.     while ( (**buffer != '}') && (isspace(**buffer)) )
  415.         (*buffer)++;
  416.     if (**buffer == '}') {
  417.         (*buffer)++;
  418.         return NO;
  419.     }
  420.     /* Collect the type of the instance variable */
  421.     while (!isspace(**buffer) ) {
  422.         type[i++] = **buffer;
  423.         (*buffer)++;
  424.     }
  425.     type[i] = '\0';
  426.     
  427.     /* Eat white space */
  428.     while ( (**buffer != '}') && (isspace(**buffer)) )
  429.         (*buffer)++;
  430.     if (**buffer == '}') {
  431.         (*buffer)++;
  432.         return NO;
  433.     }
  434.     /* Collect the name of the variable */
  435.     while ((**buffer) != '\n') {
  436.         nom[j++] = **buffer;
  437.         (*buffer)++;
  438.     }
  439.     nom[j] =  '\0';
  440.     
  441.     return YES;
  442. }
  443.  
  444. /*
  445.  * I never claimed to be an OO expert and stuff like this
  446.  * shows it.
  447.  */
  448. - insertOrdinaryText: (const char *) text
  449. {
  450.     [rtfInstance    changeToTimes];
  451.     [rtfInstance    setFontSize: TEXTFONTSIZE];
  452.     [rtfInstance    bold: NO];
  453.     [rtfInstance    tab];
  454.     [rtfInstance    append: text];    
  455.     [rtfInstance    bold: YES];
  456.     [rtfInstance    setFontSize: SUBTITLEFONTSIZE];
  457.     [rtfInstance    changeToHelvetica];
  458.  
  459.     return self;
  460. }
  461.  
  462. - free
  463. {
  464.     if (name)
  465.         NX_FREE(name);
  466. #if 0
  467.     return [super free];
  468. #endif
  469.     return nil;
  470. }
  471.  
  472. /*
  473.  * Recover the class name and the parent class name.
  474.  */
  475. - (BOOL) getClass: (char **) className 
  476.        andParentName: (char **) parentClassName
  477.        fromBuffer: (char **) buf
  478. {
  479.     char    *endOfClassName = NULL;
  480.     char    *endOfParentClassName = NULL;
  481.     char    *buffer = *buf;
  482.     
  483.     *className = strstr(buffer, CLASSNAMEMARKER);
  484.     if (!(*className))
  485.         return NO; /* Not an Objective-C header file */
  486.     *className += strlen(CLASSNAMEMARKER);
  487.     while (!isalpha(**className))
  488.         (*className)++;
  489.  
  490.     *parentClassName = NULL;
  491.     endOfClassName = index(*className, ':');
  492.     *endOfClassName = '\0';
  493.     *parentClassName = endOfClassName + 1;
  494.     endOfParentClassName = index(*parentClassName, '\n');
  495.     *endOfParentClassName = '\0';
  496.     
  497.     /* Move through the buffer */
  498.     *buf = endOfParentClassName + 1;
  499.     
  500.     if (!parentClassName)
  501.         return NO;
  502.     else
  503.         return YES;
  504. }
  505.  
  506. /* This method can be called repeatedly with a string containing a
  507.  * method definition. It returns the components of that string. These
  508.  * parts can then be written onto the manual page in bold or italic
  509.  * text as needed.
  510.  * It is assumed that the string passed in is of the correct format.
  511.  * If the parsing fails, NO is returned.
  512.  * Do not forget to eventually free the strings in the calling method.
  513.  * Disclaimer: I am not proud of this parsing code. I promise to learn
  514.  * about lex in the future.
  515.  */
  516. - (BOOL) getNamePart: (char **) namePart typePart: (char **) typePart
  517.     argPart: (char **) argPart
  518.     fromString: (char **) string
  519. {
  520.     char    buffer[BUFSIZE], *marker;
  521.     BOOL    bracedType = NO;
  522.     int        j = 0;
  523.     
  524. #ifdef TRANSLATOR_DEBUG
  525.     printf("%s\n", *string);
  526. #endif
  527.  
  528.     marker = *string;
  529.     
  530.     /* Move past white space */
  531.     while (isspace(*marker))
  532.         marker++;
  533.  
  534.     /* The name part is up to the first colon */
  535.     while ((*marker) && (*marker != ':') ) {
  536.         buffer[j++] = *marker++;
  537.     }
  538.     if (*marker == ':')
  539.         buffer[j++] = *marker++; /* Include the colon */
  540.     buffer[j] = '\0';
  541.     *namePart = NXCopyStringBuffer(buffer);
  542.  
  543.     if ( (!marker) || (strcmp(marker, "") == 0) ) {
  544.         *typePart = NXCopyStringBuffer("");
  545.         *argPart = NXCopyStringBuffer("");
  546.         *string = marker; /* move on to the next part of the line */
  547.         return YES;
  548.     }
  549.     
  550.     /* The type part is next, a type expression may be within braces */
  551.     j = 0;
  552.     /* Move past white space */
  553.     while (isspace(*marker))
  554.         marker++;
  555.     
  556.     if (*marker == '(')
  557.         bracedType = YES;
  558.         
  559.     if (!bracedType) { /* No braces used */
  560.         while ((*marker) && !isspace(*marker))
  561.             buffer[j++] = *marker++;
  562.     }
  563.     else { /* White space is part of the type expression */
  564.         while (*marker != ')')
  565.             buffer[j++] = *marker++;
  566.         buffer[j++] = *marker++; /* Include the closing brace */
  567.     }
  568.     buffer[j] = '\0';
  569.     *typePart = NXCopyStringBuffer(buffer);
  570.  
  571.     /* Move past white space */
  572.     while (isspace(*marker))
  573.         marker++;
  574.         
  575.     /* The argument part is next, it runs up to the next space or new line */
  576.     j = 0;
  577.     while (*marker && !isspace(*marker) && (*marker != ';') )
  578.         buffer[j++] = *marker++;
  579.  
  580.     buffer[j] = '\0';
  581.     *argPart = NXCopyStringBuffer(buffer);
  582.  
  583.     /* Gulp, now a confusing bit. The above code assumes that method 
  584.      * definitions are made up of blocks of three parts, a name, a
  585.      * type and a variable name. Of course if the type is (id) it can
  586.      * be ommitted and the code given above fails. eg. "myMethod: anObj;"
  587.      * So if the variable name has a colon in it or is a null string,
  588.      * make the type string null and the variable name equal to the type
  589.      * string.
  590.      */
  591.     *string = marker;
  592.     if (index(*argPart, ':') || (strcmp(*argPart, "") == 0) ) {
  593.         char    *tmp;
  594.         
  595.         /* Must rewind the string pointer !! Arrrg */
  596.         if (tmp = index(*argPart, ':')) {
  597.             int    lengthToRewind = strlen(*argPart);
  598.             
  599.             *string = *string - lengthToRewind;
  600.         }
  601.  
  602.         *argPart = NXCopyStringBuffer(*typePart);
  603.         *typePart = NXCopyStringBuffer("");        
  604.     }
  605.         
  606.     return YES;
  607. }
  608.  
  609. @end
  610.