View Javadoc

1   package org.apache.torque.generator.template.velocity;
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.StringWriter;
23  import java.io.Writer;
24  import java.util.Properties;
25  import java.util.Set;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.torque.generator.GeneratorException;
30  import org.apache.torque.generator.configuration.ConfigurationException;
31  import org.apache.torque.generator.configuration.ConfigurationProvider;
32  import org.apache.torque.generator.control.ControllerState;
33  import org.apache.torque.generator.option.Option;
34  import org.apache.torque.generator.option.Options;
35  import org.apache.torque.generator.outlet.OutletResult;
36  import org.apache.torque.generator.qname.Namespace;
37  import org.apache.torque.generator.qname.QualifiedName;
38  import org.apache.torque.generator.qname.QualifiedNameMap;
39  import org.apache.torque.generator.source.SourceElement;
40  import org.apache.torque.generator.template.TemplateOutletImpl;
41  import org.apache.torque.generator.variable.Variable;
42  import org.apache.torque.generator.variable.VariableStore;
43  import org.apache.velocity.VelocityContext;
44  import org.apache.velocity.app.Velocity;
45  import org.apache.velocity.context.Context;
46  import org.apache.velocity.runtime.RuntimeConstants;
47  import org.apache.velocity.runtime.log.Log4JLogChute;
48  import org.apache.velocity.util.StringUtils;
49  
50  /**
51   * A Outlet which uses a velocity template for generation.
52   */
53  public class VelocityOutlet extends TemplateOutletImpl
54  {
55      /**
56       * The name under which the Torque generator interface will be put
57       * into the context.
58       */
59      public static final String TORQUE_GEN_CONTEXT_NAME = "torqueGen";
60  
61      /**
62       * The name under which the velocity StringUtils will be put
63       * into the context.
64       */
65      public static final String STRING_UTILS_CONTEXT_NAME = "stringUtils";
66  
67      /**
68       * The key under which the null attribute of a source element is put
69       * into the context.
70       */
71      public static final String NULL_KEY_CONTEXT_NAME = "value";
72  
73      /** The log. */
74      private static Log log = LogFactory.getLog(VelocityOutlet.class);
75  
76      /**
77       * Whether the options should be put into the context.
78       */
79      private boolean optionsInContext = true;
80  
81      /**
82       * Whether the variables should be put into the context.
83       */
84      private boolean variablesInContext = true;
85  
86      /**
87       * Whether the attributes of the current source element should be put
88       * into the context.
89       */
90      private boolean sourceAttributesInContext = true;
91  
92      /**
93       * Constructs a new VelocityTemplateOutlet.
94       *
95       * @param name the name of this outlet, not null.
96       * @param configurationProvider the provider for reading the templates,
97       *        not null.
98       * @param path the path to the templates, not null.
99       *        May contain tokens of the form ${....}, these are parsed.
100      * @param encoding the encoding of the file, or null if the system's
101      *        default encoding should be used.
102      *
103      * @throws NullPointerException if name, path or directories are null.
104      * @throws ConfigurationException if an error occurs while reading the
105      *         template.
106      */
107     public VelocityOutlet(
108             QualifiedName name,
109             ConfigurationProvider configurationProvider,
110             String path,
111             String encoding)
112         throws ConfigurationException
113     {
114         super(name,
115               configurationProvider,
116               path,
117               encoding,
118               new VelocityTemplateFilter());
119     }
120 
121     /**
122      * Tells the outlet to put all options which name space is visible to
123      * the namespace of this outlet into the context.
124      * Only the variable names are used as keys in the context, the namespaces
125      * are stripped.
126      *
127      * @param optionsInContext whether to put the options into the context.
128      */
129     public void setOptionsInContext(boolean optionsInContext)
130     {
131         this.optionsInContext = optionsInContext;
132     }
133 
134     /**
135      * Returns whether all options which namespaces are visible to
136      * the name space of this outlet are put into the context.
137      *
138      * @return whether the outlet puts the options into the context.
139      */
140     public boolean isOptionsInContext()
141     {
142         return optionsInContext;
143     }
144 
145     /**
146      * Tells the outlet to put all variables which are visible to this
147      * outlet into the context.
148      * Only the variable names are used as keys in the context, the namespaces
149      * are stripped.
150      *
151      * @param variablesInContext whether to put the variables into the context.
152      */
153     public void setVariablesInContext(boolean variablesInContext)
154     {
155         this.variablesInContext = variablesInContext;
156     }
157 
158     /**
159      * Returns whether all variables which are visible to this
160      * outlet are put into the context.
161      *
162      * @return whether the outlet puts the variables into the context.
163      */
164     public boolean isVariablesInContext()
165     {
166         return variablesInContext;
167     }
168 
169     /**
170      * Tells the outlet to put the attributes of the current source element
171      * into the context or not.
172      *
173      * @param sourceAttributesInContext whether to put the source attributes
174      *        into the context.
175      */
176     public void setSourceAttributesInContext(
177             boolean sourceAttributesInContext)
178     {
179         this.sourceAttributesInContext = sourceAttributesInContext;
180     }
181 
182     /**
183      * Returns whether the attributes of the current source element
184      * are put into the context.
185      *
186      * @return whether the outlet puts the attributes of the current source
187      *         element into the context.
188      */
189     public boolean isSourceAttributesInContext()
190     {
191         return sourceAttributesInContext;
192     }
193 
194     /**
195      * Executes the generation process; the result is returned.
196      *
197      * @param controllerState the current controller state.
198      *
199      * @return the result of the generation, not null.
200      *
201      * @see org.apache.torque.generator.outlet.Outlet#execute(ControllerState)
202      */
203     @Override
204     public OutletResult execute(ControllerState controllerState)
205         throws GeneratorException
206 
207     {
208         if (log.isDebugEnabled())
209         {
210             log.debug("Start executing VelocityOutlet " + getName());
211         }
212 
213         try
214         {
215             try
216             {
217                 Properties properties = new Properties();
218                 properties.put(
219                         RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
220                         Log4JLogChute.class.getName());
221                 properties.put(
222                         RuntimeConstants.RUNTIME_LOG,
223                         "");
224                 Velocity.init(properties);
225             }
226             catch (Exception e)
227             {
228                 throw new GeneratorException(
229                         "Could not initialize velocity",
230                         e);
231             }
232 
233             SourceElement sourceElement = controllerState.getSourceElement();
234 
235             String inputElementName = getInputElementName();
236             if (inputElementName != null
237                     && !inputElementName.equals(sourceElement.getName()))
238             {
239                 throw new GeneratorException("Input element name, "
240                         + sourceElement.getName()
241                         + ", is not the expected name, "
242                         + getInputElementName()
243                         + ", for outlet "
244                         + getName());
245             }
246 
247             Context context = createVelocityContext(controllerState);
248 
249             Writer writer = new StringWriter();
250             try
251             {
252                 Velocity.evaluate(context, writer,
253                         "VelocityTemplateOutlet:" + getName(),
254                         getContent(controllerState));
255                 writer.flush();
256                 writer.close();
257             }
258             catch (Exception e)
259             {
260                 log.error("error during execution of outlet "
261                             + getName()
262                             + " : " + e.getMessage(),
263                         e);
264                 throw new GeneratorException(
265                         "Error during execution of outlet "
266                             + getName()
267                             + " : "
268                             + e.getMessage(),
269                         e);
270             }
271 
272             return new OutletResult(writer.toString());
273         }
274         finally
275         {
276             if (log.isDebugEnabled())
277             {
278                 log.debug("End executing VelocityOutlet " + getName());
279             }
280         }
281     }
282 
283     /**
284      * Creates the velocity context for the outlet.
285      *
286      * @param controllerState the controller state, not null.
287      * @return
288      */
289     private Context createVelocityContext(
290             ControllerState controllerState)
291     {
292         Context context = new VelocityContext();
293         context.put(
294                 TORQUE_GEN_CONTEXT_NAME,
295                 new TorqueGenVelocity(this, controllerState));
296         context.put(
297                 STRING_UTILS_CONTEXT_NAME,
298                 new StringUtils());
299 
300         if (optionsInContext)
301         {
302             // Only consider options visible from the current namespace.
303             Options visibleOptions
304                     = controllerState.getVisibleOptions();
305             for (Option option : visibleOptions.values())
306             {
307                 QualifiedName qualifiedName = option.getQualifiedName();
308                 context.put(qualifiedName.getName(), option.getValue());
309             }
310             log.debug("Put options in context " + visibleOptions.keySet());
311         }
312         else
313         {
314             log.debug("options in context are disabled");
315         }
316 
317         SourceElement sourceElement = controllerState.getSourceElement();
318         if (sourceAttributesInContext)
319         {
320             Set<String> attributes = sourceElement.getAttributeNames();
321             for (String key : attributes)
322             {
323                 Object value = sourceElement.getAttribute(key);
324                 if (key == null)
325                 {
326                     // The null key cannot be accessed in the context.
327                     // So if the attribute NULL_KEY_CONTEXT_NAME does not
328                     // exist, use this as attribute name.
329                     if (sourceElement.getAttributeNames().contains(
330                             NULL_KEY_CONTEXT_NAME))
331                     {
332                         continue;
333                     }
334                     key = NULL_KEY_CONTEXT_NAME;
335                 }
336                 context.put(key, value);
337             }
338             log.debug("Put attributes in context " + attributes);
339         }
340         else
341         {
342             log.debug("source attributes in context are disabled");
343         }
344 
345         if (variablesInContext)
346         {
347             // Only consider variables visible from the namespace
348             // of this outlet. If a name exists in different
349             // namespaces visible from this namespace,
350             // only consider the most significant name.
351             Namespace namespace = getName().getNamespace();
352             VariableStore variableStore
353                     = controllerState.getVariableStore();
354             QualifiedNameMap<Variable> visibleVariables
355                     = variableStore
356                         .getContent()
357                         .getInHierarchy(namespace);
358             for (Variable variable : visibleVariables.values())
359             {
360                 QualifiedName qualifiedName = variable.getName();
361                 context.put(
362                         qualifiedName.getName(),
363                         variable.getValue());
364             }
365             log.debug("Put variables in context "
366                     + visibleVariables.keySet());
367         }
368         else
369         {
370             log.debug("variables in context are disabled");
371         }
372 
373         return context;
374     }
375 }