| package org.apache.velocity.runtime; |
| |
| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2000-2001 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, if |
| * any, must include the following acknowlegement: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowlegement may appear in the software itself, |
| * if and wherever such third-party acknowlegements normally appear. |
| * |
| * 4. The names "The Jakarta Project", "Velocity", and "Apache Software |
| * Foundation" must not be used to endorse or promote products derived |
| * from this software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache" |
| * nor may "Apache" appear in their names without prior written |
| * permission of the Apache Group. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| |
| import org.apache.velocity.Template; |
| import org.apache.velocity.runtime.directive.Directive; |
| import org.apache.velocity.runtime.directive.VelocimacroProxy; |
| import org.apache.velocity.runtime.parser.node.SimpleNode; |
| |
| import java.util.Vector; |
| import java.util.Map; |
| import java.util.HashMap; |
| |
| /** |
| * VelocimacroFactory.java |
| * |
| * manages the set of VMs in a running Velocity engine. |
| * |
| * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> |
| * @version $Id: VelocimacroFactory.java,v 1.15 2001/08/13 13:58:34 geirm Exp $ |
| */ |
| public class VelocimacroFactory |
| { |
| /** |
| * runtime services for this instance |
| */ |
| private RuntimeServices rsvc = null; |
| |
| /** |
| * VMManager : deal with namespace management |
| * and actually keeps all the VM definitions |
| */ |
| private VelocimacroManager vmManager = null; |
| |
| /** |
| * determines if replacement of global VMs are allowed |
| * controlled by VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL |
| */ |
| private boolean replaceAllowed = false; |
| |
| /** |
| * controls if new VMs can be added. Set by |
| * VM_PERM_ALLOW_INLINE Note the assumption that only |
| * through inline defs can this happen. |
| * additions through autoloaded VMs is allowed |
| */ |
| private boolean addNewAllowed = true; |
| |
| /** |
| * sets if template-local namespace in used |
| */ |
| private boolean templateLocal = false; |
| |
| /** |
| * controls log output |
| */ |
| private boolean blather = false; |
| |
| /** |
| * determines if the libraries are auto-loaded |
| * when they change |
| */ |
| private boolean autoReloadLibrary = false; |
| |
| /** |
| * vector of the library names |
| */ |
| private Vector macroLibVec = null; |
| |
| /** |
| * map of the library Template objects |
| * used for reload determination |
| */ |
| private Map libModMap; |
| |
| /** |
| * CTOR : requires a runtime services from now |
| * on |
| */ |
| public VelocimacroFactory( RuntimeServices rs ) |
| { |
| this.rsvc = rs; |
| |
| /* |
| * we always access in a synchronized(), so we |
| * can use an unsynchronized hashmap |
| */ |
| libModMap = new HashMap(); |
| |
| vmManager = new VelocimacroManager( rsvc ); |
| } |
| |
| /** |
| * initialize the factory - setup all permissions |
| * load all global libraries. |
| */ |
| public void initVelocimacro() |
| { |
| /* |
| * maybe I'm just paranoid... |
| */ |
| synchronized( this ) |
| { |
| /* |
| * allow replacements while we add the libraries, if exist |
| */ |
| setReplacementPermission( true ); |
| setBlather( true ); |
| |
| logVMMessageInfo("Velocimacro : initialization starting."); |
| |
| /* |
| * add all library macros to the global namespace |
| */ |
| |
| vmManager.setNamespaceUsage( false ); |
| |
| /* |
| * now, if there is a global or local libraries specified, use them. |
| * All we have to do is get the template. The template will be parsed; |
| * VM's are added during the parse phase |
| */ |
| |
| Object libfiles = rsvc.getProperty( RuntimeConstants.VM_LIBRARY ); |
| |
| if( libfiles != null) |
| { |
| if (libfiles instanceof Vector) |
| { |
| macroLibVec = (Vector) libfiles; |
| } |
| else if (libfiles instanceof String) |
| { |
| macroLibVec = new Vector(); |
| macroLibVec.addElement( libfiles ); |
| } |
| |
| for( int i = 0; i < macroLibVec.size(); i++) |
| { |
| String lib = (String) macroLibVec.elementAt(i); |
| |
| /* |
| * only if it's a non-empty string do we bother |
| */ |
| |
| if (lib != null && !lib.equals("")) |
| { |
| /* |
| * let the VMManager know that the following is coming |
| * from libraries - need to know for auto-load |
| */ |
| |
| vmManager.setRegisterFromLib( true ); |
| |
| logVMMessageInfo("Velocimacro : adding VMs from " + |
| "VM library template : " + lib ); |
| |
| try |
| { |
| Template template = rsvc.getTemplate( lib ); |
| |
| /* |
| * save the template. This depends on the assumption |
| * that the Template object won't change - currently |
| * this is how the Resource manager works |
| */ |
| |
| libModMap.put( lib, template ); |
| } |
| catch (Exception e) |
| { |
| logVMMessageInfo("Velocimacro : error using VM " + |
| "library template " + lib + " : " + e ); |
| } |
| |
| logVMMessageInfo("Velocimacro : VM library template " + |
| "macro registration complete." ); |
| |
| vmManager.setRegisterFromLib( false ); |
| } |
| } |
| } |
| |
| /* |
| * now, the permissions |
| */ |
| |
| /* |
| * allowinline : anything after this will be an inline macro, I think |
| * there is the question if a #include is an inline, and I think so |
| * |
| * default = true |
| */ |
| setAddMacroPermission( true ); |
| |
| if ( !rsvc.getBoolean( RuntimeConstants.VM_PERM_ALLOW_INLINE, true) ) |
| { |
| setAddMacroPermission( false ); |
| |
| logVMMessageInfo("Velocimacro : allowInline = false : VMs can not " + |
| "be defined inline in templates"); |
| } |
| else |
| { |
| logVMMessageInfo("Velocimacro : allowInline = true : VMs can be " + |
| "defined inline in templates"); |
| } |
| |
| /* |
| * allowInlineToReplaceGlobal : allows an inline VM , if allowed at all, |
| * to replace an existing global VM |
| * |
| * default = false |
| */ |
| setReplacementPermission( false ); |
| |
| if ( rsvc.getBoolean( |
| RuntimeConstants.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL, false) ) |
| { |
| setReplacementPermission( true ); |
| |
| logVMMessageInfo("Velocimacro : allowInlineToOverride = true : VMs " + |
| "defined inline may replace previous VM definitions"); |
| } |
| else |
| { |
| logVMMessageInfo("Velocimacro : allowInlineToOverride = false : VMs " + |
| "defined inline may NOT replace previous VM definitions"); |
| } |
| |
| /* |
| * now turn on namespace handling as far as permissions allow in the |
| * manager, and also set it here for gating purposes |
| */ |
| vmManager.setNamespaceUsage( true ); |
| |
| /* |
| * template-local inline VM mode : default is off |
| */ |
| setTemplateLocalInline( rsvc.getBoolean( |
| RuntimeConstants.VM_PERM_INLINE_LOCAL, false) ); |
| |
| if ( getTemplateLocalInline() ) |
| { |
| logVMMessageInfo("Velocimacro : allowInlineLocal = true : VMs " + |
| "defined inline will be local to their defining template only."); |
| } |
| else |
| { |
| logVMMessageInfo("Velocimacro : allowInlineLocal = false : VMs " + |
| "defined inline will be global in scope if allowed."); |
| } |
| |
| vmManager.setTemplateLocalInlineVM( getTemplateLocalInline() ); |
| |
| /* |
| * general message switch. default is on |
| */ |
| setBlather( rsvc.getBoolean( RuntimeConstants.VM_MESSAGES_ON, true )); |
| |
| if (getBlather()) |
| { |
| logVMMessageInfo("Velocimacro : messages on : VM system " + |
| "will output logging messages"); |
| } |
| else |
| { |
| logVMMessageInfo("Velocimacro : messages off : VM system will be quiet"); |
| } |
| |
| /* |
| * autoload VM libraries |
| */ |
| setAutoload( rsvc.getBoolean( RuntimeConstants.VM_LIBRARY_AUTORELOAD, false )); |
| |
| if (getAutoload()) |
| { |
| logVMMessageInfo("Velocimacro : autoload on : VM system " + |
| "will automatically reload global library macros"); |
| } |
| else |
| { |
| logVMMessageInfo("Velocimacro : autoload off : VM system " + |
| "will not automatically reload global library macros"); |
| } |
| |
| rsvc.info("Velocimacro : initialization complete."); |
| } |
| |
| return; |
| } |
| |
| /** |
| * adds a macro to the factory. |
| */ |
| public boolean addVelocimacro( String name, String macroBody, |
| String argArray[], String sourceTemplate ) |
| { |
| /* |
| * maybe we should throw an exception, maybe just tell |
| * the caller like this... |
| * |
| * I hate this : maybe exceptions are in order here... |
| */ |
| if ( name == null || macroBody == null || argArray == null || |
| sourceTemplate == null ) |
| { |
| logVMMessageWarn("Velocimacro : VM addition rejected : " + |
| "programmer error : arg null" ); |
| |
| return false; |
| } |
| |
| /* |
| * see if the current ruleset allows this addition |
| */ |
| |
| if (!canAddVelocimacro( name, sourceTemplate )) |
| { |
| return false; |
| } |
| |
| /* |
| * seems like all is good. Lets do it. |
| */ |
| synchronized( this ) |
| { |
| vmManager.addVM( name, macroBody, argArray, sourceTemplate ); |
| } |
| |
| /* |
| * if we are to blather, blather... |
| */ |
| if ( blather) |
| { |
| String s = "#" + argArray[0]; |
| s += "("; |
| |
| for( int i=1; i < argArray.length; i++) |
| { |
| s += " "; |
| s += argArray[i]; |
| } |
| |
| s += " ) : source = "; |
| s += sourceTemplate; |
| |
| logVMMessageInfo( "Velocimacro : added new VM : " + s ); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * determines if a given macro/namespace (name, source) combo is allowed |
| * to be added |
| * |
| * @param name Name of VM to add |
| * @param sourceTemplate Source template that contains the defintion of the VM |
| * @return true if it is allowed to be added, false otherwise |
| */ |
| private boolean canAddVelocimacro( String name, String sourceTemplate) |
| { |
| /* |
| * short circuit and do it if autoloader is on, and the |
| * template is one of the library templates |
| */ |
| |
| if ( getAutoload() ) |
| { |
| /* |
| * see if this is a library template |
| */ |
| |
| for( int i = 0; i < macroLibVec.size(); i++) |
| { |
| String lib = (String) macroLibVec.elementAt(i); |
| |
| if (lib.equals( sourceTemplate ) ) |
| { |
| return true; |
| } |
| } |
| } |
| |
| |
| /* |
| * maybe the rules should be in manager? I dunno. It's to manage |
| * the namespace issues first, are we allowed to add VMs at all? |
| * This trumps all. |
| */ |
| if (!addNewAllowed) |
| { |
| logVMMessageWarn("Velocimacro : VM addition rejected : " + name + |
| " : inline VMs not allowed." ); |
| |
| return false; |
| } |
| |
| /* |
| * are they local in scope? Then it is ok to add. |
| */ |
| if (!templateLocal) |
| { |
| /* |
| * otherwise, if we have it already in global namespace, and they can't replace |
| * since local templates are not allowed, the global namespace is implied. |
| * remember, we don't know anything about namespace managment here, so lets |
| * note do anything fancy like trying to give it the global namespace here |
| * |
| * so if we have it, and we aren't allowed to replace, bail |
| */ |
| if ( isVelocimacro( name, sourceTemplate ) && !replaceAllowed ) |
| { |
| logVMMessageWarn("Velocimacro : VM addition rejected : " |
| + name + " : inline not allowed to replace existing VM" ); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * localization of the logging logic |
| */ |
| private void logVMMessageInfo( String s ) |
| { |
| if (blather) |
| rsvc.info( s ); |
| } |
| |
| /** |
| * localization of the logging logic |
| */ |
| private void logVMMessageWarn( String s ) |
| { |
| if (blather) |
| rsvc.warn( s ); |
| } |
| |
| /** |
| * Tells the world if a given directive string is a Velocimacro |
| */ |
| public boolean isVelocimacro( String vm , String sourceTemplate ) |
| { |
| synchronized(this) |
| { |
| /* |
| * first we check the locals to see if we have |
| * a local definition for this template |
| */ |
| if (vmManager.get( vm, sourceTemplate ) != null) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * actual factory : creates a Directive that will |
| * behave correctly wrt getting the framework to |
| * dig out the correct # of args |
| */ |
| public Directive getVelocimacro( String vmName, String sourceTemplate ) |
| { |
| VelocimacroProxy vp = null; |
| |
| synchronized( this ) |
| { |
| /* |
| * don't ask - do |
| */ |
| |
| vp = vmManager.get( vmName, sourceTemplate); |
| |
| /* |
| * if this exists, and autoload is on, we need to check |
| * where this VM came from |
| */ |
| |
| if ( vp != null && getAutoload() ) |
| { |
| /* |
| * see if this VM came from a library. Need to pass sourceTemplate |
| * in the event namespaces are set, as it could be masked by local |
| */ |
| |
| String lib = vmManager.getLibraryName( vmName, sourceTemplate ); |
| |
| if (lib != null) |
| { |
| try |
| { |
| /* |
| * get the template from our map |
| */ |
| |
| Template template = (Template) libModMap.get(lib ); |
| |
| if (template != null) |
| { |
| /* |
| * now, compare the last modified time of the resource |
| * with the last modified time of the template |
| * if the file has changed, then reload. Otherwise, we should |
| * be ok. |
| */ |
| |
| long tt = template.getLastModified(); |
| long ft = template.getResourceLoader().getLastModified( template ); |
| |
| if ( ft > tt ) |
| { |
| logVMMessageInfo("Velocimacro : autoload reload for VMs from " + |
| "VM library template : " + lib ); |
| |
| template = rsvc.getTemplate( lib ); |
| libModMap.put( lib, template ); |
| } |
| } |
| } |
| catch (Exception e) |
| { |
| logVMMessageInfo("Velocimacro : error using VM " + |
| "library template " + lib + " : " + e ); |
| } |
| |
| /* |
| * and get again |
| */ |
| |
| vp = vmManager.get( vmName, sourceTemplate); |
| } |
| } |
| } |
| |
| return vp; |
| } |
| |
| /** |
| * tells the vmManager to dump the specified namespace |
| */ |
| public boolean dumpVMNamespace( String namespace ) |
| { |
| return vmManager.dumpNamespace( namespace ); |
| } |
| |
| /** |
| * sets permission to have VMs local in scope to their declaring template |
| * note that this is really taken care of in the VMManager class, but |
| * we need it here for gating purposes in addVM |
| * eventually, I will slide this all into the manager, maybe. |
| */ |
| private void setTemplateLocalInline( boolean b ) |
| { |
| templateLocal = b; |
| } |
| |
| private boolean getTemplateLocalInline() |
| { |
| return templateLocal; |
| } |
| |
| /** |
| * sets the permission to add new macros |
| */ |
| private boolean setAddMacroPermission( boolean arg ) |
| { |
| boolean b = addNewAllowed; |
| |
| addNewAllowed = arg; |
| return b; |
| } |
| |
| /** |
| * sets the permission for allowing addMacro() calls to |
| * replace existing VM's |
| */ |
| private boolean setReplacementPermission( boolean arg ) |
| { |
| boolean b = replaceAllowed; |
| replaceAllowed = arg; |
| return b; |
| } |
| |
| /** |
| * set output message mode |
| */ |
| private void setBlather( boolean b ) |
| { |
| blather = b; |
| } |
| |
| /** |
| * get output message mode |
| */ |
| private boolean getBlather() |
| { |
| return blather; |
| } |
| |
| /** |
| * set the switch for automatic reloading of |
| * global library-based VMs |
| */ |
| private void setAutoload( boolean b) |
| { |
| autoReloadLibrary = b; |
| } |
| |
| /** |
| * get the switch for automatic reloading of |
| * global library-based VMs |
| */ |
| private boolean getAutoload() |
| { |
| return autoReloadLibrary; |
| } |
| |
| } |
| |
| |
| |
| |
| |
| |
| |