Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 
Search the JavaFAQ.nu
1000 Java Tips ebook

1000 Java Tips - Click here for the high resolution copy!1000 Java Tips - Click here for the high resolution copy!

Java Screensaver, take it here

Free "1000 Java Tips" eBook is here! It is huge collection of big and small Java programming articles and tips. Please take your copy here.

Take your copy of free "Java Technology Screensaver"!.

Easy Learn Java: Programming Articles, Examples and Tips - Page 417


Previous 1060 Stories (530 Pages, 2 Per Page) Next

Doclet for finding missing comments

Go to all tips in Story by Dr. Kabutz

The Java Specialists' Newsletter [Issue 049] - Doclet for finding missing comments

Author: Dr. Heinz M. Kabutz

JDK version:

Category: Software Engineering

You can subscribe from our home page: http://www.javaspecialists.co.za (which also hosts all previous issues, available free of charge Smile

Welcome to the 49th edition of The Java(tm) Specialists' Newsletter, sent to over 3700 Java experts in over 82 countries. This week I am going to have a fun time running my Design Patterns Course at "The Shuttleworth Foundation (TSF)". In the unlikely case that you are unfamiliar with the name "Shuttleworth", it belongs to the first African in space, the second space tourist, Mark Shuttleworth. His space trip has inspired many young people of South Africa to strive in Science and Mathematics. TSF is a non-profit organization aimed at providing help to schools, for example, a school administration tool.

Some of my readers mentioned after newsletter 047 that they could not find my travel report of Mauritius. Please have a look at http://www.javaspecialists.co.za/designpatterns/mauritius.html for a direct link.

Times are tough, I don't know if you're feeling it as well? When I started contracting, I went for over 3 years without a single day when I didn't have programming or training work. All of a sudden, I found myself pottering around for two weeks at a time, scratching my head wondering where the next project would come from. Fortunately, I have put my time to good use and developed some courses, which have done extremely well. However, I'm not programming that much anymore, and that upsets me greatly. I feel far happier when I'm working on a program than if I have to run around trying to sell my courseware. It is not even that my hourly rate for programming is excessively high. It's not. I would love to hear from you if you have a project that could do with some outside help from a Java specialist, especially if it can be done remotely.

Doclet for finding missing comments

A few newsletter ago, I made some comments about the fact that I rarely read comments. The response from people was overwhelming. There were very few neutral voices about what I had said: I was called "childish", "inexperienced" from the one camp, and "wise", "at long last someone has the guts to say it" from the other camp. A small detail that readers from both camps missed, was that I never said that I don't write comments, I merely said that I don't read them Wink

Why do I write comments?

  1. The person who has to maintain my code may not share my enthusiasm for reading code in order to understand what I was doing.
  2. I quite like explaining in my comments the why of what I was doing.
  3. The JavaDocs are a great tool for producing API documentation.

My biggest frustration with JavaDocs is that it is so difficult to remember keeping all the comments up to date all the time. One of my readers in India shared the same frustration, so she wrote a comment checker Doclet. I used her Doclet whenever I was programming, but it wasn't really very object-oriented.

I spent some time last weekend refactoring the program so that the code would be more understandable. This is quite a long newsletter, because of all the code. I have not added comments to the "CommentChecker", you'll have to figure out yourself how it works Smile

We start with the main class called CommentChecker, called by the javadoc system. In this class, I find all the classes from the RootDoc and I run a ClassChecker against them:


import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.RootDoc;

public class CommentChecker {
  public static boolean start(RootDoc root) {
    ClassDoc[] classes = root.classes();
    for (int i = 0; i < classes.length; i++) {
      new ClassChecker(classes[i]).check();
    }
    return true;
  }
}

Let's also have two test cases, a class with comments called GoodTest ...

/** This is a test class */
public class GoodTest {
  /**
   * Constructor used for something
   * @param i used for something 
   */
  public GoodTest(int i) {}
  /**
   * No-args constructor for GoodTest.
   */
  public GoodTest() {}
  /** This is a good comment */
  private boolean good;
}

