/* * Copyright (c) 2001-2007, Inversoft Inc., All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific * language governing permissions and limitations under the License. */ package org.primeframework.mvc.freemarker; import javax.servlet.GenericServlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.primeframework.mvc.PrimeException; import org.primeframework.mvc.action.ActionInvocation; import org.primeframework.mvc.action.ActionInvocationStore; import org.primeframework.mvc.action.result.ControlsHashModel; import org.primeframework.mvc.action.result.ModelsHashModel; import org.primeframework.mvc.control.guice.ControlFactory; import org.primeframework.mvc.freemarker.guice.TemplateModelFactory; import org.primeframework.mvc.message.FieldMessage; import org.primeframework.mvc.message.Message; import org.primeframework.mvc.message.MessageStore; import org.primeframework.mvc.message.MessageType; import org.primeframework.mvc.parameter.el.ExpressionEvaluator; import org.primeframework.mvc.parameter.el.MissingPropertyExpressionException; import com.google.inject.Inject; import freemarker.ext.beans.BeansWrapper; import freemarker.ext.beans.CollectionModel; import freemarker.ext.jsp.TaglibFactory; import freemarker.ext.servlet.HttpRequestHashModel; import freemarker.ext.servlet.HttpSessionHashModel; import freemarker.ext.servlet.ServletContextHashModel; import freemarker.template.Configuration; import freemarker.template.TemplateCollectionModel; import freemarker.template.TemplateHashModelEx; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; /** * This class is a FreeMarker model that provides access in the templates to the request, session and context attributes * as well as values from the action and the Control directives via the {@link ControlsHashModel} class. * * @author Brian Pontarelli */ public class FreeMarkerMap implements TemplateHashModelEx { public static final String ALL_MESSAGES = "allMessages"; public static final String APPLICATION = "application"; public static final String APPLICATION_MODEL = "Application"; public static final String ERROR_MESSAGES = "errorMessages"; public static final String FIELD_MESSAGES = "fieldMessages"; public static final String INFO_MESSAGES = "infoMessages"; public static final String JSP_TAGLIBS = "JspTaglibs"; public static final String REQUEST = "request"; public static final String REQUEST_MODEL = "Request"; public static final String SESSION = "session"; public static final String SESSION_MODEL = "Session"; public static final String WARNING_MESSAGES = "warningMessages"; protected final ActionInvocationStore actionInvocationStore; protected final Configuration configuration; protected final ServletContext context; protected final ExpressionEvaluator expressionEvaluator; protected final Map<String, Object> objects = new HashMap<>(); protected final HttpServletRequest request; protected final TaglibFactory taglibFactory; @Inject public FreeMarkerMap(ServletContext context, HttpServletRequest request, HttpServletResponse response, ExpressionEvaluator expressionEvaluator, ActionInvocationStore actionInvocationStore, MessageStore messageStore, ControlFactory controlFactory, TemplateModelFactory templateModelFactory, Configuration configuration) { this.context = context; this.taglibFactory = new TaglibFactory(context); this.request = request; this.expressionEvaluator = expressionEvaluator; this.actionInvocationStore = actionInvocationStore; this.configuration = configuration; objects.put(REQUEST_MODEL, new HttpRequestHashModel(request, response, configuration.getObjectWrapper())); objects.put(REQUEST, request); HttpSession session = request.getSession(false); if (session != null) { objects.put(SESSION_MODEL, new HttpSessionHashModel(session, configuration.getObjectWrapper())); objects.put(SESSION, session); } objects.put(APPLICATION_MODEL, new ServletContextHashModel(new GenericServlet() { @Override public ServletConfig getServletConfig() { return this; } @Override public ServletContext getServletContext() { return FreeMarkerMap.this.context; } public void service(ServletRequest servletRequest, ServletResponse servletResponse) { } }, configuration.getObjectWrapper())); objects.put(APPLICATION, context); objects.put(JSP_TAGLIBS, new TaglibFactory(context)); List<Message> allMessages = messageStore.get(); Map<String, List<FieldMessage>> fieldMessages = messageStore.getFieldMessages(); List<Message> errorMessages = new ArrayList<>(); List<Message> infoMessages = new ArrayList<>(); List<Message> warningMessages = new ArrayList<>(); for (Message message : allMessages) { if (!(message instanceof FieldMessage)) { if (message.getType() == MessageType.ERROR) { errorMessages.add(message); } else if (message.getType() == MessageType.INFO) { infoMessages.add(message); } else if (message.getType() == MessageType.WARNING) { warningMessages.add(message); } } } objects.put(ALL_MESSAGES, allMessages); objects.put(FIELD_MESSAGES, fieldMessages); objects.put(ERROR_MESSAGES, errorMessages); objects.put(INFO_MESSAGES, infoMessages); objects.put(WARNING_MESSAGES, warningMessages); // Add the models for (String prefix : templateModelFactory.prefixes()) { objects.put(prefix, new ModelsHashModel(prefix, templateModelFactory, configuration.getObjectWrapper())); } // Add the controls for (String prefix : controlFactory.prefixes()) { objects.put(prefix, new ControlsHashModel(prefix, controlFactory, configuration.getObjectWrapper())); } // TODO add debugging for figuring out what scope an object is in } @Override public TemplateModel get(String key) { // First check the action Object value = null; Deque<ActionInvocation> actionInvocations = actionInvocationStore.getDeque(); if (actionInvocations != null) { for (ActionInvocation actionInvocation : actionInvocations) { if (actionInvocation.action != null) { try { value = expressionEvaluator.getValue(key, actionInvocation.action); if (value != null) { break; } } catch (MissingPropertyExpressionException e) { // Smother because the value is probably somewhere else } } } } // Next, check the objects if (value == null && objects.containsKey(key)) { value = objects.get(key); } // Next, check the request if (value == null) { value = request.getAttribute(key); } // Next, check the session if (value == null) { HttpSession session = request.getSession(false); if (session != null) { value = session.getAttribute(key); } } // Next, check the context if (value == null) { value = context.getAttribute(key); } try { return configuration.getObjectWrapper().wrap(value); } catch (TemplateModelException e) { throw new PrimeException(e); } } @Override public boolean isEmpty() { return size() > 0; } @Override public TemplateCollectionModel keys() { Set<String> keys = new HashSet<>(objects.keySet()); keys.addAll(enumToSet(request.getAttributeNames())); HttpSession session = request.getSession(false); if (session != null) { keys.addAll(enumToSet(session.getAttributeNames())); } keys.addAll(enumToSet(context.getAttributeNames())); Deque<ActionInvocation> actionInvocations = actionInvocationStore.getDeque(); if (actionInvocations != null) { for (ActionInvocation actionInvocation : actionInvocations) { if (actionInvocation.action != null) { keys.addAll(actionInvocation.configuration.memberNames); } } } keys.add(JSP_TAGLIBS); return new CollectionModel(keys, (BeansWrapper) configuration.getObjectWrapper()); } @Override public int size() { int size = objects.size() + count(request.getAttributeNames()); HttpSession session = request.getSession(false); if (session != null) { size += count(session.getAttributeNames()); } size += count(context.getAttributeNames()); Deque<ActionInvocation> actionInvocations = actionInvocationStore.getDeque(); if (actionInvocations != null) { for (ActionInvocation actionInvocation : actionInvocations) { if (actionInvocation.action != null) { size += actionInvocation.configuration.memberNames.size(); } } } return size; } @Override public TemplateCollectionModel values() { Collection<Object> values = new ArrayList<>(objects.values()); Deque<ActionInvocation> actionInvocations = actionInvocationStore.getDeque(); if (actionInvocations != null) { for (ActionInvocation actionInvocation : actionInvocations) { if (actionInvocation.action != null) { values.addAll(expressionEvaluator.getAllMemberValues(actionInvocation.action, actionInvocation.configuration.memberNames)); } } } Enumeration<String> en = request.getAttributeNames(); while (en.hasMoreElements()) { String name = en.nextElement(); values.add(request.getAttribute(name)); } HttpSession session = request.getSession(false); if (session != null) { en = session.getAttributeNames(); while (en.hasMoreElements()) { String name = en.nextElement(); values.add(session.getAttribute(name)); } } en = context.getAttributeNames(); while (en.hasMoreElements()) { String name = en.nextElement(); values.add(context.getAttribute(name)); } values.add(taglibFactory); return new CollectionModel(values, (BeansWrapper) configuration.getObjectWrapper()); } private int count(Enumeration<String> enumeration) { int count = 0; while (enumeration.hasMoreElements()) { count++; enumeration.nextElement(); } return count; } private Set<String> enumToSet(Enumeration<String> enumeration) { Set<String> set = new HashSet<>(); while (enumeration.hasMoreElements()) { set.add(enumeration.nextElement()); } return set; } }