Coverage Report - org.apache.tapestry.markup.MarkupWriterImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
MarkupWriterImpl
95% 
100% 
1.8
 
 1  
 // Copyright 2005 The Apache Software Foundation
 2  
 //
 3  
 // Licensed under the Apache License, Version 2.0 (the "License");
 4  
 // you may not use this file except in compliance with the License.
 5  
 // You may obtain a copy of the License at
 6  
 //
 7  
 //     http://www.apache.org/licenses/LICENSE-2.0
 8  
 //
 9  
 // Unless required by applicable law or agreed to in writing, software
 10  
 // distributed under the License is distributed on an "AS IS" BASIS,
 11  
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 // See the License for the specific language governing permissions and
 13  
 // limitations under the License.
 14  
 
 15  
 package org.apache.tapestry.markup;
 16  
 
 17  
 import org.apache.hivemind.ApplicationRuntimeException;
 18  
 import org.apache.hivemind.util.Defense;
 19  
 import org.apache.tapestry.IMarkupWriter;
 20  
 import org.apache.tapestry.NestedMarkupWriter;
 21  
 
 22  
 import java.io.PrintWriter;
 23  
 import java.util.*;
 24  
 
 25  
 /**
 26  
  * Completely revised (for 4.0) implementation of {@link org.apache.tapestry.IMarkupWriter}. No
 27  
  * longer does internal buffering (since the servlet/portlet APIs support that natively) and wraps
 28  
  * around a {@link java.io.PrintWriter} (rather than an {@link java.io.OutputStream}).
 29  
  * 
 30  
  * @author Howard M. Lewis Ship
 31  
  * @since 4.0
 32  
  */
 33  
 public class MarkupWriterImpl implements IMarkupWriter
 34  
 {
 35  
     /**
 36  
      * The underlying {@link PrintWriter}that output is sent to.
 37  
      */
 38  
 
 39  
     private PrintWriter _writer;
 40  
 
 41  
     /**
 42  
      * Filter used to "escape" characters that need any kind of special encoding for the output
 43  
      * content type.
 44  
      */
 45  
 
 46  
     private MarkupFilter _filter;
 47  
 
 48  
     /**
 49  
      * Indicates whether a tag is open or not. A tag is opened by {@link #begin(String)}or
 50  
      * {@link #beginEmpty(String)}. It stays open while calls to the <code>attribute()</code>
 51  
      * methods are made. It is closed (the '&gt;' is written) when any other method is invoked.
 52  
      */
 53  
 
 54  103
     private boolean _openTag = false;
 55  
 
 56  
     /**
 57  
      * Indicates that the tag was opened with {@link #beginEmpty(String)}, which affects how the
 58  
      * tag is closed (a slash is added to indicate the lack of a body). This is compatible with
 59  
      * HTML, but reflects an XML/XHTML leaning.
 60  
      */
 61  
 
 62  103
     private boolean _emptyTag = false;
 63  
     
 64  
     private String _contentType;
 65  
     
 66  
     /**
 67  
      * A Stack of Strings used to track the active tag elements. Elements are active until the
 68  
      * corresponding close tag is written. The {@link #push(String)}method adds elements to the
 69  
      * stack, {@link #pop()}removes them.
 70  
      */
 71  
 
 72  
     private List _activeElementStack;
 73  
     
 74  
     /**
 75  
      *  Attributes are stored in a map until an open tag is closed. The linked hashmap ensures that
 76  
      *  ordering remains constant.
 77  
      */
 78  
     
 79  103
     private final Map _attrMap = new LinkedHashMap();
 80  
     
 81  
     public MarkupWriterImpl(String contentType, PrintWriter writer, MarkupFilter filter)
 82  103
     {
 83  103
         Defense.notNull(contentType, "contentType");
 84  103
         Defense.notNull(writer, "writer");
 85  103
         Defense.notNull(filter, "filter");
 86  
 
 87  103
         _contentType = contentType;
 88  103
         _writer = writer;
 89  103
         _filter = filter;
 90  103
     }
 91  
 
 92  
     public void attribute(String name, int value)
 93  
     {
 94  2
         checkTagOpen();
 95  
         
 96  1
         _attrMap.put(name, new DefaultAttribute(String.valueOf(value), false));
 97  1
     }
 98  
 
 99  
     public void attribute(String name, boolean value)
 100  
     {
 101  3
         checkTagOpen();
 102  
         
 103  2
         _attrMap.put(name, new DefaultAttribute(String.valueOf(value), false));
 104  2
     }
 105  
 
 106  
     public void attribute(String name, String value)
 107  
     {
 108  178
         attribute(name, value, false);
 109  177
     }
 110  
 
 111  
     public void attribute(String name, String value, boolean raw)
 112  
     {
 113  178
         checkTagOpen();
 114  
         
 115  177
         _attrMap.put(name, new DefaultAttribute(value, raw));
 116  177
     }
 117  
     
 118  
     public void appendAttribute(String name, boolean value)
 119  
     {
 120  1
         checkTagOpen();
 121  
         
 122  1
         appendAttribute(name, String.valueOf(value));
 123  1
     }
 124  
     
 125  
     public void appendAttribute(String name, int value)
 126  
     {
 127  0
         checkTagOpen();
 128  
         
 129  0
         appendAttribute(name, String.valueOf(value));
 130  0
     }
 131  
     
 132  
     public void appendAttribute(String name, String value)
 133  
     {
 134  8
         checkTagOpen();
 135  
         
 136  8
         DefaultAttribute attr = (DefaultAttribute)_attrMap.get(name);
 137  
         
 138  8
         if (attr == null) {
 139  6
             attr = new DefaultAttribute(value, false);
 140  6
             _attrMap.put(name, attr);
 141  6
             return;
 142  
         }
 143  
         
 144  2
         attr.append(value);
 145  2
     }
 146  
 
 147  
     public void appendAttributeRaw(String name, String value)
 148  
     {
 149  2
         checkTagOpen();
 150  
         
 151  2
         DefaultAttribute attr = (DefaultAttribute)_attrMap.get(name);
 152  
         
 153  2
         if (attr == null) {
 154  2
             attr = new DefaultAttribute(value, true);
 155  
             
 156  2
             _attrMap.put(name, attr);
 157  2
             return;
 158  
         }
 159  
         
 160  0
         attr.setRaw(true);
 161  0
         attr.append(value);
 162  0
     }
 163  
 
 164  
     public Attribute getAttribute(String name)
 165  
     {
 166  3
         checkTagOpen();
 167  
         
 168  3
         return (Attribute)_attrMap.get(name);
 169  
     }
 170  
     
 171  
     public boolean hasAttribute(String name)
 172  
     {
 173  6
         checkTagOpen();
 174  
         
 175  6
         return _attrMap.containsKey(name);
 176  
     }
 177  
     
 178  
     public void clearAttributes()
 179  
     {
 180  1
         checkTagOpen();
 181  
         
 182  1
         _attrMap.clear();
 183  1
     }
 184  
     
 185  
     public Attribute removeAttribute(String name)
 186  
     {
 187  1
         checkTagOpen();
 188  
         
 189  1
         return (Attribute)_attrMap.remove(name);
 190  
     }
 191  
     
 192  
     /**
 193  
      * Prints the value, if non-null. May pass it through the filter, unless raw is true.
 194  
      */
 195  
 
 196  
     private void maybePrintFiltered(char[] data, int offset, int length, boolean raw, boolean isAttribute)
 197  
     {
 198  135
         if (data == null || length <= 0)
 199  3
             return;
 200  
 
 201  132
         if (raw)
 202  
         {
 203  84
             _writer.write(data, offset, length);
 204  84
             return;
 205  
         }
 206  
 
 207  48
         _filter.print(_writer, data, offset, length, isAttribute);
 208  48
     }
 209  
 
 210  
     public void attributeRaw(String name, String value)
 211  
     {
 212  0
         attribute(name, value, true);
 213  0
     }
 214  
 
 215  
     public void begin(String name)
 216  
     {
 217  101
         if (_openTag)
 218  18
             closeTag();
 219  
 
 220  101
         push(name);
 221  
 
 222  101
         _writer.print('<');
 223  101
         _writer.print(name);
 224  
 
 225  101
         _openTag = true;
 226  101
         _emptyTag = false;
 227  101
     }
 228  
 
 229  
     public void beginEmpty(String name)
 230  
     {
 231  22
         if (_openTag)
 232  13
             closeTag();
 233  
 
 234  22
         _writer.print('<');
 235  22
         _writer.print(name);
 236  
 
 237  22
         _openTag = true;
 238  22
         _emptyTag = true;
 239  22
     }
 240  
 
 241  
     public boolean checkError()
 242  
     {
 243  0
         return _writer.checkError();
 244  
     }
 245  
 
 246  
     public void close()
 247  
     {
 248  9
         if (_openTag)
 249  1
             closeTag();
 250  
 
 251  
         // Close any active elements.
 252  
 
 253  15
         while (!stackEmpty())
 254  
         {
 255  6
             _writer.print("</");
 256  6
             _writer.print(pop());
 257  6
             _writer.print('>');
 258  
         }
 259  
 
 260  9
         _writer.close();
 261  
 
 262  9
         _writer = null;
 263  9
         _filter = null;
 264  9
         _activeElementStack = null;
 265  9
     }
 266  
 
 267  
     public void closeTag()
 268  
     {
 269  122
         flushAttributes();
 270  
         
 271  122
         if (_emptyTag)
 272  21
             _writer.print(" /");
 273  
 
 274  122
         _writer.print('>');
 275  
 
 276  122
         _openTag = false;
 277  122
         _emptyTag = false;
 278  122
     }
 279  
     
 280  
     /**
 281  
      * Causes any pending attributes on the current open tag
 282  
      * to be written out to the writer.
 283  
      */
 284  
     void flushAttributes()
 285  
     {
 286  122
         if (_attrMap.size() > 0) {
 287  
             
 288  84
             Iterator it = _attrMap.keySet().iterator();
 289  267
             while (it.hasNext()) {
 290  
                 
 291  183
                 String key = (String)it.next();
 292  183
                 DefaultAttribute attr = (DefaultAttribute)_attrMap.get(key);
 293  
                 
 294  183
                 attr.print(key, _writer, _filter);
 295  183
             }
 296  
             
 297  84
             _attrMap.clear();
 298  
         }
 299  
         
 300  122
     }
 301  
     
 302  
     public void comment(String value)
 303  
     {
 304  2
         if (_openTag)
 305  1
             closeTag();
 306  
 
 307  2
         _writer.print("<!-- ");
 308  2
         _writer.print(value);
 309  2
         _writer.println(" -->");
 310  2
     }
 311  
 
 312  
     public void end()
 313  
     {
 314  78
         if (_openTag)
 315  23
             closeTag();
 316  
 
 317  78
         if (stackEmpty())
 318  1
             throw new ApplicationRuntimeException(MarkupMessages.endWithEmptyStack());
 319  
 
 320  77
         _writer.print("</");
 321  77
         _writer.print(pop());
 322  77
         _writer.print('>');
 323  77
     }
 324  
 
 325  
     public void end(String name)
 326  
     {
 327  7
         if (_openTag)
 328  3
             closeTag();
 329  
 
 330  7
         if (_activeElementStack == null || !_activeElementStack.contains(name))
 331  1
             throw new ApplicationRuntimeException(MarkupMessages.elementNotOnStack(
 332  
                     name,
 333  
                     _activeElementStack));
 334  
 
 335  
         while (true)
 336  
         {
 337  8
             String tagName = pop();
 338  
 
 339  8
             _writer.print("</");
 340  8
             _writer.print(tagName);
 341  8
             _writer.print('>');
 342  
 
 343  8
             if (tagName.equals(name))
 344  6
                 break;
 345  2
         }
 346  6
     }
 347  
 
 348  
     public void flush()
 349  
     {
 350  5
         _writer.flush();
 351  5
     }
 352  
 
 353  
     public NestedMarkupWriter getNestedWriter()
 354  
     {
 355  8
         return new NestedMarkupWriterImpl(this, _filter);
 356  
     }
 357  
 
 358  
     public void print(char[] data, int offset, int length)
 359  
     {
 360  2
         print(data, offset, length, false);
 361  2
     }
 362  
 
 363  
     public void printRaw(char[] buffer, int offset, int length)
 364  
     {
 365  1
         print(buffer, offset, length, true);
 366  1
     }
 367  
 
 368  
     public void print(char[] buffer, int offset, int length, boolean raw)
 369  
     {
 370  135
         if (_openTag)
 371  42
             closeTag();
 372  
 
 373  135
         maybePrintFiltered(buffer, offset, length, raw, false);
 374  135
     }
 375  
 
 376  
     public void print(String value)
 377  
     {
 378  43
         print(value, false);
 379  43
     }
 380  
 
 381  
     public void printRaw(String value)
 382  
     {
 383  77
         print(value, true);
 384  77
     }
 385  
 
 386  
     public void print(String value, boolean raw)
 387  
     {
 388  132
         if (value == null || value.length() == 0)
 389  
         {
 390  3
             print(null, 0, 0, raw);
 391  3
             return;
 392  
         }
 393  
 
 394  129
         char[] buffer = value.toCharArray();
 395  
 
 396  129
         print(buffer, 0, buffer.length, raw);
 397  129
     }
 398  
 
 399  
     public void print(char value)
 400  
     {
 401  1
         char[] data = new char[]
 402  
         { value };
 403  
 
 404  1
         print(data, 0, 1);
 405  1
     }
 406  
 
 407  
     public void print(int value)
 408  
     {
 409  2
         if (_openTag)
 410  1
             closeTag();
 411  
 
 412  2
         _writer.print(value);
 413  2
     }
 414  
 
 415  
     public void println()
 416  
     {
 417  23
         if (_openTag)
 418  2
             closeTag();
 419  
 
 420  23
         _writer.println();
 421  23
     }
 422  
 
 423  
     public String getContentType()
 424  
     {
 425  9
         return _contentType;
 426  
     }
 427  
 
 428  
     private void checkTagOpen()
 429  
     {
 430  205
         if (!_openTag)
 431  3
             throw new IllegalStateException(MarkupMessages.tagNotOpen());
 432  202
     }
 433  
 
 434  
     private void push(String name)
 435  
     {
 436  101
         if (_activeElementStack == null)
 437  71
             _activeElementStack = new ArrayList();
 438  
 
 439  101
         _activeElementStack.add(name);
 440  101
     }
 441  
 
 442  
     private String pop()
 443  
     {
 444  91
         int lastIndex = _activeElementStack.size() - 1;
 445  
 
 446  91
         return (String) _activeElementStack.remove(lastIndex);
 447  
     }
 448  
 
 449  
     private boolean stackEmpty()
 450  
     {
 451  93
         return _activeElementStack == null || _activeElementStack.isEmpty();
 452  
     }
 453  
 }