View Javadoc

1   package org.apache.velocity.runtime.directive;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.io.StringWriter;
24  import java.io.Writer;
25  
26  import org.apache.velocity.context.InternalContextAdapter;
27  import org.apache.velocity.exception.TemplateInitException;
28  import org.apache.velocity.runtime.Renderable;
29  import org.apache.velocity.runtime.RuntimeServices;
30  import org.apache.velocity.runtime.log.Log;
31  import org.apache.velocity.runtime.parser.node.Node;
32  
33  /**
34   * Directive that puts an unrendered AST block in the context
35   * under the specified key, postponing rendering until the
36   * reference is used and rendered.
37   *
38   * @author Andrew Tetlaw
39   * @author Nathan Bubna
40   * @author <a href="mailto:wyla@removethis.sci.fi">Jarkko Viinamaki</a>
41   * @since 1.7
42   * @version $Id: Block.java 686842 2008-08-18 18:29:31Z nbubna $
43   */
44  public abstract class Block extends Directive
45  {
46      protected Node block;
47      protected Log log;
48      protected int maxDepth;
49      protected String key;
50  
51      /**
52       * Return type of this directive.
53       */
54      public int getType()
55      {
56          return BLOCK;
57      }
58  
59      /**
60       *  simple init - get the key
61       */
62      public void init(RuntimeServices rs, InternalContextAdapter context, Node node)
63          throws TemplateInitException
64      {
65          super.init(rs, context, node);
66  
67          log = rs.getLog();
68  
69          /**
70           * No checking is done. We just grab the last child node and assume
71           * that it's the block!
72           */
73          block = node.jjtGetChild(node.jjtGetNumChildren() - 1);
74      }
75  
76      public boolean render(InternalContextAdapter context, Writer writer)
77      {
78          preRender(context);
79          try
80          {
81              return block.render(context, writer);
82          }
83          catch (IOException e)
84          {
85              String msg = "Failed to render " + id(context) + " to writer "
86                + " at " + Log.formatFileString(this);
87  
88              log.error(msg, e);
89              throw new RuntimeException(msg, e);
90          }
91          catch (StopCommand stop)
92          {
93              if (!stop.isFor(this))
94              {
95                  throw stop;
96              }
97              return true;
98          }
99          finally
100         {
101             postRender(context);
102         }
103     }
104 
105     /**
106      * Creates a string identifying the source and location of the block
107      * definition, and the current template being rendered if that is
108      * different.
109      */
110     protected String id(InternalContextAdapter context)
111     {
112         StringBuilder str = new StringBuilder(100)
113             .append("block $").append(key);
114         if (!context.getCurrentTemplateName().equals(getTemplateName()))
115         {
116             str.append(" used in ").append(context.getCurrentTemplateName());
117         }
118         return str.toString();
119     }
120 
121     /**
122      * actual class placed in the context, holds the context
123      * being used for the render, as well as the parent (which already holds
124      * everything else we need).
125      */
126     public static class Reference implements Renderable
127     {
128         private InternalContextAdapter context;
129         private Block parent;
130         private int depth;
131 
132         public Reference(InternalContextAdapter context, Block parent)
133         {
134             this.context = context;
135             this.parent = parent;
136         }
137 
138         /**
139          * Render the AST of this block into the writer using the context.
140          */
141         public boolean render(InternalContextAdapter context, Writer writer)
142         {
143             depth++;
144             if (depth > parent.maxDepth)
145             {
146                 /* this is only a debug message, as recursion can
147                  * happen in quasi-innocent situations and is relatively
148                  * harmless due to how we handle it here.
149                  * this is more to help anyone nuts enough to intentionally
150                  * use recursive block definitions and having problems
151                  * pulling it off properly.
152                  */
153                 parent.log.debug("Max recursion depth reached for " + parent.id(context)
154                     + " at " + Log.formatFileString(parent));
155                 depth--;
156                 return false;
157             }
158             else
159             {
160                 parent.render(context, writer);
161                 depth--;
162                 return true;
163             }
164         }
165 
166         /**
167          * Makes #if( $blockRef ) true without rendering, so long as we aren't beyond max depth.
168          */
169         public boolean getAsBoolean()
170         {
171             return depth <= parent.maxDepth;
172         }
173 
174         public String toString()
175         {
176             Writer writer = new StringWriter();
177             if (render(context, writer))
178             {
179                 return writer.toString();
180             }
181             return null;
182         }
183     }
184 }