Source Code Cross Referenced for Environment.java in  » Template-Engine » freemarker-2.3.10 » freemarker » core » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Template Engine » freemarker 2.3.10 » freemarker.core 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright (c) 2003 The Visigoth Software Society. All rights
0003:         * reserved.
0004:         *
0005:         * Redistribution and use in source and binary forms, with or without
0006:         * modification, are permitted provided that the following conditions
0007:         * are met:
0008:         *
0009:         * 1. Redistributions of source code must retain the above copyright
0010:         *    notice, this list of conditions and the following disclaimer.
0011:         *
0012:         * 2. Redistributions in binary form must reproduce the above copyright
0013:         *    notice, this list of conditions and the following disclaimer in
0014:         *    the documentation and/or other materials provided with the
0015:         *    distribution.
0016:         *
0017:         * 3. The end-user documentation included with the redistribution, if
0018:         *    any, must include the following acknowledgement:
0019:         *       "This product includes software developed by the
0020:         *        Visigoth Software Society (http://www.visigoths.org/)."
0021:         *    Alternately, this acknowledgement may appear in the software itself,
0022:         *    if and wherever such third-party acknowledgements normally appear.
0023:         *
0024:         * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
0025:         *    project contributors may be used to endorse or promote products derived
0026:         *    from this software without prior written permission. For written
0027:         *    permission, please contact visigoths@visigoths.org.
0028:         *
0029:         * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
0030:         *    nor may "FreeMarker" or "Visigoth" appear in their names
0031:         *    without prior written permission of the Visigoth Software Society.
0032:         *
0033:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0034:         * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0035:         * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0036:         * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
0037:         * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0038:         * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0039:         * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0040:         * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0041:         * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0042:         * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0043:         * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0044:         * SUCH DAMAGE.
0045:         * ====================================================================
0046:         *
0047:         * This software consists of voluntary contributions made by many
0048:         * individuals on behalf of the Visigoth Software Society. For more
0049:         * information on the Visigoth Software Society, please see
0050:         * http://www.visigoths.org/
0051:         */
0052:
0053:        package freemarker.core;
0054:
0055:        import java.io.*;
0056:        import java.text.*;
0057:        import java.util.*;
0058:
0059:        import freemarker.ext.beans.BeansWrapper;
0060:        import freemarker.log.Logger;
0061:        import freemarker.template.*;
0062:        import freemarker.template.utility.UndeclaredThrowableException;
0063:
0064:        /**
0065:         * Object that represents the runtime environment during template processing.
0066:         * For every invocation of a <tt>Template.process()</tt> method, a new instance
0067:         * of this object is created, and then discarded when <tt>process()</tt> returns.
0068:         * This object stores the set of temporary variables created by the template,
0069:         * the value of settings set by the template, the reference to the data model root,
0070:         * etc. Everything that is needed to fulfill the template processing job.
0071:         *
0072:         * <p>Data models that need to access the <tt>Environment</tt>
0073:         * object that represents the template processing on the current thread can use
0074:         * the {@link #getCurrentEnvironment()} method.
0075:         *
0076:         * <p>If you need to modify or read this object before or after the <tt>process</tt>
0077:         * call, use {@link Template#createProcessingEnvironment(Object rootMap, Writer out, ObjectWrapper wrapper)}
0078:         *
0079:         * @author <a href="mailto:jon@revusky.com">Jonathan Revusky</a>
0080:         * @author Attila Szegedi
0081:         */
0082:        public final class Environment extends Configurable {
0083:
0084:            private static final ThreadLocal threadEnv = new ThreadLocal();
0085:
0086:            private static final Logger logger = Logger
0087:                    .getLogger("freemarker.runtime");
0088:
0089:            private static final Map localizedNumberFormats = new HashMap();
0090:            private static final Map localizedDateFormats = new HashMap();
0091:
0092:            private final TemplateHashModel rootDataModel;
0093:            private final ArrayList elementStack = new ArrayList();
0094:            private final ArrayList recoveredErrorStack = new ArrayList();
0095:
0096:            private NumberFormat numberFormat;
0097:            private Map numberFormats;
0098:
0099:            private DateFormat timeFormat, dateFormat, dateTimeFormat;
0100:            private Map[] dateFormats;
0101:
0102:            private Collator collator;
0103:
0104:            private Writer out;
0105:            private Macro.Context currentMacroContext;
0106:            private ArrayList localContextStack;
0107:            private Namespace mainNamespace, currentNamespace, globalNamespace;
0108:            private HashMap loadedLibs;
0109:
0110:            private Throwable lastThrowable;
0111:
0112:            private TemplateModel lastReturnValue;
0113:            private HashMap macroToNamespaceLookup = new HashMap();
0114:
0115:            private TemplateNodeModel currentVisitorNode;
0116:            private TemplateSequenceModel nodeNamespaces;
0117:            // Things we keep track of for the fallback mechanism.
0118:            private int nodeNamespaceIndex;
0119:            private String currentNodeName, currentNodeNS;
0120:
0121:            private String cachedURLEscapingCharset;
0122:            private boolean urlEscapingCharsetCached;
0123:
0124:            /**
0125:             * Retrieves the environment object associated with the current
0126:             * thread. Data model implementations that need access to the
0127:             * environment can call this method to obtain the environment object
0128:             * that represents the template processing that is currently running
0129:             * on the current thread.
0130:             */
0131:            public static Environment getCurrentEnvironment() {
0132:                return (Environment) threadEnv.get();
0133:            }
0134:
0135:            public Environment(Template template,
0136:                    final TemplateHashModel rootDataModel, Writer out) {
0137:                super (template);
0138:                this .globalNamespace = new Namespace(null);
0139:                this .currentNamespace = mainNamespace = new Namespace(template);
0140:                this .out = out;
0141:                this .rootDataModel = rootDataModel;
0142:                importMacros(template);
0143:            }
0144:
0145:            /**
0146:             * Retrieves the currently processed template.
0147:             */
0148:            public Template getTemplate() {
0149:                return (Template) getParent();
0150:            }
0151:
0152:            /**
0153:             * Deletes cached values that meant to be valid only during a single
0154:             * template execution. 
0155:             */
0156:            private void clearCachedValues() {
0157:                numberFormats = null;
0158:                numberFormat = null;
0159:                dateFormats = null;
0160:                collator = null;
0161:                cachedURLEscapingCharset = null;
0162:                urlEscapingCharsetCached = false;
0163:            }
0164:
0165:            /**
0166:             * Processes the template to which this environment belongs.
0167:             */
0168:            public void process() throws TemplateException, IOException {
0169:                Object savedEnv = threadEnv.get();
0170:                threadEnv.set(this );
0171:                try {
0172:                    // Cached values from a previous execution are possibly outdated.
0173:                    clearCachedValues();
0174:                    try {
0175:                        visit(getTemplate().getRootTreeNode());
0176:                        // Do not flush if there was an exception.
0177:                        out.flush();
0178:                    } finally {
0179:                        // It's just to allow the GC to free memory...
0180:                        clearCachedValues();
0181:                    }
0182:                } finally {
0183:                    threadEnv.set(savedEnv);
0184:                }
0185:            }
0186:
0187:            /**
0188:             * "Visit" the template element.
0189:             */
0190:            void visit(TemplateElement element) throws TemplateException,
0191:                    IOException {
0192:                pushElement(element);
0193:                try {
0194:                    element.accept(this );
0195:                } catch (TemplateException te) {
0196:                    handleTemplateException(te);
0197:                } finally {
0198:                    popElement();
0199:                }
0200:            }
0201:
0202:            /**
0203:             * "Visit" the template element, passing the output
0204:             * through a TemplateTransformModel
0205:             * @param element the element to visit through a transform
0206:             * @param transform the transform to pass the element output
0207:             * through
0208:             * @param args optional arguments fed to the transform
0209:             */
0210:            void visit(TemplateElement element,
0211:                    TemplateTransformModel transform, Map args)
0212:                    throws TemplateException, IOException {
0213:                try {
0214:                    Writer tw = transform.getWriter(out, args);
0215:                    if (tw == null)
0216:                        tw = EMPTY_BODY_WRITER;
0217:                    TransformControl tc = tw instanceof  TransformControl ? (TransformControl) tw
0218:                            : null;
0219:
0220:                    Writer prevOut = out;
0221:                    out = tw;
0222:                    try {
0223:                        if (tc == null
0224:                                || tc.onStart() != TransformControl.SKIP_BODY) {
0225:                            do {
0226:                                if (element != null) {
0227:                                    visit(element);
0228:                                }
0229:                            } while (tc != null
0230:                                    && tc.afterBody() == TransformControl.REPEAT_EVALUATION);
0231:                        }
0232:                    } catch (Throwable t) {
0233:                        try {
0234:                            if (tc != null) {
0235:                                tc.onError(t);
0236:                            } else {
0237:                                throw t;
0238:                            }
0239:                        } catch (TemplateException e) {
0240:                            throw e;
0241:                        } catch (IOException e) {
0242:                            throw e;
0243:                        } catch (RuntimeException e) {
0244:                            throw e;
0245:                        } catch (Error e) {
0246:                            throw e;
0247:                        } catch (Throwable e) {
0248:                            throw new UndeclaredThrowableException(e);
0249:                        }
0250:                    } finally {
0251:                        out = prevOut;
0252:                        tw.close();
0253:                    }
0254:                } catch (TemplateException te) {
0255:                    handleTemplateException(te);
0256:                }
0257:            }
0258:
0259:            /**
0260:             * Visit a block using buffering/recovery
0261:             */
0262:
0263:            void visit(TemplateElement attemptBlock,
0264:                    TemplateElement recoveryBlock) throws TemplateException,
0265:                    IOException {
0266:                Writer prevOut = this .out;
0267:                StringWriter sw = new StringWriter();
0268:                this .out = sw;
0269:                TemplateException thrownException = null;
0270:                try {
0271:                    visit(attemptBlock);
0272:                } catch (TemplateException te) {
0273:                    thrownException = te;
0274:                } finally {
0275:                    this .out = prevOut;
0276:                }
0277:                if (thrownException != null) {
0278:                    if (logger.isErrorEnabled()) {
0279:                        String msg = "Error in attempt block "
0280:                                + attemptBlock.getStartLocation();
0281:                        logger.error(msg, thrownException);
0282:                    }
0283:                    try {
0284:                        recoveredErrorStack.add(thrownException.getMessage());
0285:                        visit(recoveryBlock);
0286:                    } finally {
0287:                        recoveredErrorStack
0288:                                .remove(recoveredErrorStack.size() - 1);
0289:                    }
0290:                } else {
0291:                    out.write(sw.toString());
0292:                }
0293:            }
0294:
0295:            String getCurrentRecoveredErrorMesssage() throws TemplateException {
0296:                if (recoveredErrorStack.isEmpty()) {
0297:                    throw new TemplateException(
0298:                            ".error is not available outside of a <#recover> block",
0299:                            this );
0300:                }
0301:                return (String) recoveredErrorStack.get(recoveredErrorStack
0302:                        .size() - 1);
0303:            }
0304:
0305:            void visit(BodyInstruction.Context bctxt) throws TemplateException,
0306:                    IOException {
0307:                Macro.Context invokingMacroContext = getCurrentMacroContext();
0308:                ArrayList prevLocalContextStack = localContextStack;
0309:                TemplateElement body = invokingMacroContext.body;
0310:                if (body != null) {
0311:                    this .currentMacroContext = invokingMacroContext.prevMacroContext;
0312:                    currentNamespace = invokingMacroContext.bodyNamespace;
0313:                    Configurable prevParent = getParent();
0314:                    setParent(currentNamespace.getTemplate());
0315:                    this .localContextStack = invokingMacroContext.prevLocalContextStack;
0316:                    if (invokingMacroContext.bodyParameterNames != null) {
0317:                        pushLocalContext(bctxt);
0318:                    }
0319:                    try {
0320:                        visit(body);
0321:                    } finally {
0322:                        if (invokingMacroContext.bodyParameterNames != null) {
0323:                            popLocalContext();
0324:                        }
0325:                        this .currentMacroContext = invokingMacroContext;
0326:                        currentNamespace = getMacroNamespace(invokingMacroContext
0327:                                .getMacro());
0328:                        setParent(prevParent);
0329:                        this .localContextStack = prevLocalContextStack;
0330:                    }
0331:                }
0332:            }
0333:
0334:            /**
0335:             * "visit" an IteratorBlock
0336:             */
0337:            void visit(IteratorBlock.Context ictxt) throws TemplateException,
0338:                    IOException {
0339:                pushLocalContext(ictxt);
0340:                try {
0341:                    ictxt.runLoop(this );
0342:                } catch (BreakInstruction.Break br) {
0343:                } catch (TemplateException te) {
0344:                    handleTemplateException(te);
0345:                } finally {
0346:                    popLocalContext();
0347:                }
0348:            }
0349:
0350:            /**
0351:             * "Visit" A TemplateNodeModel
0352:             */
0353:
0354:            void visit(TemplateNodeModel node, TemplateSequenceModel namespaces)
0355:                    throws TemplateException, IOException {
0356:                if (nodeNamespaces == null) {
0357:                    SimpleSequence ss = new SimpleSequence(1);
0358:                    ss.add(currentNamespace);
0359:                    nodeNamespaces = ss;
0360:                }
0361:                int prevNodeNamespaceIndex = this .nodeNamespaceIndex;
0362:                String prevNodeName = this .currentNodeName;
0363:                String prevNodeNS = this .currentNodeNS;
0364:                TemplateSequenceModel prevNodeNamespaces = nodeNamespaces;
0365:                TemplateNodeModel prevVisitorNode = currentVisitorNode;
0366:                currentVisitorNode = node;
0367:                if (namespaces != null) {
0368:                    this .nodeNamespaces = namespaces;
0369:                }
0370:                try {
0371:                    TemplateModel macroOrTransform = getNodeProcessor(node);
0372:                    if (macroOrTransform instanceof  Macro) {
0373:                        visit((Macro) macroOrTransform, null, null, null, null);
0374:                    } else if (macroOrTransform instanceof  TemplateTransformModel) {
0375:                        visit(null, (TemplateTransformModel) macroOrTransform,
0376:                                null);
0377:                    } else {
0378:                        String nodeType = node.getNodeType();
0379:                        if (nodeType != null) {
0380:                            // If the node's type is 'text', we just output it.
0381:                            if ((nodeType.equals("text") && node instanceof  TemplateScalarModel)) {
0382:                                out.write(((TemplateScalarModel) node)
0383:                                        .getAsString());
0384:                            } else if (nodeType.equals("document")) {
0385:                                recurse(node, namespaces);
0386:                            }
0387:                            // We complain here, unless the node's type is 'pi', or "comment" or "document_type", in which case
0388:                            // we just ignore it.
0389:                            else if (!nodeType.equals("pi")
0390:                                    && !nodeType.equals("comment")
0391:                                    && !nodeType.equals("document_type")) {
0392:                                String nsBit = "";
0393:                                String ns = node.getNodeNamespace();
0394:                                if (ns != null) {
0395:                                    if (ns.length() > 0) {
0396:                                        nsBit = " and namespace " + ns;
0397:                                    } else {
0398:                                        nsBit = " and no namespace";
0399:                                    }
0400:                                }
0401:                                throw new TemplateException(
0402:                                        "No macro or transform defined for node named "
0403:                                                + node.getNodeName()
0404:                                                + nsBit
0405:                                                + ", and there is no fallback handler called @"
0406:                                                + nodeType + " either.", this );
0407:                            }
0408:                        } else {
0409:                            String nsBit = "";
0410:                            String ns = node.getNodeNamespace();
0411:                            if (ns != null) {
0412:                                if (ns.length() > 0) {
0413:                                    nsBit = " and namespace " + ns;
0414:                                } else {
0415:                                    nsBit = " and no namespace";
0416:                                }
0417:                            }
0418:                            throw new TemplateException(
0419:                                    "No macro or transform defined for node with name "
0420:                                            + node.getNodeName()
0421:                                            + nsBit
0422:                                            + ", and there is no macro or transform called @default either.",
0423:                                    this );
0424:                        }
0425:                    }
0426:                } finally {
0427:                    this .currentVisitorNode = prevVisitorNode;
0428:                    this .nodeNamespaceIndex = prevNodeNamespaceIndex;
0429:                    this .currentNodeName = prevNodeName;
0430:                    this .currentNodeNS = prevNodeNS;
0431:                    this .nodeNamespaces = prevNodeNamespaces;
0432:                }
0433:            }
0434:
0435:            void fallback() throws TemplateException, IOException {
0436:                TemplateModel macroOrTransform = getNodeProcessor(
0437:                        currentNodeName, currentNodeNS, nodeNamespaceIndex);
0438:                if (macroOrTransform instanceof  Macro) {
0439:                    visit((Macro) macroOrTransform, null, null, null, null);
0440:                } else if (macroOrTransform instanceof  TemplateTransformModel) {
0441:                    visit(null, (TemplateTransformModel) macroOrTransform, null);
0442:                }
0443:            }
0444:
0445:            /**
0446:             * "visit" a macro.
0447:             */
0448:
0449:            void visit(Macro macro, Map namedArgs, List positionalArgs,
0450:                    List bodyParameterNames, TemplateElement nestedBlock)
0451:                    throws TemplateException, IOException {
0452:                if (macro == Macro.DO_NOTHING_MACRO) {
0453:                    return;
0454:                }
0455:                pushElement(macro);
0456:                try {
0457:                    Macro.Context previousMacroContext = currentMacroContext;
0458:                    Macro.Context mc = macro.new Context(this , nestedBlock,
0459:                            bodyParameterNames);
0460:
0461:                    String catchAll = macro.getCatchAll();
0462:                    TemplateModel unknownVars = null;
0463:
0464:                    if (namedArgs != null) {
0465:                        if (catchAll != null)
0466:                            unknownVars = new SimpleHash();
0467:                        for (Iterator it = namedArgs.entrySet().iterator(); it
0468:                                .hasNext();) {
0469:                            Map.Entry entry = (Map.Entry) it.next();
0470:                            String varName = (String) entry.getKey();
0471:                            boolean hasVar = macro.hasArgNamed(varName);
0472:                            if (hasVar || catchAll != null) {
0473:                                Expression arg = (Expression) entry.getValue();
0474:                                TemplateModel value = arg
0475:                                        .getAsTemplateModel(this );
0476:                                if (hasVar) {
0477:                                    mc.setLocalVar(varName, value);
0478:                                } else {
0479:                                    ((SimpleHash) unknownVars).put(varName,
0480:                                            value);
0481:                                }
0482:                            } else {
0483:                                String msg = "Macro " + macro.getName()
0484:                                        + " has no such argument: " + varName;
0485:                                throw new TemplateException(msg, this );
0486:                            }
0487:                        }
0488:                    } else if (positionalArgs != null) {
0489:                        if (catchAll != null)
0490:                            unknownVars = new SimpleSequence();
0491:                        String[] argumentNames = macro.getArgumentNames();
0492:                        int size = positionalArgs.size();
0493:                        if (argumentNames.length < size && catchAll == null) {
0494:                            throw new TemplateException("Macro "
0495:                                    + macro.getName() + " only accepts "
0496:                                    + argumentNames.length + " parameters.",
0497:                                    this );
0498:                        }
0499:                        for (int i = 0; i < size; i++) {
0500:                            Expression argExp = (Expression) positionalArgs
0501:                                    .get(i);
0502:                            TemplateModel argModel = argExp
0503:                                    .getAsTemplateModel(this );
0504:                            try {
0505:                                if (i < argumentNames.length) {
0506:                                    String argName = argumentNames[i];
0507:                                    mc.setLocalVar(argName, argModel);
0508:                                } else {
0509:                                    ((SimpleSequence) unknownVars)
0510:                                            .add(argModel);
0511:                                }
0512:                            } catch (RuntimeException re) {
0513:                                throw new TemplateException(re, this );
0514:                            }
0515:                        }
0516:                    }
0517:                    if (catchAll != null) {
0518:                        mc.setLocalVar(catchAll, unknownVars);
0519:                    }
0520:                    ArrayList prevLocalContextStack = localContextStack;
0521:                    localContextStack = null;
0522:                    Namespace prevNamespace = currentNamespace;
0523:                    Configurable prevParent = getParent();
0524:                    currentNamespace = (Namespace) macroToNamespaceLookup
0525:                            .get(macro);
0526:                    currentMacroContext = mc;
0527:                    try {
0528:                        mc.runMacro(this );
0529:                    } catch (ReturnInstruction.Return re) {
0530:                    } catch (TemplateException te) {
0531:                        handleTemplateException(te);
0532:                    } finally {
0533:                        currentMacroContext = previousMacroContext;
0534:                        localContextStack = prevLocalContextStack;
0535:                        currentNamespace = prevNamespace;
0536:                        setParent(prevParent);
0537:                    }
0538:                } finally {
0539:                    popElement();
0540:                }
0541:            }
0542:
0543:            void visitMacroDef(Macro macro) {
0544:                macroToNamespaceLookup.put(macro, currentNamespace);
0545:                currentNamespace.put(macro.getName(), macro);
0546:            }
0547:
0548:            Namespace getMacroNamespace(Macro macro) {
0549:                return (Namespace) macroToNamespaceLookup.get(macro);
0550:            }
0551:
0552:            void recurse(TemplateNodeModel node,
0553:                    TemplateSequenceModel namespaces) throws TemplateException,
0554:                    IOException {
0555:                if (node == null) {
0556:                    node = this .getCurrentVisitorNode();
0557:                    if (node == null) {
0558:                        throw new TemplateModelException(
0559:                                "The target node of recursion is missing or null.");
0560:                    }
0561:                }
0562:                TemplateSequenceModel children = node.getChildNodes();
0563:                if (children == null)
0564:                    return;
0565:                for (int i = 0; i < children.size(); i++) {
0566:                    TemplateNodeModel child = (TemplateNodeModel) children
0567:                            .get(i);
0568:                    if (child != null) {
0569:                        visit(child, namespaces);
0570:                    }
0571:                }
0572:            }
0573:
0574:            Macro.Context getCurrentMacroContext() {
0575:                return currentMacroContext;
0576:            }
0577:
0578:            private void handleTemplateException(TemplateException te)
0579:                    throws TemplateException {
0580:                // Logic to prevent double-handling of the exception in
0581:                // nested visit() calls.
0582:                if (lastThrowable == te) {
0583:                    throw te;
0584:                }
0585:                lastThrowable = te;
0586:
0587:                // Log the exception
0588:                if (logger.isErrorEnabled()) {
0589:                    logger.error("", te);
0590:                }
0591:
0592:                // Stop exception is not passed to the handler, but
0593:                // explicitly rethrown.
0594:                if (te instanceof  StopException) {
0595:                    throw te;
0596:                }
0597:
0598:                // Finally, pass the exception to the handler
0599:                getTemplateExceptionHandler().handleTemplateException(te, this ,
0600:                        out);
0601:            }
0602:
0603:            public void setTemplateExceptionHandler(
0604:                    TemplateExceptionHandler templateExceptionHandler) {
0605:                super .setTemplateExceptionHandler(templateExceptionHandler);
0606:                lastThrowable = null;
0607:            }
0608:
0609:            public void setLocale(Locale locale) {
0610:                super .setLocale(locale);
0611:                // Clear local format cache
0612:                numberFormats = null;
0613:                numberFormat = null;
0614:
0615:                dateFormats = null;
0616:                timeFormat = dateFormat = dateTimeFormat = null;
0617:
0618:                collator = null;
0619:            }
0620:
0621:            public void setTimeZone(TimeZone timeZone) {
0622:                super .setTimeZone(timeZone);
0623:                // Clear local date format cache
0624:                dateFormats = null;
0625:                timeFormat = dateFormat = dateTimeFormat = null;
0626:            }
0627:
0628:            public void setURLEscapingCharset(String urlEscapingCharset) {
0629:                urlEscapingCharsetCached = false;
0630:                super .setURLEscapingCharset(urlEscapingCharset);
0631:            }
0632:
0633:            /*
0634:             * Note that altough it is not allowed to set this setting with the
0635:             * <tt>setting</tt> directive, it still must be allowed to set it from Java
0636:             * code while the template executes, since some frameworks allow templates
0637:             * to actually change the output encoding on-the-fly.
0638:             */
0639:            public void setOutputEncoding(String outputEncoding) {
0640:                urlEscapingCharsetCached = false;
0641:                super .setOutputEncoding(outputEncoding);
0642:            }
0643:
0644:            /**
0645:             * Returns the name of the charset that should be used for URL encoding.
0646:             * This will be <code>null</code> if the information is not available.
0647:             * The function caches the return value, so it is quick to call it
0648:             * repeately. 
0649:             */
0650:            String getEffectiveURLEscapingCharset() {
0651:                if (!urlEscapingCharsetCached) {
0652:                    cachedURLEscapingCharset = getURLEscapingCharset();
0653:                    if (cachedURLEscapingCharset == null) {
0654:                        cachedURLEscapingCharset = getOutputEncoding();
0655:                    }
0656:                    urlEscapingCharsetCached = true;
0657:                }
0658:                return cachedURLEscapingCharset;
0659:            }
0660:
0661:            Collator getCollator() {
0662:                if (collator == null) {
0663:                    collator = Collator.getInstance(getLocale());
0664:                }
0665:                return collator;
0666:            }
0667:
0668:            public void setOut(Writer out) {
0669:                this .out = out;
0670:            }
0671:
0672:            public Writer getOut() {
0673:                return out;
0674:            }
0675:
0676:            String formatNumber(Number number) {
0677:                if (numberFormat == null) {
0678:                    numberFormat = getNumberFormatObject(getNumberFormat());
0679:                }
0680:                return numberFormat.format(number);
0681:            }
0682:
0683:            public void setNumberFormat(String formatName) {
0684:                super .setNumberFormat(formatName);
0685:                numberFormat = null;
0686:            }
0687:
0688:            String formatDate(Date date, int type)
0689:                    throws TemplateModelException {
0690:                DateFormat df = getDateFormatObject(type);
0691:                if (df == null) {
0692:                    throw new TemplateModelException(
0693:                            "Can't convert the date to string, because it is not known which parts of the date variable are in use. Use ?date, ?time or ?datetime built-in, or ?string.<format> or ?string(format) built-in with this date.");
0694:                }
0695:                return df.format(date);
0696:            }
0697:
0698:            public void setTimeFormat(String formatName) {
0699:                super .setTimeFormat(formatName);
0700:                timeFormat = null;
0701:            }
0702:
0703:            public void setDateFormat(String formatName) {
0704:                super .setDateFormat(formatName);
0705:                dateFormat = null;
0706:            }
0707:
0708:            public void setDateTimeFormat(String formatName) {
0709:                super .setDateTimeFormat(formatName);
0710:                dateTimeFormat = null;
0711:            }
0712:
0713:            public Configuration getConfiguration() {
0714:                return getTemplate().getConfiguration();
0715:            }
0716:
0717:            TemplateModel getLastReturnValue() {
0718:                return lastReturnValue;
0719:            }
0720:
0721:            void setLastReturnValue(TemplateModel lastReturnValue) {
0722:                this .lastReturnValue = lastReturnValue;
0723:            }
0724:
0725:            void clearLastReturnValue() {
0726:                this .lastReturnValue = null;
0727:            }
0728:
0729:            NumberFormat getNumberFormatObject(String pattern) {
0730:                if (numberFormats == null) {
0731:                    numberFormats = new HashMap();
0732:                }
0733:
0734:                NumberFormat format = (NumberFormat) numberFormats.get(pattern);
0735:                if (format != null) {
0736:                    return format;
0737:                }
0738:
0739:                // Get format from global format cache
0740:                synchronized (localizedNumberFormats) {
0741:                    Locale locale = getLocale();
0742:                    NumberFormatKey fk = new NumberFormatKey(pattern, locale);
0743:                    format = (NumberFormat) localizedNumberFormats.get(fk);
0744:                    if (format == null) {
0745:                        // Add format to global format cache. Note this is
0746:                        // globally done once per locale per pattern.
0747:                        if ("number".equals(pattern)) {
0748:                            format = NumberFormat.getNumberInstance(locale);
0749:                        } else if ("currency".equals(pattern)) {
0750:                            format = NumberFormat.getCurrencyInstance(locale);
0751:                        } else if ("percent".equals(pattern)) {
0752:                            format = NumberFormat.getPercentInstance(locale);
0753:                        } else {
0754:                            format = new DecimalFormat(pattern,
0755:                                    new DecimalFormatSymbols(getLocale()));
0756:                        }
0757:                        localizedNumberFormats.put(fk, format);
0758:                    }
0759:                }
0760:
0761:                // Clone it and store the clone in the local cache
0762:                format = (NumberFormat) format.clone();
0763:                numberFormats.put(pattern, format);
0764:                return format;
0765:            }
0766:
0767:            DateFormat getDateFormatObject(int dateType)
0768:                    throws TemplateModelException {
0769:                switch (dateType) {
0770:                case TemplateDateModel.UNKNOWN: {
0771:                    return null;
0772:                }
0773:                case TemplateDateModel.TIME: {
0774:                    if (timeFormat == null) {
0775:                        timeFormat = getDateFormatObject(dateType,
0776:                                getTimeFormat());
0777:                    }
0778:                    return timeFormat;
0779:                }
0780:                case TemplateDateModel.DATE: {
0781:                    if (dateFormat == null) {
0782:                        dateFormat = getDateFormatObject(dateType,
0783:                                getDateFormat());
0784:                    }
0785:                    return dateFormat;
0786:                }
0787:                case TemplateDateModel.DATETIME: {
0788:                    if (dateTimeFormat == null) {
0789:                        dateTimeFormat = getDateFormatObject(dateType,
0790:                                getDateTimeFormat());
0791:                    }
0792:                    return dateTimeFormat;
0793:                }
0794:                default: {
0795:                    throw new TemplateModelException("Unrecognized date type "
0796:                            + dateType);
0797:                }
0798:                }
0799:            }
0800:
0801:            DateFormat getDateFormatObject(int dateType, String pattern)
0802:                    throws TemplateModelException {
0803:                if (dateFormats == null) {
0804:                    dateFormats = new Map[4];
0805:                    dateFormats[TemplateDateModel.UNKNOWN] = new HashMap();
0806:                    dateFormats[TemplateDateModel.TIME] = new HashMap();
0807:                    dateFormats[TemplateDateModel.DATE] = new HashMap();
0808:                    dateFormats[TemplateDateModel.DATETIME] = new HashMap();
0809:                }
0810:                Map typedDateFormat = dateFormats[dateType];
0811:
0812:                DateFormat format = (DateFormat) typedDateFormat.get(pattern);
0813:                if (format != null) {
0814:                    return format;
0815:                }
0816:
0817:                // Get format from global format cache
0818:                synchronized (localizedDateFormats) {
0819:                    Locale locale = getLocale();
0820:                    TimeZone timeZone = getTimeZone();
0821:                    DateFormatKey fk = new DateFormatKey(dateType, pattern,
0822:                            locale, timeZone);
0823:                    format = (DateFormat) localizedDateFormats.get(fk);
0824:                    if (format == null) {
0825:                        // Add format to global format cache. Note this is
0826:                        // globally done once per locale per pattern.
0827:                        StringTokenizer tok = new StringTokenizer(pattern, "_");
0828:                        int style = tok.hasMoreTokens() ? parseDateStyleToken(tok
0829:                                .nextToken())
0830:                                : DateFormat.DEFAULT;
0831:                        if (style != -1) {
0832:                            switch (dateType) {
0833:                            case TemplateDateModel.UNKNOWN: {
0834:                                throw new TemplateModelException(
0835:                                        "Can't convert the date to string using a "
0836:                                                + "built-in format, because it is not known which "
0837:                                                + "parts of the date variable are in use. Use "
0838:                                                + "?date, ?time or ?datetime built-in, or "
0839:                                                + "?string.<format> or ?string(<format>) built-in "
0840:                                                + "with explicit formatting pattern with this date.");
0841:                            }
0842:                            case TemplateDateModel.TIME: {
0843:                                format = DateFormat.getTimeInstance(style,
0844:                                        locale);
0845:                                break;
0846:                            }
0847:                            case TemplateDateModel.DATE: {
0848:                                format = DateFormat.getDateInstance(style,
0849:                                        locale);
0850:                                break;
0851:                            }
0852:                            case TemplateDateModel.DATETIME: {
0853:                                int timestyle = tok.hasMoreTokens() ? parseDateStyleToken(tok
0854:                                        .nextToken())
0855:                                        : style;
0856:                                if (timestyle != -1) {
0857:                                    format = DateFormat.getDateTimeInstance(
0858:                                            style, timestyle, locale);
0859:                                }
0860:                                break;
0861:                            }
0862:                            }
0863:                        }
0864:                        if (format == null) {
0865:                            try {
0866:                                format = new SimpleDateFormat(pattern, locale);
0867:                            } catch (IllegalArgumentException e) {
0868:                                throw new TemplateModelException("Can't parse "
0869:                                        + pattern + " to a date format.", e);
0870:                            }
0871:                        }
0872:                        format.setTimeZone(timeZone);
0873:                        localizedDateFormats.put(fk, format);
0874:                    }
0875:                }
0876:
0877:                // Clone it and store the clone in the local cache
0878:                format = (DateFormat) format.clone();
0879:                typedDateFormat.put(pattern, format);
0880:                return format;
0881:            }
0882:
0883:            int parseDateStyleToken(String token) {
0884:                if ("short".equals(token)) {
0885:                    return DateFormat.SHORT;
0886:                }
0887:                if ("medium".equals(token)) {
0888:                    return DateFormat.MEDIUM;
0889:                }
0890:                if ("long".equals(token)) {
0891:                    return DateFormat.LONG;
0892:                }
0893:                if ("full".equals(token)) {
0894:                    return DateFormat.FULL;
0895:                }
0896:                return -1;
0897:            }
0898:
0899:            TemplateTransformModel getTransform(Expression exp)
0900:                    throws TemplateException {
0901:                TemplateTransformModel ttm = null;
0902:                TemplateModel tm = exp.getAsTemplateModel(this );
0903:                if (tm instanceof  TemplateTransformModel) {
0904:                    ttm = (TemplateTransformModel) tm;
0905:                } else if (exp instanceof  Identifier) {
0906:                    tm = getConfiguration().getSharedVariable(exp.toString());
0907:                    if (tm instanceof  TemplateTransformModel) {
0908:                        ttm = (TemplateTransformModel) tm;
0909:                    }
0910:                }
0911:                return ttm;
0912:            }
0913:
0914:            /**
0915:             * Returns the loop or macro local variable corresponding to this
0916:             * variable name. Possibly null.
0917:             * (Note that the misnomer is kept for backward compatibility: loop variables
0918:             * are not local variables according to our terminology.)
0919:             */
0920:            public TemplateModel getLocalVariable(String name)
0921:                    throws TemplateModelException {
0922:                if (localContextStack != null) {
0923:                    for (int i = localContextStack.size() - 1; i >= 0; i--) {
0924:                        LocalContext lc = (LocalContext) localContextStack
0925:                                .get(i);
0926:                        TemplateModel tm = lc.getLocalVariable(name);
0927:                        if (tm != null) {
0928:                            return tm;
0929:                        }
0930:                    }
0931:                }
0932:                return currentMacroContext == null ? null : currentMacroContext
0933:                        .getLocalVariable(name);
0934:            }
0935:
0936:            /**
0937:             * Returns the variable that is visible in this context.
0938:             * This is the correspondent to an FTL top-level variable reading expression.
0939:             * That is, it tries to find the the variable in this order:
0940:             * <ol>
0941:             *   <li>An loop variable (if we're in a loop or user defined directive body) such as foo_has_next
0942:             *   <li>A local variable (if we're in a macro)
0943:             *   <li>A variable defined in the current namespace (say, via &lt;#assign ...&gt;)
0944:             *   <li>A variable defined globally (say, via &lt;#global ....&gt;)
0945:             *   <li>Variable in the data model:
0946:             *     <ol>
0947:             *       <li>A variable in the root hash that was exposed to this
0948:                         rendering environment in the Template.process(...) call
0949:             *       <li>A shared variable set in the configuration via a call to Configuration.setSharedVariable(...)
0950:             *     </ol>
0951:             *   </li>
0952:             * </ol>
0953:             */
0954:            public TemplateModel getVariable(String name)
0955:                    throws TemplateModelException {
0956:                TemplateModel result = getLocalVariable(name);
0957:                if (result == null) {
0958:                    result = currentNamespace.get(name);
0959:                }
0960:                if (result == null) {
0961:                    result = getGlobalVariable(name);
0962:                }
0963:                return result;
0964:            }
0965:
0966:            /**
0967:             * Returns the globally visible variable of the given name (or null).
0968:             * This is correspondent to FTL <code>.globals.<i>name</i></code>.
0969:             * This will first look at variables that were assigned globally via:
0970:             * &lt;#global ...&gt; and then at the data model exposed to the template.
0971:             */
0972:            public TemplateModel getGlobalVariable(String name)
0973:                    throws TemplateModelException {
0974:                TemplateModel result = globalNamespace.get(name);
0975:                if (result == null) {
0976:                    result = rootDataModel.get(name);
0977:                }
0978:                if (result == null) {
0979:                    result = getConfiguration().getSharedVariable(name);
0980:                }
0981:                return result;
0982:            }
0983:
0984:            /**
0985:             * Sets a variable that is visible globally.
0986:             * This is correspondent to FTL <code><#global <i>name</i>=<i>model</i>></code>.
0987:             * This can be considered a convenient shorthand for:
0988:             * getGlobalNamespace().put(name, model)
0989:             */
0990:            public void setGlobalVariable(String name, TemplateModel model) {
0991:                globalNamespace.put(name, model);
0992:            }
0993:
0994:            /**
0995:             * Sets a variable in the current namespace.
0996:             * This is correspondent to FTL <code><#assign <i>name</i>=<i>model</i>></code>.
0997:             * This can be considered a convenient shorthand for:
0998:             * getCurrentNamespace().put(name, model)
0999:             */
1000:            public void setVariable(String name, TemplateModel model) {
1001:                currentNamespace.put(name, model);
1002:            }
1003:
1004:            /**
1005:             * Sets a local variable (one effective only during a macro invocation).
1006:             * This is correspondent to FTL <code><#local <i>name</i>=<i>model</i>></code>.
1007:             * @param name the identifier of the variable
1008:             * @param model the value of the variable.
1009:             * @throws IllegalStateException if the environment is not executing a
1010:             * macro body.
1011:             */
1012:            public void setLocalVariable(String name, TemplateModel model) {
1013:                if (currentMacroContext == null) {
1014:                    throw new IllegalStateException("Not executing macro body");
1015:                }
1016:                currentMacroContext.setLocalVar(name, model);
1017:            }
1018:
1019:            /**
1020:             * Returns a set of variable names that are known at the time of call. This
1021:             * includes names of all shared variables in the {@link Configuration},
1022:             * names of all global variables that were assigned during the template processing,
1023:             * names of all variables in the current name-space, names of all local variables
1024:             * and loop variables. If the passed root data model implements the
1025:             * {@link TemplateHashModelEx} interface, then all names it retrieves through a call to
1026:             * {@link TemplateHashModelEx#keys()} method are returned as well.
1027:             * The method returns a new Set object on each call that is completely
1028:             * disconnected from the Environment. That is, modifying the set will have
1029:             * no effect on the Environment object.
1030:             */
1031:            public Set getKnownVariableNames() throws TemplateModelException {
1032:                // shared vars.
1033:                Set set = getConfiguration().getSharedVariableNames();
1034:
1035:                // root hash
1036:                if (rootDataModel instanceof  TemplateHashModelEx) {
1037:                    TemplateModelIterator rootNames = ((TemplateHashModelEx) rootDataModel)
1038:                            .keys().iterator();
1039:                    while (rootNames.hasNext()) {
1040:                        set.add(((TemplateScalarModel) rootNames.next())
1041:                                .getAsString());
1042:                    }
1043:                }
1044:
1045:                // globals
1046:                for (TemplateModelIterator tmi = globalNamespace.keys()
1047:                        .iterator(); tmi.hasNext();) {
1048:                    set.add(((TemplateScalarModel) tmi.next()).getAsString());
1049:                }
1050:
1051:                // current name-space
1052:                for (TemplateModelIterator tmi = currentNamespace.keys()
1053:                        .iterator(); tmi.hasNext();) {
1054:                    set.add(((TemplateScalarModel) tmi.next()).getAsString());
1055:                }
1056:
1057:                // locals and loop vars
1058:                if (currentMacroContext != null) {
1059:                    set.addAll(currentMacroContext.getLocalVariableNames());
1060:                }
1061:                if (localContextStack != null) {
1062:                    for (int i = localContextStack.size() - 1; i >= 0; i--) {
1063:                        LocalContext lc = (LocalContext) localContextStack
1064:                                .get(i);
1065:                        set.addAll(lc.getLocalVariableNames());
1066:                    }
1067:                }
1068:                return set;
1069:            }
1070:
1071:            /**
1072:             * Outputs the instruction stack. Useful for debugging.
1073:             * {@link TemplateException}s incorporate this information in their stack
1074:             * traces.
1075:             */
1076:            public void outputInstructionStack(PrintWriter pw) {
1077:                pw.println("----------");
1078:                ListIterator iter = elementStack.listIterator(elementStack
1079:                        .size());
1080:                if (iter.hasPrevious()) {
1081:                    pw.print("==> ");
1082:                    TemplateElement prev = (TemplateElement) iter.previous();
1083:                    pw.print(prev.getDescription());
1084:                    pw.print(" [");
1085:                    pw.print(prev.getStartLocation());
1086:                    pw.println("]");
1087:                }
1088:                while (iter.hasPrevious()) {
1089:                    TemplateElement prev = (TemplateElement) iter.previous();
1090:                    if (prev instanceof  UnifiedCall || prev instanceof  Include) {
1091:                        String location = prev.getDescription() + " ["
1092:                                + prev.getStartLocation() + "]";
1093:                        if (location != null && location.length() > 0) {
1094:                            pw.print(" in ");
1095:                            pw.println(location);
1096:                        }
1097:                    }
1098:                }
1099:                pw.println("----------");
1100:                pw.flush();
1101:            }
1102:
1103:            private void pushLocalContext(LocalContext localContext) {
1104:                if (localContextStack == null) {
1105:                    localContextStack = new ArrayList();
1106:                }
1107:                localContextStack.add(localContext);
1108:            }
1109:
1110:            private void popLocalContext() {
1111:                localContextStack.remove(localContextStack.size() - 1);
1112:            }
1113:
1114:            ArrayList getLocalContextStack() {
1115:                return localContextStack;
1116:            }
1117:
1118:            /**
1119:             * Returns the name-space for the name if exists, or null.
1120:             * @param name the template path that you have used with the <code>import</code> directive
1121:             *     or {@link #importLib(String, String)} call, in normalized form. That is, the path must be an absolute
1122:             *     path, and it must not contain "/../" or "/./". The leading "/" is optional.
1123:             */
1124:            public Namespace getNamespace(String name) {
1125:                if (name.startsWith("/"))
1126:                    name = name.substring(1);
1127:                if (loadedLibs != null) {
1128:                    return (Namespace) loadedLibs.get(name);
1129:                } else {
1130:                    return null;
1131:                }
1132:            }
1133:
1134:            /**
1135:             * Returns the main name-space.
1136:             * This is correspondent of FTL <code>.main</code> hash.
1137:             */
1138:            public Namespace getMainNamespace() {
1139:                return mainNamespace;
1140:            }
1141:
1142:            /**
1143:             * Returns the main name-space.
1144:             * This is correspondent of FTL <code>.namespace</code> hash.
1145:             */
1146:            public Namespace getCurrentNamespace() {
1147:                return currentNamespace;
1148:            }
1149:
1150:            /**
1151:             * Returns a fictitious name-space that contains the globally visible variables
1152:             * that were created in the template, but not the variables of the data-model.
1153:             * There is no such thing in FTL; this strange method was added because of the
1154:             * JSP taglib support, since this imaginary name-space contains the page-scope
1155:             * attributes.
1156:             */
1157:            public Namespace getGlobalNamespace() {
1158:                return globalNamespace;
1159:            }
1160:
1161:            /**
1162:             * Returns the data model hash.
1163:             * This is correspondent of FTL <code>.datamodel</code> hash.
1164:             * That is, it contains both the variables of the root hash passed to the
1165:             * <code>Template.process(...)</code>, and the shared variables in the
1166:             * <code>Configuration</code>.
1167:             */
1168:            public TemplateHashModel getDataModel() {
1169:                return new TemplateHashModel() {
1170:                    public boolean isEmpty() {
1171:                        return false;
1172:                    }
1173:
1174:                    public TemplateModel get(String key)
1175:                            throws TemplateModelException {
1176:                        TemplateModel result = rootDataModel.get(key);
1177:                        if (result == null) {
1178:                            result = getConfiguration().getSharedVariable(key);
1179:                        }
1180:                        return result;
1181:                    }
1182:                };
1183:            }
1184:
1185:            /**
1186:             * Returns the read-only hash of globally visible variables.
1187:             * This is the correspondent of FTL <code>.globals</code> hash.
1188:             * That is, you see the variables created with
1189:             * <code>&lt;#global ...></code>, and the variables of the data-model.
1190:             * To create new global variables, use {@link #setGlobalVariable setGlobalVariable}.
1191:             */
1192:            public TemplateHashModel getGlobalVariables() {
1193:                return new TemplateHashModel() {
1194:                    public boolean isEmpty() {
1195:                        return false;
1196:                    }
1197:
1198:                    public TemplateModel get(String key)
1199:                            throws TemplateModelException {
1200:                        TemplateModel result = globalNamespace.get(key);
1201:                        if (result == null) {
1202:                            result = rootDataModel.get(key);
1203:                        }
1204:                        if (result == null) {
1205:                            result = getConfiguration().getSharedVariable(key);
1206:                        }
1207:                        return result;
1208:                    }
1209:                };
1210:            }
1211:
1212:            private void pushElement(TemplateElement element) {
1213:                elementStack.add(element);
1214:            }
1215:
1216:            private void popElement() {
1217:                elementStack.remove(elementStack.size() - 1);
1218:            }
1219:
1220:            public TemplateNodeModel getCurrentVisitorNode() {
1221:                return currentVisitorNode;
1222:            }
1223:
1224:            /**
1225:             * sets TemplateNodeModel as the current visitor node. <tt>.current_node</tt>
1226:             */
1227:            public void setCurrentVisitorNode(TemplateNodeModel node) {
1228:                currentVisitorNode = node;
1229:            }
1230:
1231:            TemplateModel getNodeProcessor(TemplateNodeModel node)
1232:                    throws TemplateException {
1233:                String nodeName = node.getNodeName();
1234:                if (nodeName == null) {
1235:                    throw new TemplateException("Node name is null.", this );
1236:                }
1237:                TemplateModel result = getNodeProcessor(nodeName, node
1238:                        .getNodeNamespace(), 0);
1239:
1240:                if (result == null) {
1241:                    String type = node.getNodeType();
1242:
1243:                    /* DD: Original version: */
1244:                    if (type == null) {
1245:                        type = "default";
1246:                    }
1247:                    result = getNodeProcessor("@" + type, null, 0);
1248:
1249:                    /* DD: Jonathan's non-BC version and IMHO otherwise wrong version:
1250:                    if (type != null) {
1251:                        result = getNodeProcessor("@" + type, null, 0);
1252:                    }
1253:                    if (result == null) {
1254:                        result = getNodeProcessor("@default", null, 0);
1255:                    }
1256:                     */
1257:                }
1258:                return result;
1259:            }
1260:
1261:            private TemplateModel getNodeProcessor(final String nodeName,
1262:                    final String nsURI, int startIndex)
1263:                    throws TemplateException {
1264:                TemplateModel result = null;
1265:                int i;
1266:                for (i = startIndex; i < nodeNamespaces.size(); i++) {
1267:                    Namespace ns = null;
1268:                    try {
1269:                        ns = (Namespace) nodeNamespaces.get(i);
1270:                    } catch (ClassCastException cce) {
1271:                        throw new InvalidReferenceException(
1272:                                "A using clause should contain a sequence of namespaces or strings that indicate the location of importable macro libraries.",
1273:                                this );
1274:                    }
1275:                    result = getNodeProcessor(ns, nodeName, nsURI);
1276:                    if (result != null)
1277:                        break;
1278:                }
1279:                if (result != null) {
1280:                    this .nodeNamespaceIndex = i + 1;
1281:                    this .currentNodeName = nodeName;
1282:                    this .currentNodeNS = nsURI;
1283:                }
1284:                return result;
1285:            }
1286:
1287:            private TemplateModel getNodeProcessor(Namespace ns,
1288:                    String localName, String nsURI) throws TemplateException {
1289:                TemplateModel result = null;
1290:                if (nsURI == null) {
1291:                    result = ns.get(localName);
1292:                    if (!(result instanceof  Macro)
1293:                            && !(result instanceof  TemplateTransformModel)) {
1294:                        result = null;
1295:                    }
1296:                } else {
1297:                    Template template = ns.getTemplate();
1298:                    String prefix = template.getPrefixForNamespace(nsURI);
1299:                    if (prefix == null) {
1300:                        // The other template cannot handle this node
1301:                        // since it has no prefix registered for the namespace
1302:                        return null;
1303:                    }
1304:                    if (prefix.length() > 0) {
1305:                        result = ns.get(prefix + ":" + localName);
1306:                        if (!(result instanceof  Macro)
1307:                                && !(result instanceof  TemplateTransformModel)) {
1308:                            result = null;
1309:                        }
1310:                    } else {
1311:                        if (nsURI.length() == 0) {
1312:                            result = ns.get(Template.NO_NS_PREFIX + ":"
1313:                                    + localName);
1314:                            if (!(result instanceof  Macro)
1315:                                    && !(result instanceof  TemplateTransformModel)) {
1316:                                result = null;
1317:                            }
1318:                        }
1319:                        if (nsURI.equals(template.getDefaultNS())) {
1320:                            result = ns.get(Template.DEFAULT_NAMESPACE_PREFIX
1321:                                    + ":" + localName);
1322:                            if (!(result instanceof  Macro)
1323:                                    && !(result instanceof  TemplateTransformModel)) {
1324:                                result = null;
1325:                            }
1326:                        }
1327:                        if (result == null) {
1328:                            result = ns.get(localName);
1329:                            if (!(result instanceof  Macro)
1330:                                    && !(result instanceof  TemplateTransformModel)) {
1331:                                result = null;
1332:                            }
1333:                        }
1334:                    }
1335:                }
1336:                return result;
1337:            }
1338:
1339:            /**
1340:             * Emulates <code>include</code> directive, except that <code>name</code> must be tempate
1341:             * root relative.
1342:             *
1343:             * <p>It's the same as <code>include(getTemplateForInclusion(name, encoding, parse))</code>.
1344:             * But, you may want to separately call these two methods, so you can determine the source of
1345:             * exceptions more precisely, and thus achieve more intelligent error handling.
1346:             *
1347:             * @see #getTemplateForInclusion(String name, String encoding, boolean parse)
1348:             * @see #include(Template includedTemplate)
1349:             */
1350:            public void include(String name, String encoding, boolean parse)
1351:                    throws IOException, TemplateException {
1352:                include(getTemplateForInclusion(name, encoding, parse));
1353:            }
1354:
1355:            /**
1356:             * Gets a template for inclusion; used with {@link #include(Template includedTemplate)}.
1357:             * The advantage over simply using <code>config.getTemplate(...)</code> is that it chooses
1358:             * the default encoding as the <code>include</code> directive does.
1359:             *
1360:             * @param name the name of the template, relatively to the template root directory
1361:             * (not the to the directory of the currently executing template file!).
1362:             * (Note that you can use {@link freemarker.cache.TemplateCache#getFullTemplatePath}
1363:             * to convert paths to template root relative paths.)
1364:             * @param encoding the encoding of the obtained template. If null,
1365:             * the encoding of the Template that is currently being processed in this
1366:             * Environment is used.
1367:             * @param parse whether to process a parsed template or just include the
1368:             * unparsed template source.
1369:             */
1370:            public Template getTemplateForInclusion(String name,
1371:                    String encoding, boolean parse) throws IOException {
1372:                if (encoding == null) {
1373:                    encoding = getTemplate().getEncoding();
1374:                }
1375:                if (encoding == null) {
1376:                    encoding = getConfiguration().getEncoding(this .getLocale());
1377:                }
1378:                return getConfiguration().getTemplate(name, getLocale(),
1379:                        encoding, parse);
1380:            }
1381:
1382:            /**
1383:             * Processes a Template in the context of this <code>Environment</code>, including its
1384:             * output in the <code>Environment</code>'s Writer.
1385:             *
1386:             * @param includedTemplate the template to process. Note that it does <em>not</em> need
1387:             * to be a template returned by
1388:             * {@link #getTemplateForInclusion(String name, String encoding, boolean parse)}.
1389:             */
1390:            public void include(Template includedTemplate)
1391:                    throws TemplateException, IOException {
1392:                Template prevTemplate = getTemplate();
1393:                setParent(includedTemplate);
1394:                importMacros(includedTemplate);
1395:                try {
1396:                    visit(includedTemplate.getRootTreeNode());
1397:                } finally {
1398:                    setParent(prevTemplate);
1399:                }
1400:            }
1401:
1402:            /**
1403:             * Emulates <code>import</code> directive, except that <code>name</code> must be tempate
1404:             * root relative.
1405:             *
1406:             * <p>It's the same as <code>importLib(getTemplateForImporting(name), namespace)</code>.
1407:             * But, you may want to separately call these two methods, so you can determine the source of
1408:             * exceptions more precisely, and thus achieve more intelligent error handling.
1409:             *
1410:             * @see #getTemplateForImporting(String name)
1411:             * @see #importLib(Template includedTemplate, String namespace)
1412:             */
1413:            public Namespace importLib(String name, String namespace)
1414:                    throws IOException, TemplateException {
1415:                return importLib(getTemplateForImporting(name), namespace);
1416:            }
1417:
1418:            /**
1419:             * Gets a template for importing; used with
1420:             * {@link #importLib(Template importedTemplate, String namespace)}. The advantage
1421:             * over simply using <code>config.getTemplate(...)</code> is that it chooses the encoding
1422:             * as the <code>import</code> directive does.
1423:             *
1424:             * @param name the name of the template, relatively to the template root directory
1425:             * (not the to the directory of the currently executing template file!).
1426:             * (Note that you can use {@link freemarker.cache.TemplateCache#getFullTemplatePath}
1427:             * to convert paths to template root relative paths.)
1428:             */
1429:            public Template getTemplateForImporting(String name)
1430:                    throws IOException {
1431:                return getTemplateForInclusion(name, null, true);
1432:            }
1433:
1434:            /**
1435:             * Emulates <code>import</code> directive.
1436:             *
1437:             * @param loadedTemplate the template to import. Note that it does <em>not</em> need
1438:             * to be a template returned by {@link #getTemplateForImporting(String name)}.
1439:             */
1440:            public Namespace importLib(Template loadedTemplate, String namespace)
1441:                    throws IOException, TemplateException {
1442:                if (loadedLibs == null) {
1443:                    loadedLibs = new HashMap();
1444:                }
1445:                String templateName = loadedTemplate.getName();
1446:                Namespace existingNamespace = (Namespace) loadedLibs
1447:                        .get(templateName);
1448:                if (existingNamespace != null) {
1449:                    if (namespace != null) {
1450:                        setVariable(namespace, existingNamespace);
1451:                    }
1452:                } else {
1453:                    Namespace newNamespace = new Namespace(loadedTemplate);
1454:                    if (namespace != null) {
1455:                        currentNamespace.put(namespace, newNamespace);
1456:                        if (currentNamespace == mainNamespace) {
1457:                            globalNamespace.put(namespace, newNamespace);
1458:                        }
1459:                    }
1460:                    Namespace prevNamespace = this .currentNamespace;
1461:                    this .currentNamespace = newNamespace;
1462:                    loadedLibs.put(templateName, currentNamespace);
1463:                    Writer prevOut = out;
1464:                    this .out = NULL_WRITER;
1465:                    try {
1466:                        include(loadedTemplate);
1467:                    } finally {
1468:                        this .out = prevOut;
1469:                        this .currentNamespace = prevNamespace;
1470:                    }
1471:                }
1472:                return (Namespace) loadedLibs.get(templateName);
1473:            }
1474:
1475:            String renderElementToString(TemplateElement te)
1476:                    throws IOException, TemplateException {
1477:                Writer prevOut = out;
1478:                try {
1479:                    StringWriter sw = new StringWriter();
1480:                    this .out = sw;
1481:                    visit(te);
1482:                    return sw.toString();
1483:                } finally {
1484:                    this .out = prevOut;
1485:                }
1486:            }
1487:
1488:            void importMacros(Template template) {
1489:                for (Iterator it = template.getMacros().values().iterator(); it
1490:                        .hasNext();) {
1491:                    visitMacroDef((Macro) it.next());
1492:                }
1493:            }
1494:
1495:            /**
1496:             * @return the namespace URI registered for this prefix, or null.
1497:             * This is based on the mappings registered in the current namespace.
1498:             */
1499:            public String getNamespaceForPrefix(String prefix) {
1500:                return currentNamespace.getTemplate().getNamespaceForPrefix(
1501:                        prefix);
1502:            }
1503:
1504:            public String getPrefixForNamespace(String nsURI) {
1505:                return currentNamespace.getTemplate().getPrefixForNamespace(
1506:                        nsURI);
1507:            }
1508:
1509:            /**
1510:             * @return the default node namespace for the current FTL namespace
1511:             */
1512:            public String getDefaultNS() {
1513:                return currentNamespace.getTemplate().getDefaultNS();
1514:            }
1515:
1516:            /**
1517:             * A hook that Jython uses.
1518:             */
1519:            public Object __getitem__(String key) throws TemplateModelException {
1520:                return BeansWrapper.getDefaultInstance().unwrap(
1521:                        getVariable(key));
1522:            }
1523:
1524:            /**
1525:             * A hook that Jython uses.
1526:             */
1527:            public void __setitem__(String key, Object o)
1528:                    throws TemplateException {
1529:                setGlobalVariable(key, getObjectWrapper().wrap(o));
1530:            }
1531:
1532:            private static final class NumberFormatKey {
1533:                private final String pattern;
1534:                private final Locale locale;
1535:
1536:                NumberFormatKey(String pattern, Locale locale) {
1537:                    this .pattern = pattern;
1538:                    this .locale = locale;
1539:                }
1540:
1541:                public boolean equals(Object o) {
1542:                    if (o instanceof  NumberFormatKey) {
1543:                        NumberFormatKey fk = (NumberFormatKey) o;
1544:                        return fk.pattern.equals(pattern)
1545:                                && fk.locale.equals(locale);
1546:                    }
1547:                    return false;
1548:                }
1549:
1550:                public int hashCode() {
1551:                    return pattern.hashCode() ^ locale.hashCode();
1552:                }
1553:            }
1554:
1555:            private static final class DateFormatKey {
1556:                private final int dateType;
1557:                private final String pattern;
1558:                private final Locale locale;
1559:                private final TimeZone timeZone;
1560:
1561:                DateFormatKey(int dateType, String pattern, Locale locale,
1562:                        TimeZone timeZone) {
1563:                    this .dateType = dateType;
1564:                    this .pattern = pattern;
1565:                    this .locale = locale;
1566:                    this .timeZone = timeZone;
1567:                }
1568:
1569:                public boolean equals(Object o) {
1570:                    if (o instanceof  DateFormatKey) {
1571:                        DateFormatKey fk = (DateFormatKey) o;
1572:                        return dateType == fk.dateType
1573:                                && fk.pattern.equals(pattern)
1574:                                && fk.locale.equals(locale)
1575:                                && fk.timeZone.equals(timeZone);
1576:                    }
1577:                    return false;
1578:                }
1579:
1580:                public int hashCode() {
1581:                    return dateType ^ pattern.hashCode() ^ locale.hashCode()
1582:                            ^ timeZone.hashCode();
1583:                }
1584:            }
1585:
1586:            public class Namespace extends SimpleHash {
1587:
1588:                private Template template;
1589:
1590:                Namespace() {
1591:                    this .template = Environment.this .getTemplate();
1592:                }
1593:
1594:                Namespace(Template template) {
1595:                    this .template = template;
1596:                }
1597:
1598:                /**
1599:                 * @return the Template object with which this Namespace is associated.
1600:                 */
1601:                public Template getTemplate() {
1602:                    return template == null ? Environment.this .getTemplate()
1603:                            : template;
1604:                }
1605:            }
1606:
1607:            static final Writer NULL_WRITER = new Writer() {
1608:                public void write(char cbuf[], int off, int len) {
1609:                }
1610:
1611:                public void flush() {
1612:                }
1613:
1614:                public void close() {
1615:                }
1616:            };
1617:
1618:            private static final Writer EMPTY_BODY_WRITER = new Writer() {
1619:
1620:                public void write(char[] cbuf, int off, int len)
1621:                        throws IOException {
1622:                    if (len > 0) {
1623:                        throw new IOException(
1624:                                "This transform does not allow nested content.");
1625:                    }
1626:                }
1627:
1628:                public void flush() {
1629:                }
1630:
1631:                public void close() {
1632:                }
1633:            };
1634:
1635:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.