... and a class with invalid or missing comments called BadTest.

public class BadTest {
  public BadTest(int i) {}
  /**
   * @param someone means nothing
   * @return always true
   * @throws bla if something bad happens
   */
  public BadTest() {}
  /**
   * @return nothing at all!
   * @return nothing at all!
   * @throws Exception if nothing happens
   * @throws Exception if something happens
   */
  public void method1() throws NullPointerException, Exception {}
  private boolean bad;
}

In order to call this, we execute the following command. To also check private data members / functions, we add the -private option.

javadoc -private -doclet CommentChecker *Test.java

For the GoodTest class, there is no output to System.err (because no comments are missing!). Depending on your company standards, you can change the Doclet to, for example, insist on an @author tag in the JavaDocs. My comments in the GoodTest class are nonsense of course - they have no meaning! "In the real world", I would have more sensibly named classes than GoodTest and the comments would also add value to the class. The output from running this doclet is:

BadTest misses comment
BadTest.BadTest(int) misses comment
BadTest.BadTest(int) misses comment for parameter "i"
BadTest.BadTest() misses comment
BadTest.BadTest() has unnecessary return comment
BadTest.BadTest() parameter "someone" does not exist
BadTest.BadTest() has unnecessary comment for exception "bla"
BadTest.method1() misses comment
BadTest.method1() has unnecessary return comment
BadTest.method1() has multiple comments for exception "Exception"
BadTest.method1() is missing comments for exception "NullPointerException"
BadTest.bad misses comment

Oh, I haven't shown you the rest of the classes, of course! I just wanted to whet your appetite so that you'll read the rest of this newsletter. As you can see, the output from the Doclet can be really useful if you want to make sure that you (or your client) have added all the necessary comments.

The hierarchy for my checking classes is as follows:

Checker
  |
  +-ClassChecker
  |
  +-ExecutableChecker
  |   |
  |   +-ConstructorChecker
  |   |
  |   +-MethodChecker
  |
  +-FieldChecker

Let's have a look at the Checker superclass:

import com.sun.javadoc.Doc;

/**
 * Abstract superclass for checking a code component.
 */
public abstract class Checker {
  private final Doc doc;
  public Checker(Doc doc) {
    this.doc = doc;
  }
  public abstract void check();
  protected abstract String getDescriptor();
  protected final boolean isEmpty(String s) {
    return s == null || s.trim().length() == 0;
  }  
  public void checkComments() {
    if (isEmpty(doc.commentText()))
      error("misses comment");
  }
  protected void error(String msg) {
    System.err.println(getDescriptor() + ' ' + msg);
  }
}

We keep a handle to "Doc", which we use to test whether this code element has any comments. We also have an abstract check() method, which will be implemented differently for each code element. Each code element has a descriptor that we use to display which element an error belongs to.

Next we look at the class that checks whether a class has adequate comments:

import com.sun.javadoc.*;

/**
 * Check whether the class has comments
 */
public class ClassChecker extends Checker {
  private final ClassDoc doc;
  public ClassChecker(ClassDoc doc) {
    super(doc);
    this.doc = doc;
  }
  protected String getDescriptor() {
    return doc.qualifiedName();
  }
  public void check() {
    checkComments(); // calls superclass
    checkConstructors();
    checkMethods();
    checkFields();
  }
  private void checkConstructors() {
    ConstructorDoc[] constructors = doc.constructors();
    for (int i = 0; i < constructors.length; i++) {
      new ConstructorChecker(this, constructors[i]).check();
    }
  }
  private void checkMethods() {
    MethodDoc[] methods = doc.methods();
    for (int i = 0; i < methods.length; i++) {
      new MethodChecker(this, methods[i]).check();
    }
  }
  private void checkFields() {
    FieldDoc[] fields = doc.fields();
     for (int i = 0; i < fields.length; i++) {
      new FieldChecker(this, fields[i]).check();
    }
  }
}

This leads us to have a look at the ExecutableChecker class, a superclass of checking the comments of methods and constructors. The only difference between methods and constructors (as far as we are concerned) is that the constructor may not have a @return tag.

