Coverage Report - org.apache.tapestry.markup.MarkupWriterImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
MarkupWriterImpl
0%
0/175
0%
0/56
1.9
 
 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  0
     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  0
     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  0
     private final Map _attrMap = new LinkedHashMap();
 80  
     
 81  
     public MarkupWriterImpl(String contentType, PrintWriter writer, MarkupFilter filter)
 82  0
     {
 83  0
         Defense.notNull(contentType, "contentType");
 84  0
         Defense.notNull(writer, "writer");
 85  0
         Defense.notNull(filter, "filter");
 86  
 
 87  0
         _contentType = contentType;
 88  0
         _writer = writer;
 89  0
         _filter = filter;
 90  0
     }
 91  
 
 92  
     public void attribute(String name, int value)
 93  
     {
 94  0
         checkTagOpen();
 95  
         
 96  0
         _attrMap.put(name, new DefaultAttribute(String.valueOf(value), false));
 97  0
     }
 98  
 
 99  
     public void attribute(String name, boolean value)
 100  
     {
 101  0
         checkTagOpen();
 102  
         
 103  0
         _attrMap.put(name, new DefaultAttribute(String.valueOf(value), false));
 104  0
     }
 105  
 
 106  
     public void attribute(String name, String value)
 107  
     {
 108  0
         attribute(name, value, false);
 109  0
     }
 110  
 
 111  
     public void attribute(String name, String value, boolean raw)
 112  
     {
 113  0
         checkTagOpen();
 114  
         
 115  0
         _attrMap.put(name, new DefaultAttribute(value, raw));
 116  0
     }
 117  
     
 118  
     public void appendAttribute(String name, boolean value)
 119  
     {
 120  0
         checkTagOpen();
 121  
         
 122  0
         appendAttribute(name, String.valueOf(value));
 123  0
     }
 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  0
         checkTagOpen();
 135  
         
 136  0
         DefaultAttribute attr = (DefaultAttribute)_attrMap.get(name);
 137  
         
 138  0
         if (attr == null)
 139  
         {
 140  0
             attr = new DefaultAttribute(value, false);
 141  
             
 142  0
             _attrMap.put(name, attr);
 143  0
             return;
 144  
         }
 145  
         
 146  0
         attr.append(value);
 147  0
     }
 148  
 
 149  
     public void appendAttributeRaw(String name, String value)
 150  
     {
 151  0
         checkTagOpen();
 152  
         
 153  0
         DefaultAttribute attr = (DefaultAttribute)_attrMap.get(name);
 154  
         
 155  0
         if (attr == null) {
 156  0
             attr = new DefaultAttribute(value, true);
 157  
             
 158  0
             _attrMap.put(name, attr);
 159  0
             return;
 160  
         }
 161  
         
 162  0
         attr.setRaw(true);
 163  0
         attr.append(value);
 164  0
     }
 165  
 
 166  
     public Attribute getAttribute(String name)
 167  
     {
 168  0
         checkTagOpen();
 169  
         
 170  0
         return (Attribute)_attrMap.get(name);
 171  
     }
 172  
     
 173  
     public boolean hasAttribute(String name)
 174  
     {
 175  0
         checkTagOpen();
 176  
         
 177  0
         return _attrMap.containsKey(name);
 178  
     }
 179  
     
 180  
     public void clearAttributes()
 181  
     {
 182  0
         checkTagOpen();
 183  
         
 184  0
         _attrMap.clear();
 185  0
     }
 186  
     
 187  
     public Attribute removeAttribute(String name)
 188  
     {
 189  0
         checkTagOpen();
 190  
         
 191  0
         return (Attribute)_attrMap.remove(name);
 192  
     }
 193  
     
 194  
     /**
 195  
      * Prints the value, if non-null. May pass it through the filter, unless raw is true.
 196  
      */
 197  
 
 198  
     private void maybePrintFiltered(char[] data, int offset, int length, boolean raw, boolean isAttribute)
 199  
     {
 200  0
         if (data == null || length <= 0)
 201  0
             return;
 202  
 
 203  0
         if (raw)
 204  
         {
 205  0
             _writer.write(data, offset, length);
 206  0
             return;
 207  
         }
 208  
 
 209  0
         _filter.print(_writer, data, offset, length, isAttribute);
 210  0
     }
 211  
 
 212  
     public void attributeRaw(String name, String value)
 213  
     {
 214  0
         attribute(name, value, true);
 215  0
     }
 216  
 
 217  
     public void begin(String name)
 218  
     {
 219  0
         if (_openTag)
 220  0
             closeTag();
 221  
 
 222  0
         push(name);
 223  
 
 224  0
         _writer.print('<');
 225  0
         _writer.print(name);
 226  
 
 227  0
         _openTag = true;
 228  0
         _emptyTag = false;
 229  0
     }
 230  
 
 231  
     public void beginEmpty(String name)
 232  
     {
 233  0
         if (_openTag)
 234  0
             closeTag();
 235  
 
 236  0
         _writer.print('<');
 237  0
         _writer.print(name);
 238  
 
 239  0
         _openTag = true;
 240  0
         _emptyTag = true;
 241  0
     }
 242  
 
 243  
     public boolean checkError()
 244  
     {
 245  0
         return _writer.checkError();
 246  
     }
 247  
 
 248  
     public void close()
 249  
     {
 250  0
         if (_openTag)
 251  0
             closeTag();
 252  
 
 253  
         // Close any active elements.
 254  
 
 255  0
         while (!stackEmpty())
 256  
         {
 257  0
             _writer.print("</");
 258  0
             _writer.print(pop());
 259  0
             _writer.print('>');
 260  
         }
 261  
 
 262  0
         _writer.close();
 263  
 
 264  0
         _writer = null;
 265  0
         _filter = null;
 266  0
         _activeElementStack = null;
 267  0
     }
 268  
 
 269  
     public void closeTag()
 270  
     {
 271  0
         flushAttributes();
 272  
         
 273  0
         if (_emptyTag)
 274  0
             _writer.print(" /");
 275  
 
 276  0
         _writer.print('>');
 277  
 
 278  0
         _openTag = false;
 279  0
         _emptyTag = false;
 280  0
     }
 281  
     
 282  
     /**
 283  
      * Causes any pending attributes on the current open tag
 284  
      * to be written out to the writer.
 285  
      */
 286  
     void flushAttributes()
 287  
     {
 288  0
         if (_attrMap.size() > 0) {
 289  
             
 290  0
             Iterator it = _attrMap.keySet().iterator();
 291  0
             while (it.hasNext()) {
 292  
                 
 293  0
                 String key = (String)it.next();
 294  0
                 DefaultAttribute attr = (DefaultAttribute)_attrMap.get(key);
 295  
                 
 296  0
                 attr.print(key, _writer, _filter);
 297  0
             }
 298  
             
 299  0
             _attrMap.clear();
 300  
         }
 301  
         
 302  0
     }
 303  
     
 304  
     public void comment(String value)
 305  
     {
 306  0
         if (_openTag)
 307  0
             closeTag();
 308  
 
 309  0
         _writer.print("<!-- ");
 310  0
         _writer.print(value);
 311  0
         _writer.println(" -->");
 312  0
     }
 313  
 
 314  
     public void end()
 315  
     {
 316  0
         if (_openTag)
 317  0
             closeTag();
 318  
 
 319  0
         if (stackEmpty())
 320  0
             throw new ApplicationRuntimeException(MarkupMessages.endWithEmptyStack());
 321  
 
 322  0
         _writer.print("</");
 323  0
         _writer.print(pop());
 324  0
         _writer.print('>');
 325  0
     }
 326  
 
 327  
     public void end(String name)
 328  
     {
 329  0
         if (_openTag)
 330  0
             closeTag();
 331  
 
 332  0
         if (_activeElementStack == null || !_activeElementStack.contains(name))
 333  0
             throw new ApplicationRuntimeException(MarkupMessages.elementNotOnStack(
 334  
                     name,
 335  
                     _activeElementStack));
 336  
 
 337  
         while (true)
 338  
         {
 339  0
             String tagName = pop();
 340  
 
 341  0
             _writer.print("</");
 342  0
             _writer.print(tagName);
 343  0
             _writer.print('>');
 344  
 
 345  0
             if (tagName.equals(name))
 346  0
                 break;
 347  0
         }
 348  0
     }
 349  
 
 350  
     public void flush()
 351  
     {
 352  0
         _writer.flush();
 353  0
     }
 354  
 
 355  
     public NestedMarkupWriter getNestedWriter()
 356  
     {
 357  0
         return new NestedMarkupWriterImpl(this, _filter);
 358  
     }
 359  
 
 360  
     public void print(char[] data, int offset, int length)
 361  
     {
 362  0
         print(data, offset, length, false);
 363  0
     }
 364  
 
 365  
     public void printRaw(char[] buffer, int offset, int length)
 366  
     {
 367  0
         print(buffer, offset, length, true);
 368  0
     }
 369  
 
 370  
     public void print(char[] buffer, int offset, int length, boolean raw)
 371  
     {
 372  0
         if (_openTag)
 373  0
             closeTag();
 374  
 
 375  0
         maybePrintFiltered(buffer, offset, length, raw, false);
 376  0
     }
 377  
 
 378  
     public void print(String value)
 379  
     {
 380  0
         print(value, false);
 381  0
     }
 382  
 
 383  
     public void printRaw(String value)
 384  
     {
 385  0
         print(value, true);
 386  0
     }
 387  
 
 388  
     public void print(String value, boolean raw)
 389  
     {
 390  0
         if (value == null || value.length() == 0)
 391  
         {
 392  0
             print(null, 0, 0, raw);
 393  0
             return;
 394  
         }
 395  
 
 396  0
         char[] buffer = value.toCharArray();
 397  
 
 398  0
         print(buffer, 0, buffer.length, raw);
 399  0
     }
 400  
 
 401  
     public void print(char value)
 402  
     {
 403  0
         char[] data = new char[]
 404  
         { value };
 405  
 
 406  0
         print(data, 0, 1);
 407  0
     }
 408  
 
 409  
     public void print(int value)
 410  
     {
 411  0
         if (_openTag)
 412  0
             closeTag();
 413  
 
 414  0
         _writer.print(value);
 415  0
     }
 416  
 
 417  
     public void println()
 418  
     {
 419  0
         if (_openTag)
 420  0
             closeTag();
 421  
 
 422  0
         _writer.println();
 423  0
     }
 424  
 
 425  
     public String getContentType()
 426  
     {
 427  0
         return _contentType;
 428  
     }
 429  
 
 430  
     private void checkTagOpen()
 431  
     {
 432  0
         if (!_openTag)
 433  0
             throw new IllegalStateException(MarkupMessages.tagNotOpen());
 434  0
     }
 435  
 
 436  
     private void push(String name)
 437  
     {
 438  0
         if (_activeElementStack == null)
 439  0
             _activeElementStack = new ArrayList();
 440  
 
 441  0
         _activeElementStack.add(name);
 442  0
     }
 443  
 
 444  
     private String pop()
 445  
     {
 446  0
         int lastIndex = _activeElementStack.size() - 1;
 447  
 
 448  0
         return (String) _activeElementStack.remove(lastIndex);
 449  
     }
 450  
 
 451  
     private boolean stackEmpty()
 452  
     {
 453  0
         return _activeElementStack == null || _activeElementStack.isEmpty();
 454  
     }
 455  
 }