Clover coverage report - XJavaDoc - 1.1
Coverage timestamp: Mon Oct 4 2004 23:49:51 BST
file stats: LOC: 508   Methods: 23
NCLOC: 295   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
DefaultXTag.java 63.2% 68.2% 52.2% 65%
coverage coverage
 1   
 /*
 2   
  * Copyright (c) 2001-2003 The XDoclet team
 3   
  * All rights reserved.
 4   
  */
 5   
 package xjavadoc;
 6   
 
 7   
 import java.util.*;
 8   
 
 9   
 import xjavadoc.event.XTagListener;
 10   
 import xjavadoc.event.XTagEvent;
 11   
 import xjavadoc.filesystem.AbstractFile;
 12   
 
 13   
 /**
 14   
  * @author    Aslak Hellesøy
 15   
  * @created   11. januar 2002
 16   
  */
 17   
 public class DefaultXTag implements XTag
 18   
 {
 19   
     public static int  instanceCount = 0;
 20   
 
 21   
     /**
 22   
      * tag name
 23   
      */
 24   
     private String     _name;
 25   
 
 26   
     /**
 27   
      * string representation of tag
 28   
      */
 29   
     private String     _value;
 30   
 
 31   
     /**
 32   
      * attribute map
 33   
      */
 34   
     private Map        _attributes;
 35   
 
 36   
     /**
 37   
      * Ordered List of attribute names
 38   
      */
 39   
     private List       _attributeNames = null;
 40   
 
 41   
     /**
 42   
      * tag parse status
 43   
      */
 44   
     private boolean    _isParsed = false;
 45   
 
 46   
     private int        _hash = Integer.MIN_VALUE;
 47   
 
 48   
     /**
 49   
      * indicate dirty state
 50   
      */
 51   
     private boolean    _isDirty = false;
 52   
 
 53   
     private XDoc       _doc;
 54   
 
 55   
     /**
 56   
      * tag listeners interested in changes. This would be parent xdoc
 57   
      */
 58   
     private Set        _tagListeners;
 59   
 
 60   
     private int        _lineNumber;
 61   
 
 62   
     private XJavaDoc _xJavaDoc;
 63   
 
 64  356
     public DefaultXTag()
 65   
     {
 66  356
         instanceCount++;
 67   
     }
 68   
 
 69   
     /**
 70   
      * Skips whitespaces, starting from index i till the first non-whitespace
 71   
      * character or end of s and returns the new index.
 72   
      *
 73   
      * @param s  Describe what the parameter does
 74   
      * @param i  Describe what the parameter does
 75   
      * @return   Describe the return value
 76   
      */
 77  69
     private static int skipWhitespace( String s, int i )
 78   
     {
 79  69
         while( i < s.length() && Character.isWhitespace( s.charAt( i ) ) )
 80   
         {
 81  12
             i++;
 82   
         }
 83  69
         return i;
 84   
     }
 85   
 
 86  0
     public final XDoc getDoc()
 87   
     {
 88  0
         return _doc;
 89   
     }
 90   
 
 91   
     /**
 92   
      * Returns the first tag parameter with the given name, or null if none exist;
 93   
      *
 94   
      * @param attributeName  Describe what the parameter does
 95   
      * @return               The Parameter value
 96   
      */
 97  14
     public final String getAttributeValue( String attributeName )
 98   
     {
 99  14
         if( !_isParsed )
 100   
         {
 101  3
             parse();
 102   
         }
 103   
 
 104  14
         return _attributes == null ? null : ( String ) _attributes.get( attributeName );
 105   
     }
 106   
 
 107   
     /**
 108   
      * Returns all tag parameters with the given name, or an empty List if none
 109   
      * exist;
 110   
      *
 111   
      * @return   The Parameters value
 112   
      */
 113  9
     public final Collection getAttributeNames()
 114   
     {
 115  9
         if( !_isParsed )
 116   
         {
 117  0
             parse();
 118   
         }
 119  9
         return _attributeNames == null ? AbstractProgramElement.EMPTY_LIST : _attributeNames;
 120   
     }
 121   
 
 122   
     /**
 123   
      * Returns the full name of the tag, excluding the @
 124   
      *
 125   
      * @return   tag name
 126   
      */
 127  14
     public final String getName()
 128   
     {
 129  14
         if( !_isParsed )
 130   
         {
 131  13
             parse();
 132   
         }
 133  14
         return _name;
 134   
     }
 135   
 
 136   
     /**
 137   
      * Returns the full value of the tag.
 138   
      *
 139   
      * @return   full value of the tag
 140   
      */
 141  159
     public final String getValue()
 142   
     {
 143  159
         return _value;
 144   
     }
 145   
 
 146  0
     public final int getLineNumber()
 147   
     {
 148  0
         return _lineNumber;
 149   
     }
 150   
 
 151  0
     public final String getInfo()
 152   
     {
 153  0
         XProgramElement pe = getDoc().getOwner();
 154  0
         AbstractFile file = XJavaDoc.getSourceFileFor( pe );
 155   
 
 156  0
         return "@" + getName() + " at " + file.getPath() + ":" + getLineNumber();
 157   
     }
 158   
 
 159   
     /**
 160   
      * Adds a parameter
 161   
      *
 162   
      * @param attributeName   The new Attribute value
 163   
      * @param attributeValue  The new Attribute value
 164   
      */
 165  0
     public final void setAttribute( String attributeName, String attributeValue )
 166   
     {
 167  0
         if( !_isParsed )
 168   
         {
 169  0
             parse();
 170   
         }
 171  0
         setAttribute_Impl( attributeName, attributeValue );
 172  0
         fireTagChanged();
 173  0
         _isDirty = true;
 174  0
         _value = null;
 175   
     }
 176   
 
 177   
     /**
 178   
      * add doc listener interested in chages
 179   
      *
 180   
      * @param tagListener  The feature to be added to the TagListener attribute
 181   
      */
 182  355
     public final void addTagListener( XTagListener tagListener )
 183   
     {
 184  355
         ensureTagListenersInitialised();
 185   
 
 186  355
         _tagListeners.add( tagListener );
 187   
     }
 188   
 
 189   
     /**
 190   
      * remove doc listener
 191   
      *
 192   
      * @param tagListener
 193   
      */
 194  0
     public final void removeTagListener( XTagListener tagListener )
 195   
     {
 196  0
         ensureTagListenersInitialised();
 197   
 
 198  0
         _tagListeners.remove( tagListener );
 199   
     }
 200   
 
 201   
     /**
 202   
      * Removes an attribute
 203   
      *
 204   
      * @param attributeName  atribute to remove
 205   
      * @return               the removed attribute value or null if it didn't exist
 206   
      */
 207  0
     public final String removeAttribute( String attributeName )
 208   
     {
 209  0
         if( !_isParsed )
 210   
         {
 211  0
             parse();
 212   
         }
 213  0
         _isDirty = true;
 214  0
         resetValue();
 215  0
         fireTagChanged();
 216   
 
 217  0
         String removed = ( String ) _attributes.remove( attributeName );
 218   
 
 219  0
         if( removed != null )
 220   
         {
 221  0
             _attributeNames.remove( attributeName );
 222  0
             _value = null;
 223   
         }
 224  0
         return removed;
 225   
     }
 226   
 
 227  0
     public final boolean equals( Object o )
 228   
     {
 229   
         // we compare by equality
 230  0
         return this == o;
 231   
     }
 232   
 
 233  0
     public final int hashCode()
 234   
     {
 235  0
         if( _hash == Integer.MIN_VALUE )
 236   
         {
 237  0
             _hash += _name.hashCode();
 238   
         }
 239  0
         return _hash;
 240   
     }
 241   
 
 242   
     /**
 243   
      * Validates the tag
 244   
      *
 245   
      * @exception TagValidationException
 246   
      */
 247  0
     public void validate() throws TagValidationException
 248   
     {
 249   
         // Default is OK.
 250   
     }
 251   
 
 252   
     /**
 253   
      * Utility method that should be called from {@link #validate()} in
 254   
      * case ov a validation failure. Throws a new TagValidationException
 255   
      * with
 256   
      * @param message the message to include
 257   
      * @throws TagValidationException always thrown.
 258   
      */
 259  0
     protected final void fail(String message) throws TagValidationException {
 260  0
         throw new TagValidationException( message, this );
 261   
     }
 262   
 
 263   
     /**
 264   
      * Sets the name and value. Called immediately after initialisation by
 265   
      * XTagFactory. Don't call this method from anywhere else.
 266   
      *
 267   
      * @param name
 268   
      * @param value
 269   
      * @param doc
 270   
      * @param lineNumber
 271   
      */
 272  356
     final void init( String name, String value, XDoc doc, int lineNumber )
 273   
     {
 274  356
         _name = name;
 275  356
         _doc = doc;
 276  356
         _lineNumber = lineNumber;
 277  356
         _isDirty = false;
 278  356
         _value = value;
 279   
 
 280   
         // we register ourself as one of the global tags in the corresponding
 281   
         // SourceClass. This is to make it easier for the SourceClass to
 282   
         // loop over all tags and ask them to validate themselves when the parsing
 283   
         // is done.
 284   
 
 285  356
         if( doc != null )
 286   
         {
 287   
             // In fact, doc should never be null. -But some of the JUnit tests
 288   
             // fail to set up mocks properly, so they pass in null for doc.
 289   
             // This is only to avoid NPE from tht JUnit tests. It's a dirty
 290   
             // hack and the tests should be fixed.
 291  355
             XProgramElement owner = doc.getOwner();
 292  355
             _xJavaDoc = owner.getXJavaDoc();
 293  355
             _value = _xJavaDoc.dereferenceProperties( value );
 294   
 
 295  355
             if( owner != null )
 296   
             {
 297  355
                 SourceClass sourceClass;
 298   
 
 299  355
                 if( owner.getContainingClass() != null )
 300   
                 {
 301  345
                     sourceClass = ( SourceClass ) owner.getContainingClass();
 302   
                 }
 303   
                 else
 304   
                 {
 305  10
                     sourceClass = ( SourceClass ) owner;
 306   
                 }
 307  355
                 sourceClass.addTagForValidation( this );
 308   
             }
 309   
         }
 310   
     }
 311   
 
 312  19
     private final void setAttribute_Impl( String attributeName, String attributeValue )
 313   
     {
 314  19
         if( attributeName == null )
 315   
         {
 316  0
             throw new IllegalArgumentException( "attributeName can't be null!" );
 317   
         }
 318  19
         if( _attributes == null )
 319   
         {
 320  11
             _attributes = new HashMap();
 321  11
             _attributeNames = new LinkedList();
 322   
         }
 323  19
         if( !_attributes.containsKey( attributeName ) )
 324   
         {
 325   
             // New attribute. Just append it.
 326  19
             _attributeNames.add( attributeName );
 327   
         }
 328   
 
 329  19
         if( _xJavaDoc != null ) {
 330  16
             attributeValue = _xJavaDoc.dereferenceProperties( attributeValue );
 331   
         }
 332  19
         _attributes.put( attributeName, attributeValue );
 333   
 
 334  19
         resetValue();
 335   
     }
 336   
 
 337  355
     private final void ensureTagListenersInitialised()
 338   
     {
 339  355
         if( _tagListeners == null )
 340   
         {
 341  355
             _tagListeners = new HashSet();
 342   
         }
 343   
     }
 344   
 
 345   
     /**
 346   
      * fire tagChanged event
 347   
      */
 348  0
     private void fireTagChanged()
 349   
     {
 350  0
         if( _tagListeners == null )
 351  0
             return;
 352   
 
 353  0
         for( Iterator i = _tagListeners.iterator(); i.hasNext();  )
 354   
         {
 355  0
             XTagListener tagListener = ( XTagListener ) i.next();
 356   
 
 357  0
             tagListener.tagChanged( new XTagEvent( this ) );
 358   
         }
 359   
     }
 360   
 
 361   
     /**
 362   
      * Given the raw javadoc tag content as the <i>value</i> parameter parses it
 363   
      * and sets the parameter. If anything is malformed (not (foo="xxx")+), then
 364   
      * nothing is set.
 365   
      */
 366  16
     private final void parse()
 367   
     {
 368  16
         if( !_isParsed )
 369   
         {
 370  16
             String attributeName = null;
 371  16
             StringBuffer attributeValue = new StringBuffer();
 372  16
             int i = 0;
 373  16
             int end = 0;
 374   
 
 375  16
             String value = getValue();
 376   
 
 377  16
             while( i < value.length() )
 378   
             {
 379  23
                 i = skipWhitespace( value, i );
 380   
 
 381   
                 //explicitly to handle the tailing white spaces
 382   
 
 383  23
                 if( i >= value.length() )
 384   
                 {
 385  0
                     break;
 386   
                 }
 387   
 
 388   
                 //read attribute name
 389   
 
 390  23
                 end = i;
 391  23
                 while( end < value.length() && value.charAt( end ) != '=' && ( !Character.isWhitespace( value.charAt( end ) ) ) )
 392   
                 {
 393  103
                     end++;
 394   
                 }
 395   
 
 396  23
                 attributeName = value.substring( i, end );
 397  23
                 i = skipWhitespace( value, end );
 398   
 
 399   
                 //skip = sign
 400   
 
 401  23
                 if( i < value.length() && value.charAt( i ) == '=' )
 402   
                 {
 403  19
                     i++;
 404   
                 }
 405   
 
 406   
                 /*
 407   
                  * removed single valued
 408   
                  */
 409  23
                 i = skipWhitespace( value, i );
 410   
 
 411   
                 //skip " sign
 412   
 
 413  23
                 if( i < value.length() && value.charAt( i ) == '"' )
 414   
                 {
 415  19
                     i++;
 416   
                 }
 417   
                 else
 418   
                 {
 419   
                     //if (_log.isDebugEnabled()) _log.debug("Error in @tag: \" sign expected but something different found, @tags=" + value);
 420  4
                     _isParsed = true;
 421  4
                     return;
 422   
                 }
 423   
 
 424   
                 //read attribute value
 425   
 
 426  181
                 while( i < value.length() )
 427   
                 {
 428  181
                     if( value.charAt( i ) == '"' )
 429   
                     {
 430   
                         //if not escaped \" char
 431   
 
 432  19
                         if( value.charAt( i - 1 ) != '\\' )
 433   
                         {
 434   
                             //if last " (last parameter) in whole value string
 435   
 
 436  19
                             if( i + 1 >= value.length() )
 437   
                             {
 438  11
                                 break;
 439   
                             }
 440   
                             else
 441   
                             {
 442   
                                 //if tailing " with whitespace after it
 443   
 
 444  8
                                 if( Character.isWhitespace( value.charAt( i + 1 ) ) )
 445   
                                 {
 446  8
                                     break;
 447   
                                 }
 448   
                                 else
 449   
                                 {
 450   
                                     //probably user does not know escaping is needed!
 451   
                                     //if (_log.isDebugEnabled()) _log.debug("Error in @tag: to put \" in a parameter value you need to escape \" character with \\\", @tags=" + value);
 452  0
                                     _isParsed = true;
 453  0
                                     return;
 454   
                                 }
 455   
                             }
 456   
                         }
 457   
                         else
 458   
                         {
 459   
                             //remove previous \
 460  0
                             attributeValue.delete( attributeValue.length() - 1, attributeValue.length() );
 461   
                         }
 462   
                     }
 463   
 
 464  162
                     attributeValue.append( value.charAt( i ) );
 465   
 
 466  162
                     i++;
 467   
                 }
 468   
 
 469   
                 //skip " sign
 470   
 
 471  19
                 if( i < value.length() && value.charAt( i ) == '"' )
 472   
                 {
 473  19
                     i++;
 474   
                 }
 475   
                 else
 476   
                 {
 477   
                     //_log.warn("Error in @tag: tailing \" sign expected but not found, @tags=" + value);
 478  0
                     _isParsed = true;
 479  0
                     return;
 480   
                 }
 481  19
                 setAttribute_Impl( attributeName, attributeValue.toString() );
 482  19
                 attributeName = null;
 483  19
                 attributeValue.delete( 0, attributeValue.length() );
 484   
             }
 485  12
             _isParsed = true;
 486   
         }
 487   
     }
 488   
 
 489  19
     private final void resetValue() {
 490  19
         StringBuffer sb = new StringBuffer();
 491   
 
 492  19
         if( _attributeNames != null )
 493   
         {
 494  19
             for( Iterator attributeNames = _attributeNames.iterator(); attributeNames.hasNext();  )
 495   
             {
 496  28
                 String attributeName = ( String ) attributeNames.next();
 497  28
                 String attributeValue = ( String ) _attributes.get( attributeName );
 498   
 
 499  28
                 sb.append( attributeName );
 500  28
                 sb.append( "=\"" );
 501  28
                 sb.append( attributeValue.trim() );
 502  28
                 sb.append( "\" " );
 503   
             }
 504   
         }
 505  19
         _value = sb.toString().trim();
 506   
     }
 507   
 }
 508