BTW, on a slightly different note, did you know that the following code compiles? i.e. you can have a method with the same name as the class. It can happen quite easily that you mean to write a constructor, but being a diligent C++ programmer you add the void keyword before the "constructor", thus actually writing a method. I discovered this a few years ago when one of my Bruce Eckel "Handson Java" students did this accidentally.

public class A {
  public void A() {}
}

Back to the problem on hand, a checker for methods and constructors. Since the only difference in our checking has to do with the return value, we make an abstract method called checkReturnComments(). I'll let you figure out the checkParametersForComments() and checkExceptionComments() methods yourself.

import com.sun.javadoc.*;
import java.util.*;

public abstract class ExecutableChecker extends Checker {
  protected final String descriptor;
  private final ExecutableMemberDoc doc;
  public ExecutableChecker(ClassChecker parentChecker,
      ExecutableMemberDoc doc) {
    super(doc);
    descriptor = parentChecker.getDescriptor() + '.' + 
      doc.name() + doc.flatSignature();
    this.doc = doc;
  }
  protected String getDescriptor() {
    return descriptor;
  }
  public void check() {
    checkComments(); // calls superclass
    checkReturnComments(); // calls subclass
    checkParametersForComments();
    checkExceptionComments();
  }
  public abstract void checkReturnComments();
  private void checkParametersForComments() {
    ParamTag[] tags = doc.paramTags();
    Map tagMap = new HashMap(tags.length);
    for (int i = 0; i < tags.length; i++) {
      if (tagMap.containsKey(tags[i].parameterName()))
        error("parameter "" + tags[i].parameterName()
            + "" has multiple comments");
      else if (!isEmpty(tags[i].parameterComment()))
        tagMap.put(tags[i].parameterName(), tags[i]);
    }
    Parameter[] params = doc.parameters();
    for (int i = 0; i < params.length; i++) {
      if (tagMap.remove(params[i].name()) == null
          && !params[i].name().equals("this$0")) {
        error("misses comment for parameter "" + 
          params[i].name() + """);
      }
    }
    Iterator it = tagMap.keySet().iterator();
    while (it.hasNext()) {
      error("parameter "" + it.next() + "" does not exist");
    }
  }
  private void checkExceptionComments() {
    ThrowsTag[] tags = doc.throwsTags();
    Map tagMap = new HashMap(tags.length);
    for (int i = 0; i < tags.length; i++) {
      if (tagMap.containsKey(tags[i].exceptionName()))
        error("has multiple comments for exception "" +
          tags[i].exceptionName() + """);
      else if (!isEmpty(tags[i].exceptionComment()))
        tagMap.put(tags[i].exceptionName(), tags[i]);
    }
    ClassDoc[] exceptions = doc.thrownExceptions();
    for (int i = 0; i < exceptions.length; i++) {
      if (tagMap.remove(exceptions[i].name()) == null)
        error("is missing comments for exception "" +
          exceptions[i].name() + """);
    }
    Iterator it = tagMap.keySet().iterator();
    while (it.hasNext()) {
      error("has unnecessary comment for exception "" +
        it.next() + '"');
    }
  }
  protected void foundCommentsForNonExistentReturnValue() {
    error("has unnecessary return comment");
  }
}

Jetzt haben wir das schlimmste hinter uns. Ooops - sorry - when I am tired I sometimes revert to my mother language Wink Let's have a look at the checker for the constructors. All we do is check whether there is a tag for @return and if there is, the checker complains.

import com.sun.javadoc.ConstructorDoc;

public class ConstructorChecker extends ExecutableChecker {
  private final ConstructorDoc doc;
  public ConstructorChecker(ClassChecker parent, 
      ConstructorDoc doc) {
    super(parent, doc);
    this.doc = doc;
  }
  public void checkReturnComments() {
    if (doc.tags("return").length > 0)
      foundCommentsForNonExistentReturnValue();
  }
}

The checker for methods is only marginally more complicated than that for constructors:

import com.sun.javadoc.*;

public class MethodChecker extends ExecutableChecker {
  private final MethodDoc doc;
  public MethodChecker(ClassChecker parent, MethodDoc doc) {
    super(parent, doc);
    this.doc = doc;
  }
  public void checkReturnComments() {
    Tag[] tags = doc.tags("return");
    if ("void".equals(doc.returnType().qualifiedTypeName())) {
      if (tags.length != 0) {
        foundCommentsForNonExistentReturnValue();
      }
    } else if (tags.length == 0 || isEmpty(tags[0].text())) {
      error("missing return comment");
    } else if (tags.length > 1) {
      error("has multiple return comments");
    }
  }
}

Lastly, the checker for fields. We don't need to worry about return types, parameters and exceptions, so we simply check that it has a comment at all.

import com.sun.javadoc.FieldDoc;

public class FieldChecker extends Checker {
  private final String descriptor;
  public FieldChecker(ClassChecker parent, FieldDoc doc) {
    super(doc);
    descriptor = parent.getDescriptor() + '.' + doc.name();
  }
  public void check() {
    checkComments();
  }
  protected String getDescriptor() {
    return descriptor;
  }
}

If you stick all these classes in a directory and point JavaDoc onto them, you can use them to check that you have put comments with each important element. What's really nifty is that you can decide at runtime whether to show only public/protected elements or also package private or private.

The way that I use this Doclet is to only release classes once no messages are generated by this CommentChecker. When I change a method significantly, I will generally delete the comment, and then the comment checker will remind me at the next build that I need to add a comment. Because I get reminded to add the comments before I get to release the code, I avoid the pitfall of only adding the comments several months after I wrote the code.

This Doclet has been very helpful to me, in that it made my code look "very professional" (I can't believe I'm saying that Wink.

Attention: A lot of readers ask me whether they are allowed to use the code in my newsletters for their own projects (without paying me). Yes, you may freely use the code in my newsletters (at your sole risk), provided that you have a reference and acknowledgement in your code to my newsletter webpage http://www.javaspecialists.co.za. However, if my newsletters have really helped you or you are using code from my newsletters (especially this one), you can gladly make a donation to Feeding the 5000 a non-profit organisation for providing food and clothing to the really really poor in the Helderberg, the area of South Africa where I live.

In my next newsletter, I am going to make you scratch your head. I am going to demonstrate that it is possible to make your compiler fail because of what is contained inside a comment.

Until then ...

Heinz


Copyright 2000-2004 Maximum Solutions, South Africa

Reprint Rights. Copyright subsists in all the material included in this email, but you may freely share the entire email with anyone you feel may be interested, and you may reprint excerpts both online and offline provided that you acknowledge the source as follows: This material from The Java(tm) Specialists' Newsletter by Maximum Solutions (South Africa). Please contact Maximum Solutions for more information.

Java and Sun are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. Maximum Solutions is independent of Sun Microsystems, Inc.

20276 bytes more | comments? | Printer Friendly Page  Send to a Friend | Score: 0
Posted by jalex on Saturday, November 12, 2005 (00:00:00) (2162 reads)

Q: Why are there no global variables in Java?

Go to all tips in Java IAQ by Peter Norvig

Q: Why are there no global variables in Java?

Answer: Global variables are considered bad form for a variety of reasons:
  • Adding state variables breaks referential transparency (you no longer can understand a statement or expression on its own: you need to understand it in the context of the settings of the global variables).
  • State variables lessen the cohesion of a program: you need to know more to understand how something works. A major point of Object-Oriented programming is to break up global state into more easily understood collections of local state.
  • When you add one variable, you limit the use of your program to one instance. What you thought was global, someone else might think of as local: they may want to run two copies of your program at once.
For these reasons, Java decided to ban global variables.

This tip is reprinted on JavaFAQ.nu by by courtesy of Peter Norvig I am thankful for his important contributions to my site - 21 Infrequently Answered Java Questions. Alexandre Patchine



comments? | Printer Friendly Page  Send to a Friend | Score: 0
Posted by jalex on Wednesday, November 09, 2005 (00:00:00) (12667 reads)

Previous 1060 Stories (530 Pages, 2 Per Page) Next

530| 529| 528| 527| 526| 525| 524| 523| 522| 521| 520| 519| 518| 517| 516| 515| 514| 513| 512| 511| 510| 509| 508| 507| 506| 505| 504| 503| 502| 501| 500| 499| 498| 497| 496| 495| 494| 493| 492| 491| 490| 489| 488| 487| 486| 485| 484| 483| 482| 481| 480| 479| 478| 477| 476| 475| 474| 473| 472| 471| 470| 469| 468| 467| 466| 465| 464| 463| 462| 461| 460| 459| 458| 457| 456| 455| 454| 453| 452| 451| 450| 449| 448| 447| 446| 445| 444| 443| 442| 441| 440| 439| 438| 437| 436| 435| 434| 433| 432| 431| 430| 429| 428| 427| 426| 425| 424| 423| 422| 421| 420| 419| 418|
417
| 416| 415| 414| 413| 412| 411| 410| 409| 408| 407| 406| 405| 404| 403| 402| 401| 400| 399| 398| 397| 396| 395| 394| 393| 392| 391| 390| 389| 388| 387| 386| 385| 384| 383| 382| 381| 380| 379| 378| 377| 376| 375| 374| 373| 372| 371| 370| 369| 368| 367| 366| 365| 364| 363| 362| 361| 360| 359| 358| 357| 356| 355| 354| 353| 352| 351| 350| 349| 348| 347| 346| 345| 344| 343| 342| 341| 340| 339| 338| 337| 336| 335| 334| 333| 332| 331| 330| 329| 328| 327| 326| 325| 324| 323| 322| 321| 320| 319| 318| 317| 316| 315| 314| 313| 312| 311| 310| 309| 308| 307| 306| 305| 304| 303| 302| 301| 300| 299| 298| 297| 296| 295| 294| 293| 292| 291| 290| 289| 288| 287| 286| 285| 284| 283| 282| 281| 280| 279| 278| 277| 276| 275| 274| 273| 272| 271| 270| 269| 268| 267| 266| 265| 264| 263| 262| 261| 260| 259| 258| 257| 256| 255| 254| 253| 252| 251| 250| 249| 248| 247| 246| 245| 244| 243| 242| 241| 240| 239| 238| 237| 236| 235| 234| 233| 232| 231| 230| 229| 228| 227| 226| 225| 224| 223| 222| 221| 220| 219| 218| 217| 216| 215| 214| 213| 212| 211| 210| 209| 208| 207| 206| 205| 204| 203| 202| 201| 200| 199| 198| 197| 196| 195| 194| 193| 192| 191| 190| 189| 188| 187| 186| 185| 184| 183| 182| 181| 180| 179| 178| 177| 176| 175| 174| 173| 172| 171| 170| 169| 168| 167| 166| 165| 164| 163| 162| 161| 160| 159| 158| 157| 156| 155| 154| 153| 152| 151| 150| 149| 148| 147| 146| 145| 144| 143| 142| 141| 140| 139| 138| 137| 136| 135| 134| 133| 132| 131| 130| 129| 128| 127| 126| 125| 124| 123| 122| 121| 120| 119| 118| 117| 116| 115| 114| 113| 112| 111| 110| 109| 108| 107| 106| 105| 104| 103| 102| 101| 100| 99| 98| 97| 96| 95| 94| 93| 92| 91| 90| 89| 88| 87| 86| 85| 84| 83| 82| 81| 80| 79| 78| 77| 76| 75| 74| 73| 72| 71| 70| 69| 68| 67| 66| 65| 64| 63| 62| 61| 60| 59| 58| 57| 56| 55| 54| 53| 52| 51| 50| 49| 48| 47| 46| 45| 44| 43| 42| 41| 40| 39| 38| 37| 36| 35| 34| 33| 32| 31| 30| 29| 28| 27| 26| 25| 24| 23| 22| 21| 20| 19| 18| 17| 16| 15| 14| 13| 12| 11| 10| 9| 8| 7| 6| 5| 4| 3| 2| 1|


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy