Up to now, as far as your code projects at LMU are concerned, you have probably been responsible
for some sort of a file header, and a descriptive set of comments in all the code that you turn
in. However, since this course is supposed to give you an idea of real-world
software engineering and development projects, there will be fairly stringent coding standards
imposed, and which you will be expected to follow as a matter of course in your project
development. As stated in the syllabus, you will be graded on the coding standards you are
expected to use at all times.
Now, I know what you're thinking. . . #@$!!** coding standards!!! I'm going to write
code that works! I'll come back later and put in the comments and make it all pretty so I can
get a good grade.
WRONG!!! . . . . . (Thanks for playing)
. . . . . for several reasons:
In the first place, you are highly unlikely to go back to your code at the end of the term to insert comments, even to improve your grade. Even with the best of intentions, it just doesn't happen. You will be just too flippin' busy at the end to have time to go back to revisit every module. There are at least 50 notebooks upstairs in the archives from past completed projects that bear witness to this fact.
In the second place, starting out with the comments, instead of the code, will help you stay on track with what the module or block is supposed to do.
Finally, people will be looking at your code during the semester, not just at the end of the term, and your grade will suffer or benefit accordingly.
Believe me, though, when you go to work for Electronic Arts or Google or Amazon and your first job is to make some addition or a bug fix to somebody else's code, and you discover they didn't follow any sort of standard (and didn't put in any comments) you'll soon see the logic behind this. (Take a look at the bottom of this page to get an idea of what I mean!)
There are standard rules for this course that will apply to source files for all languages used in a project, whether it be SQL, Java, Javascript, PHP, Tcl/TK, Objective-C, Ada, Swift, GO, ML, PL/I, or FORTRAN:
~) if desired.
Javadocs
All Java source code must be commented in Javadocs style. If you are not familiar with this
excellent documentation tool, you can visit the link at Sun's
java documentation web site. Also, here
is a link to the Javadoc 6.0 tool main page. From there you can get to the pages that show all
the @ tags
and how to use them by clicking on the appropriate link under Javadoc
Tool Reference Page
.
All Java source files must have a file header. All classes, fields, constructors, and methods must also be documented in good Javadocs style. There are examples available on the pages at the links listed above.
File headers must have the following information:
You may want to include a version number constituting the module version, like 1.4.2, but this is optional. If your configuration management tool handles this for you, it's OK to leave the internal number out of the file header, since it becomes redundant.
Note that with Javadocs there will be order dependency with several of the tags required for these items. See the links above for details. Also, there is some good information in the Java In A Nutshell book.
Here is a trivial file that illustrates Javadocs basics: | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | /** @(#)Whatever.java 1.0.0 20-May-2006 (NOTE: This line may be omitted if using CVS) * Purpose : Provide a simple class file Javadoc comment example * Author : Homer Simpson * Description: This file provides a simple class file comment description which illustrates the * formatting of the code for the CMSI 401/402 "senior project" coding standards. It is expected * that all students supply this information in all code file headers. */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Revision History: * ----------------- * * Ver Date Modified by: Description of change/modification * ----- ----------- ------------ ----------------------------------------------------------------- * 1.0.0 20-May-2006 H. Simpson Initial version/release * 1.0.1 21-May-2006 H. Simpson Added comments to class definition * 1.1.0 02-Jun-2006 H. Simpson Fixed bug in method whatever * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ import java.awt.Image; import java.net.MalformedURLException; import java.net.URL; /** * This is the Whatever class that will just show a "\/\/" on the CRT * * @author Homer Simpson */ public class Whatever { /** * Class constructor. */ public Whatever() { int i = 5; } /** * Class constructor specifying number of objects to create. * @param nudnik a dummy parameter for illustration */ public Whatever( int n ) { int i = n; } /** * Returns an Image object that can then be painted on the screen. The url argument must specify * an absolute {@link URL}. The name argument is a specifier that is relative to the url argument. * * @param url an absolute URL giving the base location of the image * @param name the location of the image, relative to the url argument * @return the image at the specified URL or null if none exists * @exception none */ public Image getImage( URL url, String name ) { try { return getImage( new URL( "127.0.0.1" ), name ); } catch( MalformedURLException mfe ) { return null; } } } |
The Javadocs output of this class file can be seen with this link.
Doxygen
Source code in other languages like 'C' and C++ can still be documented using an automated tool.
One such tool which I have used in the past onseveral projects is called Doxygen
.
It is free, just like Javadocs, and can be found at this URL.
There are binary distributions for Linux and Windows, and there is a handy configuration GUI that
helps maintain the configuration file that builds the html output. It will do Java code, too.
Just like in the Java section above, all non-Java source files must have a file header. All classes, fields, constructors, and methods must also be documented. Doxygen is really easy to use; the GUI makes it simple to configure, build, and maintain the documentation. There is no dependence on tag order, either.
File headers must still have the following information:
Here is another trivial file that illustrates the use of Doxygen for a code module in 'C'.
Here is another trivial file that illustrates Doxygen basics: | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | /** * \file rptgen_version.c * \brief Report Generator Server (RGS) version * function source code * \author Original author ~ B.J. Johnson * \date Original date ~ 10-Mar-2004 * \version 1.0 Initial release * \version 1.0.1 Updated comments to reflect better Doxygen format * * Project: * \par * Readiness Tester Project * * Functional Subsystem: * \par * All * * Description: * \par * This module contains the functions needed to properly format a string containing the program name * and the version and build information for return to the Mobius test executive for display. This * is usually done at startup, but there is a service provided for the purpose so that it can be done * from the Mobius command line. * */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * INCLUDE FILES (All other include-type stuff is in these files) * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include "rptgenHeader.h" #include "rptgenMsgTokens.h" #include "rptgenPrototypes.h" /** * \fn getProgramName() * \brief Function to get name of program in string format * \return String formatted server program name * \exception None * * Description: * \par * This routine is a no-brainer, returning a "#define"ed constant * *Side effects: * \par * None * */ char * getProgramName() { return( PROGRAM_NAME ); } /** * \fn getVersionInformation( char * version ) * \brief Function to get version of program in string format * \param version character pointer to formatted version string * \exception None * * Description: * \par * This is also a no-brainer, except that the version number needs * some explanation. Version numbers include a major, a minor, and * a "patch" number, according to the CM ladies. The major and minor * numbers are self-explanatory, but the patch number is a bit * confusing. The usage is similar to what other software packages * frequently call the "build" number or "release" number. However, * in the BSS case, if the patch number is zero, it will not appear. * In other words, if the patch is zero, the version string will be * "1.2", not "1.2.0". * *Side effects: * \par * The version string argument is modified. * */ void getVersionInformation( char * version ) { if( 0 == PATCH_VERSION ) { sprintf( version, "Revision %s.%d, built on %s, %s", MAJOR_VERSION, MINOR_VERSION, BUILD_DATE, BUILD_TIME ); } else { sprintf( version, "Revision %s.%d-%d, built on %s", MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, BUILD_DATE, BUILD_TIME ); } } /** * \fn getVersion( RsmpCmdHandle handle, RsmpUserData * data, char * userCmd, char * response ) * \brief Function to get the debug level value * \param handle RsmpCmdHandle struct pointer to RSMP commmand * \param data RsmpUserData struct pointer to RSMP data * \param userCmd Character pointer to something * \param response Character pointer to something else * \return integer status value * \exception None * * Description: * \par * This is the function that acutally makes the uptime string for display. It checks to ensure that * there is only one argument (which is really not used, but might have a "?" in it), then if it's * cool, proceeds to call the "getVersionInformation()" to get the program's build information, then * coupled with a call to the "getProgramName()" function, assembles the string and puts it in the * "response" string argument. That's that. * * Side effects: * \par * The response string is modified on success, and both the response and errMsg strings are modified * on failure. */ int getVersion( RsmpCmdHandle handle, RsmpUserData * data, char * userCmd, char * response ) { char versionInformation[VERSION_STRING_SIZE]; int status = NO_ERROR; getVersionInformation( versionInformation ); sprintf( response, "%s: %s", getProgramName(), versionInformation ); return( status ); } |
The Doxygen output of this source file can be seen with this link.
Don't let this happen to you!
Here is an example file from a real project. This is the epitome of the bad
example
so you can see what you're up against. Notice that the indenting is not consistent, there
are almost NO comments, the variable names aren't descriptive, the stars are misaligned on
the header block, and on and on.
The project that we had to do was to translate this code from 'C' language into Tcl/TK language, and make it work the same as the original. This is just one module, which I have presented as an example. There were over 100 modules of code to be ported for this project, and all of them looked like this! Obviously, this original code is so bad that you can tell the task was extraordinarily difficult.
Here is an example of some of the worst errors in coding standards: | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | /************************************************************************/ /* File Name: R93.c * /* Project: CTCU/RTCU * /* Language: Borland C++ Compiler (version 3.0) * /* Disclosure: Copyright (C) 1995 Hughes Aircraft Co. * /* ALL RIGHTS RESERVED * /* * /* The contents of this medium may not be reproduced in whole * /* or in part without the written consent of the Hughes Aircraft * /* Company except as authorized by section 117 of the U.S. * /* Copyright Law. * /* * /* Description: This is the source code for test(s) corresponding to * /* the section(s) in the Test Specification (TS80346-110): * /* *** TLM Unconditioned Analog Functional test *** * /* Refer to the file specref.h for specific paragraphs * /* in the test specification. * /************************************************************************/ /************************************************************************/ /* Revision Change History: * /* * /* Rev Date PTR# S/W Engineer Comments * /* _____ ________ ____ _____________ ____________________________ * /* 11/09/95 L. Onishi Refer to TS by mnemonic Refer to spec by mnemonic since paragraph #'s for the CTCU portion of the spec keep changing. /* 05/03/96 L. Onishi Check failsafe Abort if return status from begin_test fails because we have to check FAILSAFE tp and abort if it is invalid if testing a CTCU. /* 11/7/96 L. Onishi Reduce test time /* 11/12/96 Production test vs. troubleshooting We were checking all tlm channels to verify uniqueness. Test Spec indicates we only have to check the channel that is uniquely loaded. We have to remember that this is only a production test, and not for diagnostic purposes. We had setup the test to check all telemetry channels so that we could identify which channels had problems in the event of a failure. That is fine for troubleshooting, but we have to remember that this is only a production test, and troubleshooting features are optional. The test now matches the verbage in the test spec and we only check the channel that has the unique voltage. /************************************************************************/ /************************************************************************/ /* *** NOTES *** /* * see testnote.txt /************************************************************************/ #define TLM_AN_TEST 0 #include "..\abc_test.c" /*#define ACTIVE_CH 0xAF00 /* expected data on active channel */ /*#define OTHER_CH 0x9600 /* expected data on other channels */ double dac_vlt[] = {1.025, 1.505, 1.985, 2.465, 2.945, 3.425}; unsigned int exp_avlt[] = {0x3300, 0x4b00, 0x6300, 0x7b00, 0x9300, 0xab00}; int max_loads; double active_vlt, other_vlt; void clr_active_ch(int ch) { char msg[20]; sprintf(msg, "%dl-ch,%d,0", (ch/64)+1, ch%64); exec(msg); } void set_active_channel(int ch) { char msg[80]; sprintf(msg, "%dl-ch,%d,1", (ch/64)+1, ch%64); /* configure active channel */ exec(msg); } void main(int argc, char *argv[]) { int channel; /* tlm load card channel number */ unsigned exp_data; /* expected serial data */ char msg[80]; /* temp string */ int n1, n2, n3, n4; char prefix[20]; char strx[100]; /* [0] = "is" spur signals */ char sbx[100]; char data_word[8], exp_word[8]; char pattern[3][100]; /* for special header */ char *pat[3]; /* for special header */ int num = 8; int errix; initTest(testName, argc, argv[1], argv[2]); /*======================================================================*/ /* begin_test /* apply bus (MIN bus, unit ON, tlm ON) /*======================================================================*/ if (ux_begin_test(UX_MINBUS, ON, ON) != 0) { exitTest(ABORT); } /****************************************************************** 1.3a Unconditional analog uniqueness test. ******************************************************************/ printSubHeader("Unconditioned Analog channel uniqueness (1.3a) "); exec("l-adj,on"); max_loads = ux_valid_tlm_ch/64; for(n1=1; n1<=max_loads; n1++) { sprintf(msg, "%dl-ld,0,dac1", n1); /* direct load 1 to DAC 0 */ exec(msg); sprintf(msg, "%dl-dac,1,%f",n1,dac_vlt[0]); /* set DAC for load card */ exec(msg); sprintf(msg, "%dl-ld,1,dac0", n1); exec(msg); sprintf(msg, "%dl-dac,0,%f", n1, dac_vlt[n1]); exec(msg); } for(n1=1; n1<=(ux_valid_tlm_ch/64); n1++) { for (n2 = 0; n2 < 4; n2++) { sprintf(msg, "%dl-set,%d,0", n1, n2); exec(msg); } } ux_twait(100); for (n1 = 0; n1 < 3; n1++) { for (n2 = 0; n2 < 82; n2++) { pattern[n1][n2] = 0; } } for (n1 = 0; n1 < 3; n1++) { for (n2 = 0; n2 < 82; n2++) { pattern[n1][n2] = 0; } } sprintf(pattern[0], "%-21s\0","(starting chan)"); sprintf(pattern[1], "%-21s\0","---------------"); num = 8; for (n1 = 0; n1 < num; n1++) { sprintf(msg, "+%03d \0", n1); strcat(pattern[0], msg); sprintf(msg, "%-6s\0", "-----"); strcat(pattern[1], msg); } *pattern[2] = '\0'; n1 = 0; while(n1 < 3) pat[n1] = pattern[n1++]; setSpecialHeader(ON, pat); channel = 0; while(channel < ux_valid_tlm_ch) { if (channel%64 == 0) { sprintf(msg, " - Testing ch %03d-%03d", channel, channel+63); ux_section_hdr(msg, 0); sprintf(msg, "i-load,an%d_u.fp2", channel/64); exec(msg); } /****************************************************************** Configure 1553 data word to request current active channel. ******************************************************************/ n3 = 0; n1 = 0; while (n1 < 8) { errix = 0; sprintf(prefix, "%03d", channel); sprintf(strx, "%03d (AN) ch%03d ", channel, channel); sprintf(sbx, "%03d should be: ", channel); n4 = 0; while (n4 < 8) { set_active_channel(channel); /* set active telemetry channel */ exec("i-run,bc,5,1"); ux_twait(200); n2 = channel%64; n3 = 0; if ((channel >= 0) && (channel < 4)) /* exclude ch 0-3 */ n3--; if ((channel >= 64) && (channel < 68)) /* exclude ch 64-67 */ n3--; if ((channel >= 192) && (channel < 212)) /* exclude ch 192-211 */ n3--; if (n3 >= 0) { sprintf(msg, "i-data,view,%d", (n2*2)+6); exec(msg); ux_chk1553Rsp(prefix, msg, 0); exp_data = exp_avlt[(channel/64)+1]; if (ts->testdata[0] == 0) { /* check 1553 status, no error */ sprintf(data_word, " %04x ", ts->testdata[3] & ux_rt_ad_mask); if ((ts->testdata[3] & ux_rt_ad_mcmp) != (exp_data & ux_rt_ad_mcmp)) { data_word[5] = '*'; sprintf(exp_word, " %04x*", exp_data); errix++; } else { sprintf(exp_word, "%-6.6s", " "); } } else { sprintf(data_word, " *****"); sprintf(exp_word, " %04x*", exp_data); errix++; } } else { sprintf(data_word, " n/a "); sprintf(exp_word, " n/a "); } strcat(strx, data_word); strcat(sbx, exp_word); clr_active_ch(channel); channel++; n4++; } if (errix != 0) { /* if an error occurred */ sprintf(msg, "%-82.82s *FAIL*", strx); /* space out to the error column */ strcpy(strx, msg); } /* end if */ prtSpecial(PDATA, errix, strx); /* output the results */ if (errix != 0) { prt(PERROR, sbx); } n1++; } } setSpecialHeader(OFF, pat); ux_end_test(); exitTest(NORMAL); } /************************************************************************/ /* END OF TEST /************************************************************************/ |