aboutsummaryrefslogtreecommitdiff
path: root/velocity-engine-core/src/main/java/org/apache/velocity/runtime/directive/Directive.java
blob: 9d938f78d10644aa2d8241603f709a96945f6881 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
package org.apache.velocity.runtime.directive;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.velocity.Template;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.TemplateInitException;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.parser.ParseException;
import org.apache.velocity.runtime.parser.Token;
import org.apache.velocity.runtime.parser.node.Node;

import org.slf4j.Logger;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;

/**
 * Base class for all directives used in Velocity.
 *
 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 * @author Nathan Bubna
 * @version $Id$
 */
public abstract class Directive implements DirectiveConstants, Cloneable
{
    private int line = 0;
    private int column = 0;
    private boolean provideScope = false;
    private Template template;

    protected Logger log = null;

    /**
     *
     */
    protected RuntimeServices rsvc = null;

    /**
     * Return the name of this directive.
     * @return The name of this directive.
     */
    public abstract String getName();

    /**
     * Get the directive type BLOCK/LINE.
     * @return The directive type BLOCK/LINE.
     */
    public abstract int getType();

    /**
     * Allows the template location to be set.
     * @param line
     * @param column
     */
    public void setLocation( int line, int column )
    {
        this.line = line;
        this.column = column;
    }

    /**
     * Allows the template location to be set.
     * @param line
     * @param column
     * @param template
     */
    public void setLocation(int line, int column, Template template)
    {
        setLocation(line, column);
        this.template = template;
    }

    /**
     * returns the template in which this directive appears
     * @return template
     */
    public Template getTemplate()
    {
        return template;
    }

    /**
     * for log msg purposes
     * @return The current line for log msg purposes.
     */
    public int getLine()
    {
        return line;
    }

    /**
     * for log msg purposes
     * @return The current column for log msg purposes.
     */
    public int getColumn()
    {
        return column;
    }

    /**
     * @return The template file name this directive was defined in, or null if not
     * defined in a file.
     */
    public String getTemplateName()
    {
      return template.getName();
    }

    /**
     * @return the name to be used when a scope control is provided for this
     * directive.
     */
    public String getScopeName()
    {
        return getName();
    }

    /**
     * @return true if there will be a scope control injected into the context
     * when rendering this directive.
     */
    public boolean isScopeProvided()
    {
        return provideScope;
    }

    /**
     * How this directive is to be initialized.
     * @param rs
     * @param context
     * @param node
     * @throws TemplateInitException
     */
    public void init( RuntimeServices rs, InternalContextAdapter context,
                      Node node)
        throws TemplateInitException
    {
        rsvc = rs;
        log = rsvc.getLog("directive." + getName());

        provideScope = rsvc.isScopeControlEnabled(getScopeName());
    }

    /**
     * The Parser calls this method during template parsing to check the arguments
     * types.  Be aware that this method is called pre init, so not all data
     * is available in this method.  The default implementation does not peform any
     * checking.  We do this so that Custom directives do not trigger any parse
     * errors in IDEs.
     * @param argtypes type, Array of argument types of each argument to the directive
     * for example ParserTreeConstants.JJTWORD
     * @param t token of directive
     * @param templateName the name of the template this directive is referenced in.
     * @throws ParseException
     */
    public void checkArgs(ArrayList<Integer> argtypes,  Token t, String templateName)
        throws ParseException
    {
    }

    /**
     * How this directive is to be rendered
     * @param context
     * @param writer
     * @param node
     * @return True if the directive rendered successfully.
     * @throws IOException
     * @throws ResourceNotFoundException
     * @throws ParseErrorException
     * @throws MethodInvocationException
     */
    public abstract boolean render( InternalContextAdapter context,
                                    Writer writer, Node node )
           throws IOException, ResourceNotFoundException, ParseErrorException,
                MethodInvocationException;


    /**
     * This creates and places the scope control for this directive
     * into the context (if scope provision is turned on).
     * @param context
     */
    protected void preRender(InternalContextAdapter context)
    {
        if (isScopeProvided())
        {
            String name = getScopeName();
            Object previous = context.get(name);
            context.put(name, makeScope(previous));
        }
    }

    /**
     * @param prev
     * @return scope
     */
    protected Scope makeScope(Object prev)
    {
        return new Scope(this, prev);
    }

    /**
     * This cleans up any scope control for this directive after rendering,
     * assuming the scope control was turned on.
     * @param context
     */
    protected void postRender(InternalContextAdapter context)
    {
        if (isScopeProvided())
        {
            String name = getScopeName();
            Object obj = context.get(name);

            try
            {
                Scope scope = (Scope)obj;
                if (scope.getParent() != null)
                {
                    context.put(name, scope.getParent());
                }
                else if (scope.getReplaced() != null)
                {
                    context.put(name, scope.getReplaced());
                }
                else
                {
                    context.remove(name);
                }
            }
            catch (ClassCastException cce)
            {
                // the user can override the scope with a #set,
                // since that means they don't care about a replaced value
                // and obviously aren't too keen on their scope control,
                // and especially since #set is meant to be handled globally,
                // we'll assume they know what they're doing and not worry
                // about replacing anything superseded by this directive's scope
            }
        }
    }

}