View Javadoc

1   /*
2    * $Id: VelocityManager.java 454565 2006-10-10 00:02:56Z jmitchell $
3    *
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.struts2.views.velocity;
19  
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Properties;
29  import java.util.StringTokenizer;
30  
31  import javax.servlet.ServletContext;
32  import javax.servlet.http.HttpServletRequest;
33  import javax.servlet.http.HttpServletResponse;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.struts2.ServletActionContext;
38  import org.apache.struts2.StrutsConstants;
39  import org.apache.struts2.StrutsException;
40  import org.apache.struts2.config.Settings;
41  import org.apache.struts2.util.VelocityStrutsUtil;
42  import org.apache.struts2.views.jsp.ui.OgnlTool;
43  import org.apache.struts2.views.util.ContextUtil;
44  import org.apache.struts2.views.velocity.components.ActionDirective;
45  import org.apache.struts2.views.velocity.components.ActionErrorDirective;
46  import org.apache.struts2.views.velocity.components.ActionMessageDirective;
47  import org.apache.struts2.views.velocity.components.AnchorDirective;
48  import org.apache.struts2.views.velocity.components.BeanDirective;
49  import org.apache.struts2.views.velocity.components.CheckBoxDirective;
50  import org.apache.struts2.views.velocity.components.CheckBoxListDirective;
51  import org.apache.struts2.views.velocity.components.ComboBoxDirective;
52  import org.apache.struts2.views.velocity.components.ComponentDirective;
53  import org.apache.struts2.views.velocity.components.DateDirective;
54  import org.apache.struts2.views.velocity.components.DatePickerDirective;
55  import org.apache.struts2.views.velocity.components.DivDirective;
56  import org.apache.struts2.views.velocity.components.DoubleSelectDirective;
57  import org.apache.struts2.views.velocity.components.FieldErrorDirective;
58  import org.apache.struts2.views.velocity.components.FileDirective;
59  import org.apache.struts2.views.velocity.components.FormDirective;
60  import org.apache.struts2.views.velocity.components.HeadDirective;
61  import org.apache.struts2.views.velocity.components.HiddenDirective;
62  import org.apache.struts2.views.velocity.components.I18nDirective;
63  import org.apache.struts2.views.velocity.components.IncludeDirective;
64  import org.apache.struts2.views.velocity.components.LabelDirective;
65  import org.apache.struts2.views.velocity.components.OptionTransferSelectDirective;
66  import org.apache.struts2.views.velocity.components.PanelDirective;
67  import org.apache.struts2.views.velocity.components.ParamDirective;
68  import org.apache.struts2.views.velocity.components.PasswordDirective;
69  import org.apache.struts2.views.velocity.components.PropertyDirective;
70  import org.apache.struts2.views.velocity.components.PushDirective;
71  import org.apache.struts2.views.velocity.components.RadioDirective;
72  import org.apache.struts2.views.velocity.components.ResetDirective;
73  import org.apache.struts2.views.velocity.components.SelectDirective;
74  import org.apache.struts2.views.velocity.components.SetDirective;
75  import org.apache.struts2.views.velocity.components.SubmitDirective;
76  import org.apache.struts2.views.velocity.components.TabbedPanelDirective;
77  import org.apache.struts2.views.velocity.components.TextAreaDirective;
78  import org.apache.struts2.views.velocity.components.TextDirective;
79  import org.apache.struts2.views.velocity.components.TextFieldDirective;
80  import org.apache.struts2.views.velocity.components.TokenDirective;
81  import org.apache.struts2.views.velocity.components.TreeDirective;
82  import org.apache.struts2.views.velocity.components.TreeNodeDirective;
83  import org.apache.struts2.views.velocity.components.URLDirective;
84  import org.apache.struts2.views.velocity.components.UpDownSelectDirective;
85  import org.apache.struts2.views.velocity.components.WebTableDirective;
86  import org.apache.velocity.VelocityContext;
87  import org.apache.velocity.app.Velocity;
88  import org.apache.velocity.app.VelocityEngine;
89  import org.apache.velocity.context.Context;
90  import org.apache.velocity.tools.view.ToolboxManager;
91  import org.apache.velocity.tools.view.context.ChainedContext;
92  import org.apache.velocity.tools.view.servlet.ServletToolboxManager;
93  
94  import com.opensymphony.xwork2.ObjectFactory;
95  import com.opensymphony.xwork2.util.ValueStack;
96  
97  
98  /***
99   * Manages the environment for Velocity result types
100  *
101  */
102 public class VelocityManager {
103     private static final Log log = LogFactory.getLog(VelocityManager.class);
104     private static VelocityManager instance;
105     public static final String STRUTS = "struts";
106 
107     /***
108      * the parent JSP tag
109      */
110     public static final String PARENT = "parent";
111 
112     /***
113      * the current JSP tag
114      */
115     public static final String TAG = "tag";
116 
117     private VelocityEngine velocityEngine;
118 
119     /***
120      * A reference to the toolbox manager.
121      */
122     protected ToolboxManager toolboxManager = null;
123     private String toolBoxLocation;
124 
125 
126     /***
127      * Names of contexts that will be chained on every request
128      */
129     private String[] chainedContextNames;
130     
131     private Properties velocityProperties;
132 
133     protected VelocityManager() {
134         init();
135     }
136 
137     /***
138      * retrieve an instance to the current VelocityManager
139      */
140     public synchronized static VelocityManager getInstance() {
141         if (instance == null) {
142             String classname = VelocityManager.class.getName();
143 
144             if (Settings.isSet(StrutsConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME)) {
145                 classname = Settings.get(StrutsConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME).trim();
146             }
147 
148             if (!classname.equals(VelocityManager.class.getName())) {
149                 try {
150                     log.info("Instantiating VelocityManager!, " + classname);
151                     // singleton instances shouldn't be built accessing request or session-specific context data
152                     instance = (VelocityManager) ObjectFactory.getObjectFactory().buildBean(classname, null);
153                 } catch (Exception e) {
154                     log.fatal("Fatal exception occurred while trying to instantiate a VelocityManager instance, " + classname, e);
155                     instance = new VelocityManager();
156                 }
157             } else {
158                 instance = new VelocityManager();
159             }
160         }
161 
162         return instance;
163     }
164 
165     /***
166      * @return a reference to the VelocityEngine used by <b>all</b> struts velocity thingies with the exception of
167      *         directly accessed *.vm pages
168      */
169     public VelocityEngine getVelocityEngine() {
170         return velocityEngine;
171     }
172 
173     /***
174      * This method is responsible for creating the standard VelocityContext used by all WW2 velocity views.  The
175      * following context parameters are defined:
176      * <p/>
177      * <ul>
178      * <li><strong>request</strong> - the current HttpServletRequest</li>
179      * <li><strong>response</strong> - the current HttpServletResponse</li>
180      * <li><strong>stack</strong> - the current {@link ValueStack}</li>
181      * <li><strong>ognl</strong> - an {@link OgnlTool}</li>
182      * <li><strong>struts</strong> - an instance of {@link org.apache.struts2.util.StrutsUtil}</li>
183      * <li><strong>action</strong> - the current Struts action</li>
184      * </ul>
185      *
186      * @return a new StrutsVelocityContext
187      */
188     public Context createContext(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
189         VelocityContext[] chainedContexts = prepareChainedContexts(req, res, stack.getContext());
190         StrutsVelocityContext context = new StrutsVelocityContext(chainedContexts, stack);
191         Map standardMap = ContextUtil.getStandardContext(stack, req, res);
192         for (Iterator iterator = standardMap.entrySet().iterator(); iterator.hasNext();) {
193             Map.Entry entry = (Map.Entry) iterator.next();
194             context.put((String) entry.getKey(), entry.getValue());
195         }
196         context.put(STRUTS, new VelocityStrutsUtil(context, stack, req, res));
197 
198 
199         ServletContext ctx = null;
200         try {
201             ctx = ServletActionContext.getServletContext();
202         } catch (NullPointerException npe) {
203             // in case this was used outside the lifecycle of struts servlet
204             log.debug("internal toolbox context ignored");
205         }
206 
207         if (toolboxManager != null && ctx != null) {
208             ChainedContext chained = new ChainedContext(context, req, res, ctx);
209             chained.setToolbox(toolboxManager.getToolboxContext(chained));
210             return chained;
211         } else {
212             return context;
213         }
214 
215     }
216 
217     /***
218      * constructs contexts for chaining on this request.  This method does not
219      * perform any initialization of the contexts.  All that must be done in the
220      * context itself.
221      *
222      * @param servletRequest
223      * @param servletResponse
224      * @param extraContext
225      * @return an VelocityContext[] of contexts to chain
226      */
227     protected VelocityContext[] prepareChainedContexts(HttpServletRequest servletRequest, HttpServletResponse servletResponse, Map extraContext) {
228         if (this.chainedContextNames == null) {
229             return null;
230         }
231         List contextList = new ArrayList();
232         for (int i = 0; i < chainedContextNames.length; i++) {
233             String className = chainedContextNames[i];
234             try {
235                 VelocityContext velocityContext = (VelocityContext) ObjectFactory.getObjectFactory().buildBean(className, null);
236                 contextList.add(velocityContext);
237             } catch (Exception e) {
238                 log.warn("Warning.  " + e.getClass().getName() + " caught while attempting to instantiate a chained VelocityContext, " + className + " -- skipping");
239             }
240         }
241         if (contextList.size() > 0) {
242             VelocityContext[] extraContexts = new VelocityContext[contextList.size()];
243             contextList.toArray(extraContexts);
244             return extraContexts;
245         } else {
246             return null;
247         }
248     }
249 
250     /***
251      * initializes the VelocityManager.  this should be called during the initialization process, say by
252      * ServletDispatcher.  this may be called multiple times safely although calls beyond the first won't do anything
253      *
254      * @param context the current servlet context
255      */
256     public synchronized void init(ServletContext context) {
257         if (velocityEngine == null) {
258             velocityEngine = newVelocityEngine(context);
259         }
260         this.initToolbox(context);
261     }
262 
263     /***
264      * load optional velocity properties using the following loading strategy
265      * <ul>
266      * <li>relative to the servlet context path</li>
267      * <li>relative to the WEB-INF directory</li>
268      * <li>on the classpath</li>
269      * </ul>
270      *
271      * @param context the current ServletContext.  may <b>not</b> be null
272      * @return the optional properties if struts.velocity.configfile was specified, an empty Properties file otherwise
273      */
274     public Properties loadConfiguration(ServletContext context) {
275         if (context == null) {
276             String gripe = "Error attempting to create a loadConfiguration from a null ServletContext!";
277             log.error(gripe);
278             throw new IllegalArgumentException(gripe);
279         }
280 
281         Properties properties = new Properties();
282 
283         // now apply our systemic defaults, then allow user to override
284         applyDefaultConfiguration(context, properties);
285         
286 
287         String defaultUserDirective = properties.getProperty("userdirective");
288 
289         /***
290          * if the user has specified an external velocity configuration file, we'll want to search for it in the
291          * following order
292          *
293          * 1. relative to the context path
294          * 2. relative to /WEB-INF
295          * 3. in the class path
296          */
297         String configfile;
298 
299         if (Settings.isSet(StrutsConstants.STRUTS_VELOCITY_CONFIGFILE)) {
300             configfile = Settings.get(StrutsConstants.STRUTS_VELOCITY_CONFIGFILE);
301         } else {
302             configfile = "velocity.properties";
303         }
304 
305         configfile = configfile.trim();
306 
307         InputStream in = null;
308         String resourceLocation = null;
309 
310         try {
311             if (context.getRealPath(configfile) != null) {
312                 // 1. relative to context path, i.e. /velocity.properties
313                 String filename = context.getRealPath(configfile);
314 
315                 if (filename != null) {
316                     File file = new File(filename);
317 
318                     if (file.isFile()) {
319                         resourceLocation = file.getCanonicalPath() + " from file system";
320                         in = new FileInputStream(file);
321                     }
322 
323                     // 2. if nothing was found relative to the context path, search relative to the WEB-INF directory
324                     if (in == null) {
325                         file = new File(context.getRealPath("/WEB-INF/" + configfile));
326 
327                         if (file.isFile()) {
328                             resourceLocation = file.getCanonicalPath() + " from file system";
329                             in = new FileInputStream(file);
330                         }
331                     }
332                 }
333             }
334 
335             // 3. finally, if there's no physical file, how about something in our classpath
336             if (in == null) {
337                 in = VelocityManager.class.getClassLoader().getResourceAsStream(configfile);
338                 if (in != null) {
339                     resourceLocation = configfile + " from classloader";
340                 }
341             }
342 
343             // if we've got something, load 'er up
344             if (in != null) {
345                 log.info("Initializing velocity using " + resourceLocation);
346                 properties.load(in);
347             }
348         } catch (IOException e) {
349             log.warn("Unable to load velocity configuration " + resourceLocation, e);
350         } finally {
351             if (in != null) {
352                 try {
353                     in.close();
354                 } catch (IOException e) {
355                 }
356             }
357         }
358         
359         // overide with programmatically set properties
360         if (this.velocityProperties != null) {
361         	Iterator keys = this.velocityProperties.keySet().iterator();
362         	while (keys.hasNext()) {
363         		String key = (String) keys.next();
364         		properties.setProperty(key, this.velocityProperties.getProperty(key));
365 			}
366         }
367 
368         String userdirective = properties.getProperty("userdirective");
369 
370         if ((userdirective == null) || userdirective.trim().equals("")) {
371             userdirective = defaultUserDirective;
372         } else {
373             userdirective = userdirective.trim() + "," + defaultUserDirective;
374         }
375 
376         properties.setProperty("userdirective", userdirective);
377 
378         
379         // for debugging purposes, allows users to dump out the properties that have been configured
380         if (log.isDebugEnabled()) {
381             log.debug("Initializing Velocity with the following properties ...");
382 
383             for (Iterator iter = properties.keySet().iterator();
384                  iter.hasNext();) {
385                 String key = (String) iter.next();
386                 String value = properties.getProperty(key);
387 
388                 if (log.isDebugEnabled()) {
389                     log.debug("    '" + key + "' = '" + value + "'");
390                 }
391             }
392         }
393 
394         return properties;
395     }
396 
397     /***
398      * performs one-time initializations
399      */
400     protected void init() {
401 
402         // read in the names of contexts to add to each request
403         initChainedContexts();
404 
405 
406         if (Settings.isSet(StrutsConstants.STRUTS_VELOCITY_TOOLBOXLOCATION)) {
407             toolBoxLocation = Settings.get(StrutsConstants.STRUTS_VELOCITY_TOOLBOXLOCATION).toString();
408         }
409 
410     }
411 
412 
413     /***
414      * Initializes the ServletToolboxManager for this servlet's
415      * toolbox (if any).
416      */
417     protected void initToolbox(ServletContext context) {
418         /* if we have a toolbox, get a manager for it */
419         if (toolBoxLocation != null) {
420             toolboxManager = ServletToolboxManager.getInstance(context, toolBoxLocation);
421         } else {
422             Velocity.info("VelocityViewServlet: No toolbox entry in configuration.");
423         }
424     }
425 
426 
427     /***
428      * allow users to specify via the struts.properties file a set of additional VelocityContexts to chain to the
429      * the StrutsVelocityContext.  The intent is to allow these contexts to store helper objects that the ui
430      * developer may want access to.  Examples of reasonable VelocityContexts would be an IoCVelocityContext, a
431      * SpringReferenceVelocityContext, and a ToolboxVelocityContext
432      */
433     protected void initChainedContexts() {
434 
435         if (Settings.isSet(StrutsConstants.STRUTS_VELOCITY_CONTEXTS)) {
436             // we expect contexts to be a comma separated list of classnames
437             String contexts = Settings.get(StrutsConstants.STRUTS_VELOCITY_CONTEXTS).toString();
438             StringTokenizer st = new StringTokenizer(contexts, ",");
439             List contextList = new ArrayList();
440 
441             while (st.hasMoreTokens()) {
442                 String classname = st.nextToken();
443                 contextList.add(classname);
444             }
445             if (contextList.size() > 0) {
446                 String[] chainedContexts = new String[contextList.size()];
447                 contextList.toArray(chainedContexts);
448                 this.chainedContextNames = chainedContexts;
449             }
450 
451 
452         }
453 
454     }
455 
456     /***
457      * <p/>
458      * Instantiates a new VelocityEngine.
459      * </p>
460      * <p/>
461      * The following is the default Velocity configuration
462      * </p>
463      * <pre>
464      *  resource.loader = file, class
465      *  file.resource.loader.path = real path of webapp
466      *  class.resource.loader.description = Velocity Classpath Resource Loader
467      *  class.resource.loader.class = org.apache.struts2.views.velocity.StrutsResourceLoader
468      * </pre>
469      * <p/>
470      * this default configuration can be overridden by specifying a struts.velocity.configfile property in the
471      * struts.properties file.  the specified config file will be searched for in the following order:
472      * </p>
473      * <ul>
474      * <li>relative to the servlet context path</li>
475      * <li>relative to the WEB-INF directory</li>
476      * <li>on the classpath</li>
477      * </ul>
478      *
479      * @param context the current ServletContext.  may <b>not</b> be null
480      */
481     protected VelocityEngine newVelocityEngine(ServletContext context) {
482         if (context == null) {
483             String gripe = "Error attempting to create a new VelocityEngine from a null ServletContext!";
484             log.error(gripe);
485             throw new IllegalArgumentException(gripe);
486         }
487 
488         Properties p = loadConfiguration(context);
489 
490         VelocityEngine velocityEngine = new VelocityEngine();
491         
492         //	Set the velocity attribute for the servlet context
493         //  if this is not set the webapp loader WILL NOT WORK
494         velocityEngine.setApplicationAttribute(ServletContext.class.getName(),
495                 context);
496 
497         try {
498             velocityEngine.init(p);
499         } catch (Exception e) {
500             String gripe = "Unable to instantiate VelocityEngine!";
501             throw new StrutsException(gripe, e);
502         }
503 
504         return velocityEngine;
505     }
506 
507     /***
508      * once we've loaded up the user defined configurations, we will want to apply Struts specification configurations.
509      * <ul>
510      * <li>if Velocity.RESOURCE_LOADER has not been defined, then we will use the defaults which is a joined file,
511      * class loader for unpackaed wars and a straight class loader otherwise</li>
512      * <li>we need to define the various Struts custom user directives such as #param, #tag, and #bodytag</li>
513      * </ul>
514      *
515      * @param context
516      * @param p
517      */
518     private void applyDefaultConfiguration(ServletContext context, Properties p) {
519         // ensure that caching isn't overly aggressive
520 
521         /***
522          * Load a default resource loader definition if there isn't one present.
523          * Ben Hall (22/08/2003)
524          */
525         if (p.getProperty(Velocity.RESOURCE_LOADER) == null) {
526             p.setProperty(Velocity.RESOURCE_LOADER, "strutsfile, strutsclass");
527         }
528 
529         /***
530          * If there's a "real" path add it for the strutsfile resource loader.
531          * If there's no real path and they haven't configured a loader then we change
532          * resource loader property to just use the strutsclass loader
533          * Ben Hall (22/08/2003)
534          */
535         if (context.getRealPath("") != null) {
536             p.setProperty("strutsfile.resource.loader.description", "Velocity File Resource Loader");
537             p.setProperty("strutsfile.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
538             p.setProperty("strutsfile.resource.loader.path", context.getRealPath(""));
539             p.setProperty("strutsfile.resource.loader.modificationCheckInterval", "2");
540             p.setProperty("strutsfile.resource.loader.cache", "true");
541         } else {
542             // remove strutsfile from resource loader property
543             String prop = p.getProperty(Velocity.RESOURCE_LOADER);
544             if (prop.indexOf("strutsfile,") != -1) {
545                 prop = replace(prop, "strutsfile,", "");
546             } else if (prop.indexOf(", strutsfile") != -1) {
547                 prop = replace(prop, ", strutsfile", "");
548             } else if (prop.indexOf("strutsfile") != -1) {
549                 prop = replace(prop, "strutsfile", "");
550             }
551 
552             p.setProperty(Velocity.RESOURCE_LOADER, prop);
553         }
554 
555         /***
556          * Refactored the Velocity templates for the Struts taglib into the classpath from the web path.  This will
557          * enable Struts projects to have access to the templates by simply including the Struts jar file.
558          * Unfortunately, there does not appear to be a macro for the class loader keywords
559          * Matt Ho - Mon Mar 17 00:21:46 PST 2003
560          */
561         p.setProperty("strutsclass.resource.loader.description", "Velocity Classpath Resource Loader");
562         p.setProperty("strutsclass.resource.loader.class", "org.apache.struts2.views.velocity.StrutsResourceLoader");
563         p.setProperty("strutsclass.resource.loader.modificationCheckInterval", "2");
564         p.setProperty("strutsclass.resource.loader.cache", "true");
565 
566         // components
567         StringBuffer sb = new StringBuffer();
568 
569         addDirective(sb, ActionDirective.class);
570         addDirective(sb, BeanDirective.class);
571         addDirective(sb, CheckBoxDirective.class);
572         addDirective(sb, CheckBoxListDirective.class);
573         addDirective(sb, ComboBoxDirective.class);
574         addDirective(sb, ComponentDirective.class);
575         addDirective(sb, DateDirective.class);
576         addDirective(sb, DatePickerDirective.class);
577         addDirective(sb, DivDirective.class);
578         addDirective(sb, DoubleSelectDirective.class);
579         addDirective(sb, FileDirective.class);
580         addDirective(sb, FormDirective.class);
581         addDirective(sb, HeadDirective.class);
582         addDirective(sb, HiddenDirective.class);
583         addDirective(sb, AnchorDirective.class);
584         addDirective(sb, I18nDirective.class);
585         addDirective(sb, IncludeDirective.class);
586         addDirective(sb, LabelDirective.class);
587         addDirective(sb, PanelDirective.class);
588         addDirective(sb, ParamDirective.class);
589         addDirective(sb, PasswordDirective.class);
590         addDirective(sb, PushDirective.class);
591         addDirective(sb, PropertyDirective.class);
592         addDirective(sb, RadioDirective.class);
593         addDirective(sb, SelectDirective.class);
594         addDirective(sb, SetDirective.class);
595         addDirective(sb, SubmitDirective.class);
596         addDirective(sb, ResetDirective.class);
597         addDirective(sb, TabbedPanelDirective.class);
598         addDirective(sb, TextAreaDirective.class);
599         addDirective(sb, TextDirective.class);
600         addDirective(sb, TextFieldDirective.class);
601         addDirective(sb, TokenDirective.class);
602         addDirective(sb, TreeDirective.class);
603         addDirective(sb, TreeNodeDirective.class);
604         addDirective(sb, URLDirective.class);
605         addDirective(sb, WebTableDirective.class);
606         addDirective(sb, ActionErrorDirective.class);
607         addDirective(sb, ActionMessageDirective.class);
608         addDirective(sb, FieldErrorDirective.class);
609         addDirective(sb, OptionTransferSelectDirective.class);
610         addDirective(sb, UpDownSelectDirective.class);
611 
612         String directives = sb.toString();
613 
614         String userdirective = p.getProperty("userdirective");
615         if ((userdirective == null) || userdirective.trim().equals("")) {
616             userdirective = directives;
617         } else {
618             userdirective = userdirective.trim() + "," + directives;
619         }
620 
621         p.setProperty("userdirective", userdirective);
622     }
623 
624     private void addDirective(StringBuffer sb, Class clazz) {
625         sb.append(clazz.getName()).append(",");
626     }
627 
628     private static final String replace(String string, String oldString, String newString) {
629         if (string == null) {
630             return null;
631         }
632         // If the newString is null, just return the string since there's nothing to replace.
633         if (newString == null) {
634             return string;
635         }
636         int i = 0;
637         // Make sure that oldString appears at least once before doing any processing.
638         if ((i = string.indexOf(oldString, i)) >= 0) {
639             // Use char []'s, as they are more efficient to deal with.
640             char[] string2 = string.toCharArray();
641             char[] newString2 = newString.toCharArray();
642             int oLength = oldString.length();
643             StringBuffer buf = new StringBuffer(string2.length);
644             buf.append(string2, 0, i).append(newString2);
645             i += oLength;
646             int j = i;
647             // Replace all remaining instances of oldString with newString.
648             while ((i = string.indexOf(oldString, i)) > 0) {
649                 buf.append(string2, j, i - j).append(newString2);
650                 i += oLength;
651                 j = i;
652             }
653             buf.append(string2, j, string2.length - j);
654             return buf.toString();
655         }
656         return string;
657     }
658 
659 	/***
660 	 * @return the velocityProperties
661 	 */
662 	public Properties getVelocityProperties() {
663 		return velocityProperties;
664 	}
665 
666 	/***
667 	 * @param velocityProperties the velocityProperties to set
668 	 */
669 	public void setVelocityProperties(Properties velocityProperties) {
670 		this.velocityProperties = velocityProperties;
671 	}
672 }